Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to group list items under sticky headers in Flutter

I'm trying to group my list items and create a sticky header for each group. Number of the items in each group may be different. So, I searched and found a package called sticky_headers 0.1.8+1. It is cool but it is creating header for each row. I can't find a way to group similar items under same header. This is my code;

class FixtureList extends StatelessWidget {
  final dateFormat = DateFormat('MMMd');
  final timeFormat = DateFormat('Hm');

  @override
  Widget build(BuildContext context) {
    return Consumer<FixtureData>(
      builder: (context, fixtureData, child) {
        return ListView.builder(
          scrollDirection: Axis.vertical,
          shrinkWrap: true,
          itemBuilder: (context, index) {
            final fixture = fixtureData.fixtures[index];

            return StickyHeader(
              header: Container(
                height: 30.0,
                color: Colors.lightBlueAccent,
                padding: EdgeInsets.symmetric(horizontal: 8.0),
                alignment: Alignment.centerLeft,
                child: Text(
                  dateFormat.format(fixture.dateTime.toLocal()),
                  style: const TextStyle(color: Colors.black),
                ),
              ),
              content: FixtureTile(
                date: dateFormat.format(fixture.dateTime.toLocal()),
                time: timeFormat.format(fixture.dateTime.toLocal()),
                homeTeam: fixture.homeTeam,
                awayTeam: fixture.awayTeam,
                homeOdds: fixture.homeOdds,
                drawOdds: fixture.drawOdds,
                awayOdds: fixture.awayOdds,
                isHomeSelected: fixture.homeSelected,
                isDrawSelected: fixture.drawSelected,
                isAwaySelected: fixture.awaySelected,
                homeCallBack: () =>
                    fixtureData.updateSelection(fixture, 'home'),
                drawCallBack: () =>
                    fixtureData.updateSelection(fixture, 'draw'),
                awayCallBack: () =>
                    fixtureData.updateSelection(fixture, 'away'),
              ),
            );
          },
          itemCount: fixtureData.fixtures.length,
        );
      },
    );
  }
}
like image 681
wierdo Avatar asked Nov 03 '25 16:11

wierdo


2 Answers

you could use the grouped_list package for this use case. It allows you to group your data by a custom criteria and it easy to use if you already use a ListView.

class FixtureList extends StatelessWidget {
    final dateFormat = DateFormat('MMMd');
    final timeFormat = DateFormat('Hm');

    @override
    Widget build(BuildContext context) {
    return Consumer<FixtureData>(
      builder: (context, fixtureData, child) {
        return GroupedListView<FixtureData, string>(
          scrollDirection: Axis.vertical,
          shrinkWrap: true,
          groupBy: (fixture) => // return the value by which the fixtures should be grouped,
          groupSeparatorBuilder: (String groupByValue) {
            // your widget for group headers here
          },
          itemBuilder: (context, fixture) {
            // your widget for items here
          },
          elements: fixtureData.fixtures, // instead of itemCount here the list of objects is passed
        );
      },
    );
  }
}
like image 158
Dimitrios Begnis Avatar answered Nov 06 '25 05:11

Dimitrios Begnis


Please check below example which uses flutter_sticky_header plugin.

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark()
          .copyWith(scaffoldBackgroundColor: Color.fromARGB(255, 18, 32, 47)),
      debugShowCheckedModeBanner: false,
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: ListExample()
    );
  }

}

class ListExample extends StatelessWidget {
  const ListExample({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return AppScaffold(
      title: 'List Example',
      slivers: [
        _StickyHeaderList(index: 0),
        _StickyHeaderList(index: 1),
        _StickyHeaderList(index: 2),
        _StickyHeaderList(index: 3),
      ],
    );
  }
}

class _StickyHeaderList extends StatelessWidget {
  const _StickyHeaderList({
    Key key,
    this.index,
  }) : super(key: key);

  final int index;

  @override
  Widget build(BuildContext context) {
    return SliverStickyHeader(
      header: Header(index: index),
      sliver: SliverList(
        delegate: SliverChildBuilderDelegate(
              (context, i) =>
              ListTile(
                leading: CircleAvatar(
                  child: Text('$index'),
                ),
                title: Text('List tile #$i'),
              ),
          childCount: 6,
        ),
      ),
    );
  }
}

class Header extends StatelessWidget {
  const Header({
    Key key,
    this.index,
    this.title,
    this.color = Colors.lightBlue,
  }) : super(key: key);

  final String title;
  final int index;
  final Color color;

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 60,
      color: color,
      padding: EdgeInsets.symmetric(horizontal: 16.0),
      alignment: Alignment.centerLeft,
      child: Text(
        title ?? 'Header #$index',
        style: const TextStyle(color: Colors.white),
      ),
    );
  }
}

class AppScaffold extends StatelessWidget {
  const AppScaffold({
    Key key,
    @required this.title,
    @required this.slivers,
    this.reverse = false,
  }) : super(key: key);

  final String title;
  final List<Widget> slivers;
  final bool reverse;

  @override
  Widget build(BuildContext context) {
    return DefaultStickyHeaderController(
      child: Scaffold(
        appBar: AppBar(
          title: Text(title),
        ),
        body: CustomScrollView(
          slivers: slivers,
          reverse: reverse,
        ),
      ),
    );
  }
}

You can create an array of slivers based on you conditions.

Demo

like image 31
Sanjay Sharma Avatar answered Nov 06 '25 06:11

Sanjay Sharma



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!