从列表中删除项目后更新UI [英] Update UI after removing items from List

查看:102
本文介绍了从列表中删除项目后更新UI的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我删除或添加项目,我想更新ListView。现在,我只想删除项目,然后立即删除它们。



我的应用程序更复杂,因此我写了一个小的示例项目来展示我的问题。 / p>

TestItem 类包含一些数据条目:

  class TestItem {
static int id = 1;
bool isFinished = false;
字符串文本;
TestItem(){
text = Item $ {id ++};
}
}

ItemInfoViewWidget TestItem 的UI表示形式,如果完成,则将其删除(只要Checkbox更改为true)。

 类ItemInfoViewWidget扩展了StatefulWidget {
TestItem项目;
List< TestItem>物品;

ItemInfoViewWidget(this.items,this.item);

@override
_ItemInfoViewWidgetState createState()=>
_ItemInfoViewWidgetState(this.items,this.item);
}

类_ItemInfoViewWidgetState扩展State< ItemInfoViewWidget> {
TestItem项;
List< TestItem>物品;

_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(
值:this.item.isFinished,onChanged:isFinishedChanged)
],
),
);
}

void isFinishedChanged(布尔值){
setState((){
this.item.isFinished = value;
this.items.remove (this.item);
});
}
}

ItemViewWidget 类构建 ListView

 类ItemViewWidget扩展了StatefulWidget {
List< TestItem>物品;

ItemViewWidget(this.items);

@override
_ItemViewWidgetState createState()=> _ItemViewWidgetState(this.items);
}

类_ItemViewWidgetState扩展State< ItemViewWidget> {
List< TestItem>物品;

_ItemViewWidgetState(this.items);

@override
Widget build(BuildContext context){
return Scaffold(
appBar:AppBar(
title:new Text('Test'),
),
正文:ListView.builder(
itemCount:this.items.length,
itemBuilder:(BuildContext context,int index){
返回新的ItemInfoViewWidget( this.items,this.items [index]);
}),
);
}
}

MyApp 显示一个 TestItem 和一个导航到 ItemViewWidget 页面的按钮。

  void main()=> runApp(new MyApp()); 

类MyApp扩展了StatelessWidget {
//此小部件是应用程序的根。
@override
小部件构建(BuildContext上下文){
返回新的MaterialApp(
标题: Flutter Demo,
主题:新的ThemeData(
primarySwatch :Colors.blue,
),
home:new MyHomePage(title:'Flutter Demo Home Page'),
);
}
}

类MyHomePage扩展了StatefulWidget {
MyHomePage({Key key,this.title}):super(key:key);

最终字符串标题;

@override
_MyHomePageState createState()=>新的_MyHomePageState();
}

类_MyHomePageState扩展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(
标题:new Text(widget.title),
),
正文:new Column(
子项:< Widget> [
ItemInfoViewWidget(this.items,this。 items.first),
FlatButton(
子级:new Text('打开详细视图'),
onPressed:buttonClicked,

],
));
}

void buttonClicked(){
Navigator.push(
context,
MaterialPageRoute(builder:(context)=> ItemViewWidget(this。项目)),
);
}
}

如果我切换第一个项目的复选框,复选框标记为已完成(按预期方式),但并未从UI中删除-而是从列表中删除了。



然后我回到主页,可以看到在那里也检查了项目1。



因此,如果我再次进入 ItemViewWidget 页面,则可以看到已检查的项目不再存在。
基于这些观察,我得出的结论是我的实现有效,但是我的UI没有更新。



如何更改代码以使代码UI的立即更新可能吗?



编辑:这不是重复的,因为


  1. 我不想仅为了更新UI而创建列表的新实例。

  2. 答案不起作用:我添加了 this.items = List.from(this.items); ,但是我的应用程序的行为与上面已经描述的相同。

  3. 我不想破坏我的引用通过调用 List.from 链接,因为我的体系结构具有一个由多个类引用的列表。如果我中断了链接,我必须自己更新所有参考。我的架构有问题吗?


解决方案


我不想创建我的新实例列表只是为了更新UI。


Flutter使用不可变对象。不遵循此规则会违反反应框架。



事实是,这种不可改变性特别是在防止开发人员执行当前操作时:拥有一个依赖于共享程序的程序类之间对象的相同实例;






真正的问题在于,这是您的列表项删除了



问题是由于是您的项目在进行计算,因此永远不会通知父项列表已更改。因此,它不知道应该重新呈现。因此,视觉上没有任何变化。



要解决此问题,您应该将删除逻辑移到父级。并确保父级相应地正确调用 setState 。这将转化为将回调传递给您的列表项,删除后会调用该回调。



下面是一个示例:

  class MyList扩展了StatefulWidget {
@override
_MyListState createState()=> _MyListState();
}

类_MyListState扩展了State< MyList> {
List< String> list = List.generate(100,(i)=> i.toString());

@override
小部件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 );
});
}
}

类MyItem扩展了StatelessWidget {
最终字符串标题;
最终的VoidCallback onDelete;

MyItem(this.title,{this.onDelete});

@override
窗口小部件build(BuildContext context){
return ListTile(
title:Text(this.title),
onTap:this.onDelete ,
);
}
}


I want to update my ListView if i remove or add items. Right now i just want to delete items and see the deletion of the items immediately.

My application is more complex so i wrote a small example project to show my problems.

The TestItem class holds some data entries:

class TestItem {
  static int id = 1;
  bool isFinished = false;
  String text;
  TestItem() {
    text = "Item ${id++}";
  }
}

The ItemInfoViewWidget is the UI representation of the TestItem and removes the item if it is finished (whenever the Checkbox is changed to true).

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);
    });
  }
}

The ItemViewWidget class builds the 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]);
          }),
    );
  }
}

The MyApp shows one TestItem and a button that navigates to the ItemViewWidget page.

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)),
    );
  }
}

If i toggle the Checkbox of the first item, the Checkbox is marked as finished (as expected), but it is not removed from the UI - however it is removed from the list.

Then I go back to the Main page and I can observe that Item 1 is checked there as well.

So if I go to the ItemViewWidget page again, I can observe that the checked items are no longer present. Based on these observations, I come to the conclusion that my implementation works, but my UI is not updating.

How can I change my code to make an immediate update of the UI possible?

Edit: This is not a duplicate, because

  1. I dont want to create a new instance of my list just to get the UI updated.
  2. The answer does not work: I added this.items = List.from(this.items); but the behavior of my app is the same as already described above.
  3. I don't want to break my reference chain by calling List.from, because my architecture has one list that is referenced by several classes. If i break the chain i have to update all references by my own. Is there a problem with my architecture?

解决方案

I dont want to create a new instance of my list just to get the UI updated.

Flutter uses immutable object. Not following this rule is going against the reactive framework. It is a voluntary requirement to reduce bugs.

Fact is, this immutability is here especially to prevents developers from doing what you currently do: Having a program that depends on sharing the same instance of an object between classes; as multiple classes may want to modify it.


The real problem lies in the fact that it is your list item that removes delete an element from your list.

The thing is since it's your item which does the computing, the parent is never notified that the list changed. Therefore it doesn't know it should rerender. So nothing visually change.

To fix that you should move the deletion logic to the parent. And make sure that the parent correctly calls setState accordingly. This would translate into passing a callback to your list item, which will be called on deletion.

Here's an example:

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,
    );
  }
}

这篇关于从列表中删除项目后更新UI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆