Nested ListView with ScrollController Not Scrolling

2,133

Resolved by wrapping the parent ListView in a PrimaryScrollController then setting primary = true on the ListView.

This lets PrimaryScrollController manage the controller, but the ListView maintains typical scrolling behaviour as it no longer manages the ScrollController.

See Flutter docs: https://api.flutter.dev/flutter/widgets/ScrollView/primary.html

Share:
2,133
Josh Kahane
Author by

Josh Kahane

Updated on December 01, 2022

Comments

  • Josh Kahane
    Josh Kahane over 1 year

    I am building a widget with a custom navigation bar which includes a Stack with the main body and the navigation bar.

    @override
      Widget build(BuildContext context) {
        return Stack(
          children: [
            _buildBody(),
            _buildNavigationBar(),
          ],
        );
      }
    

    The body consists of a ListView with a header and a ListView.builder nested below that for the contents.

    Widget _buildBody() {
        return ListView(
          padding: EdgeInsets.only(top: 44.0 + MediaQuery.of(context).padding.top),
          controller: _scrollController,
          children: [
            Opacity(
              opacity: 1.0 - _navigationBarOpacity,
              child: Padding(
                padding: const EdgeInsets.only(bottom: 15.0),
                child: _buildHeaderTitle(
                  title: widget.title,
                  fontSize: _largeFontSize,
                  fontWeight: _largeFontWeight,
                  color: widget.color,
                ),
              ),
            ),
            ListView.builder(
              padding: EdgeInsets.zero,
              shrinkWrap: true,
              physics: NeverScrollableScrollPhysics(),
              itemCount: widget.itemCount,
              itemBuilder: (context, index) {
                return widget.itemBuilder(context, index);
              },
            ),
          ],
        );
      }
    

    I'm using a ScrollController on the primary/root ListView in order to fade my navigation bar in and out. This works as expected.

    However, adding the ScrollController to the ListView stops any scrolling/bouncing interaction. How can I fix this?

    ScrollController _scrollController = ScrollController();
    
    @override
      void initState() {
        super.initState();
    
        _scrollController.addListener(() {
          setState(() {
            double min = 0.0;
            double max = 25.0;
            _navigationBarOpacity = (_scrollController.offset - min) / (max - min);
            if (_navigationBarOpacity < 0) _navigationBarOpacity = 0;
            if (_navigationBarOpacity > 1) _navigationBarOpacity = 1;
          });
        });  
      }
    

    Interestingly, if I move the ScrollController to the nestedt ListView.builder, the root ListView scrolls/bounces, but then I can no longer adjust the UI based on the offset as it's on the wrong ListView.

    Another point of interest, the root ListView scrolls normally if it's children exceed it's height. However, there are cases where it won't have that and I'd expect it just to bounce on pulling.


    Update

    Even if I remove the stack and nested ListView, a simple, singlular ListView with the ScrollController won't scroll on drag, without it, it will. This is the root issue that needs resolving.

    @override
      Widget build(BuildContext context) {
        return ListView(
            padding: EdgeInsets.only(top: 44.0 + MediaQuery.of(context).padding.top),
            controller: _scrollController,
            children: [
              Opacity(
                opacity: 1.0 - _navigationBarOpacity,
                child: Padding(
                  padding: const EdgeInsets.only(bottom: 15.0),
                  child: _buildHeaderTitle(
                    title: widget.title,
                    fontSize: _largeFontSize,
                    fontWeight: _largeFontWeight,
                    color: widget.color,
                  ),
                ),
              ),
            ],
          );
      }
    
    • Pavel
      Pavel over 3 years
      Consider using CustomScrollView with SliverAppBar and SliverList (SliverChildBuilderDelegate)
    • Josh Kahane
      Josh Kahane over 3 years
      This doesn't create the effect I desire, nor does it provide me the flexibility to customise it further to me needs going forward. Hence the need to resolve, or at least understand why I am facing the ScrollController issue.