CustomScrollView scroll behavior changes when scrollController is passed

1,068

I think you may notice the explanation of the example here: NestedScrollView class

// 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.
// The PageStorageKey should be unique to this ScrollView;
// it allows the list to remember its scroll position when
// the tab view is not on the screen.

and

// This Builder is needed to provide a BuildContext that is
// "inside" the NestedScrollView, so that
// sliverOverlapAbsorberHandleFor() can find the
// NestedScrollView.

The correct way is to get the controller used by CustomScrollView, not to add a new one to it.

It can be done by adding a Builder above the CustomScrollView) and assign the controller to _scrollController.

...
  body: NestedScrollView(
    ...
    body: Builder(
      builder: (context){
        _scrollController = PrimaryScrollController.of(context);
        return CustomScrollView(
          ...
      },
    ),
  ),
...
Share:
1,068
RaSha
Author by

RaSha

I'm a software engineer and mobile developer trying to learn more...

Updated on December 26, 2022

Comments

  • RaSha
    RaSha over 1 year

    I'm trying to auto scroll to the end of a SliverList that is inside a CustomScrollView. SliverList itself doesn't have a controller property so I have to pass a ScrollController to the CustomScrollView.

    The problem is when I pass a controller to the CustomScrollView its behavior changes and it's no longer scrolls the outer list and would not cause a collapsed SliverAppBar widget. How can I auto scroll SliverList and also keep the behavior of CustomScrollView as before?

    Here is my code:

    class _MyHomePageState extends State<MyHomePage> {
      ScrollController _scrollController = ScrollController();
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
              title: Text(widget.title),
            ),
            floatingActionButton: FloatingActionButton(
              onPressed: () {
                _scrollController.animateTo(
                  _scrollController.position.maxScrollExtent,
                  curve: Curves.easeOut,
                  duration: const Duration(seconds: 1),
                );
              },
            ),
            body: NestedScrollView(
              headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
                return <Widget>[
                  SliverAppBar(
                    expandedHeight: 230.0,
                    pinned: true,
                    flexibleSpace: FlexibleSpaceBar(
                      title: Text('SliverAppBar Expand'),
                    ),
                  )
                ];
              },
              body: CustomScrollView(
                    //When controller is passed to CustomScrollView, its behavior changes
                    // controller: _scrollController,  
                    slivers: [
                      //Some widgets are here
                      SliverList(
                        delegate: SliverChildBuilderDelegate(
                          (context, index) {
                            return Container(
                              height: 80,
                              color: Colors.primaries[index % Colors.primaries.length],
                              alignment: Alignment.center,
                              child: Text(
                                'Item : $index',
                              ),
                            );
                          },
                          childCount: 20,
                        ),
                      ),
                    ],
                  ),
                ) ,
             );
          }
        }
    
  • RaSha
    RaSha over 3 years
    Thank you for such a detailed answer!