如何在flutter中调用formKey.currentState?.validate()后找到第一个出错的TextFormField

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

我在

TextFormField()
中的
Form()
小部件中有一个
CustomScrollView()
小部件列表。当我调用表单的
validate()
方法时,所有字段都已正确验证。但是,由于存在很多字段,因此实际出错的第一个(也可能是唯一的)字段可能会在视图之外。因此表单不会提交,但是用户需要手动滚动以查找错误。

如何让

scrollView
滚动到错误的第一个字段?

我知道我可以为表单字段设置一个

FocusNode
,然后发出
requestFocus()
scrollView
将滚动到该字段 - 这就是我想要的,但是,我无法弄清楚哪个字段实际上是错误的吗?

有谁知道实现我的目标的最佳方法是什么,即在

TestFormField
中错误地滚动到第一个
Form
scrollView

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

我创建了一个示例,可能是您的解决方案,请根据您的需求进行调整:

class _TestState extends State<Test> {
  final _controllerUsername = TextEditingController();
  final _controllerPassword = TextEditingController();
  final _controllerEmail = TextEditingController();
  final _controllerAddress = TextEditingController();
  final _focusAddress = FocusNode();
  final _focusUsername = FocusNode();
  final _focusPassword = FocusNode();
  final _focusEmail = FocusNode();
  int? errorIndex;
  final _formKey = GlobalKey<FormState>();
  late List<FocusNode> focusNodes = [
    _focusUsername,
    _focusPassword,
    _focusEmail,
    _focusAddress,
  ];

  void validateForm() {
    if (_formKey.currentState!.validate()) {
      errorIndex = null;
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Processing Data')),
      );
    } else {
      print("error occured at $errorIndex form field");
      focusNodes[errorIndex!].requestFocus();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Form(
          key: _formKey,
          child: Column(
            children: [
              TextFormField(
                decoration: InputDecoration(hintText: 'username'),
                textInputAction: TextInputAction.next,
                controller: _controllerUsername,
                focusNode: _focusUsername,
                validator: (value) {
                  var validation = value?.isNotEmpty ?? true;
                  errorIndex = validation ? null : 0;
                  return validation ? null : "Input your username";
                },
              ),
              TextFormField(
                decoration: InputDecoration(hintText: 'password'),
                textInputAction: TextInputAction.next,
                controller: _controllerPassword,
                focusNode: _focusPassword,
                validator: (value) {
                  var validation = value?.isNotEmpty ?? true;
                  errorIndex = validation ? errorIndex : errorIndex ?? 1;
                  return validation ? null : "Input your password";
                },
              ),
              TextFormField(
                decoration: InputDecoration(hintText: 'email'),
                textInputAction: TextInputAction.next,
                controller: _controllerEmail,
                focusNode: _focusEmail,
                validator: (value) {
                  var validation = value?.isNotEmpty ?? true;
                  errorIndex = validation ? errorIndex : errorIndex ?? 2;
                  return validation ? null : "Input your email";
                },
              ),
              TextFormField(
                decoration: InputDecoration(hintText: 'address'),
                textInputAction: TextInputAction.done,
                controller: _controllerAddress,
                focusNode: _focusAddress,
                validator: (value) {
                  var validation = value?.isNotEmpty ?? true;
                  errorIndex = validation ? errorIndex : errorIndex ?? 3;
                  return validation ? null : "Input your address";
                },
              ),
              ElevatedButton(onPressed: validateForm, child: Text('submit'))
            ],
          ),
        ),
      ),
    );
  }
}

0
投票
import 'package:flutter/material.dart';

class TestForm extends StatefulWidget {
  const TestForm({super.key});

  @override
  State<TestForm> createState() => _TestFormState();
}

class _TestFormState extends State<TestForm> {
  final formKey = GlobalKey<FormState>();

  void validateForm() {
    final invalidFields = formKey.currentState?.validateGranularly();
    if (invalidFields != null && invalidFields.isNotEmpty) {
      Scrollable.ensureVisible(invalidFields.first.context);
    }
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Form(
        key: formKey,
        child: Column(
          children: [
            const TextField(),
            const TextField(),
            const TextField(),
            OutlinedButton(
              onPressed: validateForm,
              child: const Text('Validate'),
            ),
          ],
        ),
      ),
    );
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.