dart Flutter:获取窗体中所有值的最佳方法

kxe2p93d  于 2023-03-15  发布在  Flutter
关注(0)|答案(6)|浏览(158)

我正在做一个数据收集应用程序,它有多个TextField,比如超过12个。我使用一个Form键来验证所有的TextField。我想要所有文本字段的值,这样我就可以把它们保存到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个控制器。有更好的方法吗?

kq4fsx7k

kq4fsx7k1#

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

*_表格密钥.当前状态.保存();*在每个textFormField项上调用onSaved(),它会给所有的字段赋值,你可以根据需要使用,可以尝试使用 _formKey.currentState.save(); 就在 _formKey.currentState.validate() 被评估为真之后。

表单代码如下所示:

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

oug3syen2#

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

更新答案

使用onSubmitted获取值

onSubmitted: (String value){email=value;},
y1aodyip

y1aodyip3#

我对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包,用FormBuilderTextField小部件替换TextFormFieldFormBuilderTextField小部件是旧的普通TextField的 Package 器。
现在你需要做的就是指定表单中每个字段的名称,然后在_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'),
      ),
    ]),
  );
}
kokeuurv

kokeuurv4#

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

eivgtgni5#

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

  • 格式键
final _signUpKey = GlobalKey<FormState>();
  • 声明文本编辑控制器
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: example@email.com, phone: 0000000000, password: password}
            },
            child: const Text('submit'),
          )
        ],
      ),
    );
  • 从Map获取数据的扩展方法〈String,TextEditingController〉
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;
      }
    }
xpszyzbs

xpszyzbs6#

尝试使用flutter包flutter_form_builder,它可以帮助你为每个表单域创建多个控制器,从而避免重复操作;此外,它还可以帮助你验证表单,并通过使用一个简单的表单键来控制整个表单,从而简单地更新表单。

相关问题