【发布时间】:2022-01-06 17:28:34
【问题描述】:
我对如何使用 StatefulWidget 管理状态有点困惑。以下是我遇到的问题的简化示例:
我有一个StatefulWidget,用于显示事物列表。它由从 API 检索到的列表构成。该小部件还允许用户将新项目添加到列表中,点击 API 即可。 API 在添加项目后依次返回整个列表。
我遇到的挑战是小部件是用api.List 对象构造的。 Dart 要求 StatefulWdigets 是不可变的(因此所有字段都是 final 的),因此当用户向其中添加项目时,该列表不能被 API 返回的新列表替换。这部分对我来说很有意义:可变状态应该存在于状态对象中,而不是小部件中。所以这表明状态对象应该跟踪列表。这会导致两个不同的问题:
-
状态对象是在
StatefulWidget的createState方法中创建的。如果我们想将列表传递给状态对象的构造函数,则列表也需要作为最终字段存储在StatefulWidget上。现在小部件和它的状态对象都会引用列表。当用户将项目添加到列表中时,状态对象将其列表替换为 API 返回的新副本,这两者将不同步。 -
即使你这样做了,linter 也会抱怨在
createState方法中包含逻辑:https://dart-lang.github.io/linter/lints/no_logic_in_create_state.html。他们似乎强烈建议状态对象应始终具有 0 参数构造函数,并且您永远不应该将任何东西从StatefulWidget传递给State对象。他们建议始终通过State对象的widget访问此类数据。但这会让您回到StatefulWidget不可变的问题:如果列表是StatefulWidget对象上的最终字段,您将无法替换该列表。
有问题的设计 1
在这里,我们避免了 createState 方法中存在逻辑的 linting 问题。但是你不能用newList 替换widget.list,因为它是最终的。
class ListWidget extends StatefulWidget {
const ListWidget({Key? key, required this.list }) : super(key: key);
final api.List list;
@override
State<StatefulWidget> createState() => _ListWidgetState();
}
class _ListWidgetState extends State<ListWidget> {
@override
Widget build(BuildContext context) {
return Scaffold(
... // bunch of widgets
ElevatedButton(
...
onPressed: () async {
final newList = await api.addNewListItem(...);
// Can't do this because widget.list is final. Makes sense as it just seems wrong to store mutable state in the widget instead of the state.
setState(() => widget.list = newList);
},
)
);
}
}
如果不是替换 widget.list 对象,而是“内部”更改其所有字段以匹配 newList,则可以使此设计起作用。这似乎很尴尬且容易出错。此外,您最终还是会(内部)变异widget.list,这感觉非常错误。看起来flutter的意图是可变状态真的不应该存在于StatefulWidget中。
有问题的设计 2
这可行,但 linter 抱怨将列表传递给 createState 中的 _ListWidgetState 构造函数。此外,当用户向列表中添加项目时,小部件的 list 对象和状态的 list 对象变得不同步。
class ListWidget extends StatefulWidget {
const ListWidget({Key? key, required this.list }) : super(key: key);
final api.List list;
@override
State<StatefulWidget> createState() => _ListWidgetState(list); // linter complains
}
class _ListWidgetState extends State<ListWidget> {
_ListWidgetState(this.list);
api.List list;
@override
Widget build(BuildContext context) {
return Scaffold(
... // bunch of widgets
ElevatedButton(
...
onPressed: () async {
final newList = await api.addNewListItem(...);
// This works, but now widget.list and list are out of sync.
setState(() => list = newList);
},
)
);
}
}
Perils of Flutter state in StatefulWidget rather than State? 似乎正在解决类似的问题。那里的建议是创建小部件的新实例。不过,我不确定您如何将 ListWidget 完全替换为带有 newList 的新构造的 onPressed 回调。
【问题讨论】:
标签: flutter flutter-state