【发布时间】:2019-08-02 07:16:18
【问题描述】:
setState 实际上是如何工作的?
当应该重建的小部件被构建在构建器函数中时,它似乎没有做我期望它做的事情。我目前遇到的问题是 ListView.builder 和 AlertDialog 中的按钮。
这里的按钮之一是“AutoClean”,它会自动从对话框中显示的列表中删除某些项目。
注意:此处的目的是显示确认,其中包含将提交的“工作”列表。这些作业被标记以显示哪些看起来无效。用户可以返回更新参数,也可以按“自动清理”删除无效的参数。
onTap 按钮如下所示:
GeneralButton(
color: Colors.yellow,
label: 'Clear Overdue',
onTap: () {
print('Nr of jobs BEFORE: ${jobQueue.length}');
for (int i = jobQueue.length - 1; i >= 0; i--) {
print('Checking item at $i');
Map task = jobQueue[i];
if (cuttoffTime.isAfter(task['dt'])) {
print('Removing item $i');
setState(() { // NOT WORKING
jobQueue = List<Map<String, dynamic>>.from(jobQueue)
..removeAt(i); // THIS WORKS
});
}
}
print('Nr of jobs AFTER: ${jobQueue.length}');
updateTaskListState(); // NOT WORKING
print('New Task-list state: $taskListState');
},
),
其中jobQueue 用作构建ListView 的源。
updateTaskListState 看起来像这样:
void updateTaskListState() {
DateTime cuttoffTime = DateTime.now().add(Duration(minutes: 10));
if (jobQueue.length == 0) {
setState(() {
taskListState = TaskListState.empty;
});
return;
}
bool allDone = true;
bool foundOverdue = false;
for (Map task in jobQueue) {
if (task['result'] == null) allDone = false;
if (cuttoffTime.isAfter(task['dt'])) foundOverdue = true;
}
if (allDone) {
setState(() {
taskListState = TaskListState.done;
});
return;
}
if (foundOverdue) {
setState(() {
taskListState = TaskListState.needsCleaning;
});
return;
}
setState(() {
taskListState = TaskListState.ready;
});
}
TaskListState 只是一个枚举,用于决定作业队列是否准备好提交。
一旦 taskListState 设置为TaskListState.ready,“提交”按钮应该会变为活动状态。 AlertDialog 按钮行使用 taskListState,如下所示:
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
if (taskListState == TaskListState.ready)
ConfirmButton(
onTap: (isValid && isOnlineNow)
? () {
postAllInstructions().then((_) {
updateTaskListState();
// navigateBack();
});
: null),
从控制台输出中,我可以看到这种情况正在发生,但它不起作用。它似乎与同一问题有关。
当我使用build 内部的一个简单的小部件树构建所有小部件时,我似乎没有遇到这种问题。但在这种情况下,我无法更新对话框的显示以显示没有删除项目的新列表。
这篇文章越来越长,但 AleryDialog 内的 ListView 构建器如下所示:
Flexible(
child: ListView.builder(
itemBuilder: (BuildContext context, int itemIndex) {
DateTime itemTime = jobQueue[itemIndex]['dt'];
bool isPastCutoff = itemTime.isBefore(cuttoffTime);
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Text(
userDateFormat.format(itemTime),
style: TextStyle(
color:
isPastCutoff ? Colors.deepOrangeAccent : Colors.blue,
),
),
Icon(
isPastCutoff ? Icons.warning : Icons.cached,
color: isPastCutoff ? Colors.red : Colors.green,
)
],
);
},
itemCount: jobQueue.length,
),
),
但由于带有按钮的 Row() 也不会对 setState 做出反应,我怀疑问题出在构建器函数本身。
FWIW 所有代码,除了像“GeneralButton”这样的一些项目,它只是一个样板小部件,都驻留在屏幕的 State 类中。
我的直觉是,这与jobQueue 没有传递给任何小部件的事实有关。 builder函数引用jobQueue[itemIndex],它直接访问jobQueue属性。
我可能会尝试将 AlertDialog 提取到外部小部件中。这样做意味着它只能访问jobQueue,如果它被传递给Widget的构造函数......
【问题讨论】:
-
在将 AlertDialog 的
content提取到一个小部件中后,它得到了jobQueue和 ``taskListState` 以及所有回调作为参数,我仍然得到完全相同的行为。