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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 02:50:33