SliverFixedExtendListWithTabs
A sliver with a pinned tab bar over a fixed-extent list of sections — the pattern behind restaurant menus and catalogue screens. The tab bar follows the scroll position, tapping a tab scrolls to its section, and a footer fills the gap under a last section that is too short to reach the top.
Installation
dependencies:
sliver_fixed_extend_list_with_tabs: ^1.0.0
Requires Dart 3.8 / Flutter 3.32 or newer.
Usage
Describe your rows by extending HeaderItem and ChildItem, then hand the
sections over. Scroll offsets are computed for you.
class Course extends HeaderItem {
const Course({required super.key, required this.name, required super.tabLabel});
final String name;
}
class Dish extends ChildItem {
const Dish({required super.key, required this.name});
final String name;
}
CustomScrollView(
controller: _controller,
slivers: <Widget>[
const SliverAppBar(pinned: true, title: Text('Menu')),
SliverFixedExtendListWithTabs(
controller: _controller,
listItemHeight: 64,
sections: sections,
headerBuilder: (context, item) => Text((item as Course).name),
childBuilder: (context, item) => Text((item as Dish).name),
onSectionChanged: (index) => setState(() => _section = index),
),
],
)
The controller must be the same one the enclosing CustomScrollView uses.
Custom tabs
tabLabel gives the default tab its text. For anything else, build the tab
yourself:
SliverFixedExtendListWithTabs(
tabBuilder: (BuildContext context, HeaderItem item, int index) => Tab(
icon: Icon(icons[index]),
text: (item as Course).name,
),
...
)
Slivers above the list
If something expandable precedes this sliver — a SliverAppBar with
expandedHeight, for instance — tell it how much, so tab taps land in the right
place:
SliverFixedExtendListWithTabs(startOffset: 200, ...)
Parameters
| Parameter | Default | Description |
|---|---|---|
sections |
required | Header + children per section. |
listItemHeight |
required | Height of every row, headers included. |
controller |
required | The enclosing scroll view's controller. |
childBuilder / headerBuilder |
required / null | Row builders. |
tabBuilder |
null | Builds a tab; falls back to HeaderItem.tabLabel. |
onSectionChanged |
null | Called with the section index on every change. |
scrollAnimated |
true |
Animate the scroll on a tab tap instead of jumping. |
scrollDuration |
300 ms | Duration of that scroll and of the tab bar animation. |
startOffset |
0 |
Extent of the slivers above this one. |
customFooterWidget |
null | Drawn in the space under a short last section. |
tabBarIndicator, indicatorPadding, tabBarIndicatorSize, tabAlignment |
— | Passed to TabBar. |
labelColor, unselectedLabelColor, labelStyle, unselectedLabelStyle, dividerColor |
— | Passed to TabBar. |
tabBarBackgroundColor, tabBarHeight, tabBarPadding |
— | Tab bar box. |
tabBarCurveAnimation, listScrollCurveAnimation |
linear / easeInOut | Animation curves. |
Migrating from 0.0.3
- Tabs now show your own label. They used to render an internal debug string
(
Tab 0 0,Tab 1 11, …) with no way to change it. PasstabLabelon the header item, or atabBuilder. offsetStart,childrenCountandchildrenHeightonHeaderItemare no longer needed. The package computes offsets from the section order andlistItemHeight. The parameters still exist, are deprecated and ignored, so existing subclasses keep compiling — you can delete the arithmetic.
License
MIT.
Libraries
- sliver_fixed_extend_list_with_tabs
- A sliver with a pinned tab bar over a fixed-extent list of sections.