Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(三)


前言

项目实现请看上篇文章,今天来讲讲项目实现后的第一个棘手问题,粘性头部切换过程中偶尔会有空隙。这个问题真是逼死我这个强迫症😣,差点都想尝试其他方式从头实现了。

项目地址

问题描述

话不多说,先看看问题是个什么情况:

screenshot1

向下滑动过程中,Header #0和Header #3之间存在时有时无的空隙,空隙大小也不是固定的,同时向上滑动过程中是正常的。

问题分析

首先判断粘性头部切换过程中的偏移量计算方式是否有问题,如果是计算方式导致的偏差,那应该不止出现空隙这么简单,同时向下滑动过程中应该也会出现空隙。先大胆假设计算方式没有问题,那么很有可能是参与计算的参数有问题。

计算当前粘性头部偏移量源码:

Offset _calculateOffset(StickyHeaderInfo stickyHeaderInfo, StickyHeaderInfo nextStickyHeaderInfo) {
  var d = nextStickyHeaderInfo.offset.dy - stickyHeaderInfo.size.height;
  d = min(0.0, d);
  return Offset(0.0, d);
}

计算偏移量用到了两个参数,一是下一个头部组件相对滚动组件的偏移量,二是粘性头部的高度。粘性头部的高度是固定的,所以如果计算参数有问题,那必然是下一个头部组件相对滚动组件的偏移量有偏差。项目的核心就是基于头部组件偏移量实现的(详见上一篇文章),现在才跟我说这个计算出来的偏移量不靠谱😱,跟我闹着玩呢😭!

莫慌,先确认一下头部组件相对滚动组件的偏移量是否真的有偏差。

screenshot2

正常来说,Header #0回到原位后,相对滚动组件的偏移量应该是0才对,但是却出现一点点偏差,这说明前面的假设是对的,获取的偏移量有问题。

解决方案

解决这个问题最直接的方法就是查明引起这个问题的原因,可惜的是我查了一番没得出什么结果,如果有人知道问题所在欢迎评论留言。既然原因不好找,那只能试着修正偏移量的偏差。修正偏移量的源码如下所示:

void _correctOffset(StickyHeaderInfo stickyHeaderInfo) {
  // currentPixels: 当前滚动偏移量,相对滚动组件起始位置的偏移量
  // stickyHeaderInfo.offset: 头部组件相对滚动组件的偏移量
  // stickyHeaderInfo.pixels: 头部组件相对滚动组件起始位置的偏移量
  double deviation = currentPixels + stickyHeaderInfo.offset.dy - stickyHeaderInfo.pixels;
  stickyHeaderInfo.offset -= Offset(0.0, deviation);
}

源码中的三个偏移量关系如下图所示:

screenshot3

修正方法是有了,可是头部组件相对滚动组件起始位置的偏移量从哪里获取呢?

最简单的方法,在构建StickyContainerWidget时传入不就好了吗?这么一想我可真是个小机灵鬼👏。机灵个…鬼啊,项目做成这样谁用啊,自己都嫌弃,还谈什么易用性😣。好在翻Flutter源码的时候发现了getOffsetToReveal方法,可以实现自动获取头部组件相对滚动组件起始位置的偏移量。

到此,这个问题已经解决了…才怪!

为了提升性能,减少getOffsetToReveal方法的调用,我做了缓存处理,只获取一次。在大部分场景下,这并没有什么问题,但是当遇到由多个SliverList组成的滚动组件时问题出现了。该场景下getOffsetToReveal方法获取的值不是固定的,只有在快滚动到头部组件时获取的值才是准确的。

screenshot4

没办法,为了解决这个问题只能增加一个新的performancePriority属性,当该属性设为false时,不做缓存处理,改为实时获取。建议使用默认的true,除非你遇到了这个问题。如果你对性能有极高的要求并且明确知道每一个头部组件的pixels(头部组件相对滚动组件起始位置的偏移量),那么可以在构建StickyContainerWidget时指定pixels,从而进一步优化性能。注意,这是可选的,相信我,一般不传性能也是很可观的,不然我费那么大劲干嘛。

RenderStickyContainer部分源码:

class RenderStickyContainer extends RenderProxyBox {
  double? _pixelsCache;

  double? pixels;
  bool performancePriority;

  double get _pixels {
    if (pixels != null) {
      return pixels ?? 0.0;
    } else {
      if (_pixelsCache == null || !performancePriority) {
        _pixelsCache = RenderAbstractViewport.of(this)?.getOffsetToReveal(this, 0.0).offset;
      }
      return _pixelsCache ?? 0.0;
    }
  }
}

总结

写到这,我以为这个问题的讨论应该就此结束了,但是呢,写着写着我发现我走了弯路,这个问题还有更好的解决办法。可能已经有人通过上面的内容发现了,欢迎大家评论留言。下一篇文章会继续写这个问题,如果你有兴趣了解的话,欢迎查看后续文章。

最后

如果这个项目对你有所帮助,点赞👍加星🌟安排一下~

如果你发现了bug或想要新功能,欢迎在issue留言讨论,同时也欢迎你加入到这个项目中,为这项目出一份力。


系列文章

Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(一)
Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(二)
Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(三)
Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(四)
Flutter - 一个易用且功能强大的粘性头部组件库,适用于任何支持滚动的组件(五)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值