Flutter中如何在ListView末尾显示总计金额?
问题原因
你遇到的问题根源在于ListView.builder的itemBuilder方法会被重复调用:当ListView滚动时,屏幕外的item会被销毁,新进入屏幕的item会触发itemBuilder重建。你原来的写法是在itemBuilder里逐个累加/扣除金额,每次itemBuilder执行都会修改expTotal,导致这个变量被重复计算,最终数值越来越大。
哪怕把expTotal放在build方法内,只要itemBuilder多次执行,还是会在每次构建item时重复累加——因为build执行一次,itemBuilder会执行transactionList.length +1次,每次执行都在修改同一个expTotal变量。
解决方案
核心思路是提前计算总金额,不在itemBuilder内做累加操作,确保总金额只计算一次(或按需实时计算,但不会重复累加)。
方法1:在build方法内提前计算总金额
在ListView.builder之前就计算好总金额,这样无论itemBuilder调用多少次,总金额都是固定的正确值:
@override Widget build(BuildContext context) { // 提前计算总金额,仅在build执行时计算一次 int expTotal = 0; for (var transaction in transactionList) { if (transaction.isExpense) { expTotal += transaction.amount; } else { expTotal -= transaction.amount; } } // 也可以用fold方法简化计算 // int expTotal = transactionList.fold(0, (total, transaction) { // return transaction.isExpense ? total + transaction.amount : total - transaction.amount; // }); return Scaffold( backgroundColor: Colors.grey.shade100, body: Padding( padding: const EdgeInsets.all(8.0), child: Column( children: [ Container( height: 400, color: Colors.transparent, ), Expanded( child: ListView.builder( padding: EdgeInsets.zero, itemCount: transactionList.length + 1, itemBuilder: (context, index) { if (index == transactionList.length) { return ListTile( tileColor: Colors.grey, leading: CircleAvatar( backgroundColor: Colors.grey, ), title: Center(child: Text('Total ')), trailing: Text(expTotal.toString()), ); } final transactionModel = transactionList[index]; return ListTile( leading: CircleAvatar( child: Text(transactionModel.id), ), title: Text(transactionModel.category), trailing: Text( transactionModel.amount.toString(), style: TextStyle( color: transactionModel.isExpense ? Colors.red : Colors.blue), ), ); }), ), ], ), ), ); }
方法2:使用计算属性(Getter)
如果transactionList会动态变化,用Getter可以实时计算最新的总金额,代码更简洁:
// 在类中定义计算属性 int get expTotal { return transactionList.fold(0, (total, transaction) { return transaction.isExpense ? total + transaction.amount : total - transaction.amount; }); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.grey.shade100, body: Padding( padding: const EdgeInsets.all(8.0), child: Column( children: [ Container( height: 400, color: Colors.transparent, ), Expanded( child: ListView.builder( padding: EdgeInsets.zero, itemCount: transactionList.length + 1, itemBuilder: (context, index) { if (index == transactionList.length) { return ListTile( tileColor: Colors.grey, leading: CircleAvatar( backgroundColor: Colors.grey, ), title: Center(child: Text('Total ')), trailing: Text(expTotal.toString()), ); } final transactionModel = transactionList[index]; return ListTile( leading: CircleAvatar( child: Text(transactionModel.id), ), title: Text(transactionModel.category), trailing: Text( transactionModel.amount.toString(), style: TextStyle( color: transactionModel.isExpense ? Colors.red : Colors.blue), ), ); }), ), ], ), ), ); }
关键注意点
永远不要在ListView.builder的itemBuilder中做状态累加/修改操作,因为这个方法会随着列表滚动被频繁调用,任何在其中的状态修改都会导致不可预期的重复计算。
内容的提问来源于stack exchange,提问作者user22362195
相关产品推荐
相关产品推荐

