我有一个有状态的小部件列表,用户可以在其中添加,删除列表中的项目并与之交互。从列表中删除项目会导致列表中的后续项目随着它们移动以填充已删除的行而重建。这会导致这些小部件的状态数据丢失-尽管除了在屏幕上的位置外,它们应保持不变。我希望能够保持列表中其余项目的状态,即使它们的位置发生变化。
下面是我的应用程序的简化版本,主要由StatefulWidgets列表组成。用户可以通过浮动操作按钮将项目添加到列表中(在我的应用中为“任务”),也可以通过滑动将其删除。轻按项目即可突出显示列表中的任何项目,这会更改项目背景颜色的状态。如果列表中突出显示了多个项目,并且删除了一个项目(列表中的最后一个项目除外),则替换替换已删除项目的项目将丢失其状态数据(即背景颜色重置为透明)。我怀疑这是因为自删除任务后调用setState()来更新显示,因为_taskList会重建。我想知道从_taskList中删除任务后是否有一种干净的方法来维护其余任务的状态数据。
lucy_web_question
答案 0 :(得分:1)
我最终通过创建一个中间类解决了这个问题,该中间类包含对StatefulWidget的引用并在所有状态变量上进行了传递。 State类通过对中间类的引用来访问状态变量。包含和管理StatefulWidget列表的更高级别的小部件现在通过此中间类访问StatefulWidget。我对解决方案的“正确性”不完全自信,因为我还没有找到其他示例,因此我仍然愿意提出建议。
我的中级班如下:
class TaskItemData {
// StatefulWidget reference
TaskItem widget;
Color _color = Colors.transparent;
TaskItemData({String name: "",}) {
_color = Colors.transparent;
widget = TaskItem(name: name, stateData: this,);
}
}
我的StatefulWidget及其对应的State类几乎不变,只是状态变量不再位于State类中。我还在StatefulWidget中添加了对中间类的引用,该引用在构造函数中初始化。现在可以通过对中间类的引用来访问我的State类中状态变量的先前用法。修改后的StatefulWidget和State类如下:
class TaskItem extends StatefulWidget {
final String name;
// intermediate class reference
final TaskItemData stateData;
TaskItem({Key key, this.name, this.stateData}) : super(key: key);
@override
State<StatefulWidget> createState() => new _TaskItemState();
}
class _TaskItemState extends State<TaskItem> {
static final _taskFont =
const TextStyle(fontSize: 26.0, fontWeight: FontWeight.bold);
void _highlightTask() {
setState(() {
if(widget.stateData._color == Colors.transparent) {
widget.stateData._color = Colors.greenAccent;
}
else {
widget.stateData._color = Colors.transparent;
}
});
}
@override
Widget build(BuildContext context) {
return Column(children: <Widget>[
Material(
color: widget.stateData._color,
child: ListTile(
title: Text(
widget.name,
style: _taskFont,
textAlign: TextAlign.center,
),
onTap: () {
_highlightTask();
},
),
),
Divider(
height: 0.0,
),
]);
}
}
包含TaskItem对象列表的小部件已替换为TaskItemData列表。现在,ListViewBuilder子级通过中间类访问TaskItem小部件(即子级:_taskList [index],已更改为子级:_taskList [index] .widget,)。如下:
class _TimeTrackHomeState extends State<TimeTrackHome> {
TextEditingController _textController;
List<TaskItemData> _taskList = new List<TaskItemData>();
void _addTaskDialog() async {
_textController = TextEditingController();
await showDialog(
context: context,
builder: (_) => new AlertDialog(
title: new Text("Add A New Task"),
content: new TextField(
controller: _textController,
decoration: InputDecoration(
border: InputBorder.none, hintText: 'Enter the task name'),
),
actions: <Widget>[
new FlatButton(
onPressed: () => Navigator.pop(context),
child: const Text("CANCEL")),
new FlatButton(
onPressed: (() {
Navigator.pop(context);
_addTask(_textController.text);
}),
child: const Text("ADD"))
],
));
}
void _addTask(String title) {
setState(() {
// add the new task
_taskList.add(TaskItemData(
name: title,
));
});
}
@override
void initState() {
_taskList = List<TaskItemData>();
super.initState();
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Align(
alignment: Alignment.topCenter,
child: ListView.builder(
padding: EdgeInsets.all(0.0),
itemExtent: 60.0,
itemCount: _taskList.length,
itemBuilder: (BuildContext context, int index) {
if (index < _taskList.length) {
return Dismissible(
key: ObjectKey(_taskList[index]),
onDismissed: (direction) {
if(this.mounted) {
setState(() {
_taskList.removeAt(index);
});
}
},
child: _taskList[index].widget,
);
}
}),
),
floatingActionButton: new FloatingActionButton(
onPressed: _addTaskDialog,
tooltip: 'Click to add a new task',
child: new Icon(Icons.add),
),
);
}
}