RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1211645
Accepted
Александр Инженер
Александр Инженер
Asked:2021-11-30 05:02:26 +0000 UTC2021-11-30 05:02:26 +0000 UTC 2021-11-30 05:02:26 +0000 UTC

Flutter ListView key 有什么区别:UniqueKey() 和 GlobalKey()

  • 772

ListView 传递了一个列表。有必要通过点击从这个 ListView 中删除一个元素。当 ListView builder 的参数没有指定key:或指定时,GlobalKey()那么当 list 元素被删除时,元素的编号飞快,列表在视觉上正确显示,但是如果你尝试编辑某些元素,那么这样的元素会有不正确的数据。例如,如果在 ListView 构建器中指定了某些唯一标识符,key: UniqueKey()则 ListView 元素的行为变得正确且可预测,但在删除任何元素后,列表将自动滚动到最开始,到它的第一个元素。我想从 ListView 中删除一个元素,以便之后它的行为是正确的,并且 ListView 本身不会滚动到最开始。

在这里,我存储了一个列表,我将其传递给 ListView

List<Channel> _editorChannels;

这就是我删除元素的方式。我通过唯一 ID 在列表中查找它并将其删除。

onDelete(Channel c) {
    final f = _editorChannels.firstWhere((element) => element.Id == c.Id,
        orElse: () => null);
    if (f != null) setState(() => _editorChannels.remove(f));
  }

这就是 ListView 的样子,它位于 DefaultTabController 选项卡之一中,我将仅简要显示一个选项卡的代码,以免代码过多。

      Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Expanded(
                child: Scaffold(
                  body: ListView.builder(
                      key: UniqueKey(),
                      itemCount: _editorChannels == null
                          ? 0
                          : _editorChannels.length,
                      itemBuilder: (context, index) {
                        final item = _editorChannels[index];
                        return Card(
                          shadowColor: Colors.black26,
                          margin: EdgeInsets.all(3.0),
                          clipBehavior: Clip.antiAlias,
                          shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(2),
                          ),
                          child: ListTile(
                              title: Container(
                                  child: Text(
                                item.Name != null ? item.Name : '',
                                style: new TextStyle(
                                    fontWeight: FontWeight.bold,
                                    fontSize: 16.0),
                              )),
                              subtitle: Text(item.Url),
                              onTap: () => {
                                    Navigator.push(
                                      context,
                                      MaterialPageRoute(
                                          builder: (context) =>
                                              ChewieDemo(channel: item)),
                                    )
                                  },
                              isThreeLine: false,
                              leading: getIconbyId(item.Status),
                              trailing: PopMenuWidget(
                                channel: item,
                                onDelete: () => onDelete(item),
                                onUpdate: (item) => onUpdate(item),
                              )),
                        );
                      }),
                ),
              )
            ],
          ),
listview
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    Spatz
    2021-11-30T20:46:11Z2021-11-30T20:46:11Z

    使用该参数key: UniqueKey()会导致每次重绘屏幕时都重新创建小部件。先前创建的小部件视图通过给定的键映射到新的视图,并且该键始终是唯一的。至于GlobalKey,它并没有对将小部件与渲染对象匹配的机制进行任何特殊更改,因为 本质上是对小部件状态的全局引用。

    在您的示例中,使用UniqueKey会导致 ListView 在其原始状态下完全重新创建,除了保存当前位置的问题外,还会降低性能。

    说到性能,使用ListView.builder创建的小部件使用优化来仅呈现列表的可见元素并在滚动时移除远处的元素。第二点,可能在您的情况下导致视图与模型“不匹配”,即当从模型中删除元素时,旧的列表元素不会从视图中删除,而是与新参数一起重用。如果列表元素中没有具有自己状态的元素,那么这不会导致问题。否则,有状态小部件仍将使用其旧状态,尽管通过参数将完全不同的列表元素的数据传递给它。这可以通过一个简单的例子看出:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MaterialApp(home: ListViewPitfall()));
    }
    
    class ListViewPitfall extends StatefulWidget {
      @override
      _ListViewPitfallState createState() => _ListViewPitfallState();
    }
    
    class _ListViewPitfallState extends State<ListViewPitfall> {
      final _content =
          List<String>.generate(64, (index) => "List item №${index + 1}");
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: ListView.builder(
            itemCount: _content.length,
            itemBuilder: (context, index) => ListTile(
              title: Row(
                children: [
                  Expanded(child: Text(_content[index])),
                  ClumsyWidget(
                    // this widget will be recreated on each render
                    // if you uncomment next line
                    // key: UniqueKey(),
                    title: _content[index],
                  ),
                  SizedBox(width: 6.0),
                  RaisedButton(
                    child: Text("delete"),
                    onPressed: () => setState(() {
                      _content.removeAt(index);
                    }),
                  )
                ],
              ),
              onTap: () => print(_content[index]),
            ),
          ),
        );
      }
    }
    
    class ClumsyWidget extends StatefulWidget {
      final String title;
    
      const ClumsyWidget({Key key, this.title}) : super(key: key);
      @override
      _ClumsyWidgetState createState() => _ClumsyWidgetState();
    }
    
    class _ClumsyWidgetState extends State<ClumsyWidget> {
      String statefulTitle;
    
      @override
      void initState() {
        super.initState();
        statefulTitle = widget.title;
      }
    
      @override
      Widget build(BuildContext context) {
        return OutlineButton(
          onPressed: () {
            setState(() {
              statefulTitle = widget.title;
            });
          },
          child: Text(statefulTitle),
        );
      }
    }
    

    在行的左侧,使用了无状态小部件,而在右侧,在删除按钮之前,是有状态的。删除行时,下方元素的左右部分不匹配。如果您将列表滚动到末尾并返回,那么不匹配就会消失。有状态的小部件由一个按钮制成,当单击该按钮时,它会根据小部件的参数更新状态。此外,代码中有一个注释掉的片段,它使用UniqueKey作为“拐杖”。

    • 3

相关问题

  • FutureBuilder 和 ListView 在达到屏幕长度的 80% 时的颤动滚动事件?

  • 仅在特定区域可见的 ListView

  • 如何在 Row 中定位元素?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何从列表中打印最大元素(str 类型)的长度?

    • 2 个回答
  • Marko Smith

    如何在 PyQT5 中清除 QFrame 的内容

    • 1 个回答
  • Marko Smith

    如何将具有特定字符的字符串拆分为两个不同的列表?

    • 2 个回答
  • Marko Smith

    导航栏活动元素

    • 1 个回答
  • Marko Smith

    是否可以将文本放入数组中?[关闭]

    • 1 个回答
  • Marko Smith

    如何一次用多个分隔符拆分字符串?

    • 1 个回答
  • Marko Smith

    如何通过 ClassPath 创建 InputStream?

    • 2 个回答
  • Marko Smith

    在一个查询中连接多个表

    • 1 个回答
  • Marko Smith

    对列表列表中的所有值求和

    • 3 个回答
  • Marko Smith

    如何对齐 string.Format 中的列?

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5