ListVIew 再次构建后不会滚动到正确的偏移位置

Posted

技术标签:

【中文标题】ListVIew 再次构建后不会滚动到正确的偏移位置【英文标题】:ListVIew doesn't scroll to the correct offset position after its built again 【发布时间】:2019-10-15 07:47:33 【问题描述】:

我希望列表视图以偏移量开始。我试图通过在 ListView 中使用以下代码来实现。

控制器:ScrollController(initialScrollOffset: 30 * ITEM_HEIGHT),

最初在第一次加载时,列表会加载正确的偏移量。

当通过从父小部件调用 set state 再次构建列表时,列表会更新,但滚动偏移量的行为很奇怪。

有两种情况:

    我不滚动列表:在此之后,如果调用设置状态,一切正常。列表得到更新,并且始终处于正确的偏移量。 我滚动列表:如果我滚动列表然后重建列表, 滚动偏移量减少了几项。列表得到更新,这很好。

是因为当我滚动时它保持最后滚动位置并且抵消了我的计算?我认为这不应该发生,因为它是一个无状态的小部件。

class DaysManager extends StatelessWidget 
  final int daysBeforeFocusDate = 30;
  final int totalDaysToInit = 61;
  static final double ITEM_HEIGHT = 108.00;

  ScrollController scrollController;

  List<Day> days;

  DaysManager(DateTime focusDate) 
      final DateTime startDate =
      focusDate.subtract(Duration(days: daysBeforeFocusDate));
      days = List.generate(totalDaysToInit, (int index) 
      return Day(
       date: startDate.add(
        Duration(days: index),
    ),
  );
);

scrollController = ScrollController(initialScrollOffset: 30 *ITEM_HEIGHT);


  @override
  Widget build(BuildContext context) 
   return _buildScrollView();
  

  ListView _buildScrollView() 
    ListView listView = ListView.builder(
    cacheExtent: 0,
    controller: scrollController,
    itemCount: days.length,
    itemBuilder: (BuildContext context, int index) 
      return days[index];
     );

    return listView;
  
 

【问题讨论】:

你在哪里有这个scrollController = ScrollController(initialScrollOffset: 30 *ITEM_HEIGHT); ?直接在类或方法中? 我已经添加了代码。请看那里。 【参考方案1】:

我联系了 Flutter Slack 社区并得到了有效的答案。都归功于楼上那边。这是那次谈话的副本。

ScrollController 将其滚动位置保存在 它所附加到的列表的PageStorage 记录中……而不是它自己的 PageStorage。因此,当重新创建小部件时,由于您没有为 listview 小部件指定新的key,它会重用相同的键(flutter 内部的许多优化之一以提高性能)。你可以通过添加两行来解决这个问题:

import 'dart:math';
...
ListView listView = ListView.builder(
  key: ValueKey<int>(Random(DateTime.now().millisecondsSinceEpoch).nextInt(4294967296)),
...

每次重新创建列表视图时,您都需要给它一个新的随机 key,这样它就不会加载它的 PageStorage 值。

这是您的示例代码中DaysManager 的完整更新代码:

class DaysManager extends StatelessWidget 
  final int daysBeforeFocusDate = 30;
  final int totalDaysToInit = 61;
  static final double ITEM_HEIGHT = 108.00;

  ScrollController scrollController;

  List<Day> days;

  DaysManager(
    DateTime focusDate,
  ) 
    final DateTime startDate = focusDate.subtract(Duration(days: daysBeforeFocusDate));
    days = List.generate(totalDaysToInit, (int index) 
      return Day(
        date: startDate.add(
          Duration(days: index),
        ),
      );
    );

    scrollController = ScrollController(
      initialScrollOffset: 30 *ITEM_HEIGHT,
    );
  

  @override
  Widget build(BuildContext context) 
    return _buildScrollView();
  

  ListView _buildScrollView() 
    ListView listView = ListView.builder(
      key: ValueKey<int>(Random(DateTime.now().millisecondsSinceEpoch).nextInt(4294967296)),
      cacheExtent: 0,
      controller: scrollController,
      itemCount: days.length,
      itemBuilder: (BuildContext context, int index) 
        return days[index];
      );

    return listView;
  

【讨论】:

一旦我意识到我们不想在两次绘制之间保持滚动位置,而是想将其重置到新选定项目的顶部(正好在 30 下清单),我理解了问题并创建了解决方案。然后我解释了为什么这是解决方案。感谢@will-luce 的支持 哇,这太棒了。最后我们得到了解决这个问题的办法。感谢您的回答。 虽然我需要了解PageStorage是什么以及它是如何工作的,它存储了什么样的信息。如果您了解更多请做解释。 PageStorage 保存了许多 Flutter 核心小部件上各种数据的键值对。您甚至可以将它用于您自己的小部件。不过,这一切都基于小部件的 Key 。将其视为特定于每个有状态小部件的数据的全局存储空间。它允许您保留小部件的数据,即使完全销毁小部件,只要您使用相同的 Key。在很多方面,它就像 Flutter 的内部状态管理。这是一个关于 Keys 的好视频youtube.com/watch?v=kn0EOS-ZiIc ...还有一篇关于PageStorage的文章steemit.com/utopian-io/@tensor/…

以上是关于ListVIew 再次构建后不会滚动到正确的偏移位置的主要内容,如果未能解决你的问题,请参考以下文章

即使我总是可滚动,我的 Listview 也不会滚动

ListView 滚动 - 一个一个

Android ListView 不会在滚动时重绘

如何根据条件为单元格正确分配样式,而不会在快速滚动后出现奇怪的样式行为?

ListView 项目在滚动后保持突出显示

动态设置Listview高度后正确检测Listview中的setOnScrollListener