sliver_tabbar_with_mixed_list 1.0.1 copy "sliver_tabbar_with_mixed_list: ^1.0.1" to clipboard
sliver_tabbar_with_mixed_list: ^1.0.1 copied to clipboard

A sliver with a pinned button tab bar over sections that may nest and mix row heights. The tab bar follows the scroll position.

example/lib/main.dart

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

void main() => runApp(const MyApp());

const double kHeaderHeight = 64;
const double kSubHeaderHeight = 44;
const double kDishHeight = 72;
const double kFeaturedHeight = 110;

class Course extends HeaderItem {
  const Course({
    required super.key,
    required this.name,
    required super.tabLabel,
    super.children,
    super.subSections,
  }) : super(itemHeight: kHeaderHeight);

  final String name;
}

class SubCourse extends SubheaderItem {
  const SubCourse({required super.key, required this.name, super.children})
    : super(itemHeight: kSubHeaderHeight);

  final String name;
}

class Dish extends ChildItem {
  const Dish({required super.key, required this.name, required this.price})
    : super(itemHeight: kDishHeight);

  final String name;
  final String price;
}

/// A taller row, drawn by `variantChildBuilder`.
class Featured extends VariantChildItem {
  const Featured({required super.key, required this.name})
    : super(itemHeight: kFeaturedHeight);

  final String name;
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SliverTabBarWithMixedList Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static const List<String> _courses = <String>[
    'Starters',
    'Soups',
    'Mains',
    'Grill',
    'Desserts',
    'Drinks',
  ];

  final ScrollController _controller = ScrollController();
  late final List<HeaderItem> _sections;
  int _section = 0;

  @override
  void initState() {
    super.initState();
    // No offset arithmetic anywhere: the package sums the row heights itself.
    _sections = <HeaderItem>[
      for (int i = 0; i < _courses.length; i++)
        Course(
          key: ValueKey<int>(i),
          name: _courses[i],
          tabLabel: _courses[i],
          children: <ChildItem>[
            if (i == 0)
              Featured(key: ValueKey<String>('f$i'), name: 'Chef pick'),
            for (int j = 0; j < 3; j++)
              Dish(
                key: ValueKey<String>('$i-$j'),
                name: '${_courses[i]} ${j + 1}',
                price: '${(j + 1) * 9 + i} zl',
              ),
          ],
          subSections: i.isEven
              ? <SubheaderItem>[
                  SubCourse(
                    key: ValueKey<String>('sub-$i'),
                    name: '${_courses[i]} — house selection',
                    children: <ChildItem>[
                      for (int j = 0; j < 2; j++)
                        Dish(
                          key: ValueKey<String>('$i-s-$j'),
                          name: 'House ${_courses[i]} ${j + 1}',
                          price: '${(j + 1) * 12}  zl',
                        ),
                    ],
                  ),
                ]
              : null,
        ),
    ];
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final ColorScheme colors = Theme.of(context).colorScheme;
    return Scaffold(
      body: CustomScrollView(
        controller: _controller,
        slivers: <Widget>[
          SliverAppBar(
            pinned: true,
            title: const Text('Menu'),
            actions: <Widget>[
              Center(
                child: Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 16),
                  child: Text('section $_section'),
                ),
              ),
            ],
          ),
          SliverTabBarWithMixedList(
            controller: _controller,
            listItemHeight: kDishHeight,
            sections: _sections,
            onSectionChanged: (int index) => setState(() => _section = index),
            tabBarPadding: const EdgeInsets.symmetric(vertical: 4),
            headerBuilder: (BuildContext context, HeaderItem item) =>
                ColoredBox(
                  color: colors.primaryContainer,
                  child: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 16),
                    child: Align(
                      alignment: Alignment.centerLeft,
                      child: Text(
                        (item as Course).name,
                        style: Theme.of(context).textTheme.titleLarge,
                      ),
                    ),
                  ),
                ),
            subHeaderBuilder: (BuildContext context, SubheaderItem item) =>
                ColoredBox(
                  color: colors.secondaryContainer,
                  child: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 24),
                    child: Align(
                      alignment: Alignment.centerLeft,
                      child: Text((item as SubCourse).name),
                    ),
                  ),
                ),
            childBuilder: (BuildContext context, ChildItem item) {
              final Dish dish = item as Dish;
              return ListTile(
                title: Text(dish.name),
                trailing: Text(dish.price),
              );
            },
            variantChildBuilder:
                (BuildContext context, VariantChildItem item) => Card(
                  margin: const EdgeInsets.all(8),
                  color: colors.tertiaryContainer,
                  child: Center(child: Text((item as Featured).name)),
                ),
            customFooterWidget: const Center(child: Text('end of the menu')),
          ),
        ],
      ),
    );
  }
}
1
likes
160
points
186
downloads

Documentation

API reference

Publisher

verified publisherbomsamdi.com

Weekly Downloads

A sliver with a pinned button tab bar over sections that may nest and mix row heights. The tab bar follows the scroll position.

Repository (GitHub)
View/report issues

Topics

#sliver #tabs #list #scrolling #widget

License

MIT (license)

Dependencies

after_first_frame_mixin, flutter, sliver_tools

More

Packages that depend on sliver_tabbar_with_mixed_list