21

For the last few days, I've been reading through flutter framework documentation and especially the sliver part but I'm not quite sure where to start. I'm trying to implement the sticky headers and snap effect. Might the RenderSliverList be a good start? Do I need to re-layout things? Do I need to do additional drawing? And if so where?

Any help on where to start would be a huge help, thanks in advance!

Edit: I think I understood the layout part now, but I just can't find where the painting is supposed to happen.

Edit 2: For clarification, this is the desired "sticky header effect":

How can I make sticky headers in RecyclerView? (Without external lib)

and this is the "snap" effect:

https://rubensousa.github.io/2016/08/recyclerviewsnap

Norbert
  • 947
  • 2
  • 10
  • 15

6 Answers6

26

For the "sticky header effect" I ran into this problem myself, so I created this package to manage sticky headers with slivers: https://github.com/letsar/flutter_sticky_header

Flutter Sticky Headers

To use it you have to create one SliverStickyHeader per section in a CustomScrollView.

One section can be wrote like this:

new SliverStickyHeader(
  header: new Container(
    height: 60.0,
    color: Colors.lightBlue,
    padding: EdgeInsets.symmetric(horizontal: 16.0),
    alignment: Alignment.centerLeft,
    child: new Text(
      'Header #0',
      style: const TextStyle(color: Colors.white),
    ),
  ),
  sliver: new SliverList(
    delegate: new SliverChildBuilderDelegate(
      (context, i) => new ListTile(
            leading: new CircleAvatar(
              child: new Text('0'),
            ),
            title: new Text('List tile #$i'),
          ),
      childCount: 4,
    ),
  ),
);

If you want, the entire source code for the above demo is here: https://github.com/letsar/flutter_sticky_header/blob/master/example/lib/main.dart

I hope this will help you.

Romain Rastel
  • 5,144
  • 2
  • 25
  • 23
  • #Romain Rastel what if I want to stick only one header, for example, #header2 at the top?? How do I achieve that using this package? – satish Feb 19 '20 at 09:24
16

It's dead simple :

Use a CustomScrollView and give it as child both a SliverList and a SliverAppBar. You may replace the SliverList with a SliverGrid if you need to.

Then, depending on the effect you want to achieve, there are a few properties you may set on SliverAppBar:

  • snap
  • expandedHeight (+ flexibleSpace)
  • floating
  • pinned

In the end, you may have something similar to :

new CustomScrollView(
    slivers: <Widget>[
        new SliverAppBar(
            title: new Text("Title"),
            snap: true,
            floating: true,
        ),
        new SliverFixedExtentList(
            itemExtent: 50.0,
            delegate: new SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                    return new Container(
                        alignment: Alignment.center,
                        color: Colors.lightBlue[100 * (index % 9)],
                        child: new Text('list item $index'),
                    );
                },
            ),
        ),
    ],
)

Even better, you can concatenate different scroll behaviour inside a single CustomScrollView. Which means you can potentially have a grid followed by a list just by adding a SliverGrid as a child to your scrollView.

I know I know, flutter is awesome.

Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • Thanks for the answer, indeed flutter is awesome! Unfortunately, I was looking for an effect like this: https://stackoverflow.com/questions/32949971/how-can-i-make-sticky-headers-in-recyclerview-without-external-lib. But I like the concept of concatenating different slivers, do you think it would be possible to achieve that effect with only standard slivers? – Norbert Feb 05 '18 at 13:09
  • Oh, I see. In this case, you should split your question in two : One for the sticky header. And another one for the snap effect. – Rémi Rousselet Feb 05 '18 at 13:19
  • But in short, your "sticky effect" most likely don't have a built-in solution. But you may look into `SliverPersistentHeader` code to make your custom behaviour. – Rémi Rousselet Feb 05 '18 at 13:24
  • As for the snap effect, it can be achieved with a scroll controller on your scroll element. Add a listener, and on scroll end, use `animateTo`. – Rémi Rousselet Feb 05 '18 at 13:25
  • Thanks, that should work as the snap. Regarding the "sticky effect", yes I thought so, I've been reading through documentation the last couple of day but I'm not entirely sure how I would approach that. – Norbert Feb 05 '18 at 13:32
  • Hi Rémi Rousselet, do you know how I could use the _SliverAppBarDelegate without providing it a collapsedHeight and an expandedHeight? I would just like the stickyheader to wrap content like how one would do it in android. Please see my answer to this question and comment if you know. – Simon Apr 15 '18 at 19:28
10

I managed to do the stickyheader effect on Flutter for an iOS app using the following code - credit goes to this piece of code written here from where I drew my inspiration (https://github.com/flutter/flutter/blob/master/examples/flutter_gallery/lib/demo/animation/home.dart#L112):

class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  _SliverAppBarDelegate({
    @required this.collapsedHeight,
    @required this.expandedHeight,}
      );

  final double expandedHeight;
  final double collapsedHeight;

  @override double get minExtent => collapsedHeight;
  @override double get maxExtent => math.max(expandedHeight, minExtent);

  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    return new Container(color: Colors.red,
        child: new Padding(
          padding: const EdgeInsets.only(
              left: 8.0, top: 8.0, bottom: 8.0, right: 8.0),
          child: new Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              new Text("Time"), new Text("Price"), new Text("Hotness")
            ],
          ),
        )
    );
  }

  @override
  bool shouldRebuild(@checked _SliverAppBarDelegate oldDelegate) {
    return expandedHeight != oldDelegate.expandedHeight
        || collapsedHeight != oldDelegate.collapsedHeight;
  }
}

To make it sticky, add the _SliverAppBarDelegate to the silvers widget list:

 new SliverPersistentHeader(delegate: new _SliverAppBarDelegate(collapsedHeight: 36.0, expandedHeight: 36.0), pinned: true, ),

I'm not really sure how to make the _SliverAppBarDelegate wrap the content though, I had to provide it with a size of 36 logical pixels to get it to work. If anyone know how it could just wrap content, please drop a comment to the answer below.

enter image description here

Simon
  • 19,658
  • 27
  • 149
  • 217
  • Is it possible to have something similar at the bottom of the page, like the red header only as footer? – Wout Jun 10 '18 at 17:02
  • i think it is possible. You will need to place the SliverPersistentHeader last in the build method and wrap the items before the SliverPersistentHeader in a Expanded widget so that it pushes the header to the bottom. Just an idea, try it maybe? – Simon Jun 10 '18 at 17:14
  • Nope, that did not work for me. I worked around it with a stack for now, that just places the footer as a column at MainAxisAlignment.end on the second layer. To make sure the end of the list is showing, I had to put the SliverList in a SliverPadding with bottom insets. It works, but feels like kind of overkill. A minor disadvantage is that the scroll end animation does not show up at the right place (it's under the footer layer). – Wout Jun 14 '18 at 21:39
4

I solved this problem, try sticky_and_expandable_list.

enter image description here

Features

  • Support build an expandable ListView, which can expand/collapse section or create sticky section header.
  • Use it with CustomScrollView、SliverAppBar.
  • Listen the scroll offset of current sticky header, current sticky header index and switching header index.
  • Only use one list widget, so it supports large data and a small memory usage. More section customization support, you can return a new section widget by sectionBuilder, to customize background,expand/collapse animation, section layout, and so on.
  • Support add divider.
Milo
  • 49
  • 2
0

As per documentation, you can place a StickyHeader or StickyHeaderBuilder inside any scrollable content.

The documentation page provides only an example how to apply a StickyHeader with a ListView, ok what about the other widgets such as SingleChildScrollView or CustomScrollView ?

In this example I will provide a very simple StickyHeader with a SingleChildScrollView and sure you can put it inside any SingleChildScrollView you want:

return Container(
    child: SingleChildScrollView(
      scrollDirection: Axis.vertical,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text("My title"),
          StickyHeader(
            header: Container(
                  child: // Put here whatever widget you like as the sticky widget
                ),
            content: Column(
              children: [
                Container(
                  child: // Put here whatever widget you like as scrolling content (Column, Text, ListView, etc...)
                ),
              ],
            ),
          ),
        ],
      ),
    ),
  );
Osama Remlawi
  • 2,356
  • 20
  • 21
0

without implementing yours using sliver, you can achieve this using flutter community awesome plugin.

https://pub.dev/packages/sticky_headers

IonicFireBaseApp
  • 925
  • 3
  • 10