Skip to content

1、Observing child widgets those are being displayed in the ScrollView

林洵锋 edited this page Dec 11, 2023 · 4 revisions

Usage

Parameter description of ListViewObserver:

Parameter Required Description
child yes Create [ListView] as a child of [ListViewObserver]
sliverListContexts no In this callback, we need to return all [BuildContext] of the [ListView] those needs to be observed. This property is only used when [BuildContext] needs to be specified exactly
onObserve no This callback can listen for information about the child widgets those are currently being displayed in the current first [Sliver]
onObserveAll no This callback can listen for information about all the children of slivers that are currently being displayed. This callback is only needed when there are more than one [Sliver]
leadingOffset no The offset of the head of scroll view. Find the first child start at this offset.
dynamicLeadingOffset no This is a callback that provides [leadingOffset], used when the leading offset in the head of the scroll view is dynamic. It has a higher priority than [leadingOffset]
toNextOverPercent no When the percentage of the first child widget is blocked reaches this value, the next child widget will be the first child that is displaying. The default value is 1
autoTriggerObserveTypes no Used to set types those can trigger observe automatically
triggerOnObserveType no Used to set the prerequisite for triggering [onObserve] callback and [onObserveAll] callback

Method 1: General (Recommended)

It is relatively simple to use and has a wide application range. In general, only this method is needed

Build ListViewObserver and pass the ListView instance to the child parameter

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

By default, ListView relevant data will only be observed when scrolling.

If needed, you can use ListObserverController triggered an observation manually.

// Create an instance of [ListObserverController]
ListObserverController controller = ListObserverController();
...

// Pass the controller instance to the 'controller' parameter of 'ListViewObserver'
ListViewObserver(
  ...
  controller: controller,
  ...
)
...

// Trigger an observation manually.
controller.dispatchOnceObserve();

Definition of dispatchOnceObserve method:

Take dispatchOnceObserve of ListObserverController as an example.

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

Parameter description of dispatchOnceObserve:

Parameter Required Description
sliverContext no The BuildContext of the scrollView is only used when CustomScrollView has multiple Sliver
isForce no Whether to enforce observation, equivalent to ObserverTriggerOnObserveType.directly
isDependObserveCallback no Whether it depends on judging whether onObserve and other callbacks are implemented. If true is passed, even if the corresponding callback is not implemented, the observation result can still be obtained

Its return value can directly get the result of observation!

Method 2: Specify BuildContext for Sliver

Relatively complex to use, the scope of application is small, there are more than one Sliver is possible to use this method

Detailed instructions
BuildContext? _sliverListViewContext;

Create a ListView and record BuildContext in its builder callback

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

Create ListViewObserver

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

    // Prints the first child widget index that is currently being displayed
    print('firstChild.index -- ${model.firstChild?.index}');

    // Prints the index of all child widgets those are currently being displayed
    print('displaying -- ${model.displayingChildIndexList}');
  },
)

By default, ListView relevant data will only be observed when scrolling.

If needed, you can use ListViewOnceObserveNotification triggered an observation manually.

ListViewOnceObserveNotification().dispatch(_sliverListViewContext);

1.1、Parameter autoTriggerObserveTypes

Used to set types those can trigger observe automatically, defined as follows:

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

Defaults to [.scrollStart, .scrollUpdate, .scrollEnd].

Description of enum values as follow:

value Desc
scrollStart when a [Scrollable] widget has started scrolling.
scrollUpdate when a [Scrollable] widget has changed its scroll position.
scrollEnd when a [Scrollable] widget has stopped scrolling.

1.2、Parameter triggerOnObserveType

Used to set the prerequisite for triggering [onObserve] callback and [onObserveAll] callback, defined as follows:

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

Defaults to .displayingItemsChange.

Description of enum values as follow:

value Desc
directly The callback onObserve will be called directly when getting observed result for scrollView.
displayingItemsChange The callback onObserve will be called when child widget comes in and out or when the number of child widget changes.

1.3、Callback onObserveViewport

Only CustomScrollView is supported.

Used to observe which specified slivers are being displayed in the Viewport of CustomScrollView.

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.4、Callback customTargetRenderSliverType

Only ListViewObserver and GridViewObserver are supported.

While maintaining the original observation logic, tell the package the RenderSliver to be observed, in order to support the observation of the list built by the third-party package.

customTargetRenderSliverType: (renderObj) {
  // Here you tell the package what type of RenderObject it needs to observe.
  return renderObj is ExtendedRenderSliverList;
},

1.5、Callback customHandleObserve

This callback is used to customize the observation logic and is used when the built-in observation logic does not meet your needs.

customHandleObserve: (context) {
  // Here you can customize the observation logic.
  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、Callback extendedHandleObserve

Only SliverViewObserver is supported.

This callback is used to supplement the original observation logic, which originally only dealt with RenderSliverList, RenderSliverFixedExtentList and RenderSliverGrid.

extendedHandleObserve: (context) {
  // An extension of the original observation logic.
  final _obj = ObserverUtils.findRenderObject(context);
  if (_obj is RenderSliverWaterfallFlow) {
    return ObserverCore.handleGridObserve(context: context);
  }
  return null;
},

Model Property

ObserveModel

The base class of the observing data.

Property Type Desc
sliver RenderSliver The target sliver.
visible bool Whether this sliver should be painted.
displayingChildIndexList List<int> Stores index list for children widgets those are displaying.
axis Axis The axis of sliver.
scrollOffset double The scroll offset of sliver.

ListViewObserveModel

A special observing models which inherits from the ObserveModel for ListView and SliverList.

Property Type Desc
sliver RenderSliverMultiBoxAdaptor The target sliverList.
firstChild ListViewObserveDisplayingChildModel The observing data of the first child widget that is displaying.
displayingChildModelList List<ListViewObserveDisplayingChildModel> Stores observing model list of displaying children widgets.

GridViewObserveModel

A special observing models which inherits from the ObserveModel for GridView and SliverGrid.

Property Type Desc
sliverGrid RenderSliverGrid The target sliverGrid.
firstGroupChildList List<GridViewObserveDisplayingChildModel> The observing datas of first group displaying child widgets.
displayingChildModelList List<GridViewObserveDisplayingChildModel> Stores observing model list of displaying children widgets.

ObserveDisplayingChildModel

Data information about the child widget that is currently being displayed.

Property Type Desc
sliver RenderSliver The target sliverList.
index int The index of child widget.
renderObject RenderBox The renderObject [RenderBox] of child widget.

ObserveDisplayingChildModelMixin

The currently displayed child widgets data information, is for ObserveDisplayingChildModel supplement.

Property Type Desc
axis Axis The axis of sliver.
size Size The size of child widget.
mainAxisSize double The size of child widget on the main axis.
scrollOffset double The scroll offset of sliver.
layoutOffset double The layout offset of child widget.
leadingMarginToViewport double The margin from the top of the child widget to the viewport.
trailingMarginToViewport double The margin from the bottom of the child widget to the viewport.
displayPercentage double The display percentage of the current widget.