3

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

4

2 回答 2

4

扩展 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();
}

要使用它,请像往常一样创建表单,但将 a 添加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();
  }
}
于 2021-07-22T23:52:43.850 回答
4

我认为不可能从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尚未使用??=运算符分配的变量。在请求将焦点放在我设置的节点上之后_fieldToFocusnull它仍然可以用于另一个验证。

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

抱歉,如果我从您的问题中得到了一些启发,但我仍然希望这会对您有所帮助。

于 2020-09-10T18:54:01.460 回答