Flutter:当 _formKey.currentState.validate() 为 false 时自动滚动到复选框(或任何表单小部件)

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

我有一份较长的表格,表格底部有保存按钮。当用户点击“保存”按钮时,表单字段验证器会根据 validator() 函数突出显示错误文本。我的问题是,当表单出现任何错误时,页面应该滚动到该错误小部件(TextFormField、复选框、滑块或任何自定义表单字段)。

我尝试使用

FocusScope.of(context).requestFocus(focus_node);
但它不滚动。 我也尝试过这样的 ScrollController,

  final RenderBox renderBoxRed = key.currentContext.findRenderObject();
  final positionRed = renderBoxRed.localToGlobal(Offset.zero);
  scrollController.animateTo(positionRed.distance, duration: Duration(milliseconds: 300), curve: Curves.easeIn);

但它总是滚动到屏幕顶部。

另一种情况是我有条款和条件屏幕,其中有较长的文本,在条款文本末尾,我添加了复选框,之后又出现了一些其他较长的文本。我在屏幕顶部添加了按钮。如果用户在检查该条款复选框之前按下该按钮,屏幕应滚动到该复选框以突出显示复选框,以便用户知道需要同意条款和条件。

如何用 Flutter 实现这一点?当

form.currentState.validate()
失败时,有没有办法自动滚动到必填字段??

validation flutter dart flutter-layout
3个回答
2
投票

Flutter 对此有一个未解决的问题 https://github.com/flutter/flutter/issues/58877

Didier 有一个非常好的解决方法来解决这个问题 https://www.didierboelens.com/2018/04/hint-4-ensure-a-textfield-or-textformfield-is-visible-in-the-viewport-when -有焦点/

基于 Didier 代码,我制作了一个包并发布在 Pub.dev 上。 https://pub.dev/packages/ensure_visible_when_focused


1
投票

当焦点无效时,您可以使用焦点节点来请求字段中的焦点。像这样的东西。

首先为每个字段创建一个焦点节点,如下所示。

FocusNode phone = FocusNode();

然后创建一个验证器类。

class VONPhoneValidator extends TextFieldValidator {
  final FocusNode focusNode;
  VONPhoneValidator(this.focusNode,
      {String errorText = 'Enter a valid phone number'})
      : super(errorText);

  @override
  bool get ignoreEmptyValues => true;

  @override
  bool isValid(String value) {
    if (hasMatch(r'^[0-9]{10}$', value)) {
      return true;
    } else {
      focusNode.requestFocus();
      return false;
    }
  }
}

这将是你的领域,

TextFormField(
      maxLengthEnforced: true,
      keyboardType: TextInputType.phone,
      controller: _phoneController,
      focusNode: phone,
      validator: VONPhoneValidator(phone, errorText: "Enter Valid Phone Number"),
      decoration: InputDecoration(
        prefixIcon: CountryCodePicker(
          hideMainText: true,
          initialSelection: 'NP',
          favorite: ['NP', 'IN'],
          showCountryOnly: true,
          showFlagMain: true,
          flagWidth: 20,
          boxDecoration: BoxDecoration(color: Colors.white),
          onChanged: (value) {
            _formController.country.value = value.toString();
          },
        ),
        labelText: 'Phone Number',
        border: OutlineInputBorder(borderSide: BorderSide()),
      ),
    );

0
投票

在构建表单时将所有字段的列表保留在列表中:

fields = <FormField>[];

还要确保在构造这些字段时向所有这些字段传递适当的全局键:

key: GlobalKey<FormFieldState>(),

在您的 Save 处理程序中或您调用

formKey.currentState!.validate()
的任何地方,也创建一个
else
分支:

if (formKey.currentState!.validate()) {
  formKey.currentState!.save();
  // ....
}
else
        }
  else
    scrollToFirstInvalid();

其中

scrollToFirstInvalid()
函数如下:

void scrollToFirstInvalid() {
  for (final field in fields) {
    if (field.key != null) {
      final key = field.key as GlobalKey;
      if (key.currentState != null && key.currentContext != null) {
        final state = key.currentState as FormFieldState;
        if (state.hasError) {
          Scrollable.ensureVisible(
            key.currentContext!,
            duration: Duration.zero,
            curve: Curves.ease,
            alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtStart,
          );
          return;
        }
      }
    }
  }
}

此函数将迭代这些已保存的字段,从附加键检索状态并在第一个有错误的字段上调用

Scrollable.ensureVisible()
。您可以更改该函数的参数以根据您的喜好自定义效果。

© www.soinside.com 2019 - 2024. All rights reserved.