Flutter中如何将带交互函数的ListView.builder改为StickyGroupList
解决方案:将ListView.builder替换为StickyGroupList并处理交互逻辑
要实现按日期分组的交易列表,同时保留长按选中、点击取消选中以及删除功能,核心是放弃依赖全局索引跟踪选中项,改用交易对象本身判断选中状态(分组后原全局索引会失效)。以下是修改后的完整代码:
步骤1:导入必要依赖
确保pubspec.yaml中添加以下包:
dependencies: flutter: sdk: flutter sticky_grouped_list: ^1.4.0 # 可自行调整为最新版本 intl: ^0.19.0 collection: ^1.18.0 get: ^4.6.5 # 保留你原项目使用的GetX依赖
步骤2:完整修改代码
import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:collection/collection.dart'; import 'package:sticky_grouped_list/sticky_grouped_list.dart'; // 以下为示例模型,需与你项目中的实际定义对齐 class TransactionModel { final double amount; final DateTime date; final String paymentmode; final Category category; final bool isexpense; TransactionModel({ required this.amount, required this.date, required this.paymentmode, required this.category, required this.isexpense, }); } class Category { final String title; final String iconurl; Category({required this.title, required this.iconurl}); } class TransactionProvider extends GetxController { List<TransactionModel> showRecentTransactions = []; List<TransactionModel> allTransactions = []; void delete_transaction(TransactionModel transaction) { showRecentTransactions.remove(transaction); allTransactions.remove(transaction); update(); } } class ShowAllTransactionScreen extends StatefulWidget { const ShowAllTransactionScreen({Key? key}) : super(key: key); @override State<ShowAllTransactionScreen> createState() => _ShowAllTransactionScreenState(); } class _ShowAllTransactionScreenState extends State<ShowAllTransactionScreen> { bool islongpressed = false; TransactionModel? selectedtransaction; // 直接用交易对象跟踪选中项,替代原selectedindex bool showit = true; final provider = Get.put(TransactionProvider()); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('交易记录'), actions: [ if (islongpressed) TextButton( onPressed: () { provider.delete_transaction(selectedtransaction!); setState(() { islongpressed = false; selectedtransaction = null; }); }, child: const Text('删除', style: TextStyle(color: Colors.white)), ) ], ), body: showTransactions(), floatingActionButton: FloatingActionButton( onPressed: () { Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (context) => const CreateSpend()), ); }, child: const Icon(Icons.add), ), ); } Widget showTransactions() { return GetBuilder<TransactionProvider>( builder: (_) { if (provider.allTransactions.isEmpty) { return const Center(child: Text('暂无交易记录')); } return StickyGroupedListView<TransactionModel, String>( // 传入所有交易数据 elements: provider.showRecentTransactions, // 按年月日分组,作为分组标识 groupBy: (transaction) => DateFormat('yyyy-MM-dd').format(transaction.date), // 分组排序:最新日期优先显示 groupComparator: (date1, date2) => date2.compareTo(date1), // 组内排序:同一日期下最新交易在前 itemComparator: (item1, item2) => item2.date.compareTo(item1.date), // 自定义分组头部样式 groupSeparatorBuilder: (groupDate) => Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), color: Colors.grey[200], child: Text( DateFormat('yyyy年MM月dd日').format(DateFormat('yyyy-MM-dd').parse(groupDate)), style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), ), ), // 构建列表项 itemBuilder: (context, transaction) { final isSelected = selectedtransaction == transaction; return TransactionCard( ontap: () { setState(() { islongpressed = false; selectedtransaction = null; }); }, onlongpressed: () { setState(() { islongpressed = true; selectedtransaction = transaction; }); }, amount: transaction.amount.toStringAsFixed(2), datetime: DateFormat('HH:mm').format(transaction.date), // 优化为显示时分 paymentby: transaction.paymentmode, category: transaction.category.title, categoryicon: transaction.category.iconurl, isexpense: transaction.isexpense, isselected: isSelected, ); }, ); }, ); } } // 以下为示例组件,需与你项目中的实际定义对齐 class TransactionCard extends StatelessWidget { final VoidCallback ontap; final VoidCallback onlongpressed; final String amount; final String datetime; final String paymentby; final String category; final String categoryicon; final bool isexpense; final bool isselected; const TransactionCard({ super.key, required this.ontap, required this.onlongpressed, required this.amount, required this.datetime, required this.paymentby, required this.category, required this.categoryicon, required this.isexpense, required this.isselected, }); @override Widget build(BuildContext context) { return ListTile( onTap: ontap, onLongPress: onlongpressed, tileColor: isselected ? Colors.blue[100] : null, title: Text(category), subtitle: Text('$datetime · $paymentby'), trailing: Text(isexpense ? '-$amount' : '+$amount', style: TextStyle(color: isexpense ? Colors.red : Colors.green)), ); } } class CreateSpend extends StatelessWidget { const CreateSpend({super.key}); @override Widget build(BuildContext context) { return Scaffold(appBar: AppBar(title: const Text('添加交易'))); } }
核心修改说明
- 替换索引跟踪逻辑:删除原
selectedindex变量,改用selectedtransaction直接存储选中的交易对象,彻底避免分组后索引混乱的问题。 - 分组配置与样式:通过
groupBy指定分组规则,groupComparator控制分组排序,groupSeparatorBuilder自定义分组头部的显示样式。 - 交互逻辑适配:点击和长按操作直接针对当前交易对象,选中状态通过
selectedtransaction == transaction判断,逻辑更清晰可靠。 - 用户体验优化:将日期和时间格式化为更友好的样式,提升列表可读性。
内容的提问来源于stack exchange,提问作者Irfan Ganatra
相关产品推荐
相关产品推荐

