Flutter:获取表单中所有值的最佳方式

问题描述 投票:0回答:7

我正在制作一个数据收集应用程序,它有多个

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 个控制器。有更好的方法吗?

flutter dart
7个回答
51
投票

您可以在以下代码中使用类似的内容:

_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),*/)),
      )
    ],
  ),
);

12
投票

我对 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'),
      ),
    ]),
  );
}

11
投票

使用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;},

7
投票

我是通过类似的搜索来到这里的。找到的所有答案都不能满足我的需求,因此我编写了一个自定义解决方案。

  • 表格键
    final _signUpKey = GlobalKey<FormState>();
  • 声明你的 TextEditingController
    final Map<String, TextEditingController> sigUpController = {
      'firstName': TextEditingController(),
      'lastName': TextEditingController(),
      'email': TextEditingController(),
      'phone': TextEditingController(),
      'password': TextEditingController(),
    };
  • 像这样将控制器传递给 TextFormField
    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'),
          )
        ],
      ),
    );
  • 从Map获取数据的扩展方法
    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;
      }
    }

5
投票

您可以使用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')),
                    ),
                  ),
                ],
              ),
            ),
          );
        },
      ),
    );
  }
}

1
投票

尝试使用这个flutter包flutter_form_builder,它将帮助您通过为每个表单字段创建多个控制器来重复自己。除此之外,它将帮助您验证表单,并通过仅使用简单的表单键来控制整个表单来简单地更新表单。


0
投票

我可以看到很多准确的答案。但不要忘记修剪文本。 .trim() 帮助您删除尾随和前导空格。

_cityController.text.trim();
© www.soinside.com 2019 - 2024. All rights reserved.