我当前项目的要求之一是多页注册表单,每个页面执行自己的独立验证。我最终决定实现此目的的方法是将其拆分为几个较小的形式,您可以将一些函数作为参数传递给表单验证和/或值更改运行时执行。下面是我为实现此目的而创建的“迷你表单”之一的示例。这收集有关用户名的信息。
import "package:flutter/material.dart";
class SignUpNameForm extends StatefulWidget {
final Function(String) onFirstNameChange;
final Function(String) onLastNameChange;
final Function(bool) onFirstNameValidationStatusUpdate;
final Function(bool) onLastNameValidationStatusUpdate;
final String firstNameInitialValue;
final String lastNameInitialValue;
SignUpNameForm(
{this.onFirstNameChange,
this.onLastNameChange,
this.onFirstNameValidationStatusUpdate,
this.onLastNameValidationStatusUpdate,
this.firstNameInitialValue,
this.lastNameInitialValue});
@override
_SignUpNameFormState createState() => _SignUpNameFormState();
}
class _SignUpNameFormState extends State<SignUpNameForm> {
final _formKey = GlobalKey<FormState>();
TextEditingController _firstName;
TextEditingController _lastName;
bool _editedFirstNameField;
bool _editedLastNameField;
@override
Widget build(BuildContext context) {
_firstName.text = widget.firstNameInitialValue;
_lastName.text = widget.lastNameInitialValue;
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextFormField(
controller: _firstName,
autovalidate: true,
decoration: InputDecoration(
hintText: "First Name",
),
keyboardType: TextInputType.text,
onChanged: (String value) {
_editedFirstNameField = true;
widget.onFirstNameChange(value);
},
validator: (String value) {
String error;
if (_editedFirstNameField) {
error = value.isEmpty ? "This field is required" : null;
bool isValid = error == null;
widget.onFirstNameValidationStatusUpdate(isValid);
}
return error;
},
),
TextFormField(
controller: _lastName,
autovalidate: true,
decoration: InputDecoration(
hintText: "Last Name",
),
keyboardType: TextInputType.text,
onChanged: (String value) {
_editedLastNameField = true;
widget.onLastNameChange(value);
},
validator: (String value) {
String error;
if (_editedLastNameField) {
error = value.isEmpty ? "This field is required" : null;
bool isValid = error == null;
widget.onLastNameValidationStatusUpdate(isValid);
}
return error;
},
),
],
),
);
}
@override
void initState() {
super.initState();
_firstName = new TextEditingController();
_lastName = new TextEditingController();
_editedFirstNameField = false;
_editedLastNameField = false;
}
@override
void dispose() {
super.dispose();
_firstName.dispose();
_lastName.dispose();
}
}
然后,在显示表单组件的小部件中,我做类似的事情。
class _SignUpFormState extends State<SignUpForm> {
String _firstName;
bool _firstNameIsValid;
String _lastName;
bool _lastNameIsValid;
int current;
final maxLength = 4;
final minLength = 0;
@override
Widget build(BuildContext context) {
// create the widget
return Container(
child: _showFormSection(),
);
}
/// Show the appropriate form section
Widget _showFormSection() {
Widget form;
switch (current) {
case 0:
form = _showNameFormSection();
break;
case 1:
form = _showEmailForm();
break;
case 2:
form = _showPasswordForm();
break;
case 3:
form = _showDobForm();
break;
}
return form;
}
// shows the name section of the form.
Widget _showNameFormSection() {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: SignUpNameForm(
firstNameInitialValue: _firstName,
lastNameInitialValue: _lastName,
onFirstNameChange: (String value) {
setState(() {
_firstName = value;
});
},
onFirstNameValidationStatusUpdate: (bool value) {
setState(() {
_firstNameIsValid = value;
});
},
onLastNameChange: (String value) {
setState(() {
_lastName = value;
});
},
onLastNameValidationStatusUpdate: (bool value) {
setState(() {
_lastNameIsValid = value;
});
},
),
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
FlatButton(
child: Text("Next"),
onPressed: (_firstNameIsValid && _lastNameIsValid)
? _showNextSection
: null,
),
],
),
)
],
),
);
}
/// Shows the next section in the form
void _showNextSection() {
print("Current Section: " + current.toString());
setState(() {
if (current >= maxLength) {
current = maxLength;
} else {
current++;
}
});
print("Next Section: " + current.toString());
}
/// Show the previous section of the form
void _showPreviousSection() {
print("Current Section:" + current.toString());
setState(() {
if (current <= minLength) {
current = minLength;
} else {
current--;
}
print("Previous Section: " + current.toString());
});
}
@override
void initState() {
super.initState();
current = 0;
_firstName = "";
_firstNameIsValid = false;
_lastName = "";
_lastNameIsValid = false;
// other initializations
}
}
正如您在此处看到的那样,我传入了一些函数以提取用户名的值以及验证的状态,并使用它们来确定是否应启用表单中的“下一个”按钮处理程序小部件。现在这会引起问题,特别是因为在重新构建窗口小部件时,我传递给“小型表单”的函数会调用initState()
。
我该如何处理呢?或者,是否有更好的方法可以实现更清洁的多页表单?
谢谢。
答案 0 :(得分:1)
build()应该快速且幂等。您不应在内部调用setState()。想象一下build()每秒被调用60次(尽管它不会由于优化而被调用),并且您将拥有正确的心态。