9

This is a repository to create a minimal reproducible example.

I want SliverAppBar hidden when ScrollablePositionedList.builder is Scrolled. This is the relevant piece of code I am including here.

          NestedScrollView(
              headerSliverBuilder: (context, innerBoxIsScrolled) => [
                    SliverAppBar(
                      backgroundColor: Colors.blue,
                      expandedHeight: 112,
                      snap: true,
                      pinned: false,
                      floating: true,
                      forceElevated: true,
                      actions: <Widget>[
                        IconButton(
                          icon: Icon(Icons.event),
                        )
                      ],
                      flexibleSpace: SafeArea(
                        child: Column(
                          children: <Widget>[
                            Container(
                              height: kToolbarHeight,
                              child: Column(
                                crossAxisAlignment: CrossAxisAlignment.center,
                                mainAxisAlignment: MainAxisAlignment.center,
                                children: <Widget>[
                                  Text(
                                    'Title',
                                    style: Theme.of(context)
                                        .textTheme
                                        .title
                                        .copyWith(
                                            fontSize: 16, color: Colors.white),
                                  ),
                                  SizedBox(
                                    height: 2,
                                  ),
                                  Text(
                                    'Date',
                                    style: Theme.of(context)
                                        .textTheme
                                        .caption
                                        .copyWith(
                                            fontSize: 10, color: Colors.white),
                                  ),
                                  SizedBox(
                                    height: 2,
                                  ),
                                  Text(
                                    'Another Text',
                                    style: Theme.of(context)
                                        .textTheme
                                        .subtitle
                                        .copyWith(
                                            fontSize: 14, color: Colors.white),
                                  ),
                                ],
                              ),
                            ),
                            Expanded(
                              child: Container(
                                height: kToolbarHeight,
                                width: MediaQuery.of(context).size.width,
                                color: Colors.white,
                                child: Row(
                                  mainAxisAlignment:
                                      MainAxisAlignment.spaceEvenly,
                                  children: <Widget>[
                                    Text(
                                      'Prev',
                                    ),
                                    Text(
                                      'Next',
                                    )
                                  ],
                                ),
                              ),
                            )
                          ],
                        ),
                      ),
                    )
                  ],
              body: ScrollablePositionedList.builder(
                  physics: ScrollPhysics(),
                  itemPositionsListener: itemPositionListener,
                  itemScrollController: _itemScrollController,
                  initialScrollIndex: 0,
                  itemCount: 500,
                  itemBuilder: (BuildContext ctxt, int index) {
                    return Container(
                        margin: EdgeInsets.all(16)
                        ,
                        child: Text('$index'));
                  })),

I tried two approaches so far none of them working properly,

Approach 1

I added physics: ScrollPhysics(), to ScrollablePositionedList.builder

Output:

enter image description here

Appraoch 2

I added physics: NeverScrollableScrollPhysics(), to ScrollablePositionedList.builder

SliverAppBar hides this time but now I can not scroll to the very end of ScrollablePositionedList.builder I have 500 items on my list but it scrolls up to only 14th item, see the output. Also, it lags too much on scroll

Output:

enter image description here

Thanks in advance.

Welz
  • 236
  • 2
  • 10
  • 21
Ravinder Kumar
  • 7,407
  • 3
  • 28
  • 54
  • why don't you try the customScroll view based approach as shown here. https://flutter.dev/docs/cookbook/lists/floating-app-bar – Darish Feb 04 '20 at 06:08
  • 2
    Because [ScrollablePositionedList](https://github.com/google/flutter.widgets/tree/master/lib/src/scrollable_positioned_list) allows to scroll to specific item. – Ravinder Kumar Feb 04 '20 at 08:02
  • man same issue here, why wouldnt anything that is out of flutter team wont work, so frustrating. Anything other than the core flutter UI doesnt work well all the time – cs guy Nov 10 '20 at 22:34

3 Answers3

14

Answering question myself

This problem has no solution for it. I have created an issue here

It looks like ScrollablePositionedList with SliverAppBar cannot work until Flutter Team does not add shrinkwrap property to ScrollablePositionedList.

Feature request to add shrinkwrap is created here

Ravinder Kumar
  • 7,407
  • 3
  • 28
  • 54
0
    It works for me
    //create list of global keys

     List<GlobalKey> _formKeys = [];
    
    
    //assign keys from your list
    
    for(int i=0 ;i< syourlist.length;i++){
    final key = GlobalKey();
    _formKeys.add(key);
    }
    
    //in list view give key as below
    
    key:_formKeys[index]
    
    
    //on button click
    
     Scrollable.ensureVisible(_formKeys[index].currentContext);
  • Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation.You can find more information on how to write good answers in the help center. See [How to Answer]https://stackoverflow.com/questions/how-to-answer) – Vimal Patel Dec 14 '21 at 15:51
  • 1
    I don't see `ScrollablePositionedList` in your code. – Ravinder Kumar Dec 16 '21 at 09:41
0

Here is a basic workaround:

  • Use the ItemsPositionsListener to listen for the current item the list has scrolled to.
  • Then create boolean values to check the scroll-direction and amount.
  • These conditions control an AnimatedContainer controlling the height of a custom header.
  • This is placed as a child in a Column with the header in a Flexible widget so the scrollablelist correctly takes up the space before and after animation.

Although this is very basic and does not use the NestedScrollView, it keeps use of the ScrollablePositionedList, and achieves a similar effect with a header that slides in and out, based on the set scroll conditions.

Providing in case helps anyone else, until the underlying issue is fixed...:)


import 'package:flutter/material.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';

class ScrollAllWords extends StatefulWidget {
  const ScrollAllWords({
    Key? key,
    required this.list,
  }) : super(key: key);

  final List<String> list;

  @override
  State<ScrollAllWords> createState() => _ScrollAllWordsState();
}

class _ScrollAllWordsState extends State<ScrollAllWords> {

/// use this listener to control the header position.
  final _itemPositionsListener = ItemPositionsListener.create();

///Can also use the ItemScrollController to animate through the list (code omitted)
final _itemScrollController = ItemScrollController();


  /// Gets the current index the list has scrolled to.
  int _currentIndex = 0;

  /// Compares against current index to determine the scroll direction.
  int _shadowIndex = 0;


  bool _reverseScrolling = false;
  bool _showHeader = true;

  @override
  void initState() {

    /// Set up the listener.
    _itemPositionsListener.itemPositions.addListener(() {
      checkScroll();
    });

    super.initState();
  }

  void checkScroll() {
    /// Gets the current index of the scroll.
    _currentIndex =
        _itemPositionsListener.itemPositions.value
            .elementAt(0)
            .index;

    /// Checks the scroll direction.
    if (_currentIndex > _shadowIndex) {
      _reverseScrolling = false;
      _shadowIndex = _currentIndex;
    }
    if (_currentIndex < _shadowIndex) {
      _reverseScrolling = true;
      _shadowIndex = _currentIndex;
    }

    /// Checks whether to show or hide the scroller (e.g. show when scrolled passed 15 items and not reversing).
    if (!_reverseScrolling && _currentIndex > 15) {
      _showHeader = false;
    } else {
      _showHeader = true;
    }

    setState(() {});
  }

  @override
  Widget build(BuildContext context) {

    return Column(
      children: [
        AnimatedContainer(
          duration: const Duration(milliseconds: 120),
          height: _showHeader ? 200 : 0,
          curve: Curves.easeOutCubic,

          child: Container(
            color: Colors.red,
            height: size.height * 0.20,
          ),
        ),

        Flexible(
          child: ScrollablePositionedList.builder(
            itemScrollController: _itemScrollController,
            itemPositionsListener: _itemPositionsListener,
            itemCount: widget.list.length,
            itemBuilder: (context, index) {
              return ListTile(
                title: Text(widget.list[index]),
              );
            },
          ),
        ),

      ],
    );
  }
}

Example

Kdon
  • 892
  • 6
  • 19