Flutter实现卡片滑动切换并展示Firestore对应账户交易记录
解决Firestore动态键数据解析错误与账户卡片切换逻辑完善
一、修正Firestore动态键数据解析错误
Firestore返回的文档数据是Map<String, dynamic>类型,其中动态键(如ac01、ac02)对应账户交易数组,不能直接强制转为List。以下是正确的解析方案:
1. 定义数据模型类
先封装交易和账户数据的模型,方便数据处理:
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:intl/intl.dart'; // 交易项模型 class TransactionItem { final String id; final double amount; final String description; final DateTime date; TransactionItem({ required this.id, required this.amount, required this.description, required this.date, }); // 从Firestore的Map数据转为模型 factory TransactionItem.fromMap(Map<String, dynamic> map) { return TransactionItem( id: map['id'] as String, amount: (map['amount'] as num).toDouble(), description: map['description'] as String, date: (map['date'] as Timestamp).toDate(), ); } } // 账户+交易集合模型 class AccountWithTransactions { final String accountId; // 对应ac01、ac02这类动态键 final List<TransactionItem> transactions; AccountWithTransactions({ required this.accountId, required this.transactions, }); }
2. 正确解析Firestore数据
遍历Map的键值对,提取每个账户的交易数据:
// 从Firestore获取并解析账户交易数据 Future<List<AccountWithTransactions>> fetchAccountsData() async { final doc = await FirebaseFirestore.instance .collection('user_accounts') // 替换为你的集合名 .doc('transactions') // 替换为你的文档ID .get(); if (!doc.exists) return []; final data = doc.data() as Map<String, dynamic>; final accounts = <AccountWithTransactions>[]; // 遍历所有动态账户键 data.forEach((accountKey, transactionsRaw) { // 将交易数组转为TransactionItem列表 final transactions = (transactionsRaw as List) .map((item) => TransactionItem.fromMap(item as Map<String, dynamic>)) .toList(); accounts.add(AccountWithTransactions( accountId: accountKey, transactions: transactions, )); }); return accounts; }
二、完善卡片切换与交易列表联动逻辑
使用PageView实现卡片左右滑动切换,同步更新下方的交易列表:
import 'package:flutter/material.dart'; class TransactionPage extends StatefulWidget { const TransactionPage({super.key}); @override State<TransactionPage> createState() => _TransactionPageState(); } class _TransactionPageState extends State<TransactionPage> { late Future<List<AccountWithTransactions>> _accountsFuture; int _currentAccountIndex = 0; @override void initState() { super.initState(); _accountsFuture = fetchAccountsData(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('我的账户交易')), body: FutureBuilder<List<AccountWithTransactions>>( future: _accountsFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError || !snapshot.hasData || snapshot.data!.isEmpty) { return const Center(child: Text('暂无账户数据')); } final accounts = snapshot.data!; final currentAccount = accounts[_currentAccountIndex]; return Column( children: [ // 可滑动的账户卡片区域 SizedBox( height: 200, child: PageView.builder( itemCount: accounts.length, // 滑动切换时更新当前账户索引 onPageChanged: (index) => setState(() => _currentAccountIndex = index), itemBuilder: (context, index) { final account = accounts[index]; return Card( elevation: 4, margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text('账户:${account.accountId}', style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 8), Text('交易笔数:${account.transactions.length}'), // 可添加余额、账户类型等其他信息(需Firestore对应字段支持) ], ), ), ); }, ), ), // 当前账户的交易列表 Expanded( child: ListView.builder( itemCount: currentAccount.transactions.length, itemBuilder: (context, index) { final transaction = currentAccount.transactions[index]; return ListTile( leading: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: transaction.amount > 0 ? Colors.green.shade100 : Colors.red.shade100, borderRadius: BorderRadius.circular(8), ), child: Text( '${transaction.amount > 0 ? '+' : ''}${transaction.amount}', style: TextStyle( color: transaction.amount > 0 ? Colors.green : Colors.red, fontWeight: FontWeight.bold, ), ), ), title: Text(transaction.description), subtitle: Text(DateFormat('yyyy-MM-dd HH:mm').format(transaction.date)), ); }, ), ), ], ); }, ), ); } }
关键逻辑说明
- 卡片滑动联动:通过
PageView的onPageChanged回调更新当前选中的账户索引,触发State重建后自动切换交易列表。 - 异步数据处理:用
FutureBuilder处理Firestore数据加载的不同状态(加载中、错误、无数据、成功),保证页面状态稳定。 - 数据解析正确性:遍历Firestore返回的Map键值对,将每个动态账户键对应的数组转为交易模型列表,避免了原代码中Map转List的类型错误。
内容的提问来源于stack exchange,提问作者Elam
相关产品推荐
相关产品推荐

