You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)),
                    );
                  },
                ),
              ),
            ],
          );
        },
      ),
    );
  }
}

关键逻辑说明

  1. 卡片滑动联动:通过PageView的onPageChanged回调更新当前选中的账户索引,触发State重建后自动切换交易列表。
  2. 异步数据处理:用FutureBuilder处理Firestore数据加载的不同状态(加载中、错误、无数据、成功),保证页面状态稳定。
  3. 数据解析正确性:遍历Firestore返回的Map键值对,将每个动态账户键对应的数组转为交易模型列表,避免了原代码中Map转List的类型错误。

内容的提问来源于stack exchange,提问作者Elam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 17:24:47