Flutter分组粘性列表插件sticky_grouped_list_plus的使用

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

Flutter分组粘性列表插件sticky_grouped_list_plus的使用

特点

  • 轻松创建类似聊天界面。
  • 列表项可以被分组。
  • 每个组可以设置单独的头部。
  • 粘性头部支持浮动选项。
  • 所有ScrollablePositionedList字段均可使用。

开始使用

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

dependencies:
  sticky_grouped_list: ^3.1.0

在你的Dart文件中导入库:

import 'package:sticky_grouped_list/sticky_grouped_list.dart';

创建一个StickyGroupedListView组件:

final GroupedItemScrollController itemScrollController = GroupedItemScrollController();

StickyGroupedListView<dynamic, String>(
  elements: _elements,
  sort: true,
  groupBy: (dynamic element) => element['group'],
  groupSeparatorBuilder: (dynamic element) => Text(element['group']),
  itemBuilder: (context, dynamic element) => Text(element['name']),
  itemComparator: (e1, e2) => e1['name'].compareTo(e2['name']), // 可选
  elementIdentifier: (element) => element.name, // 可选 - 见下文
  itemScrollController: itemScrollController, // 可选
  order: StickyGroupedListOrder.ASC, // 可选
);

如果你使用了GroupedItemScrollController,可以通过索引来程序化滚动到特定位置:

  1. 通过索引滚动到指定位置:
itemScrollController.scrollTo(index: 4, duration: Duration(seconds: 2));
itemScrollController.jumpTo(index: 4);
  1. 通过预定义的元素标识符滚动到指定元素。标识符是一个函数,接受一个元素并返回一个唯一的标识符:
final GroupedItemScrollController itemScrollController = GroupedItemScrollController();

StickyGroupedListView<dynamic, String>(
  elements: _elements,
  elementIdentifier: (element) => element.name,
  itemScrollController: itemScrollController,
  [...]
);

final scrolledItemIndex = itemScrollController.scrollToElement(identifier: 'item-1', duration: Duration(seconds: 2));
final scrolledItemIndexOnJump = itemScrollController.jumpToElement(identifier: 'item-2');

参数

名称 描述 必需 默认值
elements 需要显示的数据列表 -
itemBuilder / indexedItemBuilder 返回一个定义项的Widget的函数。indexedItemBuilder 提供当前索引作为参数。如果两者都定义了,优先使用indexedItemBuilder 是,两者之一 -
groupBy 映射元素到其分组值的函数 -
groupSeparatorBuilder 获取元素并返回一个定义分组头部分隔符的Widget的函数 -
separator 定义组内项之间分隔符的Widget 无分隔符
floatingHeader 设置为true时,粘性头部将浮动在列表上方 false
stickyHeaderBackgroundColor 定义粘性头部的背景颜色 Color(0xffF7F7F7)
itemScrollController 需要提供一个GroupedItemScrollController而不是ItemScrollController -
elementIdentifier itemScrollController使用,定义每个元素的唯一标识符。 -
order 改变分组排序顺序。设置为StickyGroupedListOrder.DESC以反转分组排序。 StickyGroupedListOrder.ASC
groupComparator 可用于定义自定义的分组排序。否则使用自然排序。 -
itemComparator 可用于定义自定义的元素内部排序。否则使用自然排序。 -
reverse 与阅读方向相反滚动(从底部开始向上滚动)。与scrollable_positioned_list相同。 false
sort 启用按给定顺序排序。 true

注意GroupedItemScrollController.scrollTo()GroupedItemScrollController.jumpTo() 自动设置alignment,使项目完全可见并位于组头部下方。这两种方法都有一个可选的automaticAlignment参数,如果指定了alignment,则需要将其设置为true

强调:可以使用ScrollablePositionedList.builder的所有字段。

聊天对话框

轻松创建聊天对话框。只需将reverse设置为true并将order设置为StickyGroupedListOrder.DESC。可以在示例中找到完整示例。初始状态下,列表将滚动到末尾,因此滚动方向将与阅读方向相反。

区别

grouped_list包中的列表视图基于默认的Flutter列表视图和银色列表。而sticky_grouped_list基于scrollable_positioned_list,这使得可以程序化滚动到列表中的特定位置。因此,如果你需要能够程序化滚动列表,请使用此包;否则,建议使用grouped_list包。

使用的包

包名 版权声明 许可证
scrollable_positioned_list Copyright 2018 the Dart project authors, Inc. All rights reserved BSD 3-Clause “New” or “Revised” License

示例代码

main.dart

import 'package:flutter/material.dart';
import 'package:sticky_grouped_list/sticky_grouped_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: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

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> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('You have pushed the button this many times:'),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

示例数据

List<Map<String, dynamic>> _elements = [
  {'name': 'Alice', 'group': 'A'},
  {'name': 'Bob', 'group': 'B'},
  {'name': 'Charlie', 'group': 'A'},
  {'name': 'David', 'group': 'B'},
];

使用示例数据的代码

import 'package:flutter/material.dart';
import 'package:sticky_grouped_list/sticky_grouped_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: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

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> {
  final List<Map<String, dynamic>> _elements = [
    {'name': 'Alice', 'group': 'A'},
    {'name': 'Bob', 'group': 'B'},
    {'name': 'Charlie', 'group': 'A'},
    {'name': 'David', 'group': 'B'},
  ];

  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: StickyGroupedListView<dynamic, String>(
          elements: _elements,
          sort: true,
          groupBy: (dynamic element) => element['group'],
          groupSeparatorBuilder: (dynamic element) => Text(element['group']),
          itemBuilder: (context, dynamic element) => Text(element['name']),
          itemComparator: (e1, e2) => e1['name'].compareTo(e2['name']),
          elementIdentifier: (element) => element['name'],
          itemScrollController: GroupedItemScrollController(),
          order: StickyGroupedListOrder.ASC,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

更多关于Flutter分组粘性列表插件sticky_grouped_list_plus的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter分组粘性列表插件sticky_grouped_list_plus的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何使用 sticky_grouped_list_plus 插件在 Flutter 中创建分组粘性列表的示例代码。这个插件允许你创建一个列表,其中每个分组都有一个粘性头部(即当滚动时头部会保持在顶部直到下一个分组头部出现)。

首先,确保你的 pubspec.yaml 文件中已经添加了 sticky_grouped_list_plus 依赖:

dependencies:
  flutter:
    sdk: flutter
  sticky_grouped_list_plus: ^x.y.z  # 请将 x.y.z 替换为最新版本号

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

接下来是一个完整的示例代码,展示如何使用 sticky_grouped_list_plus 创建一个分组粘性列表:

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

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

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

class MyHomePage extends StatelessWidget {
  final List<Map<String, dynamic>> groupedItems = [
    {
      'header': 'A',
      'items': ['Apple', 'Apricot', 'Avocado'],
    },
    {
      'header': 'B',
      'items': ['Banana', 'Blueberry', 'Blackberry'],
    },
    {
      'header': 'C',
      'items': ['Cherry', 'Cranberry', 'Cantaloupe'],
    },
    // 添加更多分组
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Sticky Grouped List Example'),
      ),
      body: StickyGroupedListView<Map<String, dynamic>>(
        elements: groupedItems,
        groupBy: (element) => element['header']!,
        groupSeparatorBuilder: (context, header) {
          return Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16.0),
            child: Text(
              header,
              style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
            ),
          );
        },
        itemBuilder: (context, element) {
          return ListTile(
            title: Text(element['items'] as String),
          );
        },
      ),
    );
  }
}

代码解释:

  1. 依赖导入

    • 导入 flutter/material.dart 以使用 Flutter 的 Material Design 组件。
    • 导入 sticky_grouped_list_plus 以使用粘性分组列表功能。
  2. 数据准备

    • groupedItems 是一个包含多个 Map 的列表,每个 Map 包含一个 header 键和一个 items 键。header 表示分组头部,items 表示该分组下的项目列表。
  3. UI 构建

    • MyApp 是一个简单的 Material 应用。
    • MyHomePage 是应用的主页,包含一个 StickyGroupedListView
  4. StickyGroupedListView 配置

    • elements:传递给列表的数据源。
    • groupBy:一个函数,用于定义如何对数据进行分组。这里我们根据每个元素的 header 键进行分组。
    • groupSeparatorBuilder:一个函数,用于构建分组头部。这里我们返回一个带有内边距和加粗文本的 Padding 组件。
    • itemBuilder:一个函数,用于构建每个项目。这里我们返回一个简单的 ListTile

运行这段代码,你将看到一个分组粘性列表,每个分组头部在滚动时会保持粘性直到下一个分组头部出现。

希望这个示例对你有帮助!如果你有任何其他问题,欢迎继续提问。

回到顶部