Flutter 在 setState() 之后不调用 Widget 中的构建,尽管状态发生了变化

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

我已调用 setState() 来更新类 MyErrorBox 的对象,但 Flutter 不会为这些对象运行构建函数。我对 Flutter 和一般编码都很陌生。下面代码中的MyText类只是一个修改后的TextField,MySubmit只是一个按钮(newFunc是传入的函数)。

这是第一个文件的代码(删除了不重要的部分):

//assume proper imports

class CreateAccountWidget extends StatefulWidget {
  static List<String> errorMessages = [];

  const CreateAccountWidget({super.key});

  @override
  State<CreateAccountWidget> createState() => _CreateAccountWidgetState();

  static void updateErrorM(List<String> erm) {
    errorMessages = erm;
    print(errorMessages);
  }
}

class _CreateAccountWidgetState extends State<CreateAccountWidget> {

  //these editors control each of the textboxes
  TextEditingController editor1 = TextEditingController();
  TextEditingController editor2 = TextEditingController();
  TextEditingController editor3 = TextEditingController();

 //em is the list of error messages in the form of Strings
  static List<String> em = [];
  List<MyErrorBox> errors = [];

  List<MyErrorBox> possibleErrs = [
    MyErrorBox(whichError: 0, key: ValueKey("error1")),
    MyErrorBox(whichError: 1, key: ValueKey("error2")),
    MyErrorBox(whichError: 2, key: ValueKey("error3")),
    MyErrorBox(whichError: 3, key: ValueKey("error4"))
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        child: Stack(
          children: [
            Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                MyText(c: editor1, h: "Enter a username", o: false),
                MyText(c: editor2, h: "Enter a password", o: true),
                MyText(c: editor3, h: "Confirm password", o: true),
                MySubmit(
                    ht: "btn7",
                    newFunc: () {
                      updateErrors(); // calling update errors here to rebuild screen
                      if (errors.isEmpty) {
                        FirebaseAuth.instance.createUserWithEmailAndPassword(
                            email: editor1.text, password: editor2.text);
                        Navigator.pushReplacementNamed(context, '/login');
                      }
                    }, 
                ),
              ],
            ),
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: errors, //this is where the errors are displayed
              ),
            )
          ],
        ),
      ),
    );
  }

//this is where I update the errors (and where I think the problem is)
  void updateErrors() {
    errors = [];
    em = [];
    print("");
    print("NEW UPDATE ERROR----------------------------");

    if (editor2.text != editor3.text) {
      em.add("Confirmed password must match");
    }
    if (editor1.text.isEmpty) {
      em.add("The username field cannot be empty");
    }
    if (editor2.text.isEmpty) {
      em.add("The password field cannot be empty");
    }
    if (editor1.text.length < 8) {
      em.add("The username must be at least 8 letters");
    }

    for (int i = 0; i < em.length; i++) {
      errors.add(possibleErrs[i]);
    }

    setState(() {
      CreateAccountWidget.updateErrorM(em);
    });

    print("");
  }
}

class MyErrorBox extends StatefulWidget {
  final int whichError;

  MyErrorBox({super.key, required this.whichError});

  @override
  State<MyErrorBox> createState() => MyErrorBoxState();
}

class MyErrorBoxState extends State<MyErrorBox> {
  late final int et;
  List<String> someL = CreateAccountWidget.errorMessages;

  @override
//updates the variable et
  void initState() {
    // TODO: implement initState
    super.initState();
    et = widget.whichError;
  }

  @override
  Widget build(BuildContext context) {
    someL = CreateAccountWidget.errorMessages;
    print("IN THE BUILD METHOD:");
    print(someL[et]);
    print("");
    return SizedBox(
      width: 280,
      height: 55,
      child: DecoratedBox(
        decoration: const BoxDecoration(color: Color.fromRGBO(255, 0, 0, .7)),
        child: Center(
          child: Text(
            someL[et],
            style: const TextStyle(
              color: Colors.white,
            ),
          ),
        ),
      ),
    );
  }
}

当我运行程序并出现错误框时,初始错误是正确的。之后,代码仅针对添加到错误列表中的任何其他错误运行构建方法,并忽略重建列表的第一部分,导致第一个错误未更新。

我期望 Flutter 重建列列表中的所有项目,因为这些错误的状态发生了变化,但事实并非如此。我不确定以前是否已经回答过这个问题,因为我看到的与此问题相关的所有其他问题都是因为用户试图更新 initState() 方法中的代码。

flutter dart widget setstate flutter-build
1个回答
0
投票

我发现您正在尝试使用静态字段来更新状态,这可能会给您带来奇怪的结果,因为 setState 所做的是更新类中的对象,

extends State<T>
(在您的情况下为
_CreateAccountWidgetState
),您应该尝试将您的列表从 StatefulWidget 传递到 State,然后在使用 setState 时修改它,以便重建您的 UI。您应该将列表传递给错误小部件,而不是对所有这些小部件使用静态,以避免将来出现问题。

现在我认为这个问题是因为你使用 possibleErrs 将错误添加到列表中,并且因为 possibleErrs 中的所有项目都是具有值和键的小部件,Flutter 优化知道它与以前是相同的对象,只更新发生变化的课程。

Flutter 会优化构建小部件,如果它们具有相同的类型和相同的键(在您的项目中正在发生)以及相同的属性(whichError 保持不变)。因此,解决这两个问题的最佳解决方案是将列表传递给这些小部件,而不是在其他地方使用静态列表

...
/// your State<>
void updateErrors() {
    errors = [];
    em = [];
    print("");
    print("NEW UPDATE ERROR----------------------------");

    if (editor2.text != editor3.text) {
      em.add("Confirmed password must match");
    }
    if (editor1.text.isEmpty) {
      em.add("The username field cannot be empty");
    }
    if (editor2.text.isEmpty) {
      em.add("The password field cannot be empty");
    }
    if (editor1.text.length < 8) {
      em.add("The username must be at least 8 letters");
    }

    for (int i = 0; i < em.length; i++) {
      errors.add(
        MyErrorBox(///Pass the list here
          whichError: 0,
          errors: em,
          key: ValueKey("error$i"),
        ),
      );
    }

    setState(() {
      CreateAccountWidget.updateErrorM(em);
    });

    print("");
  }
}
/// End of your State

class MyErrorBox extends StatefulWidget {
  final int whichError;
  final List<String> errors;

  MyErrorBox({super.key, required this.whichError, required this.errors});

  @override
  State<MyErrorBox> createState() => MyErrorBoxState();
}

class MyErrorBoxState extends State<MyErrorBox> {
  late final String errorMessage;

  @override
  //updates the variable et
  void initState() {
    // TODO: implement initState
    super.initState();
    errorMessage = widget.errors[widget.whichError];
  }

  @override
  Widget build(BuildContext context) {
    someL = CreateAccountWidget.errorMessages;
    print("IN THE BUILD METHOD:");
    print(someL[et]);
    print("");
    return SizedBox(
      width: 280,
      height: 55,
      child: DecoratedBox(
        decoration: const BoxDecoration(color: Color.fromRGBO(255, 0, 0, .7)),
        child: Center(
          child: Text(
            someL[et],
            style: const TextStyle(
              color: Colors.white,
            ),
          ),
        ),
      ),
    );
  }
}

为了获得更大的优化,您的 ErrorBox 可以仅使用文本,而您的

State<CreateAccountWidget>
应该进行解析

   for (int i = 0; i < em.length; i++) {
      errors.add(
        MyErrorBox(
          errorText: em[i],
          key: ValueKey("error$i"),
        ),
      );
    }
....
class MyErrorBox extends StatefulWidget {
  final String errorText;

  MyErrorBox({super.key, required this.errorText});

  @override
  State<MyErrorBox> createState() => MyErrorBoxState();
}
© www.soinside.com 2019 - 2024. All rights reserved.