接着上篇,没看上篇的小伙伴建议先看下上篇,免得断片中。。

我继续讲下第2个问题的解决方案。

当在里面放上tabview,并且tab是缓存状态的时候,会出现滚动会互相影响的问题

上篇我们说到 在我们的主角NestedScrollView当中,有2个ScrollController.

class _NestedScrollController extends ScrollController {_NestedScrollController(this.coordinator, {double initialScrollOffset = 0.0,String debugLabel,
复制代码

一个是inner,一个outer。 outer是负责headerSliverBuilder里面的滚动widgets inner是负责body里面的滚动widgets 当outer滚动到底了之后,就会看看inner里面是否有能滚动的东东,开始滚动

Tabview是在body里面,这里我们肯定需要对inner进行处理。 首先我们要明白,NestedScrollView是怎么处理outer和inner的关系的。

找到这个_NestedScrollCoordinator 的applyUserOffset方法中处理了整个NestedScrollView的滑动处理

 @overridevoid applyUserOffset(double delta) {updateUserScrollDirection(delta > 0.0 ? ScrollDirection.forward : ScrollDirection.reverse);assert(delta != 0.0);if (_innerPositions.isEmpty) {_outerPosition.applyFullDragUpdate(delta);} else if (delta < 0.0) {// dragging "up"// TODO(ianh): prioritize first getting rid of overscroll, and then the// outer view, so that the app bar will scroll out of the way asap.// Right now we ignore overscroll. This works fine on Android but looks// weird on iOS if you fling down then up. The problem is it's not at all// clear what this should do when you have multiple inner positions at// different levels of overscroll.final double innerDelta = _outerPosition.applyClampedDragUpdate(delta);if (innerDelta != 0.0) {for (_NestedScrollPosition position in _innerPositions)position.applyFullDragUpdate(innerDelta);}} else {// dragging "down" - delta is positive// prioritize the inner views, so that the inner content will move before the app bar growsdouble outerDelta = 0.0; // it will go positive if it changesfinal List<double> overscrolls = <double>[];final List<_NestedScrollPosition> innerPositions = _innerPositions.toList();for (_NestedScrollPosition position in innerPositions) {final double overscroll = position.applyClampedDragUpdate(delta);outerDelta = math.max(outerDelta, overscroll);overscrolls.add(overscroll);}if (outerDelta != 0.0)outerDelta -= _outerPosition.applyClampedDragUpdate(outerDelta);// now deal with any overscrollfor (int i = 0; i < innerPositions.length; ++i) {final double remainingDelta = overscrolls[i] - outerDelta;if (remainingDelta > 0.0)innerPositions[i].applyFullDragUpdate(remainingDelta);}}}
复制代码
Iterable<_NestedScrollPosition> get _innerPositions {return _innerController.nestedPositions;}
复制代码

看到_innerPositions是我们要关注的东西,通过debug,我发现,如果tabview的每个tab做了缓存,那么每个tab里面列表的ScrollPosition将一直缓存在这个ScrollController里面。 当tab到tabview的某个tab的时候,ScrollController将会将这ScrollPosition attach 上,如果没有缓存,将会在离开的时候detach掉。

@overridevoid attach(ScrollPosition position) {assert(position is _NestedScrollPosition);super.attach(position);coordinator.updateParent();coordinator.updateCanDrag();position.addListener(_scheduleUpdateShadow);_scheduleUpdateShadow();}@overridevoid detach(ScrollPosition position) {assert(position is _NestedScrollPosition);position.removeListener(_scheduleUpdateShadow);super.detach(position);_scheduleUpdateShadow();}
复制代码

真相只有一个。。是的。。可以说。。造成缓存tabview的各个tab里面的列表互相影响的原因,是因为歪果仁说: as design(我就是这样设计的,不服吗).

按照我的思想啊,我滚动的时候。当然只想影响当前显示的这个列表啊。这不科学啊。。

找到原因找到原理,一切就都好解决了。现在的关键点在于,我怎么能知道显示对应的是哪个列表的?!

这个问题问了很多人。。也查找了好久都没找到好的方式去获取当前 激活的 列表对应的 ScrollPosition。。终于我只能想到一个 workaround。暂时解决这个问题。

提供一个容器,把inner里面的滚动列表包裹起来,并且设置它的tab 的唯一key

//pack your inner scrollables which are in  NestedScrollView body
//so that it can find the active scrollable
//compare with NestedScrollViewInnerScrollPositionKeyBuilder
class NestedScrollViewInnerScrollPositionKeyWidget extends StatefulWidget {final Key scrollPositionKey;final Widget child;NestedScrollViewInnerScrollPositionKeyWidget(this.scrollPositionKey, this.child);@override_NestedScrollViewInnerScrollPositionKeyWidgetState createState() =>_NestedScrollViewInnerScrollPositionKeyWidgetState();
}class _NestedScrollViewInnerScrollPositionKeyWidgetStateextends State<NestedScrollViewInnerScrollPositionKeyWidget> {@overrideWidget build(BuildContext context) {return widget.child;}//  @override
//  void didChangeDependencies() {//    // TODO: implement didChangeDependencies
//    //print("didChangeDependencies"+widget.scrollPositionKey.toString());
//    super.didChangeDependencies();
//  }
//
//  @override
//  void didUpdateWidget(NestedScrollViewInnerScrollPositionKeyWidget oldWidget) {//    // TODO: implement didUpdateWidget
//    //print("didUpdateWidget"+widget.scrollPositionKey.toString()+oldWidget.scrollPositionKey.toString());
//    super.didUpdateWidget(oldWidget);
//  }
}
复制代码

然后在刚才attach方法中通过先祖NestedScrollViewInnerScrollPositionKeyWidget

@overridevoid attach(ScrollPosition position) {assert(position is _NestedScrollPosition);super.attach(position);attachScrollPositionKey(position as _NestedScrollPosition);coordinator.updateParent();coordinator.updateCanDrag();position.addListener(_scheduleUpdateShadow);_scheduleUpdateShadow();}@overridevoid detach(ScrollPosition position) {assert(position is _NestedScrollPosition);position.removeListener(_scheduleUpdateShadow);super.detach(position);detachScrollPositionKey(position as _NestedScrollPosition);_scheduleUpdateShadow();}void attachScrollPositionKey(_NestedScrollPosition position) {if (position != null && scrollPositionKeyMap != null) {var key = position.setScrollPositionKey();if (key != null) {if (!scrollPositionKeyMap.containsKey(key)) {scrollPositionKeyMap[key] = position;} else if (scrollPositionKeyMap[key] != position) {//in demo ,when tab to tab03, the tab02 key will be tab00 at first//then it become tab02.//this is not a good solutionposition.clearScrollPositionKey();Future.delayed(Duration(milliseconds: 500), () {attachScrollPositionKey(position);});}}}}void detachScrollPositionKey(_NestedScrollPosition position) {if (position != null &&scrollPositionKeyMap != null &&position.key != null &&scrollPositionKeyMap.containsKey(position.key)) {scrollPositionKeyMap.remove(position.key);position.clearScrollPositionKey();}}复制代码

获取先祖NestedScrollViewInnerScrollPositionKeyWidget方法

Key setScrollPositionKey() {//if (haveDimensions) {final type = _typeOf<NestedScrollViewInnerScrollPositionKeyWidget>();NestedScrollViewInnerScrollPositionKeyWidget keyWidget =(this.context as ScrollableState)?.context?.ancestorWidgetOfExactType(type);_key = keyWidget?.scrollPositionKey;return _key;}
复制代码

找到这个_NestedScrollCoordinator 的applyUserOffset方法中我们现在要替换掉 _innerPositions为_currentInnerPositions

Iterable<_NestedScrollPosition> get _innerPositions {//return _currentPositions;return _innerController.nestedPositions;}Iterable<_NestedScrollPosition> get _currentInnerPositions {return _innerController.getCurrentNestedPositions(innerScrollPositionKeyBuilder);}复制代码

getCurrentNestedPositions里面的代码

  Iterable<_NestedScrollPosition> getCurrentNestedPositions(NestedScrollViewInnerScrollPositionKeyBuilderinnerScrollPositionKeyBuilder) {if (innerScrollPositionKeyBuilder != null &&scrollPositionKeyMap.length > 1) {var key = innerScrollPositionKeyBuilder();if (scrollPositionKeyMap.containsKey(key)) {return <_NestedScrollPosition>[scrollPositionKeyMap[key]];} else {return nestedPositions;}}return nestedPositions;}
复制代码

SampeCode

extended.NestedScrollView(headerSliverBuilder: (c, f) {return _buildSliverHeader(primaryTabBar);},//pinnedHeaderSliverHeightBuilder: () {return pinnedHeaderHeight;},innerScrollPositionKeyBuilder: () {var index = "Tab";if (primaryTC.index == 0) {index +=(primaryTC.index.toString() + secondaryTC.index.toString());} else {index += primaryTC.index.toString();}return Key(index);},
复制代码

这里由你自己协定tab key。。我这里是一级tab+二级tab的index。。比如 Tab00代表一级tab第一个下面的二级tab的第一个。

定义tab里面的列表的时候如下,比如第一个tab下面的二级tab的第一个列表,那么它的key 为Tab00.

return extended.NestedScrollViewInnerScrollPositionKeyWidget(Key("Tab00"),// myRefresh.RefreshIndicator(// child:ListView.builder(itemBuilder: (c, i) {return Container(//decoration: BoxDecoration(border: Border.all(color: Colors.orange,width: 1.0)),alignment: Alignment.center,height: 60.0,child: Text(widget.tabKey.toString() + ": List$i"),);},itemCount: 100)//,//onRefresh: onRefresh,// ));
复制代码

最后放上 Github extended_nested_scroll_view,如果你有更好的方式解决这个问题或者有什么不明白的地方,都请告诉我,由衷感谢。

Flutter 扩展NestedScrollView (二)列表滚动同步解决相关推荐

  1. native固定吸顶 react_React Native固定底部TextInput,解决键盘遮挡、列表滚动问题

    效果图 timer.gif 做类似于微信聊天输入框,可能会遇到下面三个小困扰,记录一下. 目录 # 1.React Native固定底部TextInput # 2.键盘遮挡问题 # 3.列表滚动问题 ...

  2. 不一样角度带您了解 Flutter 中的滑动列表实现 | 开发者说·DTalk

    本文原作者: 恋猫de小郭,原‍文发布于: GSYTech 本篇主要帮助剖析理解 Flutter 里的列表和滑动的组成,用比较通俗易懂的方式,从常见的 ListView 到 NestedScrollV ...

  3. 有道云笔记不同步_有道云笔记笔记不能同步怎么办 有道云笔记无法同步解决教程...

    类型:商务办公大小:43.8M语言:中文 评分:10.0 标签: 立即下载 有道云笔记是一款非常好用的同步式记录软件,用户可以使用有道云笔记轻松便捷管理笔记,不过有些朋友在使用有道云笔记的时候出现了 ...

  4. 有道云笔记不同步_mac中有道云笔记不能同步解决办法介绍

    大家都知道mac系统中的有道云笔记可是一款专业的笔记软件,那么很多用户们在同步的时候容易出现问题,不要着急,下面小编就为大家带来mac中有道云笔记不能同步解决办法介绍,希望看了这款mac中有道云笔记不 ...

  5. 仿网易/QQ空间视频列表滚动连播炫酷效果

    代码地址如下: http://www.demodashi.com/demo/11201.html 一.准备工作 AndroidStudio 开发环境 需要下载七牛的开源播放器SDK 本例子实现了仿网易 ...

  6. Flutter 画笔绘制二维码扫描框

    文章目录 一.CustomPaint介绍 1. CustomPaint 2. CustomPainter 3. Paint & Canvas 4. 示例(绘制文本背景) 二.计算扫描框四个点坐 ...

  7. Mysql主从(主从不同步解决办法,常见问题及解决办法)

    一.主从不同步解决办法 先上Master库:  mysql>show processlist; 查看下进程是否Sleep太多.发现很正常.  show master status; 也正常.  ...

  8. anki android自动同步,解决Anki服务器同步问题:坚果云 Floder sync (亲测有效)

    零.为什么我要解决Anki同步问题? 0.仅实验PC和安卓端,原理很简单 1.插入图片后更新慢 2.更新不稳定 3.更好的使用Anki 一. 准备工具 1.电脑端:坚果云 2.Anki数据存放地址 C ...

  9. mac $php_autoconf,开发者说PHPersay-Mac安装Swoole扩展phpize 时 Cannot find autoconf 解决方法...

    Mac安装Swoole扩展phpize 时 Cannot find autoconf 解决方法swoole-1.9.23 phpizeConfiguring for:PHP Api Version: ...

  10. ACL扩展IP访问控制列表配置

    一.实验目标 理解扩展IP访问控制列表的原理及功能: 掌握编号的扩展IP访问控制列表的配置方法: 二.实验背景 分公司和总公司分别属于不同的网段,部门之间用路由器进行信息传递,为了安全起见,分公司领导 ...

最新文章

  1. linux uts namespace 提供了主机名和域名的隔离 docker中被用到
  2. HDU 1564 简单博弈 水
  3. SQL、LINQ、Lambda 三种用法(转)
  4. Excel Oledb设置
  5. NLP基础 : HMM 隐马尔可夫模型
  6. 让IE和Firefox(包括chrome)浏览器默认产生滚动条的滚动槽
  7. Leetcode--130. 被围绕的区域(java)
  8. [转载] 2020最新Java面试题,常见面试题及答案汇总
  9. 87-Spark推测执行spark.speculation
  10. android 入门 (分析: 非匿名内部类 监听功能的实现)
  11. BZOJ1101 [POI2007] Zap
  12. [软件工程] 数据字典
  13. 基于边缘AI计算的人员入侵检测CNN算法在实际场景中的应用
  14. 51单片机智能循迹小车的通俗易懂讲解
  15. R语言ggplot2可视化条形图:通过双色渐变配色颜色主题可视化条形图
  16. 算法很美 笔记 3.查找和排序
  17. Linux基础入门(详细教程)
  18. api文档 luci_开发OpenWrt路由器上LuCI的模块
  19. 3.2 人工智能关键技术
  20. TESLA M40折腾笔记

热门文章

  1. 阿玛尼搜索引擎收录_被全网黑的阿玛尼权利,我一个大干皮咋用的挺好?
  2. ELMo ,LM:一串词序列的概率分布probability distribution over sequences of words
  3. 建模大师怎么安装到revit中_用协同大师完成Revit协同工作的教程详解
  4. 凸优化第六章逼近与拟合 6.2最小范数问题
  5. 机器学习概念篇:监督学习、过拟合,正则化,泛化能力等概念以及防止过拟合方法总结
  6. Mapreduce从HBASE抽取数据,生成搜索下拉服务数据,hadoop jar 调用异常问题解决
  7. 【生信进阶练习1000days】day13-GEOquery
  8. Reproxy:边缘服务器反向代理工具
  9. SVN源码泄露漏洞总结
  10. 70.爬楼梯 (力扣leetcode) 博主可答疑该问题