mat*_*olo 10 list dart flutter
我正在尝试构建一个页面,用户可以在其中添加和删除列表中的元素。我正在努力解决一个问题。当我删除一个元素时,模型会在 UI 更新时正确更新,但方式错误:仅删除列表的最后一个元素。
我正在使用颤振 1.5.4。
我已经在列表中使用了更简单的元素,并且我尝试仅使用此页面构建一个新项目以消除所有可能的问题,但它仍然无法正常工作。
我也尝试使用列而不是列表,但结果总是一样的。
主要.dart:
import 'package:flutter/material.dart';
import './widget.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text('Hello'),
),
body: Center(
child: SectionWidget(),
),
),
);
}
}
Run Code Online (Sandbox Code Playgroud)
小部件.dart:
import 'package:flutter/material.dart';
class SectionWidget extends StatefulWidget {
_SectionWidgetState createState() => new _SectionWidgetState();
}
class _SectionWidgetState extends State<SectionWidget> {
List<String> _items = List<String>();
@override
Widget build(BuildContext context) {
List<Widget> children = [
ListTile(
title: Text(
"Strings",
style: TextStyle(fontWeight: FontWeight.bold),
),
trailing: IconButton(
icon: Icon(Icons.add_circle),
onPressed: () => setState(() {
_items.add('item ${_items.length}');
print("Adding "+ _items.last);
}),
),
),
];
children.addAll(_buildForms());
return ListView(
children: children,
);
}
List<Widget> _buildForms() {
List<Widget> forms = new List<Widget>();
print("Strings:" + _items.toString());
for (String item in _items) {
forms.add(
ListTile(
leading: Icon(Icons.person),
title: TextFormField(
initialValue: item,
),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => setState(() {
print("Removing $item");
_items.remove(item);
}),
),
),
);
}
return forms;
}
}
Run Code Online (Sandbox Code Playgroud)
如果我将 4 个项目添加到列表中,然后删除“项目 1”,这是控制台上的输出:
I/flutter ( 4192): Strings:[]
I/flutter ( 4192): Adding item 0
I/flutter ( 4192): Strings:[item 0]
I/flutter ( 4192): Adding item 1
I/flutter ( 4192): Strings:[item 0, item 1]
I/flutter ( 4192): Adding item 2
I/flutter ( 4192): Strings:[item 0, item 1, item 2]
I/flutter ( 4192): Adding item 3
I/flutter ( 4192): Strings:[item 0, item 1, item 2, item 3]
I/flutter ( 4192): Removing item 1
I/flutter ( 4192): Strings:[item 0, item 2, item 3]
Run Code Online (Sandbox Code Playgroud)
这是正确的,因为“项目 1”已从列表中删除,但如果我查看 UI,这就是我得到的:
列表中的元素数量正确,模型正确,但显示的元素有误。
我该如何解决?我做错了什么还是Flutter的错误?
Mar*_*łek 18
你可以做的是传递给ListView在Key其中将包含在列表中的元素的长度。
ListView(
key: Key(myList.length.toString()),
children: myList.map(...),
),
Run Code Online (Sandbox Code Playgroud)
为什么有效?
它会导致整体ListView从头开始重建,但仅当列表中的长度发生变化时。重要的是在其他情况下不会发生这种情况(例如在 TextField 中键入),因为此类操作会导致您对每种字母类型失去注意力。
希望能帮助到你 ;)
这不是一个解决方案,而是一个解释,解决方案请参阅 Marcin Sza\xc5\x82ek\ 的评论。
\n未按预期重新渲染的原因是ListViewTextFormField它是一个有状态的小部件。当你重建widgets时,你需要注意引擎盖下有3棵树,分别是Widget树、Element树和RenderObject树。RenderObject 是视觉表示,并且直接基于 Element。而Element直接基于Widget,而Widget只是配置,即您在代码中键入的内容。
当第一次实例化一个小部件时,会创建一个相应的元素。但是,当您调用setState()更改 _items 列表时,小部件将被重建,但元素不会被销毁并重新创建。相反,为了最大限度地提高效率,Flutter 首先比较初始ListTilewidget 和重建ListTilewidget 之间的差异。在这种情况下,最初有 4 个图块,后来有 3 个图块。不是重新创建新的图块,而是更新第二个和第三个图块的元素以具有重建ListTile小部件的属性(并且第一个图块没有发生任何变化,因为之前和之后的小部件/配置是相同的)。第 4 个元素从元素树中弹出。Flutter 官方团队的这段视频很好地解释了这三棵树。
使用Dart的Devtools,你可以发现第三个元素的key/id没有变化。当您检查第三个时ListTile,之前和之后的键是#3dded。
最初的
\n\n删除后
\n\n要从树中删除元素然后将它们放回原处,请按照 Marcin Sza\xc5\x82ek 的评论将密钥附加到ListView。对于有状态小部件,当相应的新小部件具有相同的小部件类型并具有相同的键时,元素将被更新。如果没有显式指定 key,元素只会检查它是否与相应的 widget 属于同一类型,并且由于所有 widget 都是运行时类型ListTile,那么元素不会有任何更新,因此您会看到 item 0 和项目 1 而不是项目 0 和项目 2。我建议您在此处阅读有关密钥的更多信息。
使用Dart的Devtools,你可以发现第三个.key的变化ListTile。最初,当它是第 1 项时,键是#5addd。然后,当第 1 项被删除时,密钥更改为#26044。
最初的
\n\n删除后
\n\n