我正在制作一个数据收集应用程序,它有多个
TextField
,比如超过 12 个。我使用 Form 键来验证所有这些。我想要所有文本字段的值,以便我可以将它们保存到 firestore。我该怎么做呢?这是我的代码:
import 'package:flutter/material.dart';
class MainForm extends StatefulWidget {
@override
_MainFormState createState() => _MainFormState();
}
class _MainFormState extends State<MainForm> {
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Center(
child: SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
Text('Enter information about PG Owner'),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
autofocus: true,
textCapitalization: TextCapitalization.words,
textAlignVertical: TextAlignVertical.center,
onTap: () {},
decoration: InputDecoration(
prefixIcon: Icon(Icons.face),
labelText: 'Enter Name of Owner',
border: OutlineInputBorder()),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
validator: (value) {
if (value.length < 15) {
return 'Address seems very short!';
}
return null;
},
keyboardType: TextInputType.text,
decoration: InputDecoration(
prefixIcon: Icon(Icons.room),
labelText: 'Enter full address of Owner',
border: OutlineInputBorder()),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
keyboardType: TextInputType.number,
validator: (value) {
if (value.length < 9) {
return 'Phone number must be 9 digits or longer';
}
return null;
},
decoration: InputDecoration(
prefixIcon: Icon(Icons.phone),
labelText: 'Phone number of Owner',
border: OutlineInputBorder()),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
validator: (value) {
if (value.isEmpty) {
return 'Please enter a valid email address';
}
if (!value.contains('@')) {
return 'Email is invalid, must contain @';
}
if (!value.contains('.')) {
return 'Email is invalid, must contain .';
}
return null;
},
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
prefixIcon: Icon(Icons.mail_outline),
labelText: 'Enter Email',
border: OutlineInputBorder()),
),
),
)
],
),
),
),
);
}
}
更新:我知道从
TextField
获取值的正确方法(我已阅读文档)是创建 controller
。但是,就我而言,有 14 个TextField
,这需要我创建 14 个控制器。有更好的方法吗?
您可以在以下代码中使用类似的内容:
_formKey.currentState.save();在每个textFormField项上调用onSaved(),它将值分配给所有字段,您可以根据需要使用它们。尝试在 _formKey.currentState.validate() 评估为 true 之后使用 _formKey.currentState.save();。
表单代码如下所示:
String contactNumber;
String pin;
return Form(
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
onSaved: (String value){contactNumber=value;},
keyboardType: TextInputType.phone,
inputFormatters: [WhitelistingTextInputFormatter.digitsOnly],
maxLength: 10,
decoration: InputDecoration(
labelText: "Enter Your Mobile Number",
hintText: "Number",
icon: Icon(Icons.phone_iphone)),
validator: (value) {
if (value.isEmpty || value.length < 10) {
return 'Please Enter 10 digit number';
}
return null;
},
),
TextFormField(
onSaved: (String value){pin=value;},
keyboardType: TextInputType.phone,
inputFormatters: [WhitelistingTextInputFormatter.digitsOnly],
maxLength: 10,
decoration: InputDecoration(
labelText: "Enter Your PIN",
hintText: "Number",
icon: Icon(Icons.lock)),
validator: (value) {
if (value.isEmpty || value.length < 6) {
return 'Please Enter 6 digit PIN';
}
return null;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: RaisedButton(
color: Colors.black,
textColor: Colors.white,
onPressed: () {
if (_formKey.currentState.validate()) {
***_formKey.currentState.save();***
bloc.loginUser(contactNumber, pin);
}
},
child: Text('Login' /*style: TextStyle(fontSize: 30),*/)),
)
],
),
);
我对 Flutter 让你自己处理表单值的方式不满意,你需要为每个字段创建一个
TextEditingController
实例,将其分配给 controller
并记住手动处理所有这些。这会导致大量样板代码并使其更容易出错:
final _formKey = GlobalKey<FormState>();
final controller1 = TextEditingController();
final controller2 = TextEditingController();
final controller3 = TextEditingController();
@override
void dispose() {
super.dispose();
controller1.dispose();
controller2.dispose();
controller3.dispose();
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(children: [
TextFormField(controller: controller1),
TextFormField(controller: controller2),
TextFormField(
controller: controller3,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
final value1 = controller1.text;
final value2 = controller2.text;
final value3 = controller3.text;
// do something with the form data
}
},
child: const Text('Submit'),
),
]),
);
}
flutter_form_builder
包,并将 TextFormField
替换为 FormBuilderTextField
小部件,它是旧的普通 TextField
的包装器。您可以在此处查看所有支持的输入小部件。
您现在需要做的就是指定表单中每个字段的名称,并在
_formKey.currentState?.value
中访问它。请参阅下面的示例:
final _formKey = GlobalKey<FormBuilderState>();
@override
Widget build(BuildContext context) {
return FormBuilder(
key: _formKey,
child: Column(children: [
FormBuilderTextField(name: 'field1'),
FormBuilderTextField(name: 'field2'),
FormBuilderTextField(
name: 'field3',
validator: FormBuilderValidators.required(
context,
errorText: 'Please enter some text',
),
),
ElevatedButton(
onPressed: () {
_formKey.currentState.save();
if (_formKey.currentState!.validate()) {
final formData = _formKey.currentState?.value;
// formData = { 'field1': ..., 'field2': ..., 'field3': ... }
// do something with the form data
}
},
child: const Text('Submit'),
),
]),
);
}
使用TextFormField中的控制器,您可以获取TextFormField的值。
TextEditingController emailEditingController = TextEditingController();
TextFormField(
controller: emailEditingController,
validator: (value) {
if (value.isEmpty) {
return 'Please enter a valid email address';
}
if (!value.contains('@')) {
return 'Email is invalid, must contain @';
}
if (!value.contains('.')) {
return 'Email is invalid, must contain .';
}
return null;
},
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
prefixIcon: Icon(Icons.mail_outline),
labelText: 'Enter Email',
border: OutlineInputBorder()),
);
获取价值,例如:
String email=emailEditingController.text;
更新答案
使用 onSubscribed 获取价值
onSubmitted: (String value){email=value;},
我是通过类似的搜索来到这里的。找到的所有答案都不能满足我的需求,因此我编写了一个自定义解决方案。
final _signUpKey = GlobalKey<FormState>();
final Map<String, TextEditingController> sigUpController = {
'firstName': TextEditingController(),
'lastName': TextEditingController(),
'email': TextEditingController(),
'phone': TextEditingController(),
'password': TextEditingController(),
};
Form(
key: _signUpKey,
child: Column(
children: [
TextFormField(
controller: sigUpController['firstName'],
validator: validator,
autofocus: autofocus,
keyboardType: TextInputType.text,
style: const TextStyle(
fontSize: 14,
),
onTap: onTap,
onChanged: onChanged,
inputFormatters: [
FilteringTextInputFormatter.allow(
RegExp(r"[a-zA-Z]+|\s"),
),
],
),
// define the other TextFormField here
TextButton(
onPressed: () {
if (!_signUpKey.currentState!.validate()) {
return;
}
// To get data I wrote an extension method bellow
final data = sigUpController.data();
print('data: $data'); // data: {firstName: John, lastName: Doe, email: [email protected], phone: 0000000000, password: password}
},
child: const Text('submit'),
)
],
),
);
extension Data on Map<String, TextEditingController> {
Map<String, dynamic> data() {
final res = <String, dynamic>{};
for (MapEntry e in entries) {
res.putIfAbsent(e.key, () => e.value?.text);
}
return res;
}
}
您可以使用flutter_form_bloc,不需要创建任何
TextEditingController
,并且可以将业务逻辑与用户界面分离,此外还提供其他优势。
dependencies:
flutter_bloc: ^0.21.0
form_bloc: ^0.4.1
flutter_form_bloc: ^0.3.0
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
import 'package:form_bloc/form_bloc.dart';
void main() => runApp(MaterialApp(home: MainForm()));
class MainFormBloc extends FormBloc<String, String> {
final nameField = TextFieldBloc();
final addressField = TextFieldBloc(validators: [
(value) => value.length < 15 ? 'Address seems very short!' : null,
]);
final phoneNumberField = TextFieldBloc(validators: [
(value) =>
value.length < 9 ? 'Phone number must be 9 digits or longer' : null,
]);
final emailField = TextFieldBloc(validators: [Validators.email]);
@override
List<FieldBloc> get fieldBlocs => [
nameField,
addressField,
phoneNumberField,
emailField,
];
@override
Stream<FormBlocState<String, String>> onSubmitting() async* {
// This method is called when you call [mainFormBloc.submit]
// and each field bloc have a valid value.
// And you can save them in firestore.
print(nameField.value);
print(addressField.value);
print(phoneNumberField.value);
print(emailField.value);
yield currentState.toSuccess('Data saved successfully.');
// yield `currentState.toLoaded()` because
// you can't submit if the state is `FormBlocSuccess`.
// In most cases you don't need to do this,
// because you only want to submit only once.
yield currentState.toLoaded();
}
}
class MainForm extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider<MainFormBloc>(
builder: (context) => MainFormBloc(),
child: Builder(
builder: (context) {
final formBloc = BlocProvider.of<MainFormBloc>(context);
return Scaffold(
appBar: AppBar(title: Text('Main Form')),
body: FormBlocListener<MainFormBloc, String, String>(
onSuccess: (context, state) {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(state.successResponse),
backgroundColor: Colors.green,
),
);
},
onSubmissionFailed: (context, state) {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Some fields have invalid data.'),
backgroundColor: Colors.red,
),
);
},
child: ListView(
children: <Widget>[
TextFieldBlocBuilder(
textFieldBloc: formBloc.nameField,
padding: const EdgeInsets.all(8.0),
autofocus: true,
textCapitalization: TextCapitalization.words,
textAlignVertical: TextAlignVertical.center,
decoration: InputDecoration(
prefixIcon: Icon(Icons.face),
labelText: 'Enter Name of Owner',
border: OutlineInputBorder()),
),
TextFieldBlocBuilder(
textFieldBloc: formBloc.addressField,
padding: const EdgeInsets.all(8.0),
keyboardType: TextInputType.text,
decoration: InputDecoration(
prefixIcon: Icon(Icons.room),
labelText: 'Enter full address of Owner',
border: OutlineInputBorder()),
),
TextFieldBlocBuilder(
textFieldBloc: formBloc.phoneNumberField,
padding: const EdgeInsets.all(8.0),
keyboardType: TextInputType.number,
decoration: InputDecoration(
prefixIcon: Icon(Icons.phone),
labelText: 'Phone number of Owner',
border: OutlineInputBorder()),
),
TextFieldBlocBuilder(
textFieldBloc: formBloc.emailField,
padding: const EdgeInsets.all(8.0),
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
prefixIcon: Icon(Icons.mail_outline),
labelText: 'Enter Email',
border: OutlineInputBorder()),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
onPressed: formBloc.submit,
child: Center(child: Text('SUBMIT')),
),
),
],
),
),
);
},
),
);
}
}
尝试使用这个flutter包flutter_form_builder,它将帮助您通过为每个表单字段创建多个控制器来重复自己。除此之外,它将帮助您验证表单,并通过仅使用简单的表单键来控制整个表单来简单地更新表单。
我可以看到很多准确的答案。但不要忘记修剪文本。 .trim() 帮助您删除尾随和前导空格。
_cityController.text.trim();