我有一份较长的表格,表格底部有保存按钮。当用户点击“保存”按钮时,表单字段验证器会根据 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()
失败时,有没有办法自动滚动到必填字段??
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
当焦点无效时,您可以使用焦点节点来请求字段中的焦点。像这样的东西。
首先为每个字段创建一个焦点节点,如下所示。
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()),
),
);
在构建表单时将所有字段的列表保留在列表中:
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()
。您可以更改该函数的参数以根据您的喜好自定义效果。