从列表中删除项目后更新UI [英] Update UI after removing items from List
问题描述
如果我删除或添加项目,我想更新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的立即更新可能吗?
编辑:这不是重复的,因为
- 我不想仅为了更新UI而创建列表的新实例。
- 答案不起作用:我添加了
this.items = List.from(this.items);
,但是我的应用程序的行为与上面已经描述的相同。 - 我不想破坏我的引用通过调用
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
- I dont want to create a new instance of my list just to get the UI updated.
- 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. - 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屋!