Flutter混合列表标签栏插件sliver_tabbar_with_mixed_list的使用

发布于 1周前 作者 htzhanglong 来自 Flutter

Flutter混合列表标签栏插件sliver_tabbar_with_mixed_list的使用

SliverTabBarWithMixedList

Pub Version License: MIT Pub Points Popularity

一个提供可定制标签栏和包含不同高度项目的混合列表(包括父项目和子项目)的Flutter插件。如果最后一部分(父项目及其子项目)太短而无法达到屏幕顶部,该插件提供了可定制的底部填充缺失的空间。

安装

在你的pubspec.yaml文件中添加以下依赖:

dependencies:
  sliver_tabbar_with_mixed_list: ^0.0.1

使用

在你的应用中使用SliverTabBarWithMixedList小部件。根据需要自定义标签栏、列表项和底部。

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

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SliverTabBarWithMixedList Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'SliverTabBarWithMixedList Demo'),
    );
  }
}

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

  final String title;

  [@override](/user/override)
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<HeaderItem> sections = [];
  double listHeaderHeight = 80;
  double listHeaderSmallHeight = 40;
  double listSubheaderHeight = 50;
  double listItemHeight = 100;
  double variantListItemHeight = 120;

  [@override](/user/override)
  void initState() {
    super.initState();
    double offsetStart = 0;
    List<ChildItem> children = List.generate(
      5,
      (index) => Child(itemHeight: listItemHeight),
    );
    List<ChildItem> subChildren = List.generate(
      3,
      (index) => Child(itemHeight: listItemHeight),
    );
    List<ChildItem> subSubChildren = List.generate(
      2,
      (index) => Child(itemHeight: listItemHeight),
    );

    List<ChildItem> variantChildrean = List.generate(
      4,
      (index) => VariantChild(itemHeight: variantListItemHeight),
    );
    for (int i = 0; i < 11; i++) {
      double offsetToAdd = 0;
      Header header;
      if (i < 10) {
        header = Header(
          key: ValueKey(i),
          name: 'Header item $i',
          offsetStart: offsetStart,
          childrenCount: children.length,
          itemHeight: listHeaderHeight,
          childrenHeight: listItemHeight,
          childrean: children,
          subSections: List.generate(
            3,
            (index) {
              var subSubSection = SubHeader(
                key: ValueKey(index),
                name: 'SubSubheader item $index',
                offsetStart: offsetStart,
                childrenCount: subSubChildren.length,
                itemHeight: listSubheaderHeight,
                childrenHeight: listItemHeight,
                childrean: subSubChildren,
              );
              offsetToAdd += subSubSection.itemHeight +
                  (subSubSection.childrenHeight * subSubSection.childrenCount);
              var section = SubHeader(
                key: ValueKey(index),
                name: 'Subheader item $index',
                offsetStart: offsetStart,
                childrenCount: subChildren.length,
                itemHeight: listSubheaderHeight,
                childrenHeight: listItemHeight,
                subSections: [subSubSection],
                childrean: subChildren,
              );

              offsetToAdd +=
                  listSubheaderHeight + (listItemHeight * subChildren.length);

              return section;
            },
          ),
        );
      } else {
        header = Header(
          key: ValueKey(i),
          name: 'Header V item $i',
          offsetStart: offsetStart,
          childrenCount: children.length,
          itemHeight: listHeaderSmallHeight,
          childrenHeight: variantListItemHeight,
          childrean: variantChildrean,
          subSections: List.generate(
            3,
            (index) {
              var subSubSection = SubHeader(
                key: ValueKey(index),
                name: 'SubSubheader V item $index',
                offsetStart: offsetStart,
                childrenCount: subSubChildren.length,
                itemHeight: listSubheaderHeight,
                childrenHeight: variantListItemHeight,
                childrean: variantChildrean,
              );
              offsetToAdd += subSubSection.itemHeight +
                  (subSubSection.childrenHeight * subSubSection.childrenCount);
              var section = SubHeader(
                key: ValueKey(index),
                name: 'Subheader  V item $index',
                offsetStart: offsetStart,
                childrenCount: subChildren.length,
                itemHeight: listSubheaderHeight,
                childrenHeight: variantListItemHeight,
                subSections: [subSubSection],
                childrean: variantChildrean,
              );

              offsetToAdd += listSubheaderHeight +
                  (variantListItemHeight * subChildren.length);

              return section;
            },
          ),
        );
      }
      offsetStart += offsetToAdd;
      offsetToAdd = 0;

      offsetStart +=
          header.itemHeight + (header.childrenHeight * header.childrenCount);
      header = Header.clone(header, offsetStart);
      sections.add(header);
    }
    sections.last = Header.clone((sections.last as Header), double.infinity);
  }

  Widget buildHeader(BuildContext context, HeaderItem item) {
    Header header = item as Header;
    return Container(
      color: Colors.orange,
      child: Center(
        child: Text(
          header.name,
          style: const TextStyle(fontSize: 30),
        ),
      ),
    );
  }

  Widget buildSubHeader(BuildContext context, covariant SubheaderItem item) {
    SubHeader header = item as SubHeader;
    return Container(
      color: Colors.green,
      child: Center(
        child: Text(
          header.name,
          style: const TextStyle(fontSize: 30),
        ),
      ),
    );
  }

  Widget buildChild(BuildContext context, ChildItem item) {
    return Container(
      color: Colors.blue.shade400,
      child: const Center(
        child: Text(
          'Child item',
          style: TextStyle(fontSize: 20),
        ),
      ),
    );
  }

  Widget buildVariantChild(
      BuildContext context, covariant VariantChildItem item) {
    return Container(
      color: Colors.purple.shade400,
      child: const Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text(
              'Variant Child item',
              style: TextStyle(fontSize: 16),
            ),
            Text(
              'Variant Child Description',
              style: TextStyle(fontSize: 12),
            ),
          ],
        ),
      ),
    );
  }

  double itemExtentBuilder(
      ListItem item, int index, SliverLayoutDimensions dimensions) {
    return item.itemHeight;
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: CustomScrollView(
          slivers: [
            SliverAppBar(
              pinned: true,
              title: Text(widget.title),
            ),
            SliverTabBarWithMixedList(
              controller: PrimaryScrollController.of(context),
              indicatorPadding: const EdgeInsets.symmetric(
                horizontal: 8.0,
                vertical: 4.0,
              ),
              tabBarIndicator: BoxDecoration(
                borderRadius: BorderRadius.circular(
                  25.0,
                ),
                color: Colors.green,
              ),
              listHeaderHeight: listHeaderHeight,
              listItemHeight: listItemHeight,
              sections: sections,
              headerBuilder: buildHeader,
              subHeaderBuilder: buildSubHeader,
              childBuilder: buildChild,
              variantChildBuilder: buildVariantChild,
              itemExtentBuilder: itemExtentBuilder,
            )
          ],
        ),
      ),
    );
  }
}

class Header extends HeaderItem {
  Header({
    required super.key,
    required this.name,
    required super.offsetStart,
    required super.itemHeight,
    required super.childrenCount,
    required super.childrenHeight,
    super.childrean,
    super.subSections,
  });
  final String name;

  Header.params({
    required super.key,
    required this.name,
    required super.offsetStart,
    required super.itemHeight,
    required super.childrenCount,
    required super.childrenHeight,
    required super.offsetEnd,
    super.childrean,
    super.subSections,
  }) : super.params();

  factory Header.clone(Header header, double offsetEnd) =>
      Header.params(
        key: header.key,
        name: header.name,
        offsetStart: header.offsetStart,
        itemHeight: header.itemHeight,
        childrenCount: header.childrenCount,
        childrenHeight: header.childrenHeight,
        offsetEnd: offsetEnd,
        childrean: header.childrean,
        subSections: header.subSections,
      );
}

class SubHeader extends SubheaderItem {
  SubHeader({
    required super.key,
    required this.name,
    required super.offsetStart,
    required super.itemHeight,
    required super.childrenCount,
    required super.childrenHeight,
    super.childrean,
    super.subSections,
  }) : super();
  final String name;
}

class Child extends ChildItem {
  Child({required super.itemHeight});
}

class VariantChild extends VariantChildItem {
  VariantChild({required super.itemHeight});
}

更多关于Flutter混合列表标签栏插件sliver_tabbar_with_mixed_list的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter混合列表标签栏插件sliver_tabbar_with_mixed_list的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何使用 sliver_tabbar_with_mixed_list 插件的示例代码。这个插件允许你在 Flutter 中创建一个混合列表和标签栏的布局。首先,确保你已经在 pubspec.yaml 文件中添加了该依赖:

dependencies:
  flutter:
    sdk: flutter
  sliver_tabbar_with_mixed_list: ^最新版本号 # 请替换为实际最新版本号

然后,运行 flutter pub get 来获取依赖。

接下来,下面是一个完整的示例代码,展示如何使用 SliverTabBarWithMixedList 创建一个混合列表和标签栏:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SliverTabBarWithMixedList Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  late TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 2, vsync: this);
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('SliverTabBarWithMixedList Demo'),
      ),
      body: NestedScrollView(
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
          return <Widget>[
            SliverTabBarWithMixedList(
              controller: _tabController,
              tabs: [
                Tab(icon: Icon(Icons.directions_car), text: 'Tab 1'),
                Tab(icon: Icon(Icons.directions_transit), text: 'Tab 2'),
              ],
              sliverListHeaderBuilder: (BuildContext context, int index) {
                return SliverPadding(
                  padding: EdgeInsets.symmetric(horizontal: 16.0),
                  sliver: SliverToBoxAdapter(
                    child: Text(
                      'Header for Tab $index',
                      style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
                    ),
                  ),
                );
              },
              sliverListBuilders: [
                (BuildContext context, int index) {
                  return SliverList(
                    delegate: SliverChildBuilderDelegate(
                      (BuildContext context, int index) {
                        return ListTile(
                          title: Text('Item $index in Tab 1'),
                        );
                      },
                      childCount: 20,
                    ),
                  );
                },
                (BuildContext context, int index) {
                  return SliverList(
                    delegate: SliverChildBuilderDelegate(
                      (BuildContext context, int index) {
                        return ListTile(
                          title: Text('Item $index in Tab 2'),
                        );
                      },
                      childCount: 20,
                    ),
                  );
                },
              ],
            ),
          ];
        },
        body: TabBarView(
          controller: _tabController,
          children: [
            // 这里可以放置与 Tab 1 相关的额外内容(如果需要)
            Container(),
            // 这里可以放置与 Tab 2 相关的额外内容(如果需要)
            Container(),
          ],
        ),
      ),
    );
  }
}

在这个示例中:

  1. SliverTabBarWithMixedList 被用来创建一个带有标签栏和混合列表的布局。
  2. SliverTabBarWithMixedList 接受一个 TabController,用于控制标签栏的状态。
  3. tabs 属性定义了标签栏中的标签。
  4. sliverListHeaderBuilder 是一个函数,用于为每个标签页生成一个头部(例如,一个标题)。
  5. sliverListBuilders 是一个函数列表,每个函数负责生成对应标签页的列表内容。
  6. TabBarView 用于显示与每个标签页相关的额外内容(如果需要)。

这个示例展示了如何使用 sliver_tabbar_with_mixed_list 插件来创建一个带有混合列表和标签栏的布局。你可以根据需要进一步自定义和扩展这个示例。

回到顶部