Flutter从API获取带复选框列表并实现多选求和功能求助
问题描述
我是Flutter新手,需要实现以下功能:
- 从API获取带复选框的条目列表,支持单条/多条选择
- 选中复选框时展示所选条目的总金额,多选时自动计算总和
以下是我当前完成的UI及代码:
主文件代码
import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:liquid_pull_to_refresh/liquid_pull_to_refresh.dart'; import 'dart:convert'; import 'dart:ffi'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:stachup/models/TeamModel.dart'; import 'package:stachup/models/newStaches.dart'; import 'package:stachup/models/TeamModel.dart'; class Stash extends StatefulWidget { String first_name; String token; String referalcode; String last_name; Stash( {Key? key, required this.first_name, required this.token, required this.last_name, required this.referalcode}) : super(key: key); @override State<Stash> createState() => _StashState(); } class _StashState extends State<Stash> { bool isChecked = false; String? first_name; String? token; String? last_name; String? referalcode; bool setup = false; String? setupError; bool _isChecked = false; late SharedPreferences logindata; userData() async { logindata = await SharedPreferences.getInstance(); first_name = logindata.getString('first_name')!; last_name = logindata.getString('last_name')!; referalcode = logindata.getString('referalcode')!; token = logindata.getString('token')!; setState(() {}); } List<Team> teams = []; Future getData() async { var response = await http.get(Uri.https( 'incoming.isplitpay.com', 'api/durationlist', {'token': token})); var jsonData = jsonDecode(response.body); print(token); for (var eachTeam in jsonData) { var datetime = DateTime.parse(eachTeam["paid_date"]); final team = Team( id_pes: eachTeam['id_pes'], amount: eachTeam['amount'], paid_date: datetime, ); teams.add(team); } // print(teams.length); } @override void initState() { super.initState(); userData(); } @override Widget build(BuildContext context) { //getData(); return Scaffold( backgroundColor: Colors.blue.shade50, body: FutureBuilder( future: getData(), builder: ((context, snapshot) { //if done loading show data if (snapshot.connectionState == ConnectionState.done) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), ), child: ListView.builder( itemCount: teams.length, itemBuilder: (context, index) { return Padding( padding: const EdgeInsets.all(10.0), child: Container( height: 80, decoration: BoxDecoration( border: Border.all( color: Colors.white, ), color: Colors.grey[50], borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Checkbox( checkColor: Colors.white, value: _isChecked, onChanged: (bool? value) { setState(() { _isChecked = value!; }); print(value); }, ), Text( 'GHS ' + teams[index].amount, style: GoogleFonts.lato( fontSize: 16, ), ), Column( children: [ Padding( padding: const EdgeInsets.only(top: 20.0), child: Text( 'Savings for', style: GoogleFonts.lato( fontSize: 16, ), ), ), Text( "${teams[index].paid_date.day.toString().padLeft(2, '0')}-${teams[index].paid_date.month.toString().padLeft(2, '0')}-${teams[index].paid_date.year}", style: GoogleFonts.lato( fontSize: 16, ), ), ], ) ], ), ), ); }), ); //else show circule progress bar } else { return Center( child: CircularProgressIndicator(), ); } }), ), ); } }
Model文件代码
import 'package:flutter/material.dart'; class Team { final String id_pes; final String amount; final DateTime paid_date; Team({ required this.id_pes, required this.amount, required this.paid_date, }); } class UserProgress { final int goaldays; final double progress; UserProgress({ required this.goaldays, required this.progress, }); }
解决方案
1. 修复复选框状态管理问题
当前用单个_isChecked变量控制所有复选框,导致状态同步错误。需要给每个条目单独维护选中状态:
- 在
_StashState中添加映射存储每个条目的选中状态:
Map<String, bool> _selectedItems = {};
- 在
getData方法中初始化每个条目的默认状态:
for (var eachTeam in jsonData) { var datetime = DateTime.parse(eachTeam["paid_date"]); final team = Team( id_pes: eachTeam['id_pes'], amount: eachTeam['amount'], paid_date: datetime, ); teams.add(team); _selectedItems[team.id_pes] = false; // 初始化未选中 }
2. 绑定复选框到对应条目状态
修改ListView.builder中的Checkbox代码,绑定当前条目的选中状态:
Checkbox( checkColor: Colors.white, value: _selectedItems[teams[index].id_pes], onChanged: (bool? value) { setState(() { _selectedItems[teams[index].id_pes] = value!; }); }, )
3. 实现总金额计算与展示
- 添加计算总金额的方法:
double calculateTotal() { double total = 0.0; for (var team in teams) { if (_selectedItems[team.id_pes] == true) { total += double.parse(team.amount); } } return total; }
- 在UI顶部添加总金额展示区域,修改
build方法中的返回结构:
return Scaffold( backgroundColor: Colors.blue.shade50, body: FutureBuilder( future: getData(), builder: ((context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { return Column( children: [ // 总金额展示栏 Container( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), color: Colors.white, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '总金额:', style: GoogleFonts.lato(fontSize: 18, fontWeight: FontWeight.bold), ), Text( 'GHS ${calculateTotal().toStringAsFixed(2)}', style: GoogleFonts.lato(fontSize: 18, color: Colors.green, fontWeight: FontWeight.bold), ), ], ), ), // 列表区域 Expanded( child: ListView.builder( itemCount: teams.length, itemBuilder: (context, index) { // 原列表项代码 }, ), ), ], ); } else { return Center(child: CircularProgressIndicator()); } }), ), );
4. 其他优化点
- 避免重复请求API:将
getData的调用移到initState,用变量存储请求Future:
Future? _fetchDataFuture; @override void initState() { super.initState(); userData().then((_) { _fetchDataFuture = getData(); }); } // FutureBuilder中使用: future: _fetchDataFuture,
- 添加API请求错误处理:在
getData中捕获异常并提示:
Future getData() async { try { var response = await http.get(Uri.https( 'incoming.isplitpay.com', 'api/durationlist', {'token': token})); if (response.statusCode == 200) { var jsonData = jsonDecode(response.body); teams.clear(); _selectedItems.clear(); for (var eachTeam in jsonData) { var datetime = DateTime.parse(eachTeam["paid_date"]); final team = Team( id_pes: eachTeam['id_pes'], amount: eachTeam['amount'], paid_date: datetime, ); teams.add(team); _selectedItems[team.id_pes] = false; } } else { setState(() { setupError = '获取数据失败'; }); } } catch (e) { setState(() { setupError = '网络异常'; }); } }
- 在UI中展示错误信息:在
ConnectionState.done分支添加判断:
if (setupError != null) { return Center(child: Text(setupError!)); }
内容的提问来源于stack exchange,提问作者Click Samuel Fortune
相关产品推荐
相关产品推荐

