Flutter listview中嵌套横向滚动的SingleChildScrollView保存滚动位置

1,865 阅读1分钟

【效果图后续补上】

结构是listview包含多种不同类型的item,然后其中一个是SingleChildScrollView。
点击SingleChildScrollView中的item刷新listview中的部分数据。
但是发现当SingleChildScrollView滑动很远之后,再滑动listview,当SingleChildScrollView滑出屏幕之后,再滑回来SingleChildScrollView就回到了初始位子,这肯定不行啊。
listview的item滑出屏幕之后会被回收的,设断点查看,来回滑动这个SingleChildScrollView并没有重新创建,但是他里面的内容会被重新创建,这样每次从屏幕外面划回来SingleChildScrollView就回到初始状态了。

接下来就是研究怎么不让它回收 重建,然后一顿查listview怎么缓存,SingleChildScrollView怎么保存滑动距离,保存状态等,查看源码发现SingleChildScrollView的controller默认是保存滑动距离的;但是因为SingleChildScrollView里面的内容都重新创建了所以滑动距离也就无用了;

无意中发现listview有 addAutomaticKeepAlives 这么一个属性,但是直接设置成 true又不好使,后来查了下发现它需要接收到需要保存状态的通知才会去保存item的,
查了下用法,在 StatefulWidget 的 state 中重写 wantKeepAlive,我的 SingleChildScrollView 里面的item正好都是 StatefulWidget;

最终参考 这个文章 修改了下 SingleChildScrollView 里面item的代码;

class TextWidget extends StatefulWidget {
  final Key key;
  final String text;
  final bool selected;

  // 接收一个Key
  TextWidget(this.key, this.text, this.selected);

  @override
  State<StatefulWidget> createState() => TextWidgetState(this.text, this.selected);
}

class TextWidgetState extends State<TextWidget> with AutomaticKeepAliveClientMixin {
  String _text = "0";
  bool _selected = false;

  TextWidgetState(this._text, this._selected);

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Text(
      _text,
      textAlign: TextAlign.center,
      style: TextStyle(
          fontSize: _selected ? 18 : 16,
          fontWeight: _selected ? FontWeight.bold : FontWeight.normal,
          color: _selected ? color333 : color777),
    );
  }

  void onPressed(bool selected) {
    setState(() => _selected = selected);
  }

  @override
  bool get wantKeepAlive => true;
}

重要的是 with AutomaticKeepAliveClientMixin@override bool get wantKeepAlive => true;