简体   繁体   English

ListVIew重新构建后无法滚动到正确的偏移位置

[英]ListVIew doesn't scroll to the correct offset position after its built again

I want the listview to start with an offset. 我希望列表视图以偏移量开头。 Which I am trying to achieve by using below code in the ListView. 我正在尝试通过在ListView中使用以下代码来实现。

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

Initially on the first load the list is loaded with the correct offset. 最初,第一次加载时,列表会加载正确的偏移量。

When the list is built again by calling set state from the parent widget, the list gets updated but the scroll offset behaves weird. 当通过从父窗口小部件调用set状态再次构建列表时,列表会更新,但滚动偏移的行为很奇怪。

There are two scenarios: 有两种情况:

  1. I don't scroll the list: After this if the set state is called everything works fine. 我不滚动列表:此后,如果调用了set状态,则一切正常。 List gets updated and is always at the correct offset. 列表会更新,并且始终处于正确的偏移量。
  2. I scroll the list: if I scroll the list and then the list is rebuilt, the scroll offset is off by a few items. 我滚动列表:如果我滚动列表,然后重新构建列表,则滚动偏移量将减少几项。 The list gets updated which is fine. 该列表已更新,这很好。

Is it because when I scroll it keeps the last scroll position and that offsets my calculation? 是因为在我滚动时它保持了最后一个滚动位置,并且这抵消了我的计算? Which I think should not happen as it is a state less widget. 我认为这不应该发生,因为它是状态少的小部件。

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;
  }
 }

I reached out the the Flutter Slack community and got an answer that works. 我接触了Flutter Slack社区,并得到了可行的答案。 All credit to Loushou over there. 全部归功于楼首。 Here's a copy of that conversation. 这是谈话内容的副本。

ScrollController saves it's scroll position inside the PageStorage record of the list it is attached to … not of it's own PageStorage. ScrollController将其滚动位置保存在其附加到列表PageStorage记录中……而不是它自己的PageStorage。 because of this, when the widget is recreated, since you do not specify a new key for the listview widget, it reuses the same key (one of the many optimizations internal to flutter for performance). 因此,在重新创建窗口小部件时,由于您没有为列表视图窗口小部件指定新key ,因此它会重复使用相同的键(为提高性能而进行内部优化的众多优化之一)。 you can solve this by adding two lines: 您可以通过添加两行来解决此问题:

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

You need to give the listview a new, random key every time you recreate it, so that it does not load up it's PageStorage values. 每次重新创建列表视图时,都需要给它一个新的随机key ,这样它就不会加载它的PageStorage值。

here is the full, updated code for DaysManager from your example code: 这是您的示例代码中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;
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM