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

A sliver with a pinned tab bar over a fixed-extent list. The tab bar follows the scroll, and tapping a tab scrolls to its section.

example/lib/main.dart

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

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

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

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

/// A section header. Only the label and whatever the app needs to draw the row.
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, required this.price});

  final String name;
  final String price;
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

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

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

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

  @override
  void initState() {
    super.initState();
    // No offset arithmetic: the package derives every section's scroll offset
    // from the section order and listItemHeight.
    _sections = <Section>[
      for (int i = 0; i < _courses.length; i++)
        Section(
          header: Course(
            key: ValueKey<int>(i),
            name: _courses[i],
            tabLabel: _courses[i],
          ),
          children: <ChildItem>[
            for (int j = 0; j < 6; j++)
              Dish(
                key: ValueKey<String>('$i-$j'),
                name: '${_courses[i]} item ${j + 1}',
                price: '${(j + 1) * 7 + i} zl',
              ),
          ],
        ),
    ];
  }

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

  Widget _buildHeader(BuildContext context, HeaderItem item) {
    final Course course = item as Course;
    return ColoredBox(
      color: Theme.of(context).colorScheme.primaryContainer,
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 16),
        child: Align(
          alignment: Alignment.centerLeft,
          child: Text(
            course.name,
            style: Theme.of(context).textTheme.titleLarge,
          ),
        ),
      ),
    );
  }

  Widget _buildChild(BuildContext context, ChildItem item) {
    final Dish dish = item as Dish;
    return ListTile(title: Text(dish.name), trailing: Text(dish.price));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        controller: _controller,
        slivers: <Widget>[
          SliverAppBar(
            pinned: true,
            title: Text(widget.title),
            actions: <Widget>[
              Center(
                child: Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 16),
                  child: Text('section $_section'),
                ),
              ),
            ],
          ),
          SliverFixedExtendListWithTabs(
            controller: _controller,
            listItemHeight: 64,
            sections: _sections,
            headerBuilder: _buildHeader,
            childBuilder: _buildChild,
            onSectionChanged: (int index) => setState(() => _section = index),
            tabBarIndicator: BoxDecoration(
              borderRadius: BorderRadius.circular(24),
              color: Theme.of(context).colorScheme.primary,
            ),
            labelColor: Theme.of(context).colorScheme.onPrimary,
            unselectedLabelColor: Theme.of(context).colorScheme.onSurface,
            indicatorPadding: const EdgeInsets.symmetric(
              horizontal: 4,
              vertical: 6,
            ),
            customFooterWidget: const Center(child: Text('end of the menu')),
          ),
        ],
      ),
    );
  }
}
2
likes
160
points
164
downloads

Documentation

API reference

Publisher

verified publisherbomsamdi.com

Weekly Downloads

A sliver with a pinned tab bar over a fixed-extent list. The tab bar follows the scroll, and tapping a tab scrolls to its section.

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_fixed_extend_list_with_tabs