Dart 从列表中删除项目后更新UI

Dart 从列表中删除项目后更新UI,dart,flutter,Dart,Flutter,如果删除或添加项目,我想更新我的ListView。现在我只想删除项目,并立即看到项目的删除 我的应用程序更复杂,所以我写了一个小的示例项目来说明我的问题 TestItem类包含一些数据项: class TestItem { static int id = 1; bool isFinished = false; String text; TestItem() { text = "Item ${id++}"; } } ItemInfoViewWidget是TestIte

如果删除或添加项目,我想更新我的ListView。现在我只想删除项目,并立即看到项目的删除

我的应用程序更复杂,所以我写了一个小的示例项目来说明我的问题

TestItem
类包含一些数据项:

class TestItem {
  static int id = 1;
  bool isFinished = false;
  String text;
  TestItem() {
    text = "Item ${id++}";
  }
}
ItemInfoViewWidget
TestItem
的UI表示,如果该项已完成(只要复选框更改为true),则会删除该项

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

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)),
    );
  }
}
void main()=>runApp(新的MyApp());
类MyApp扩展了无状态小部件{
//此小部件是应用程序的根。
@凌驾
小部件构建(构建上下文){
返回新材料PP(
标题:“颤振演示”,
主题:新主题数据(
主样本:颜色。蓝色,
),
主页:新MyHomePage(标题:“颤振演示主页”),
);
}
}
类MyHomePage扩展StatefulWidget{
MyHomePage({Key,this.title}):超级(Key:Key);
最后的字符串标题;
@凌驾
_MyHomePageState createState()=>new_MyHomePageState();
}
类_MyHomePageState扩展状态{
int _计数器=0;
列表项=新列表();
_MyHomePageState(){
对于(int i=0;i<20;i++){
this.items.add(new TestItem());
}
}
@凌驾
小部件构建(构建上下文){
归还新脚手架(
appBar:新的appBar(
标题:新文本(widget.title),
),
正文:新栏目(
儿童:[
ItemInfoViewWidget(this.items,this.items.first),
扁平按钮(
子项:新文本(“打开详细视图”),
按下:按钮已按下,
)
],
));
}
void按钮单击(){
导航器。推(
上下文
MaterialPage路由(生成器:(上下文)=>ItemViewWidget(this.items)),
);
}
}
如果我切换第一个项目的复选框,复选框将标记为已完成(如预期),但它不会从UI中删除,而是从列表中删除

然后我回到主页,我可以看到第1项也被选中了

因此,如果我再次转到
ItemViewWidget
页面,我可以观察到选中的项目不再存在。 根据这些观察,我得出结论,我的实现是可行的,但我的UI没有更新

如何更改代码以立即更新UI

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

  • 我不想仅仅为了更新UI而创建列表的新实例
  • 答案不起作用:我添加了
    this.items=List.from(this.items)但我的应用程序的行为与上面描述的相同
  • 我不想通过调用
    List.from
    来破坏我的引用链,因为我的体系结构有一个列表,它被几个类引用。如果我打破了这个链条,我必须自己更新所有的参考资料。我的架构有问题吗 我不想仅仅为了更新UI而创建列表的新实例

    颤振使用不可变对象。不遵守这一规则将违反被动框架。这是减少bug的自愿要求

    事实上,这种不变性在这里特别是为了阻止开发人员做您目前正在做的事情:拥有一个依赖于在类之间共享同一对象实例的程序;因为多个类可能需要修改它


    真正的问题在于,正是列表项从列表中删除了一个元素

    问题是,由于是您的项目进行计算,因此不会通知家长列表已更改。因此它不知道应该重新播放。所以视觉上没有什么变化

    要解决这个问题,您应该将删除逻辑移到父级。并确保父级相应地正确调用
    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,
        );
      }
    }
    
    class MyList扩展了StatefulWidget{
    @凌驾
    _MyListState createState();
    }
    类_MyListState扩展状态{
    List=List.generate(100,(i)=>i.toString());
    @凌驾
    小部件构建(构建上下文){
    返回ListView.builder(
    itemCount:list.length,
    itemBuilder:(上下文,索引){
    返回MyItem(列表[索引],onDelete:()=>removeItem(索引));
    },
    );
    }
    void removietem(int索引){
    设置状态(){
    列表=列表。从(列表)
    ..移除(索引);
    });
    }
    }
    类MyItem扩展了无状态小部件{
    最后的字符串标题;
    最终删除;
    MyItem(this.title,{this.onDelete});
    @凌驾
    小部件构建(构建上下文){
    返回列表块(
    标题:文本(此标题),
    onTap:this.onDelete,
    );
    }
    }
    
    可能重复@RémiRousselet我遵循了答案,但我的解决方案没有什么不同。该项已删除,但未在UI中更新。我也不想创建新列表,因为ItemViewWidget中的列表与MyApp列表“断开”连接。也可能是因为我的架构不好,有更好的方法来完成我想要做的事情?您的列表实例保持不变。你不应该调用
    list.remove
    。为什么我不应该调用list.remove?我用调试器查看了我的代码,比较了List.from前后实例的哈希代码。之前是623876470,之后是812698102。我猜List.from会创建一个新实例(或者hashcode不是跟踪实例的正确指标)。您应该故意创建一个新实例,从而创建
    列表。从
    中,我评估了您的解决方案,它正在按原样工作。然而,我很高兴
    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)),
        );
      }
    }
    
    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,
        );
      }
    }