Flutter implementation of sticky headers for sliver

Overview

flutter_sticky_header

A Flutter implementation of sticky headers with a sliver as a child.

Pub Donate

Screenshot

Features

  • Accepts one sliver as content.
  • Header can overlap its sliver (useful for sticky side header for example).
  • Notifies when the header scrolls outside the viewport.
  • Can scroll in any direction.
  • Supports overlapping (AppBars for example).
  • Supports not sticky headers (with sticky: false parameter).
  • Supports a controller which notifies the scroll offset of the current sticky header.

Getting started

In the pubspec.yaml of your flutter project, add the following dependency:

dependencies:
  ...
  flutter_sticky_header:

In your library add the following import:

import 'package:flutter_sticky_header/flutter_sticky_header.dart';

For help getting started with Flutter, view the online documentation.

SliverStickyHeader

You can place one or multiple SliverStickyHeaders inside a CustomScrollView.

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

SliverStickyHeader.builder

If you want to change the header layout during its scroll, you can use the SliverStickyHeader.builder constructor.

The example belows changes the opacity of the header as it scrolls off the viewport.

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

You can find more examples in the Example project.

Sponsoring

I'm working on my packages on my free-time, but I don't have as much time as I would. If this package or any other package I created is helping you, please consider to sponsor me. By doing so, I will prioritize your issues or your pull-requests before the others.

Changelog

Please see the Changelog page to know what's recently changed.

Contributions

Feel free to contribute to this project.

If you find a bug or want a feature, but don't know how to fix/implement it, please fill an issue.
If you fixed a bug or implemented a new feature, please send a pull request.

Thanks

👏 Thanks to slightfoot with it's RenderBox version (https://github.com/slightfoot/flutter_sticky_headers) which unintentionally challenged me to work in this RenderSliver version.

Comments
  • Reverse list support

    Reverse list support

    When using a CustomScrollView with the reverse property set to true it would make sense to reverse the SliverStickyHeader as well. In a reversed list, the header currently is at the bottom of the list. I suggest to add a reverse option to SliverStickyHeader as well, so it renders after the other sliver widgets, instead of before.

    opened by wwwdata 18
  • Version 0.6.2 can not response events when widget in the header, such as Inkwell、Button etc.

    Version 0.6.2 can not response events when widget in the header, such as Inkwell、Button etc.

    You can duplicate the issue by running the code as below.

    import 'package:flutter/material.dart';
    import 'package:flutter_sticky_header/flutter_sticky_header.dart';
    
    class TabHeaderExample extends StatefulWidget {
      const TabHeaderExample({Key? key}) : super(key: key);
    
      @override
      State<TabHeaderExample> createState() => _TabHeaderExampleState();
    }
    
    class _TabHeaderExampleState extends State<TabHeaderExample> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text('Tab List Example'),),
          body: CustomScrollView(
            slivers: [
              _StickyHeaderList(index: 0),
              _StickyHeaderList(index: 1),
              _StickyHeaderList(index: 2),
              _StickyHeaderList(index: 3),
            ],
          ),
        );
      }
    }
    
    class _StickyHeaderList extends StatefulWidget  {
      final int index;
      const _StickyHeaderList({Key? key, required this.index}) : super(key: key);
    
      @override
      State<_StickyHeaderList> createState() => _StickyHeaderListState();
    }
    
    class _StickyHeaderListState extends State<_StickyHeaderList> with TickerProviderStateMixin {
    
      late TabController _tabController;
    
      @override
      void initState() {
        super.initState();
        _tabController = TabController(length: 10, vsync: this);
      }
    
      @override
      Widget build(BuildContext context) {
        return SliverStickyHeader(
          header: Container(
            color: Colors.white,
            height: 47,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Expanded(
                  child: TabBar(
                    isScrollable: true,
                    controller: _tabController,
                    indicatorColor: const Color(0xFF216DDF),
                    indicatorSize: TabBarIndicatorSize.label,
                    indicatorWeight: 2,
                    indicatorPadding: EdgeInsets.only(bottom: 10),
                    unselectedLabelColor: const Color(0xFF888888),
                    unselectedLabelStyle: TextStyle(fontSize: 17),
                    labelColor: const Color(0xFF216DDF),
                    labelStyle: TextStyle(fontSize: 17, fontWeight: FontWeight.bold),
                    tabs: _createTabs(),
                    onTap: (index) { print(index);},
                  ),
                ),
                InkWell(
                  child: Container(
                    decoration: BoxDecoration(
                      color: Colors.red,
                      boxShadow: [
                        BoxShadow(
                          color: Colors.white,
                          blurRadius: 8,
                          offset: Offset(-20, 0),
                        ),
                      ],
                    ),
                    padding: EdgeInsets.only(left: 5, right: 12),
                    child: Row(
                      children: [
                        Text(
                          "全部",
                          style: TextStyle(color: const Color(0xFF101010), fontSize: 17),
                        ),
                      ],
                    ),
                  ),
                  onTap: () {
                    // Can not response
                    print("1234567");
                  },
                ),
              ],
            ),
          ),
          sliver: SliverList(
            delegate: SliverChildBuilderDelegate(
                  (context, i) => ListTile(
                onTap: () {
                  print('tile $i');
                },
                leading: CircleAvatar(
                  child: Text('${widget.index}'),
                ),
                title: Text('List tile #$i'),
              ),
              childCount: 6,
            ),
          ),
        );
      }
    
      /// 创建Tabs
      List<Widget> _createTabs() {
        return ["我是tab", "我是tab","我是tab","我是tab","我是tab","我是tab","我是tab","我是tab","我是tab","我是tab"].map((e) => Tab(text: e)).toList();
      }
    }
    
    opened by realWayneZhang 16
  • Nested SliverStickyHeader

    Nested SliverStickyHeader

    Thank you for your library, it's super useful. I have one question: Is it possible to achieve something like this:

    As you can see header with dates sticks to the screen as well as the side header with time (it is a https://github.com/google/iosched app). Example app have great section with side headers, but i tried to nest SliverStickyHeader and it doesn't work. Second Header doesn't stick to screen.

    Code

    // code from example app
    class MainScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        List<Widget> slivers = new List<Widget>();
    
        // App Bar
        slivers.add(getAppBar());
    
        slivers.add( new SliverStickyHeader(
          // Tabs header
          header:_buildHeader(1, text: 'Tabs'),
          // Side header
          sliver: SliverStickyHeader(
           overlapsContent: true,
           header: _buildSideHeader(1),
           sliver: new SliverPadding(
           // other code is same, create grid
    
      }
    
      Widget getAppBar() {
        return SliverAppBar(
          expandedHeight: 60.0,
          floating: true,
          brightness: Brightness.light,
          title: Text('Main AppBar',
              style: TextStyle(color: Colors.black.withOpacity(1.0))),
          elevation: 0.0,
          centerTitle: true,
          backgroundColor: Color.fromRGBO(255, 255, 255, 1.0),
        );
      }
    
    enhancement 
    opened by bunopus 13
  • off by one pixel

    off by one pixel

    I'm noticing a one pixel difference at the top when I use a CustomScrollView containing a SliverList, and a CustomScrollView using SliverStickyHeader.

    image

    To reproduce, clone https://github.com/SteveAlexander/one_pixel_bug then tap in the text area at the top of the screen so that it gets focus.

    Notice that the 2px blue border on the TextField is covered by the SliverStickyHeader by 1px.

    This does not happen when using regular Slivers in a CustomScrollView.

    flutter issue 
    opened by SteveAlexander 9
  • Sticky with SliverAppBar

    Sticky with SliverAppBar

    I could not manage to use sticky header with sliver app bar when pinned set to true. The sliver sticky header scrolled below sliver app bar.

    slivers.add(
          SliverAppBar(
            expandedHeight: 60.0,
            floating: true,
            pinned: true, // <-- Setting this to true make the header not sticky on scroll
            title: Text(
              'Main AppBar',
              style: TextStyle(color: Colors.black.withOpacity(1.0)),
            ),
            bottom: PreferredSize(
              preferredSize: Size.fromHeight(20.0),
              child: Text("Bottom Text"),
            ),
            elevation: 0.0,
            centerTitle: true,
          ),
        );
    
        slivers.add(_buildBuilderExample());
        int i = 0;
        slivers.addAll(_buildHeaderBuilderLists(context, i, i),
    );
    

    Thank you.

    bug 
    opened by iambudi 8
  • Fix the tap offset when overlaps is > 0

    Fix the tap offset when overlaps is > 0

    Fixes https://github.com/letsar/flutter_sticky_header/issues/80

    Issue: Issue

    Fix: Fix

    @letsar let me know if you have any comments about this, I'm open to spend time solving them.

    I'm currently needing this to finish a functionality, so having this packed in a future ^0.6.5 (or sth like that) would be awesome!

    opened by darrillaga 7
  • Tap on element inside SliverStickyHeader hit test gets broken when using with floating SliverAppBar

    Tap on element inside SliverStickyHeader hit test gets broken when using with floating SliverAppBar

    Hi, I was trying to add some interaction to elements inside SliverStickyHeader and floating SliverAppBar. Noticed that when you scroll the list down till the SliverAppBar disappears and then scroll up a little bit until SliverAppBar appears again, the onTap event won't fire any more.

    Below I'm adding the code that reproduces the issue. I have added a clickable element "Sometimes I'm not clickable" and a "Shop" title. To reproduce the issue you need to scroll the list down till the SliverAppBar disappears and then scroll up a little bit until SliverAppBar appears again. You should see that hit test moves from "Sometimes I'm not clickable" element to the "Shop" title, and "Sometimes I'm not clickable" becomes not clickable until you scroll all the way to the top of the list.

    import 'package:flutter/material.dart';
    import 'package:flutter_sticky_header/flutter_sticky_header.dart';
    
    class HomePage extends StatelessWidget {
      const HomePage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return SafeArea(
          child: Scaffold(
            body: CustomScrollView(
              slivers: [
                const SliverAppBar(title: Text('Example'), titleSpacing: 0, floating: true),
                SliverStickyHeader(
                  header: Column(
                    children: [
                      Container(
                        alignment: Alignment.center,
                        height: 60.0,
                        width: double.infinity,
                        color: Colors.white,
                        child: const Text('Shop', style: TextStyle(fontSize: 20.0)),
                      ),
                      InkWell(
                        onTap: () => print('111111'),
                        child: Container(
                            alignment: Alignment.center,
                            height: 60,
                            width: double.infinity,
                            color: Colors.red,
                            child: const Text('Sometimes I\'m not clickable')),
                      ),
                    ],
                  ),
                  sliver: SliverList(
                    delegate: SliverChildBuilderDelegate(
                      (BuildContext context, int index) {
                        return Card(
                          margin: const EdgeInsets.all(8.0),
                          child: Container(
                            color: Colors.blue[100 * (index % 9 + 1)],
                            height: 80,
                            alignment: Alignment.center,
                            child: Text("Item $index", style: const TextStyle(fontSize: 20)),
                          ),
                        );
                      },
                      childCount: 100,
                    ),
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    
    
    opened by BOGDANOLEKSANDR 6
  • Support null safety

    Support null safety

    This is a request to add support for null safety to package:flutter_sticky_header. We depend on your awesome package, so would be great to have null safety enabled.

    The Dart/Flutter team already encourages publishing the migrated packages: See this blog post.

    See the migration guide for details about enabling null safety.

    opened by IchordeDionysos 6
  • Using ListView.builder

    Using ListView.builder

    Hello can this component be used inside ListView.builder ?

    ListView.builder(
             itemBuilder: (BuildContext context, int index) =>
    SliverStickyHeader(
                    header: ...
    
                    sliver: SliverList(delegate: SliverChildBuilderDelegate(
                          (context, rowIndex) => ...                  
                   childCount: ... ,
    )
    

    Checking out the performance it seems that the memory allocation is retained longer than needed so how can we build an " infinite list ".

    opened by durduman 6
  • How I can build infinite scroll?

    How I can build infinite scroll?

    So case here. I want to build infinite both-direction list and split sections with sticky headers. How I can do this with your plugin (if it's possible in general)?

    P.S. To make both-direction infinite list I used Viewport with 2 SliverList instances, and "center" key, that points to positive direction list

    waiting for user response 
    opened by TatsuUkraine 6
  • Added the possibility to have multiple slivers as child

    Added the possibility to have multiple slivers as child

    With this pr you can have multiple slivers as child what would make it possible for instance to have multiple sticky headers as child, or 2 lists.

    Also solves #62

    opened by UnderKoen 5
  • Console has error output when scroll the CustomScrollView with SliverStickyHeader

    Console has error output when scroll the CustomScrollView with SliverStickyHeader

    environment: Flutter 3.3.8 • channel stable • https://github.com/flutter/flutter.git Framework • revision 52b3dc25f6 (7 weeks ago) • 2022-11-09 12:09:26 +0800 Engine • revision 857bd6b74c Tools • Dart 2.18.4 • DevTools 2.15.0

    error info:

    ======== Exception caught by rendering library =====================================================
    The following assertion was thrown during performLayout():
    'package:flutter/src/rendering/sliver_list.dart': Failed assertion: line 315 pos 14: 'estimatedMaxScrollOffset >= endScrollOffset - childScrollOffset(firstChild!)!': is not true.
    
    
    Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
    In either case, please report this assertion by filing a bug on GitHub:
      https://github.com/flutter/flutter/issues/new?template=2_bug.md
    
    opened by ericwangjp 0
  • Package Performance Hit

    Package Performance Hit

    Before I ask my question... the explanation of what I'm trying to achieve...

    • IM BUILDING a custom contact picker

    I have headers for each section A, B, etc...

    I have the ability to scroll to a particular section because I know the height of . the headers . and how many items are in each section (all the contacts are of the same size)

    At the moment I am using a CustomScrollView with a SliverFixedExtentList with a SliverChildBuilderDelegate

    • THE QUESTION Will using this plugin to make my headers sticky affect performance by essentially shrinking wrapping each of my SliverFixedExtentList? will performance be affected any other way? If so, by how much?
    opened by b-cancel 0
  • [BUG] CustomScrollView with

    [BUG] CustomScrollView with "center" scrolls backwards

    When using center key, the header gets stuck on the bottom. I wondering if there is a way to simply display headers for months and years indefinitely starting at a certain date in the center. There seems to be no easy way to solve this problem in Flutter.

    
    return CustomScrollView(
            cacheExtent: 300.0,
            center: centerKey,
            anchor: 0.5,
            slivers: <Widget>[
              _rowBuilder(context, date, -1),
              SliverList(
                key: centerKey,
                delegate: SliverChildListDelegate([
                  Column(
                    children: [
                      Divider(),
                      Container(
                        color: Color.fromARGB(255, 174, 12, 0),
                        child: Text('data'),
                      ),
                      Divider(),
                    ],
                  ) 
                ]),
              ),
              _rowBuilder(context, date, 1),
            ],
          );
    
    opened by Areopagitics 3
Releases(v0.4.0)
Owner
Romain Rastel
Flutter Developer
Romain Rastel
Multi directional infinite list with Sticky headers for Flutter applications

Sticky Infinite List Infinite list with sticky headers. This package was made in order to make possible render infinite list in both directions with s

Denis Beketsky 291 Dec 20, 2022
Flutter implementation of sticky headers for sliver

flutter_sticky_header A Flutter implementation of sticky headers with a sliver as a child. Features Accepts one sliver as content. Header can overlap

Romain Rastel 775 Jan 3, 2023
Build a grouped list, which support expand/collapse section and sticky headers, support use it with sliver widget.

sticky_and_expandable_list Flutter implementation of sticky headers and expandable list.Support use it in a CustomScrollView. README i18n:中文说明 Feature

tp7309 114 Nov 16, 2022
Sliver bar chart - A package that supports Bar Chart in a Flutter Sliver

Sliver bar chart - A package that supports Bar Chart in a Flutter Sliver. This Package allows us to add Bar Chart in Sliver and sets a Bar Chart as a Header on Slivers Scroll.

MindInventory 19 Oct 11, 2022
Multi directional infinite list with Sticky headers for Flutter applications

Sticky Infinite List Infinite list with sticky headers. This package was made in order to make possible render infinite list in both directions with s

Denis Beketsky 291 Dec 20, 2022
Multi directional infinite list with Sticky headers for Flutter applications

Sticky Infinite List Infinite list with sticky headers. This package was made in order to make possible render infinite list in both directions with s

Denis Beketsky 291 Dec 20, 2022
Flutter Sticky Headers - Lets you place

Flutter Sticky Headers Lets you place headers on scrollable content that will stick to the top of the container whilst the content is scrolled. Usage

Flutter Community 901 Dec 28, 2022
A Flutter sticky headers & index ListView. Based on scrollable_positioned_list.

Language: English | 中文简体 azlistview A Flutter sticky headers & index ListView. Based on scrollable_positioned_list. AzListView, SuspensionView, IndexB

Flutter中国开源项目 971 Jan 7, 2023
best flutter / dart practices + Custom Painter + Sliver App Bar + Custom Scrollview

Weekly Budget Flutter App A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get yo

Mohamed Awnallah 4 Oct 21, 2021
A set of useful sliver tools that are missing from the flutter framework

sliver_tools A set of useful sliver tools that are missing from the flutter framework. Here is a taste what you can make using this package The struct

Pieter van Loon 419 Jan 4, 2023
Reorderable table, row, column, wrap, and sliver list that allow drag and drop of the children. https://pub.dartlang.org/packages/reorderables

** Kindly submit PR if you encounter issues and please make sure you're using stable channel releases. ** Maintaining open source software ain't easy.

Hansheng 575 Jan 5, 2023
A ListView that allows you to group list items and support headers like iOS UITableView section.

GroupListView package for Flutter. A ListView that allows you to group list items and support headers like iOS UITableView section. Features List Item

Daniel Ioannou 73 Nov 21, 2022
Sticky Grid View For Flutter

Features Listing images in gridview wrapped listview gives bad performance (disp

null 1 Nov 14, 2022
Example project for sticky infinite list

sticky_infinite_list_example Example for sticky infinite list package Example for v1.x.x can be found here Getting Started This project is a starting

Denis Beketsky 6 Nov 6, 2022
Get It - Simple direct Service Locator that allows to decouple the interface from a concrete implementation and to access the concrete implementation from everywhere in your App. Maintainer: @escamoteur

❤️ Sponsor get_it This is a simple Service Locator for Dart and Flutter projects with some additional goodies highly inspired by Splat. It can be used

Flutter Community 1k Jan 1, 2023
Dart GraphQL server implementation. Utilities, code generator, examples and reference implementation.

Leto - GraphQL Server A complete implementation of the official GraphQL specification in the Dart programming language. Inspired by graphql-js, async-

Juan Manuel Castillo 29 Nov 27, 2022
Learn Flutter on Flutter! A widget directory with implementation samples!

Fludget Browse through a variety of widgets used in flutter This application is developed to learn Flutter using Flutter. Different widgets used in fl

ACM VIT 29 Nov 23, 2022
Flutter firebase auth - Simple implementation of Firebase Authentication using Flutter

FlutterFire Authentication Sample A simple implementation of Firebase Authentica

Souvik Biswas 4 Apr 2, 2022
Sardor Makhmudov 0 Feb 7, 2022
[Flutter SDK V.2] - Youtube Video is a Flutter application built to demonstrate the use of Modern development tools with best practices implementation like Clean Architecture, Modularization, Dependency Injection, BLoC, etc.

[Flutter SDK V.2] - Youtube Video is a Flutter application built to demonstrate the use of Modern development tools with best practices implementation like Clean Architecture, Modularization, Dependency Injection, BLoC, etc.

R. Rifa Fauzi Komara 17 Jan 2, 2023