我在步进器的每个步骤中都有多个表单,表单在外部文件中,因为在我的应用程序中,每个步骤都可以包含不同的表单。我希望当用户单击“继续”时,表单将被验证,在错误情况下,用户将被警告。我试图使用继承的小部件,但它给了我一个“null on getter”。下面的代码:包含步骤的屏幕
import 'package:flutter/material.dart';
import 'package:pberrycoffeemaker/widgets/function_appbar.dart';
import 'package:pberrycoffeemaker/widgets/inputs_0.dart';
import 'package:pberrycoffeemaker/widgets/stepper_banner.dart';
class FunctionScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
int indexStep = 0;
double bottomHeight = (MediaQuery.of(context).size.height * 40) / 100;
double cardHeight = (MediaQuery.of(context).size.height * 60) / 100;
double cardWidth = (MediaQuery.of(context).size.width * 85) / 100;
FirstTypeInput firstTypeOfInput = FirstTypeInput();
GlobalKey<FormState> key = new GlobalKey<FormState>();
return Scaffold(
body: Stack(
children: <Widget>[
AppbarBack(
height: bottomHeight,
),
Align(
alignment: Alignment(0.0, .65),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
child: Container(
child: StepperBanner(
firstTypeInputKey: key,
test: 18,
firstTypeInputField: {},
child: Stepper(
currentStep: indexStep,
onStepContinue: (){
print(StepperBanner.of(context).test);
//StepperBanner.of(context).firstTypeInputKey.currentState.validate();
},
//type: StepperType.horizontal,
steps: <Step>[
Step(
content: firstTypeOfInput,
title: Text("Theorical"),
),
Step(
content: //Second Step Content,
title: Text("Practical")),
],
),
),
decoration: BoxDecoration(color: Colors.white, boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 10.0,
offset: Offset(0.0, 0.75))
]),
width: cardWidth,
height: cardHeight,
),
),
),
],
),
);
}
}
第一步中包含的表单
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:pberrycoffeemaker/functions/coffeeCalculation.dart';
import 'package:pberrycoffeemaker/widgets/stepper_banner.dart';
// ignore: must_be_immutable
class FirstTypeInput extends StatefulWidget {
final Map<String, double> submittedField = {
"tds": 0.0,
"ext": 0.0,
"dw": 0.0,
"af": 0.0,
"co2": 0.0,
"co2p": 0.0,
"ih": 0.0,
"ihp": 0.0,
"wtemp": 0.0,
"twh": 0.0,
"alk": 0.0
};
@override
State<StatefulWidget> createState() {
return _FirstTypeInput();
}
}
class _FirstTypeInput extends State<FirstTypeInput> {
Map<String, FocusNode> _focusNodes;
GlobalKey<FormState> formKey;
@override
void initState() {
super.initState();
_focusNodes = {
"ext": new FocusNode(),
"dw": new FocusNode(),
"af": new FocusNode(),
"co2": new FocusNode(),
"co2P": new FocusNode(),
"ih": new FocusNode(),
"ihP": new FocusNode(),
"wt": new FocusNode(),
"twh": new FocusNode(),
"alk": new FocusNode()
};
}
String percentageValidator(String value){
if (double.parse(value) < 0 || double.parse(value) > 100){
return "Insert value between 0 - 100";
}
return null;
}
@override
Widget build(BuildContext context) {
return Form(
key: StepperBanner.of(context).firstTypeInputKey,
//key: widget.formKey,
child: Column(
children: <Widget>[
// * TDS
TextFormField(
//validator: percentageValidator,
onFieldSubmitted: (value) {
FocusScope.of(context).requestFocus(_focusNodes["ext"]);
},
decoration: InputDecoration(
labelText: "TDS%", hintText: "Insert TDS%", suffix: Text("%")),
),
// * EXT
TextFormField(
onFieldSubmitted: (value) {
FocusScope.of(context).requestFocus(_focusNodes["dw"]);
},
focusNode: _focusNodes["ext"],
decoration: InputDecoration(
labelText: "EXT%", hintText: "Insert EXT%", suffix: Text("%")),
),
// * Drink Weight
TextFormField(
onFieldSubmitted: (value) {
FocusScope.of(context).requestFocus(_focusNodes["af"]);
},
focusNode: _focusNodes["dw"],
decoration: InputDecoration(
labelText: "Drink Weight",
hintText: "Insert drink weight",
suffix: Text("g")),
),
// * Absorption Factor
TextFormField(
onFieldSubmitted: (value) {
FocusScope.of(context).requestFocus(_focusNodes["co2"]);
},
focusNode: _focusNodes["af"],
decoration: InputDecoration(
labelText: "Absorption Factor",
hintText: "Insert absorptio factor",
suffix: Text("g")),
),
// * CO2
TextFormField(
onFieldSubmitted: (value) {
FocusScope.of(context).requestFocus(_focusNodes["co2P"]);
},
focusNode: _focusNodes["co2"],
decoration: InputDecoration(
labelText: "CO2", hintText: "Insert CO2", suffix: Text("g")),
),
// * CO2 Precision
TextFormField(
onFieldSubmitted: (value) {
FocusScope.of(context).requestFocus(_focusNodes["ih"]);
},
focusNode: _focusNodes["co2P"],
decoration: InputDecoration(
labelText: "CO2 Precision",
hintText: "Insert CO2 Precision",
suffix: Text("%")),
),
// * Internal Humidity
TextFormField(
onFieldSubmitted: (value) {
FocusScope.of(context).requestFocus(_focusNodes["ihP"]);
},
focusNode: _focusNodes["ih"],
decoration: InputDecoration(
labelText: "Internal Humidity",
hintText: "Insert internal humidity",
suffix: Text("%")),
),
// * Internal Humidity Precision
TextFormField(
onFieldSubmitted: (value) {
FocusScope.of(context).requestFocus(_focusNodes["wt"]);
},
focusNode: _focusNodes["ihP"],
decoration: InputDecoration(
labelText: "Internal Humidity Precision",
hintText: "Insert internal humidity precision",
suffix: Text("%")),
),
// * Water Temperature
TextFormField(
onFieldSubmitted: (value) {
FocusScope.of(context).requestFocus(_focusNodes["twh"]);
},
focusNode: _focusNodes["wt"],
decoration: InputDecoration(
labelText: "Water Temperature",
hintText: "Insert water temperature",
//TODO da decidere se settare nelle impostazioni l'unità di misura oppure mettere un dropdown sotto
suffix: Text("C°|F°|K°")),
),
// * Total Water Hardness
TextFormField(
onFieldSubmitted: (value) {
FocusScope.of(context).requestFocus(_focusNodes["alk"]);
},
focusNode: _focusNodes["twh"],
decoration: InputDecoration(
labelText: "Total Water Hardness",
hintText: "Insert total water hardness",
//TODO da decidere se settare nelle impostazioni l'unità di misura oppure mettere un dropdown sotto
suffix: Text("PPM°|F°|D°")),
),
// * Alkalinity
TextFormField(
focusNode: _focusNodes["alk"],
decoration: InputDecoration(
labelText: "Alkalinity",
hintText: "Insert alkalinity",
//TODO da decidere se settare nelle impostazioni l'unità di misura oppure mettere un dropdown sotto
suffix: Text("PPM°|F°|D°")),
),
],
),
);
}
}
继承类,StepperBanner:
import 'package:flutter/material.dart';
class StepperBanner extends InheritedWidget {
final Map<String, double> firstTypeInputField;
final GlobalKey<FormState> firstTypeInputKey;
final int test;
StepperBanner({Widget child, this.firstTypeInputField,this.test, this.firstTypeInputKey}) : super(child: child);
@override
bool updateShouldNotify(InheritedWidget oldWidget) => true;
static StepperBanner of(BuildContext context) =>
context.inheritFromWidgetOfExactType(StepperBanner);
}
我应该使用继承类管理验证,还是有其他方法?
使用列表 GlobalKey 保留每个表单的键,并在 Continue call formKeys[currStep].currentState.validate()
formKeys 是全局变量,在你的情况下,对于单独的表单文件,你可以使用全局库来访问它 Dart 中的全局变量
对于演示,每个只有一个字段
代码片段
List<GlobalKey<FormState>> formKeys = [GlobalKey<FormState>(), GlobalKey<FormState>(), GlobalKey<FormState>(), GlobalKey<FormState>()];
...
onStepContinue: () {
setState(() {
if(formKeys[currStep].currentState.validate()) {
if (currStep < steps.length - 1) {
currStep = currStep + 1;
} else {
currStep = 0;
}
}
完整代码
import 'package:flutter/material.dart';
//import 'package:validate/validate.dart'; //for validation
void main() {
runApp( MyApp());
}
List<GlobalKey<FormState>> formKeys = [GlobalKey<FormState>(), GlobalKey<FormState>(), GlobalKey<FormState>(), GlobalKey<FormState>()];
class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return MyAppScreenMode();
}
}
class MyData {
String name = '';
String phone = '';
String email = '';
String age = '';
}
class MyAppScreenMode extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.lightGreen,
),
home: Scaffold(
appBar: AppBar(
title: Text('Steppers'),
),
body: StepperBody(),
));
}
}
class StepperBody extends StatefulWidget {
@override
_StepperBodyState createState() => _StepperBodyState();
}
class _StepperBodyState extends State<StepperBody> {
int currStep = 0;
static var _focusNode = FocusNode();
GlobalKey<FormState> _formKey = GlobalKey<FormState>();
static MyData data = MyData();
@override
void initState() {
super.initState();
_focusNode.addListener(() {
setState(() {});
print('Has focus: $_focusNode.hasFocus');
});
}
@override
void dispose() {
_focusNode.dispose();
super.dispose();
}
List<Step> steps = [
Step(
title: const Text('Name'),
//subtitle: const Text('Enter your name'),
isActive: true,
//state: StepState.error,
state: StepState.indexed,
content: Form(
key: formKeys[0],
child: Column(
children: <Widget>[
TextFormField(
focusNode: _focusNode,
keyboardType: TextInputType.text,
autocorrect: false,
onSaved: (String value) {
data.name = value;
},
maxLines: 1,
//initialValue: 'Aseem Wangoo',
validator: (value) {
if (value.isEmpty || value.length < 1) {
return 'Please enter name';
}
},
decoration: InputDecoration(
labelText: 'Enter your name',
hintText: 'Enter a name',
//filled: true,
icon: const Icon(Icons.person),
labelStyle:
TextStyle(decorationStyle: TextDecorationStyle.solid)),
),
],
),
)),
Step(
title: const Text('Phone'),
//subtitle: const Text('Subtitle'),
isActive: true,
//state: StepState.editing,
state: StepState.indexed,
content: Form(
key: formKeys[1],
child: Column(
children: <Widget>[
TextFormField(
keyboardType: TextInputType.phone,
autocorrect: false,
validator: (value) {
if (value.isEmpty || value.length < 10) {
return 'Please enter valid number';
}
},
onSaved: (String value) {
data.phone = value;
},
maxLines: 1,
decoration: InputDecoration(
labelText: 'Enter your number',
hintText: 'Enter a number',
icon: const Icon(Icons.phone),
labelStyle:
TextStyle(decorationStyle: TextDecorationStyle.solid)),
),
],
),
)),
Step(
title: const Text('Email'),
// subtitle: const Text('Subtitle'),
isActive: true,
state: StepState.indexed,
// state: StepState.disabled,
content: Form(
key: formKeys[2],
child: Column(
children: <Widget>[
TextFormField(
keyboardType: TextInputType.emailAddress,
autocorrect: false,
validator: (value) {
if (value.isEmpty || !value.contains('@')) {
return 'Please enter valid email';
}
},
onSaved: (String value) {
data.email = value;
},
maxLines: 1,
decoration: InputDecoration(
labelText: 'Enter your email',
hintText: 'Enter a email address',
icon: const Icon(Icons.email),
labelStyle:
TextStyle(decorationStyle: TextDecorationStyle.solid)),
),
],
),
)),
Step(
title: const Text('Age'),
// subtitle: const Text('Subtitle'),
isActive: true,
state: StepState.indexed,
content: Form(
key: formKeys[3],
child: Column(
children: <Widget>[
TextFormField(
keyboardType: TextInputType.number,
autocorrect: false,
validator: (value) {
if (value.isEmpty || value.length > 2) {
return 'Please enter valid age';
}
},
maxLines: 1,
onSaved: (String value) {
data.age = value;
},
decoration: InputDecoration(
labelText: 'Enter your age',
hintText: 'Enter age',
icon: const Icon(Icons.explicit),
labelStyle:
TextStyle(decorationStyle: TextDecorationStyle.solid)),
),
],
),
)),
// Step(
// title: const Text('Fifth Step'),
// subtitle: const Text('Subtitle'),
// isActive: true,
// state: StepState.complete,
// content: const Text('Enjoy Step Fifth'))
];
@override
Widget build(BuildContext context) {
void showSnackBarMessage(String message,
[MaterialColor color = Colors.red]) {
Scaffold
.of(context)
.showSnackBar( SnackBar(content: Text(message)));
}
void _submitDetails() {
final FormState formState = _formKey.currentState;
if (!formState.validate()) {
showSnackBarMessage('Please enter correct data');
} else {
formState.save();
print("Name: ${data.name}");
print("Phone: ${data.phone}");
print("Email: ${data.email}");
print("Age: ${data.age}");
showDialog(
context: context,
child: AlertDialog(
title: Text("Details"),
//content: Text("Hello World"),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text("Name : " + data.name),
Text("Phone : " + data.phone),
Text("Email : " + data.email),
Text("Age : " + data.age),
],
),
),
actions: <Widget>[
FlatButton(
child: Text('OK'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
));
}
}
return Container(
child: Form(
key: _formKey,
child: ListView(children: <Widget>[
Stepper(
steps: steps,
type: StepperType.vertical,
currentStep: this.currStep,
onStepContinue: () {
setState(() {
if(formKeys[currStep].currentState.validate()) {
if (currStep < steps.length - 1) {
currStep = currStep + 1;
} else {
currStep = 0;
}
}
// else {
// Scaffold
// .of(context)
// .showSnackBar( SnackBar(content: Text('$currStep')));
// if (currStep == 1) {
// print('First Step');
// print('object' + FocusScope.of(context).toStringDeep());
// }
// }
});
},
onStepCancel: () {
setState(() {
if (currStep > 0) {
currStep = currStep - 1;
} else {
currStep = 0;
}
});
},
onStepTapped: (step) {
setState(() {
currStep = step;
});
},
),
RaisedButton(
child: Text(
'Save details',
style: TextStyle(color: Colors.white),
),
onPressed: _submitDetails,
color: Colors.blue,
),
]),
));
}
}
工作演示
本文向大家介绍ElementUI多个子组件表单的校验管理实现,包括了ElementUI多个子组件表单的校验管理实现的使用技巧和注意事项,需要的朋友参考一下 背景 公司项目中所用到的前端框架是Vue.js + ElementUI,因为项目的业务场景中有很多的大表单,但是ElementUI的表单写法对于表单的拆分和校验其实并不是很友好。最初的项目为了方便,常常把多个表单写在一个.vue组件中,这导致单
在flatter中,我从磁盘读取一个文件,并将列表项显示为一个列表。使用ListView。生成器工作正常,但如果文本小部件显示单个值,则会出现此错误。有人能帮忙吗? 我得到的错误是以下RangeError在构建MyHomePage时抛出(脏,状态:_MyHomePageState#e9932):RangeError(索引):无效值:有效值范围为空:9
这听起来可能很容易,但是我们如何在颤振中做多行可编辑的文本字段?文本字段只适用于单行。 编辑:有些精度,因为它似乎不清楚。虽然可以将“多行”设置为虚拟包装文本内容,但它仍然不是多行。它是一行显示为多行。如果你想做这样的事情,你就不能。因为你没有权限进入按钮。没有回车键意味着没有多行。
Flutter Android Firebase认证错误,虽然应用程序没有失败,但认证通过了,但终端出错 [错误:flatter/lib/ui/ui\u dart\u state.cc(148)]未处理的异常:NoSuchMethodError:对null调用了getter“uid”。 错误似乎来自这一行代码
我可以使用“startAfter”和“limit”进行分页,但它有错误。 例如,在Firestore DB中,我有7条记录: 当页面大小为5时,第一页就可以了,因为我使用了: 它给了我1-5项。 当它加载第二页时,我使用了: 问题是第二页结果只有item7,item6消失了。“开始”也有同样的问题。 真希望它有“抵消”功能,有人有解决办法吗?
我在flutter中创建了一个应用程序,在其中我把raisedButton的东西放在任何地方。我想在NeuMorphicButton中全部更改它们。有没有一种方法可以将所有raisedbutton改为NeummorphicButton而不需要一个接一个地编辑?谢谢你。