likes
comments
collection
share

Flutter - 船新升级😱支持观察第三方构建的滚动视图💪

作者站长头像
站长
· 阅读数 26

系列文章

开源库: flutter_scrollview_observer

一、概述

感谢大家对 scrollview_observer 的支持,上一篇关于 scrollview_observer 的最新一篇文章是在2022年的10月份,不知不觉已经过去了8个月,它现在已经变得更加完善和强大,让我们一起来看看都做了哪些更新吧 🥳

版本信息

二、自定义触发观察的时机

未添加该功能前,滚动视图在整个滚动过程中都会被观察,计算找出第一个 item 和正在展示的所有 item,当第一个 item 或者正在展示的 item 的对象发生变化时才会调用 [onObserve][onObserveAll] 回调。

但往往在一些场景下,我们想要指定在某个滚动状态下才进行观察,以及观察回调的触发时机,那该怎么办呢?

1、autoTriggerObserveTypes 参数

现在你可以通过该参数设置自动触发观察的时机,定义如下:

final List<ObserverAutoTriggerObserveType>? autoTriggerObserveTypes;
enum ObserverAutoTriggerObserveType {
  scrollStart,
  scrollUpdate,
  scrollEnd,
}

其默认值为 [.scrollStart, .scrollUpdate, .scrollEnd]

枚举值说明:

枚举值描述
scrollStart开始滚动
scrollUpdate滚动中
scrollEnd结束滚动

如在视频列表的场景中,我们可能只需要在列表结束滚动时再去做观察哪个 item 是第一个,然后进行自动播放,这个时候设置 autoTriggerObserveTypes.scrollEnd 即可,同时也避免了大量不必要的计算。

2、triggerOnObserveType 参数

用于配置触发 [onObserve][onObserveAll] 回调的前提,定义如下:

final ObserverTriggerOnObserveType triggerOnObserveType;
enum ObserverTriggerOnObserveType {
  directly,
  displayingItemsChange,
}

其默认值为 .displayingItemsChange

枚举值说明:

枚举值描述
directly观察到数据后直接将数据返出
displayingItemsChange当列表子部件进出或数量发生变化时将观察到的数据返出

一般是在实时获取 itemleadingMarginToViewportitem 顶部与视窗顶部的距离) 和 trailingMarginToViewportitem 底部与视窗底部的距离)的时候比较常用。

Flutter - 船新升级😱支持观察第三方构建的滚动视图💪

三、保持IM会话位置功能增强

之前限制只支持插入一条消息时保持会话位置,现在放开支持多条,使用方式不变,效果如下

Flutter - 船新升级😱支持观察第三方构建的滚动视图💪

注:该功能依赖被插入消息前的最新消息视图做为参照去计算偏移量,所以如果一次性插入的消息数太多,导致该参照消息视图无法得到渲染,则该功能会失效,需要你自己去对 ScrollView 的 cacheExtent 设置合理的值来尽量避免这个问题!

四、支持观察瀑布流

原先仅针对 GridView 这种常规网格布局,只处理顶部偏移量一致的 item,现在调整了内部处理逻辑,已支持瀑布流这种模式

Flutter - 船新升级😱支持观察第三方构建的滚动视图💪

如上图红线所示,位于第一的 itemgrid item2grid item3

五、支持观察viewport

大家多多少少会遇到这种情况,在 CustomScrollView 中放置了好多 sliver,需要对它们进行观察,判断哪些 sliver 正在展示,而不仅仅是观察 SliverListSliverGrid 中的 item

在这里可以使用 onObserveViewport 回调得到你想要的结果,代码如下

SliverViewObserver(
  child: _buildScrollView(),
  sliverContexts: () {
    return [
      if (grid1Context != null) grid1Context!,
      if (swipeContext != null) swipeContext!,
      if (grid2Context != null) grid2Context!,
    ];
  },
  onObserveViewport: (result) {
    firstChildCtxInViewport = result.firstChild.sliverContext;
    if (firstChildCtxInViewport == grid1Context) {
      debugPrint('current first sliver in viewport - gridView1');
    } else if (firstChildCtxInViewport == swipeContext) {
      debugPrint('current first sliver in viewport - swipeView');
    } else if (firstChildCtxInViewport == grid2Context) {
      debugPrint('current first sliver in viewport - gridView2');
    }
  },
)
  1. sliverContexts 回调中返回需要观察的 sliverBuildContext
  2. onObserveViewport 回调中拿到观察结果,结果对应的类为 SliverViewportObserveModel
class SliverViewportObserveModel {
  /// 当前 CustomScrollView 所对应的 viewport 实例.
  final RenderViewportBase viewport;

  /// 根据 sliverContexts,返回当前观察到的第一个 sliver
  final SliverViewportObserveDisplayingChildModel firstChild;

  /// 根据 sliverContexts,返回当前观察到的正在展示的所有 sliver
  final List<SliverViewportObserveDisplayingChildModel>
      displayingChildModelList;
  ...
}

这里需要注意的是,观察的对象不应为嵌套的 sliver,如下代码所示:

SliverPadding(
  sliver: SliverList(
    delegate: SliverChildBuilderDelegate(
      (context, index) {
        return ListTile(title: Text('index - $index'));
      },
    ),
  ),
  padding: const EdgeInsets.all(8),
);

你需要观察的是 SliverPadding,而不是 SliverList,在给 sliverContexts 传递数据时需要留意,否则观察无效。

那一块的 SliverBuildContext 要怎么获取?

你可以使用 GlobalKey,但这里更建议使用 SliverLayoutBuilder

SliverLayoutBuilder(
  builder: (context, _) {
    // 将 context 记录起来
    if (sliverCtx != context) sliverCtx = context;
  
    SliverPadding(
      sliver: SliverList(
        delegate: SliverChildBuilderDelegate(
          ...
        ),
      ),
      padding: const EdgeInsets.all(8),
    );
  },
);

六、支持自定义观察对象和观察逻辑

1、customTargetRenderSliverType 回调

仅支持 ListViewObserverGridViewObserver

在保持原来的观察逻辑上,告诉 scrollview_observer 要处理的 RenderSliver 类型,目的是为了支持对第三方库构建的滚动视图进行观察。

customTargetRenderSliverType: (renderObj) {
  // 告诉该库它需要观察什么类型的 RenderObject
  // 这里以观察 loading_more_list 的 ListView 为例
  return renderObj is ExtendedRenderSliverList;
},

这里罗列几个在 loading_more_list 中常用的 RenderObject 供大家对照使用。

类型RenderObject
ListViewExtendedRenderSliverList
GridViewExtendedRenderSliverGrid
WaterfallFlowRenderSliverWaterfallFlow

2、customHandleObserve 回调

该回调用于自定义观察逻辑,当自带的处理逻辑不符合你的需求时使用。

customHandleObserve: (context) {
  // 可以完全自定义你的观察逻辑
  final _obj = context.findRenderObject();
  if (_obj is RenderSliverList) {
    // 可以使用该库提供的默认处理方式
    ObserverCore.handleListObserve(context: context);
  }
  if (_obj is RenderSliverGrid || _obj is RenderSliverWaterfallFlow) {
    // 可以使用该库提供的默认处理方式
    return ObserverCore.handleGridObserve(context: context);
  }
  // 处理一些由第三方构建的 Sliver
  ...
  
  // 其它类型不做处理
  return null;
},

现已将原处理 RenderSliverListRenderSliverGride 的逻辑抽离至 ObserverCore 中供大家自由组合使用。

3、extendedHandleObserve 回调

仅支持 SliverViewObserver

该回调用于对原来的观察逻辑进行补充,原来只处理 RenderSliverListRenderSliverFixedExtentListRenderSliverGrid

extendedHandleObserve: (context) {
  // 在对原来的观察逻辑进行拓展
  final _obj = context.findRenderObject();
  if (_obj is RenderSliverWaterfallFlow) {
    return ObserverCore.handleGridObserve(context: context);
  }
  return null;
},

结合 ObserverCore 和以上的几个功能点,实现了可自由观察由第三方构建的滚动视图的功能,不再局限于官方的 SliverListSliverGrid

这是一个很有用的功能,它让你的滚动视图的构建变得更加自由,比如观察使用 fluttercandies/waterfall_flow 构建的瀑布流。

七、定位Tab的下标计算支持网格类型

ObserverUtils.calcAnchorTabIndex 进行增强,现支持处理列表和网格的模块下标计算,这里直接看效果图

Flutter - 船新升级😱支持观察第三方构建的滚动视图💪

GitHub: github.com/LinXunFeng/…

Flutter - 船新升级😱支持观察第三方构建的滚动视图💪

转载自:https://juejin.cn/post/7240751116702269477
评论
请登录