我已调用 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() 方法中的代码。
我发现您正在尝试使用静态字段来更新状态,这可能会给您带来奇怪的结果,因为 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();
}