跳转至

带组头的列表

以下所有实例都是在CustomScrollView组件的slivers属性中实现的。

SliverPersistentHeader+SliverList

实现类似于 iOS 中带有组头的列表,可以使用 ListViewCustomScrollView 结合 SliverListSliverPersistentHeader

import 'package:flutter/material.dart';

class GroupedListView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: <Widget>[
        SliverPersistentHeader(
          pinned: true,
          delegate: _SliverAppBarDelegate(
            minHeight: 60.0,
            maxHeight: 60.0,
            child: Container(
              color: Colors.lightBlue,
              child: Center(child: Text('Group 1 Header')),
            ),
          ),
        ),
        SliverList(
          delegate: SliverChildBuilderDelegate(
            (BuildContext context, int index) {
              return ListTile(
                title: Text('Item #$index in Group 1'),
              );
            },
            childCount: 5,
          ),
        ),
        SliverPersistentHeader(
          pinned: true,
          delegate: _SliverAppBarDelegate(
            minHeight: 60.0,
            maxHeight: 60.0,
            child: Container(
              color: Colors.lightGreen,
              child: Center(child: Text('Group 2 Header')),
            ),
          ),
        ),
        SliverList(
          delegate: SliverChildBuilderDelegate(
            (BuildContext context, int index) {
              return ListTile(
                title: Text('Item #$index in Group 2'),
              );
            },
            childCount: 5,
          ),
        ),
      ],
    );
  }
}

class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  _SliverAppBarDelegate({
    required this.minHeight,
    required this.maxHeight,
    required this.child,
  });

  final double minHeight;
  final double maxHeight;
  final Widget child;

  @override
  double get minExtent => minHeight;

  @override
  double get maxExtent => maxHeight;

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return SizedBox.expand(child: child);
  }

  @override
  bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
    return maxHeight != oldDelegate.maxHeight ||
        minHeight != oldDelegate.minHeight ||
        child != oldDelegate.child;
  }
}

在这个示例中: - CustomScrollView 用于创建一个可滚动的视图。 - SliverPersistentHeader 用于创建固定的组头。 - SliverList 用于创建每个组中的列表项。

SliverPersistentHeader+SliverGrid

要实现一个带有组头和分组数据的页面,并且每行最多显示四个项目。

  • 使用 CustomScrollView 作为主容器。

  • 使用 SliverPersistentHeader 来显示组头。

  • 使用 SliverGrid 来显示每个组中的项目。

如果需要调整布局和样式,可以修改相应的参数和样式属性。

import 'package:flutter/material.dart';

class GroupedGridView extends StatelessWidget {
  // 数据源
  final List<Map<String, List<String>>> data = [
    {
      '食品酒水': ['早午晚餐', '烟酒茶', '水果零食'],
    },
    {
      '行车交通': ['公共交通', '打车租车', '私家车费用'],
    },
    {
      '居家物业': ['日常用品', '水电煤气', '房租', '物业管理', '维修保养'],
    },
  ];

  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: <Widget>[
        // 遍历数据源,生成组头和对应的网格
        for (var section in data)
          ...section.entries.map((entry) {
            return [
              SliverPersistentHeader(
                pinned: true,
                delegate: _SliverAppBarDelegate(
                  minHeight: 50.0,
                  maxHeight: 50.0,
                  child: Container(
                    color: Colors.lightBlue,
                    child: Center(
                      child: Text(
                        entry.key,
                        style: TextStyle(color: Colors.white, fontSize: 18),
                      ),
                    ),
                  ),
                ),
              ),
              SliverPadding(
                padding: EdgeInsets.all(8.0),
                sliver: SliverGrid(
                  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 4, // 每行显示四个项目
                    mainAxisSpacing: 8.0,
                    crossAxisSpacing: 8.0,
                    childAspectRatio: 3, // 宽高比,调整项目高度
                  ),
                  delegate: SliverChildBuilderDelegate(
                    (BuildContext context, int index) {
                      return Container(
                        alignment: Alignment.center,
                        color: Colors.teal[100 * (index % 9)],
                        child: Text(entry.value[index]),
                      );
                    },
                    childCount: entry.value.length,
                  ),
                ),
              ),
            ];
          }).expand((element) => element).toList(),
      ],
    );
  }
}

class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  _SliverAppBarDelegate({
    required this.minHeight,
    required this.maxHeight,
    required this.child,
  });

  final double minHeight;
  final double maxHeight;
  final Widget child;

  @override
  double get minExtent => minHeight;

  @override
  double get maxExtent => maxHeight;

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return SizedBox.expand(child: child);
  }

  @override
  bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
    return maxHeight != oldDelegate.maxHeight ||
        minHeight != oldDelegate.minHeight ||
        child != oldDelegate.child;
  }
}

SliverToBoxAdapter+SliverGrid

组头和网格项都会随着页面一起滚动,而不是固定在顶部,可以使用 SliverToBoxAdapter 来替代 SliverPersistentHeader。这样组头将成为普通的列表项,随页面一起滚动。

import 'package:flutter/material.dart';

class GroupedGridView extends StatelessWidget {
  // 数据源
  final List<Map<String, List<String>>> data = [
    {
      '食品酒水': ['早午晚餐', '烟酒茶', '水果零食'],
    },
    {
      '行车交通': ['公共交通', '打车租车', '私家车费用'],
    },
    {
      '居家物业': ['日常用品', '水电煤气', '房租', '物业管理', '维修保养'],
    },
  ];

  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: <Widget>[
        // 遍历数据源,生成组头和对应的网格
        for (var section in data)
          ...section.entries.map((entry) {
            return [
              SliverToBoxAdapter(
                child: Container(
                  height: 50.0,
                  color: Colors.lightBlue,
                  child: Center(
                    child: Text(
                      entry.key,
                      style: TextStyle(color: Colors.white, fontSize: 18),
                    ),
                  ),
                ),
              ),
              SliverPadding(
                padding: EdgeInsets.all(8.0),
                sliver: SliverGrid(
                  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 4, // 每行显示四个项目
                    mainAxisSpacing: 8.0,
                    crossAxisSpacing: 8.0,
                    childAspectRatio: 3, // 宽高比,调整项目高度
                  ),
                  delegate: SliverChildBuilderDelegate(
                    (BuildContext context, int index) {
                      return Container(
                        alignment: Alignment.center,
                        color: Colors.teal[100 * (index % 9)],
                        child: Text(entry.value[index]),
                      );
                    },
                    childCount: entry.value.length,
                  ),
                ),
              ),
            ];
          }).expand((element) => element).toList(),
      ],
    );
  }
}