如果要删除或添加项目,我想更新ListView。现在,我只想删除项目,然后立即查看项目的删除。
我的应用程序更复杂,因此我写了一个小示例项目来展示我的问题。
本TestItem类包含一些数据项:
TestItem
class TestItem { static int id = 1; bool isFinished = false; String text; TestItem() { text = "Item ${id++}"; } }
该ItemInfoViewWidget是对的UI表示TestItem,如果它完成(当该复选框被更改为true)中删除的项目。
ItemInfoViewWidget
class ItemInfoViewWidget extends StatefulWidget { TestItem item; List<TestItem> items; ItemInfoViewWidget(this.items, this.item); @override _ItemInfoViewWidgetState createState() => _ItemInfoViewWidgetState(this.items, this.item); } class _ItemInfoViewWidgetState extends State<ItemInfoViewWidget> { TestItem item; List<TestItem> items; _ItemInfoViewWidgetState(this.items, this.item); @override Widget build(BuildContext context) { return new Card( child: new Column( children: <Widget>[ new Text(this.item.text), new Checkbox( value: this.item.isFinished, onChanged: isFinishedChanged) ], ), ); } void isFinishedChanged(bool value) { setState(() { this.item.isFinished = value; this.items.remove(this.item); }); } }
本ItemViewWidget类构建ListView。
ItemViewWidget
ListView
class ItemViewWidget extends StatefulWidget { List<TestItem> items; ItemViewWidget(this.items); @override _ItemViewWidgetState createState() => _ItemViewWidgetState(this.items); } class _ItemViewWidgetState extends State<ItemViewWidget> { List<TestItem> items; _ItemViewWidgetState(this.items); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: new Text('Test'), ), body: ListView.builder( itemCount: this.items.length, itemBuilder: (BuildContext context, int index) { return new ItemInfoViewWidget(this.items, this.items[index]); }), ); } }
该MyApp节目之一TestItem和一个按钮,导航到该ItemViewWidget页面。
MyApp
void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return new MaterialApp( title: 'Flutter Demo', theme: new ThemeData( primarySwatch: Colors.blue, ), home: new MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => new _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; List<TestItem> items = new List<TestItem>(); _MyHomePageState() { for (int i = 0; i < 20; i++) { this.items.add(new TestItem()); } } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(widget.title), ), body: new Column( children: <Widget>[ ItemInfoViewWidget(this.items, this.items.first), FlatButton( child: new Text('Open Detailed View'), onPressed: buttonClicked, ) ], )); } void buttonClicked() { Navigator.push( context, MaterialPageRoute(builder: (context) => ItemViewWidget(this.items)), ); } }
如果我切换第一项的复选框,则该复选框被标记为已完成(如预期),但并未从UI中删除-但是已从列表中删除了该复选框。
然后,我回到主页,可以看到在那里也检查了项目1。
因此,如果我ItemViewWidget再次转到该页面,则可以看到已检查的项目不再存在。基于这些观察,我得出的结论是我的实现有效,但是我的UI没有更新。
如何更改代码以使UI可以立即更新?
编辑:这不是重复的,因为
this.items = List.from(this.items);
List.from
我不想仅为更新UI而创建列表的新实例。
Flutter使用不可变对象。不遵循此规则会违反反应框架。减少错误是自愿的。
事实是,这种不变性在这里特别是为了防止开发人员执行当前的工作:具有依赖于类之间共享对象的同一实例的程序;因为可能要修改多个类。
真正的问题在于,这是您的列表项从列表中删除删除元素。
事情是因为是您的项目在进行计算,因此永远不会通知父项列表已更改。因此,它不知道应该重新渲染。因此,外观没有任何变化。
要解决此问题,您应该将删除逻辑移至父级。并确保父级正确地进行相应的调用setState。这将转化为将回调传递给您的列表项,删除后将调用该回调。
setState
这是一个例子:
class MyList extends StatefulWidget { @override _MyListState createState() => _MyListState(); } class _MyListState extends State<MyList> { List<String> list = List.generate(100, (i) => i.toString()); @override Widget build(BuildContext context) { return ListView.builder( itemCount: list.length, itemBuilder: (context, index) { return MyItem(list[index], onDelete: () => removeItem(index)); }, ); } void removeItem(int index) { setState(() { list = List.from(list) ..removeAt(index); }); } } class MyItem extends StatelessWidget { final String title; final VoidCallback onDelete; MyItem(this.title, {this.onDelete}); @override Widget build(BuildContext context) { return ListTile( title: Text(this.title), onTap: this.onDelete, ); } }