Skip to content

1、监听滚动视图中正在显示的子部件

LinXunFeng edited this page May 20, 2024 · 4 revisions

使用

ListViewObserver 的参数说明:

参数 必传 说明
child 将构建的 ListView 做为 ListViewObserver 的子部件
sliverListContexts 该回调中返回需要被观察的 ListViewBuildContext,在需要精确指定 BuildContext 时才会用到该参数
onObserve 该回调可以监听到当前 第一个 Sliver 中正在显示中的子部件的相关信息
onObserveAll 该回调可以监听到当前 所有 Sliver 正在显示中的子部件的相关信息,当有多个 Sliver 时才需要使用到这个回调
leadingOffset 列表头部的计算偏移量,从该偏移量开始计算首个子部件
dynamicLeadingOffset leadingOffset 的动态版本,在列表头部的计算偏移量不确定时使用,优先级高于 leadingOffset
toNextOverPercent 首个子部件被遮挡的百分比达到该值时,则下一个子部件为首个子部件,默认值为 1
autoTriggerObserveTypes 用于设置自动触发观察的时机
triggerOnObserveType 用于配置触发 [onObserve] 或 [onObserveAll] 回调的前提
customHandleObserve 用于自定义观察逻辑,当自带的处理逻辑不符合你的需求时使用
customTargetRenderSliverType 在保持原来的观察逻辑上,告诉该库要处理的 RenderSliver,以支持对第三方库构建的列表进行观察。

方式一:常规(推荐)

使用上比较简单,应用范围广,一般情况下只需要使用该方式

构建 ListViewObserver,将 ListView 实例传递给 child 参数

ListViewObserver(
  child: _buildListView(),
  onObserve: (resultModel) {
    print('firstChild.index -- ${resultModel.firstChild?.index}');
    print('displaying -- ${resultModel.displayingChildIndexList}');
  },
)

默认是 ListView 在滚动的时候才会观察到相关数据。

如果需要,可以使用 ListObserverController 进行手动触发一次观察

// 创建 `ListObserverController` 实例
ListObserverController controller = ListObserverController();
...

// 传递给 `ListViewObserver` 的 `controller` 参数
ListViewObserver(
  ...
  controller: controller,
  ...
)
...

// 手动触发一次观察
controller.dispatchOnceObserve();

dispatchOnceObserve 方法的定义:

ListObserverController 的为例

Future<ListViewOnceObserveNotificationResult> dispatchOnceObserve({
  BuildContext? sliverContext,
  bool isForce = false,
  bool isDependObserveCallback = true,
})

dispatchOnceObserve 的参数说明:

参数 必传 说明
sliverContext 滚动视图的 BuildContext,只有在 CustomScrollView 有多个 Sliver 时才会使用到
isForce 是否强制观察,等同于 ObserverTriggerOnObserveType.directly
isDependObserveCallback 是否依赖于判断 onObserve 等回调有没有实现,如果传 true,即使没有实现对应的回调,也可以拿到观察结果

其返回值可以直接拿到观察的结果!

方式二:指明 SliverBuildContext

使用上相对复杂,应用范围小,存在多个 Sliver 时才有可能会用到该方式

具体说明
BuildContext? _sliverListViewContext;

创建 ListView,并在其 builder 回调中,将 BuildContext 记录起来

ListView _buildListView() {
  return ListView.separated(
    itemBuilder: (ctx, index) {
      if (_sliverListViewContext != ctx) {
        _sliverListViewContext = ctx;
      }
      ...
    },
    ...
  );
}

构建 ListViewObserver

ListViewObserver(
  child: _buildListView(),
  sliverListContexts: () {
    return [if (_sliverListViewContext != null) _sliverListViewContext!];
  },
  onObserveAll: (resultMap) {
    final model = resultMap[_sliverListViewContext];
    if (model == null) return;

    // 打印当前正在显示的第一个子部件
    print('firstChild.index -- ${model.firstChild?.index}');

    // 打印当前正在显示的所有子部件下标
    print('displaying -- ${model.displayingChildIndexList}');
  },
)

默认是 ListView 在滚动的时候才会观察到相关数据。

如果需要,可以使用 ListViewOnceObserveNotification 进行手动触发一次观察

ListViewOnceObserveNotification().dispatch(_sliverListViewContext);

1.1、autoTriggerObserveTypes 参数

用于设置自动触发观察的时机,定义如下:

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

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

枚举值说明:

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

1.2、triggerOnObserveType 参数

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

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

其默认值为 .displayingItemsChange

枚举值说明:

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

1.3、onObserveViewport 回调

仅支持 CustomScrollView

用于观察当前 CustomScrollViewViewport 中有哪些指定的 Sliver 正在展示

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');
    }
  },
)

需要注意的是,这里的 sliverContexts 参数传入的是每个外层 SliverBuildContext

如下结构中,我们需要用到的是 SliverPaddingBuildContext,而不是 SliverListBuildContext

SliverPadding(
  padding: EdgeInsets.only(bottom: 10),
  sliver: SliverList(
    delegate: SliverChildBuilderDelegate(
      (context, index) {
        ...
      },
      ...
    ),
  ),
);

1.4、customTargetRenderSliverType 回调

仅支持 ListViewObserverGridViewObserver

在保持原来的观察逻辑上,告诉该库要处理的 RenderSliver,目的是为了支持对第三方库构建的列表进行观察。

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

1.5、customHandleObserve 回调

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

customHandleObserve: (context) {
  // 完全自定义你的观察逻辑
  final _obj = ObserverUtils.findRenderObject(context);
  if (_obj is RenderSliverList) {
    ObserverCore.handleListObserve(context: context);
  }
  if (_obj is RenderSliverGrid || _obj is RenderSliverWaterfallFlow) {
    return ObserverCore.handleGridObserve(context: context);
  }
  return null;
},

1.6、extendedHandleObserve 回调

仅支持 SliverViewObserver

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

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

模型属性

ObserveModel

监听模型的基类

属性 类型 描述
sliver RenderSliver 当前 sliver
visible bool sliver 是否可见
displayingChildIndexList List<int> 当前现在显示的子部件的下标
axis Axis sliver 的方向
scrollOffset double sliver 的偏移量

ListViewObserveModel

继承自 ObserveModelListViewSliverList 专用的监听模型

属性 类型 描述
sliver RenderSliverMultiBoxAdaptor 当前 sliver
firstChild ListViewObserveDisplayingChildModel 当前第一个正在显示的子部件数据模型
displayingChildModelList List<ListViewObserveDisplayingChildModel> 当前正在显示的所有子部件数据模型

GridViewObserveModel

继承自 ObserveModelGridViewSliverGrid 专用的监听模型

属性 类型 描述
sliverGrid RenderSliverGrid 当前 sliver
firstGroupChildList List<GridViewObserveDisplayingChildModel> 当前第一排正在显示的所有子部件数据模型
displayingChildModelList List<GridViewObserveDisplayingChildModel> 当前正在显示的所有子部件数据模型

ObserveDisplayingChildModel

当前正在显示的子部件的数据信息

属性 类型 描述
sliver RenderSliver 当前 sliver
viewport RenderViewportBase 当前 sliver 对应的 viewport
index int 子部件的下标
renderObject RenderBox 子部件对应的 RenderBox 实例

ObserveDisplayingChildModelMixin

当前正在显示的子部件的数据信息,是对 ObserveDisplayingChildModel 的补充

属性 类型 描述
axis Axis sliver 的方向
size Size 子部件的大小
mainAxisSize double 子部件主轴方向上的大小
scrollOffset double sliver 的偏移量
layoutOffset double 子部件相应于 sliver 的偏移量
leadingMarginToViewport double 子部件距离视口顶部的距离
trailingMarginToViewport double 子部件距离视口尾部部的距离
visibleMainAxisSize double 子部件自身显示的大小
visibleFraction double 子部件自身大小在 sliver 所显示大小中的的占比
displayPercentage double 子部件自身大小显示的占比