在小部件构建期间调用setState()

时间:2020-01-30 20:58:00

标签: flutter dart

我当前项目的要求之一是多页注册表单,每个页面执行自己的独立验证。我最终决定实现此目的的方法是将其拆分为几个较小的形式,您可以将一些函数作为参数传递给表单验证和/或值更改运行时执行。下面是我为实现此目的而创建的“迷你表单”之一的示例。这收集有关用户名的信息。

import "package:flutter/material.dart";

class SignUpNameForm extends StatefulWidget {
  final Function(String) onFirstNameChange;
  final Function(String) onLastNameChange;
  final Function(bool) onFirstNameValidationStatusUpdate;
  final Function(bool) onLastNameValidationStatusUpdate;
  final String firstNameInitialValue;
  final String lastNameInitialValue;

  SignUpNameForm(
      {this.onFirstNameChange,
      this.onLastNameChange,
      this.onFirstNameValidationStatusUpdate,
      this.onLastNameValidationStatusUpdate,
      this.firstNameInitialValue,
      this.lastNameInitialValue});

  @override
  _SignUpNameFormState createState() => _SignUpNameFormState();
}

class _SignUpNameFormState extends State<SignUpNameForm> {
  final _formKey = GlobalKey<FormState>();
  TextEditingController _firstName;
  TextEditingController _lastName;
  bool _editedFirstNameField;
  bool _editedLastNameField;

  @override
  Widget build(BuildContext context) {

    _firstName.text = widget.firstNameInitialValue;
    _lastName.text = widget.lastNameInitialValue;

    return Form(
      key: _formKey,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          TextFormField(
            controller: _firstName,
            autovalidate: true,
            decoration: InputDecoration(
              hintText: "First Name",
            ),
            keyboardType: TextInputType.text,
            onChanged: (String value) {
              _editedFirstNameField = true;
              widget.onFirstNameChange(value);
            },
            validator: (String value) {
              String error;

              if (_editedFirstNameField) {
                error = value.isEmpty ? "This field is required" : null;
                bool isValid = error == null;
                widget.onFirstNameValidationStatusUpdate(isValid);
              }

              return error;
            },
          ),
          TextFormField(
            controller: _lastName,
            autovalidate: true,
            decoration: InputDecoration(
              hintText: "Last Name",
            ),
            keyboardType: TextInputType.text,
            onChanged: (String value) {
              _editedLastNameField = true;
              widget.onLastNameChange(value);
            },
            validator: (String value) {
              String error;

              if (_editedLastNameField) {
                error = value.isEmpty ? "This field is required" : null;
                bool isValid = error == null;
                widget.onLastNameValidationStatusUpdate(isValid);
              }

              return error;
            },
          ),
        ],
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    _firstName = new TextEditingController();
    _lastName = new TextEditingController();
    _editedFirstNameField = false;
    _editedLastNameField = false;
  }

  @override
  void dispose() {
    super.dispose();
    _firstName.dispose();
    _lastName.dispose();
  }
}

然后,在显示表单组件的小部件中,我做类似的事情。

class _SignUpFormState extends State<SignUpForm> {

  String _firstName;
  bool _firstNameIsValid;

  String _lastName;
  bool _lastNameIsValid;


  int current;
  final maxLength = 4;
  final minLength = 0;

  @override
  Widget build(BuildContext context) {
    // create the widget
    return Container(
      child: _showFormSection(),
    );
  }

  /// Show the appropriate form section
  Widget _showFormSection() {
    Widget form;

    switch (current) {
      case 0:
        form = _showNameFormSection();
        break;
      case 1:
        form = _showEmailForm();
        break;
      case 2:
        form = _showPasswordForm();
        break;
      case 3:
        form = _showDobForm();
        break;
    }

    return form;
  }

  // shows the name section of the form.
  Widget _showNameFormSection() {
    return Container(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Container(
            child: SignUpNameForm(
              firstNameInitialValue: _firstName,
              lastNameInitialValue: _lastName,
              onFirstNameChange: (String value) {
                setState(() {
                  _firstName = value;
                });
              },
              onFirstNameValidationStatusUpdate: (bool value) {
                setState(() {
                  _firstNameIsValid = value;
                });
              },
              onLastNameChange: (String value) {
                setState(() {
                  _lastName = value;
                });
              },
              onLastNameValidationStatusUpdate: (bool value) {
                setState(() {
                  _lastNameIsValid = value;
                });
              },
            ),
          ),
          Container(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.end,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                FlatButton(
                  child: Text("Next"),
                  onPressed: (_firstNameIsValid && _lastNameIsValid)
                      ? _showNextSection
                      : null,
                ),
              ],
            ),
          )
        ],
      ),
    );
  }


  /// Shows the next section in the form
  void _showNextSection() {
    print("Current Section: " + current.toString());
    setState(() {
      if (current >= maxLength) {
        current = maxLength;
      } else {
        current++;
      }
    });
    print("Next Section: " + current.toString());
  }

  /// Show the previous section of the form
  void _showPreviousSection() {
    print("Current Section:" + current.toString());
    setState(() {
      if (current <= minLength) {
        current = minLength;
      } else {
        current--;
      }
      print("Previous Section: " + current.toString());
    });
  }

  @override
  void initState() {
    super.initState();
    current = 0;
    _firstName = "";
    _firstNameIsValid = false;
    _lastName = "";
    _lastNameIsValid = false;
    // other initializations
  }
}

正如您在此处看到的那样,我传入了一些函数以提取用户名的值以及验证的状态,并使用它们来确定是否应启用表单中的“下一个”按钮处理程序小部件。现在这会引起问题,特别是因为在重新构建窗口小部件时,我传递给“小型表单”的函数会调用initState()

我该如何处理呢?或者,是否有更好的方法可以实现更清洁的多页表单?

谢谢。

1 个答案:

答案 0 :(得分:1)

build()应该快速且幂等。您不应在内部调用setState()。想象一下build()每秒被调用60次(尽管它不会由于优化而被调用),并且您将拥有正确的心态。