Flutter - 删除元素时 UI 未正确更新

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

你可以做的是传递给ListViewKey其中将包含在列表中的元素的长度。

ListView(
  key: Key(myList.length.toString()),
  children: myList.map(...),
),
Run Code Online (Sandbox Code Playgroud)

为什么有效?
它会导致整体ListView从头开始重建,但仅当列表中的长度发生变化时。重要的是在其他情况下不会发生这种情况(例如在 TextField 中键入),因为此类操作会导致您对每种字母类型失去注意力。

希望能帮助到你 ;)

  • 在浪费了一整天的时间后,我得出了这个答案。谢谢@Marcin。 (2认同)
  • 为了基督。这应该被纳入框架中。敲打我的头好几个小时。谢谢。 (2认同)

Tox*_*Tox 6

这不是一个解决方案,而是一个解释,解决方案请参阅 Marcin Sza\xc5\x82ek\ 的评论。

\n

未按预期重新渲染的原因ListViewTextFormField它是一个有状态的小部件。当你重建widgets时,你需要注意引擎盖下有3棵树,分别是Widget树、Element树和RenderObject树。RenderObject 是视觉表示,并且直接基于 Element。而Element直接基于Widget,而Widget只是配置,即您在代码中键入的内容。

\n

当第一次实例化一个小部件时,会创建一个相应的元素。但是,当您调用setState()更改 _items 列表时,小部件将被重建,但元素不会被销毁并重新创建。相反,为了最大限度地提高效率,Flutter 首先比较初始ListTilewidget 和重建ListTilewidget 之间的差异。在这种情况下,最初有 4 个图块,后来有 3 个图块。不是重新创建新的图块,而是更新第二个和第三个图块的元素以具有重建ListTile小部件的属性(并且第一个图块没有发生任何变化,因为之前和之后的小部件/配置是相同的)。第 4 个元素从元素树中弹出。Flutter 官方团队的这段视频很好地解释了这三棵树。

\n

使用Dart的Devtools,你可以发现第三个元素的key/id没有变化。当您检查第三个时ListTile,之前和之后的键是#3dded

\n
\n

最初的

\n

电话\n最初

\n

删除后

\n

电话\n最后

\n

要从树中删除元素然后将它们放回原处,请按照 Marcin Sza\xc5\x82ek 的评论将密钥附加到ListView。对于有状态小部件,当相应的新小部件具有相同的小部件类型并具有相同的键时,元素将被更新。如果没有显式指定 key,元素只会检查它是否与相应的 widget 属于同一类型,并且由于所有 widget 都是运行时类型ListTile,那么元素不会有任何更新,因此您会看到 item 0 和项目 1 而不是项目 0 和项目 2。我建议您在此处阅读有关密钥的更多信息。

\n

使用Dart的Devtools,你可以发现第三个.key的变化ListTile。最初,当它是第 1 项时,键是#5addd。然后,当第 1 项被删除时,密钥更改为#26044

\n
\n

最初的

\n

电话\n最初

\n

删除后

\n

电话\n最后

\n