Flutter ScrollView Observer - a library of widget that can be used to listen for child widgets those are being displayed in the scroll view

Overview

Flutter ScrollView Observer

author pub stars

Language: English | 中文 | Article

This is a library of widget that can be used to listen for child widgets those are being displayed in the scroll view.

Support

  • ListView
  • SliverList
  • GridView
  • SliverGrid

Installing

Add scrollview_observer to your pubspec.yaml file:

dependencies:
  scrollview_observer: latest_version

Import scrollview_observer in files that it will be used:

import 'package:scrollview_observer/scrollview_observer.dart';

Getting Started

Take ListView as an example

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

  • child: Create ListView as a child of ListViewObserver
  • sliverListContexts: In this callback, we need to return all BuildContext of the ListView those needs to be observed
  • onObserve: This callback can listen for information about the child widgets those are currently being displayed
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 rolling.

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

ListViewOnceObserveNotification().dispatch(_sliverListViewContext);

Example

Comments
  • 操作列表最后一个数据时会报错

    操作列表最后一个数据时会报错

    RenderBox.size accessed beyond the scope of resize, layout, or permitted parent access. RenderBox can always access its own size, otherwise, the only object that is allowed to read RenderBox.size is its parent, if they have said they will. It you hit this assert trying to access a child's size, pass "parentUsesSize: true" to that child's layout(). 'package:flutter/src/rendering/box.dart': Failed assertion: line 1937 pos 13: 'debugDoingThisResize || debugDoingThisLayout || _computingThisDryLayout || (RenderObject.debugActiveLayout == parent && _size._canBeUsedByParent)'

    opened by mrchenmo 20
  • 带上偏移量会出现的若干问题

    带上偏移量会出现的若干问题

    感谢大佬的插件,但是在结合我的业务需求使用时发现了一些问题,我的页面是一个stack,第一个子组件是ListViewObserver,叠加在顶部的是一个随滑动从透明变不透明的tabbar。 出现的问题是: 1.tabbar里的 observerController.animateTo()如果添加了offset

    onTap: (value) {
                      observerController.animateTo(
                        index: value,
                        duration: const Duration(milliseconds: 250),
                        curve: Curves.ease,
                        offset: (offset) {
                          return TAB_HEIGHT;
                        },
                      );
                    },
    

    listviewObserver里同时添加onObserver,在点击tab时大概率会出现tab切换错误,tab会在切换到目标tab之后跳到目标tab的上一个位置,并且offset好像并没有生效,还是有tabbar height的偏移错误

    onObserve: (resultModel) {
                _tabController.index = ObserverUtils.calcAnchorTabIndex(
                  observeModel: resultModel,
                  tabIndexs: tabIndexs,
                  currentTabIndex: _tabController.index,
                );
              },
    

    2.在我去掉onObserver后,点击tab偏移量基本正常,但是在第一个标签和最后一个标签来回切换的时候,第一次能准确定位到最后一个标签,之后定位就会定位错误(但是一个个标签点过去就不会出错)

    下面附上demo

    查看代码
      
    /*
     * @Author: LinXunFeng [email protected]
     * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer
     * @Date: 2022-08-08 00:20:03
     */
    import 'package:flutter/material.dart';
    import 'package:scrollview_observer/scrollview_observer.dart';
    

    class AnchorListPage extends StatefulWidget { const AnchorListPage({Key? key}) : super(key: key);

    @override State createState() => _AnchorListPageState(); }

    class _AnchorListPageState extends State with SingleTickerProviderStateMixin { ScrollController scrollController = ScrollController();

    late ListObserverController observerController; late TabController _tabController; List tabs = ["0", "1", "2", "3", "4"]; List tabIndexs = [0, 1, 2, 3, 4]; double TAB_HEIGHT = 48; double opacity = 0.0; GlobalKey tabbarKey = GlobalKey(); List containerHeight = [310, 210, 400, 270, 1000];

    @override void initState() { super.initState(); observerController = ListObserverController(controller: scrollController); _tabController = TabController(length: tabs.length, vsync: this); scrollController.addListener(() => _onScrollChanged()); }

    @override void dispose() { observerController.controller?.dispose(); _tabController.dispose(); super.dispose(); }

    /// 滑动事件 void _onScrollChanged() { /// 滑动显示tabbar的触发距离 double showTabBarOffset = 350;

    // 滑动距离大于触发距离时显示tabbar
    if (scrollController.offset >= showTabBarOffset) {
      setState(() {
        opacity = 1;
      });
    } else {
      // 滑动距离小于触发距离显示动态透明度
      setState(() {
        opacity = scrollController.offset / showTabBarOffset;
        if (opacity < 0) {
          opacity = 0;
        }
      });
    }
    

    }

    @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('demo')), body: Stack(children: [ ListViewObserver( controller: observerController, child: _buildListView(), // onObserve: (resultModel) { // _tabController.index = ObserverUtils.calcAnchorTabIndex( // observeModel: resultModel, // tabIndexs: tabIndexs, // currentTabIndex: _tabController.index, // ); // }, ), Positioned( top: 0, height: TAB_HEIGHT, child: Opacity( opacity: opacity, key: tabbarKey, child: Container( color: Colors.white, width: MediaQuery.of(context).size.width, height: TAB_HEIGHT, child: TabBar( controller: _tabController, tabs: tabs.map((e) => Tab(text: e)).toList(), labelColor: Colors.black, onTap: (value) { observerController.animateTo( index: value, duration: const Duration(milliseconds: 250), curve: Curves.ease, offset: (offset) { // return ObserverUtils.calcPersistentHeaderExtent( // key: tabbarKey, // offset: offset, // ); return TAB_HEIGHT; }, ); }, ), ), ), ), ]), ); }

    ListView _buildListView() { return ListView.separated( controller: scrollController, itemBuilder: (ctx, index) { return _buildListItemView(index); }, separatorBuilder: (ctx, index) { return _buildSeparatorView(); }, itemCount: tabIndexs.length, ); }

    Widget _buildListItemView(int index) { Widget item = Container();

    switch (index) {
      case 0:
        item = Container(
          height: 565,
          color: Colors.red,
          child: Center(
            child: Text('index -- $index'),
          ),
        );
    
        break;
      case 1:
        item = Container(
          height: 207,
          color: Colors.red,
          child: Center(
            child: Text('index -- $index'),
          ),
        );
        break;
      case 2:
        item = Container(
          height: 262,
          color: Colors.red,
          child: Center(
            child: Text('index -- $index'),
          ),
        );
        break;
      case 3:
        item = Container(
          height: 400,
          color: Colors.red,
          child: Center(
            child: Text('index -- $index'),
          ),
        );
        break;
      case 4:
        item = Container(
          height: 1000,
          color: Colors.red,
          child: Center(
            child: Text('index -- $index'),
          ),
        );
        break;
    }
    
    return item;
    

    }

    Container _buildSeparatorView() { return Container( color: Colors.white, height: 5, ); } }

    bug 
    opened by H-Bin 7
  • InitState Jump

    InitState Jump

    Hi! I face a problem about the package and wonder we can solve it or not.

    I want to go a spesific index when I open my book. After everything loaded, there is no problem I can go any page with enter number to method

    listObserverController.jumpTo(index: 13);

    But When I try to use it in initState, it just doesn't work. There is no error or anything.

    my initState like this

      void initState() {
        super.initState();
        bookModel = context.read<BookProvider>();
        _scrollController = ScrollController(initialScrollOffset: widget.book.lastIndex);
        listObserverController = ListObserverController(controller: _scrollController);
        _scrollController.addListener(() {
          //Save the pageIndex for next come.
          bookModel.setSavePage(widget.book, _scrollController.offset);
        });
    
        WidgetsBinding.instance.addPostFrameCallback((_) {
    
            listObserverController.jumpTo(index: 13);
          
        });
      };
    

    In first open, there is no move. When I reload the page with ctrl | cmd + S, listObserverController.jumpTo(index: 13); trigger and go the page.

    When I don't use WidgetsBinding.instance.addPostFrameCallback it doesn't work also.

    opened by cyb3rsalih 7
  • ListViewObserver下 onObserve 偶现不回调

    ListViewObserver下 onObserve 偶现不回调

    场景:TabBar 结合 ListViewObserver->ListView 使用,数据量是超过了两屏的 操作:点击tab,获取tab index,执行controller.jumpTo(index)或者controller.animateTo(index), 特定情况下没有进入onObserve回调 特定情况:listview cellIndex=0 再执行controller.jumpTo(index),index =3,jumpTo 跳转正常。但是onObserve没有回调。 其它情况都正常:只要index 不是3,都能正常回调onObserve,或者 cellIndex >0 的情况,执行controller.jumpTo 也能正常回调onObserve。

    help wanted invalid 
    opened by MarsHou 4
  • 求教复杂效果实现

    求教复杂效果实现

    楼主,请问下有没有实现过在CustomScrollView中嵌套GrdiView并且滚动置顶Tab标签数据,然后点击Tab标签数据GridView也会滚动到想关联的位置的这种效果么?类似demo中Anchor ListView页面中的效果,只是上面的Tab是在CustomScrollView中,并且可以滚动置顶,然后点击“News”,“History”标签底部数据也会滚动相应数据位置。

    opened by ling9400 3
  • Animation duration

    Animation duration

    I am using the package for a book that about 300 pages. When I want to go a spesific page, I am using

    listObserverController.jumpTo(index: 150) Which is going to 150th page.

    But it took a while to go to the page when I am in page 5. The screen animating (emulating) a real scroll. I wait until it reach the point I enter.

    Is there a way to really Jump to the spesific index. Not animate.

    opened by cyb3rsalih 2
  • [Desktop]Can not get onObserve callback

    [Desktop]Can not get onObserve callback

    Env: Flutter 3.3.6 This is my code:

      Widget getContinuousThumbnailItem(WidgetRef ref) {
        Widget builder = ListView.builder(
            itemExtent: ctWidth,
            controller: scrollController,
            scrollDirection: Axis.horizontal,
            itemCount: getCTDisplayCount(ref),
            itemBuilder: (context, i) {
              return ContinuousThumbnailItem(
                width: ctWidth,
                height: ctHeight,
                index: i,
              );
            });
        builder = ListViewObserver(
          child: builder,
          onObserve: (resultModel) {
            print('firstChild.index -- ${resultModel.firstChild?.index}');
            print('displaying -- ${resultModel.displayingChildIndexList}');
          },
        );
    
        return builder;
      }
    
    

    It seems that onObserve is not callback

    bug 
    opened by liangsmfish 1
  •  'controller != null': is not true

    'controller != null': is not true

    I've implemented the package properly, but when I try go to a spesific index, got this error.

    I did: listObserverController.jumpTo(index: 20);

    Error:

    _AssertionError ('package:scrollview_observer/src/common/observer_controller.dart': Failed assertion: line 88 pos 12: 'controller != null': is not true.)
    
    opened by cyb3rsalih 1
  • 聊天列表场景问题

    聊天列表场景问题

    根据案例代码编写,正常情况下没问题。 但是案例代码中是直接点击按钮插入的消息,少了一种情况。当主动发消息时,是需要键盘弹起的,这个时候滑动视图的最大高度和展示高度都发生了改变。发完消息就会发生错误。 standby方法中的observeSwitchShrinkWrap方法发生错误被捕捉,插件版本1.6.2-67行。报错如下 image 这个报错倒是没影响视图,影响视图的是另一个问题。isShrinkWrap变量错误的被设置。当键盘弹起的时候,最大高度变小,仅需几条消息即可超过最大高度可以滚动。键盘弹起后,发送消息或者收到消息会调用插入与standby并刷新视图,这个时候isShrinkWrap就会被标记为false,当键盘回收时,最大高度恢复,这个时候应当为true才是正确的。 刚接触大佬的代码哈,可能有点描述的不清楚

    opened by a1239897580 7
Releases(1.9.1)
  • 1.9.1(Dec 27, 2022)

  • 1.9.0(Dec 4, 2022)

    • ObserverWidget
      • Add property [autoTriggerObserveTypes] and property [triggerOnObserveType].
    • ObserverController
      • Method [dispatchOnceObserve] adds parameter [isForce].
    Source code(tar.gz)
    Source code(zip)
  • 1.8.0(Nov 29, 2022)

    • Scrolling to the specified index location
      • Supports initializing the index position of the scrollView.
      • Deprecated [clearIndexOffsetCache], please use [clearScrollIndexCache] instead.
    Source code(tar.gz)
    Source code(zip)
  • 1.7.0(Nov 24, 2022)

    • Chat Observer
      • Add the property [fixedPositionOffset].
      • Deprecated [ChatObserverClampinScrollPhysics], please use [ChatObserverClampingScrollPhysics] instead.
    Source code(tar.gz)
    Source code(zip)
  • 1.6.2(Nov 10, 2022)

  • 1.6.1(Nov 3, 2022)

  • 1.6.0(Oct 31, 2022)

  • 1.5.1(Oct 22, 2022)

  • 1.5.0(Sep 29, 2022)

    • Chat Observer
      • Quickly implement the chat session page effect.
    • Scrolling to the specified index location
      • Add the property [cacheJumpIndexOffset].
    Source code(tar.gz)
    Source code(zip)
  • 1.4.0(Aug 27, 2022)

    • Scrolling to the specified index location
      • New alignment paramter in the jumpTo and animateTo methods.
      • Fixed a bug that caused scrolling to the first child to jitter when using offset paramter.
    Source code(tar.gz)
    Source code(zip)
  • 1.3.0(Aug 21, 2022)

    • Scrolling to the specified index location supports the SliverPersistentHeader. (#9)
    • Add ObserverUtils
      • Method calcPersistentHeaderExtent: Calculate current extent of RenderSliverPersistentHeader. (#9)
      • Method calcAnchorTabIndex: Calculate the anchor tab index.
    Source code(tar.gz)
    Source code(zip)
  • 1.2.0(Aug 7, 2022)

    • The jumpTo and animateTo methods add an isFixedHeight parameter to optimize performance when the child widget is of fixed height
    • Add the properties [leadingMarginToViewport] and [trailingMarginToViewport]
    • Support mixing usage of SliverList and SliverGrid
    Source code(tar.gz)
    Source code(zip)
  • 1.1.0(Jul 19, 2022)

  • 1.0.1(Jul 3, 2022)

  • 1.0.0(Jul 3, 2022)

    • Implements a way to use without sliver [BuildContext]
    • Change [onObserve] to [onObserveAll], and add a new [onObserve] callback to listen for changes in the child widget display of the first sliver
    • Add [ObserverController]
    Source code(tar.gz)
    Source code(zip)
  • 0.1.0(Jun 5, 2022)

  • 0.0.1(May 28, 2022)

Owner
林洵锋
OC / Swift / Python / Vue / Flutter 🐝 微信公众号: FullStackAction
林洵锋
A Flutter widget which synchronize a ScrollView and a custom tab view

scrollable_list_tabview A Flutter widget which synchronize a ScrollView and a custom tab view. The main idea is to create a custom tab view synchroniz

Aswanath C K 0 Apr 12, 2022
A Flutter widget that will give a Glitch Animation Effect to it's child widget.

GlitchEffect A Flutter widget that will give a Glitch Animation Effect to it's child widget. Installation Add the latest version of package to your pu

Sameer Singh 6 Nov 25, 2022
Customizable Flutter widget which syncronize ScrollView with PageView as tabs

scrollable_list_tab_scroller Customizable Flutter widget which syncronize ScrollView with PageView as tabs. Create a custom page view as tabs which sy

Railson Ferreira de Souza 4 Dec 21, 2022
NestedScrollView: extended nested scroll view to fix following issues.

NestedScrollView: extended nested scroll view to fix following issues.

FlutterCandies 457 Jan 4, 2023
Custom bottom sheet widget, that can resize by drag and then scroll.

Bottom Sheet This package is part of the SurfGear toolkit made by Surf. About Custom bottom sheet widget that can be resized in response to drag gestu

Surf 92 Dec 16, 2022
Displays a scrollable timeline with custom child widgets and custom icons.

Flutter Timeline Widget Displays a scrollable timeline with custom child widgets and custom icons. Installation In your pubspec.yaml file within your

Furkan Tektas 375 Nov 20, 2022
Flutter Package: When your desired layout or animation is too complex for Columns and Rows, this widget lets you position/size/rotate/transform its child in complex ways.

align_positioned Widgets in this package: AlignPositioned AnimatedAlignPositioned AnimChain Why are these widgets an indispensable tool? When your des

Marcelo Glasberg 69 Dec 12, 2022
A widget that imposes different constraints on its child than it gets from its parent

A widget that imposes different constraints on its child than it gets from its parent, possibly allowing the child to overflow the parent. Similar to `OverflowBox` except that the unconstrained width or height is sized to the intrinsic size of the child, instead of being assumed to be infinite, which allows IntrinsicSizeOverflowBox to be used in a `Scrollable` widget.

Ron Booth 3 Dec 7, 2022
Rows_Columns with Child and Children Widget Demo

Rows_Columns with Child and Children Widget Demo A new Flutter project.This project is to show the combination of Rows and Columns and Child and Child

Avinandan Bose 1 Mar 17, 2022
Flutter | Snap physics for your scrollview

Snap Scroll Physics When building scrollable views, sometimes you would prefer that the scroll stopped at a given offset, or that it avoids to stop in

Jaime Blasco 46 Nov 21, 2022
A button with ripple effect while being hold

ripple_button a button with ripple effect while being hold build requirements to run this project you need a working enviroment of flutter v2 or highe

null 2 Nov 8, 2021
Displays a highly customizable week view (or day view) which is able to display events, to be scrolled, to be zoomed-in & out and a lot more !

Displays a highly customizable week view (or day view) which is able to display events, to be scrolled, to be zoomed-in & out and a lot more !

Hugo Delaunay 196 Dec 2, 2022
A Flutter widget that moves according to a scroll controller.

flutter_parallax A Flutter widget that moves according to a scroll controller. Features Can contain any widget. Configurable parallax scroll direction

Romain Rastel 100 Dec 17, 2021
A sheet that aligns to the bottom of a widget and expands as scroll up.

slinky_view A sheet that aligns to the bottom of a widget and expands as scroll up. Getting started Add dependency. dependencies: slinky_view: ^1.0.

idonun 3 Nov 21, 2022
React hooks for Flutter. Hooks are a new kind of object that manages a Widget life-cycles. They are used to increase code sharing between widgets and as a complete replacement for StatefulWidget.

English | Português Flutter Hooks A Flutter implementation of React hooks: https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889 Ho

Remi Rousselet 2.6k Dec 29, 2022
The public ui library is used with the openim demo, and you can directly use it for secondary development.

flutter_openim_widget The public ui library is used with the openim demo, and you can directly use it for secondary development. import 'package:flutt

null 28 Dec 27, 2022
A sliding up panel widget which can be used to show or hide content, beautiful and simple.

flutter_sliding_up_panel A sliding up panel widget which can be used to show or hide content, beautiful and simple. demo Getting Started dependencies:

null 25 Dec 12, 2022
Global loading widget, which can be used through simple configuration.

load Global loading widget, which can be used through simple configuration. Pure flutter library, not use native code. It is similar to OKToast in use

Caijinglong 35 Nov 4, 2022
📸 Easy to use yet very customizable zoomable image widget for Flutter, Photo View provides a gesture sensitive zoomable widget.

?? Easy to use yet very customizable zoomable image widget for Flutter, Photo View provides a gesture sensitive zoomable widget. Photo View is largely used to show interacive images and other stuff such as SVG.

Blue Fire 1.7k Jan 7, 2023