Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get offset from CustomScrollView inside NestedScrollView with PrimaryScrollController

I have a NestedScrollView with a SliverAppBar. The body of the NestedScrollView is a widget with a CustomScrollView with a SliverList.

I want to get the offset from the ScrollController that is given to the NestedScrollView but it only gives me the offset until the SliverAppBar is out of view, but after that the SliverList continues to scroll but the ScrollController doesn't give the offset.

NestedScrollView(
  headerSliverBuilder: (_, __) => SliverAppBar(..),
  body: RefreshIndicator(
    child: CustomScrollView(..),
  ),
)

The CustomScrollView will automatically use the PrimaryScrollController provided by NestedScrollView.
When a listener is attached to that scroll view, the listener will be called when the SliverAppBar slides into view and out of view until the position.extentAfter is equal to 0.0 and then it will not update at all, no matter how far you scroll down because the extentAfter always remains at 0.0.

How can you retrieve the scroll position of the CustomScrollView inside of the NestedScrollView?


Implementation

It is important that PrimaryScrollController is used because the ScrollController should have the features of PrimaryScrollController. Anyways, in order for the SliverAppBar to work, i.e. it scrolling out of view with the content, the NestedScrollView controller parameter needs to match the CustomScrollView controller (or primary) parameter somehow.

Here is a demo.

Any implementation that allows the content to be scrolled with PrimaryScrollController.of(context) is fine.
Just note that the NestedScrollView is required in order for the RefreshIndicator to appear below the SliverAppBar.

like image 957
Zap Avatar asked Mar 22 '19 20:03

Zap


2 Answers

It seems to me that you will need to attach a NotificationListener<ScrollNotification> to track the offset of your inner CustomScrollView

NestedScrollView(
  controller: // PrimaryScrollController
  headerSliverBuilder: (_, __) => SliverAppBar(..),
  body: RefreshIndicator(
    child: NotificationListener<ScrollNotification>(
      child: CustomScrollView(..),
      onNotification: (ScrollNotification scrollInfo) {
        double customPixel = scrollInfo.metrics.pixels; // same as offset
        return false;
      },
    )
  )
)

So, as you mentioned, your NestedScrollView controller’s listener will only respond up to the height of the SliverAppBar. And once that is out of view, the ScrollNotification will kick in and start responding to the CustomScrollView scrolling. Accomplished in two separate areas:

  1. NestedScrollView's PrimaryScrollController-listener for SliverAppBar
  2. CustomScrollView's NotificationListener-ScrollNotification for itself

Note, you could attach another controller/listener to the inner CustomScrollView, but that would go against this note: https://api.flutter.dev/flutter/widgets/NestedScrollView-class.html

return CustomScrollView(
  // The "controller" and "primary" members should be left
  // unset, so that the NestedScrollView can control this
  // inner scroll view.
  // If the "controller" property is set, then this scroll
  // view will not be associated with the NestedScrollView.
like image 167
TWL Avatar answered Oct 05 '22 22:10

TWL


Maybe, I can help you!

Widget build(BuildContext context) {
  return Scaffold(
    body: NestendScrollView(
      headerSliverBuilder: (BuildContext context, bool value) {
        return <Widget>[
           SliverAppBar(),
        ];
      },
        body: SafeArea(
          child: Builder(
            builder: (context) {
              final _scr = PrimaryScrollController.of(context);
              _scr.addListener(() {
                if (_scr.position.pixels == _scr.position.maxScrollExtent) {
                  print('At DOWNW!!!');
                }
              });

              return CustomScrollView(
                controller: _scr,
                slivers: <Widget>[
                  SliverOverlapAbsorber(
                    handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
                      context,
                    ),
                  ),
                  SliverList(
                    delegate: SliverChildListDelegate(
                      List.generate(100, (int index) {
                        return Text('ITEM -> $index');
                      }),
                    ),
                  )
                ],
              );
            },
          ),
        ),
    ),
  );
}
like image 26
WiRight Avatar answered Oct 05 '22 23:10

WiRight