【问题标题】:Flutter Forms: Get the list of fields in errorFlutter Forms:获取错误字段列表
【发布时间】:2020-12-29 04:24:25
【问题描述】:

我们可以在验证后从 Flutter 表单中获取错误字段列表吗?这将帮助开发人员使用焦点节点将注意力重定向到错误的字段。

【问题讨论】:

    标签: flutter flutter-form-builder


    【解决方案1】:

    根据 Guillaume 的回答,我已将功能包装到一个可重用的类中。

    您可以在此处查看 DartPad 上的工作示例:https://www.dartpad.dev/61c4ccddbf29a343c971ee75e60d1038

    import 'package:flutter/material.dart';
    
    class FormValidationManager {
      final _fieldStates = Map<String, FormFieldValidationState>();
    
      FocusNode getFocusNodeForField(key) {
          _ensureExists(key);
    
          return _fieldStates[key].focusNode;
      }
    
      FormFieldValidator<T> wrapValidator<T>(String key, FormFieldValidator<T> validator) {
          _ensureExists(key);
    
          return (input) {
              final result = validator(input);
    
              _fieldStates[key].hasError = (result?.isNotEmpty ?? false);
    
              return result;
          };
      }
    
      List<FormFieldValidationState> get erroredFields =>
          _fieldStates.entries.where((s) => s.value.hasError).map((s) => s.value).toList();
    
      void _ensureExists(String key) {
          _fieldStates[key] ??= FormFieldValidationState(key: key);
      }
    
      void dispose() {
        _fieldStates.entries.forEach((s) {
            s.value.focusNode.dispose();
        });
      }
    }
    
    class FormFieldValidationState {
      final String key;
    
      bool hasError;
      FocusNode focusNode;
    
      FormFieldValidationState({@required this.key})
          : hasError = false,
          focusNode = FocusNode();
    }
    

    要使用它,请照常创建表单,但将FormValidationManager 添加到您的状态类,然后使用该实例来包装您的验证方法。

    用法:

    class _MyWidgetState extends State<MyWidget> {
      final _formKey = GlobalKey<FormState>();
      final _formValidationManager = FormValidationManager();
    
      @override
      Widget build(BuildContext context) {
        return Form(
          key: _formKey,
          child: Column(
            children: [
              TextFormField(
                  focusNode: _formValidationManager.getFocusNodeForField('field1'),
                  validator: _formValidationManager.wrapValidator('field1', (value) {
                    if (value.isEmpty) {
                      return 'Please enter a value';
                    }
    
                    return null;
                  })),
              TextFormField(
                  focusNode: _formValidationManager.getFocusNodeForField('field2'),
                  validator: _formValidationManager.wrapValidator('field2', (value) {
                    if (value.isEmpty) {
                      return 'Please enter a value';
                    }
    
                    return null;
                  })),
              ElevatedButton(
                  onPressed: () {
                    if (!_formKey.currentState.validate()) {
                      _formValidationManager.erroredFields.first.focusNode.requestFocus();
                    }
                  },
                  child: Text('SUBMIT'))
            ],
          ),
        );
      }
    
      @override
      void dispose() {
        _formValidationManager.dispose();
        super.dispose();
      }
    }
    

    【讨论】:

    • 多么聪明的解决方案!你救了我的命❤️。给别人的备注,记得在你StatefulWidgetStatedispose()方法中调用_formValidationManager.dispose()
    【解决方案2】:

    我认为不可能从Form 对象或FormState 获取此类信息。

    但是这里有一种方法可以获得你想要的结果(关注错误的字段):

    class _MyWidgetState extends State<MyWidget> {
      FocusNode _fieldToFocus;
      List<FocusNode> _focusNodes;
    
      final _formKey = GlobalKey<FormState>();
      final _numberOfFields = 3;
    
      String _emptyFieldValidator(String val, FocusNode focusNode) {
        if (val.isEmpty) {
          _fieldToFocus ??= focusNode;
          return 'This field cannot be empty';
        }
        return null;
      }
    
      @override
      void initState() {
        super.initState();
        _focusNodes =
            List<FocusNode>.generate(_numberOfFields, (index) => FocusNode());
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(actions: [
            IconButton(
              icon: Icon(Icons.check),
              onPressed: () {
                if (_formKey.currentState.validate()) {
                  print('Valid form');
                } else {
                  _fieldToFocus?.requestFocus();
                  _fieldToFocus = null;
                }
              },
            ),
          ]),
          body: Form(
            key: _formKey,
            child: Column(children: [
              ...List<TextFormField>.generate(
                _numberOfFields,
                (index) => TextFormField(
                  decoration: InputDecoration(hintText: "Field $index"),
                  focusNode: _focusNodes[index],
                  validator: (val) => _emptyFieldValidator(val, _focusNodes[index]),
                ),
              ),
            ]),
          ),
        );
      }
    }
    

    您只需要为您的每个字段创建一个FocusNode,因此您可以在精确字段上调用requestFocus(在您的情况下,该字段被视为无效)。然后在表单字段的validator 属性中,因为它是FormState.validate() 调用的方法,您需要设置一个临时变量,该变量将包含正确的FocusNode。在我的示例中,我只设置变量 _fieldToFocus 如果尚未使用 ??= 运算符分配它。在请求关注节点后,我将_fieldToFocus 设置回null,因此它仍然可以用于另一个验证。

    您可以尝试我在DartPad 上使用的完整测试代码。

    对不起,如果我从你的问题中得到了一点启发,但我仍然希望这会对你有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-08-24
      • 1970-01-01
      • 2022-12-13
      • 1970-01-01
      • 1970-01-01
      • 2021-10-15
      • 2021-10-26
      • 2022-10-22
      相关资源
      最近更新 更多