Flutter调用Provider方法传参报错 卡路里计算结果无法正常显示
问题根因
报错和功能异常来自3个核心问题:
- 页面初始构建时
TextEditingController的text为空字符串,直接传给DateFormat.parse()会触发格式解析报错,对应Trying to read d from at position 0错误;传入controller.toString()时,传递的是控制器实例的字符串描述,并非用户输入的日期文本,同样会解析失败。 getTotal方法逻辑错误:筛选出指定日期的卡路里列表date后,求和时遍历的是全量myCalories列表,最终计算的是所有记录的总卡路里,而非指定日期的数值。- 在StatelessWidget中创建
TextEditingController未做生命周期管理,且点击搜索后未触发页面刷新,即使计算出结果也不会更新到UI上。
修复方案
1. 调整Provider层代码
给AllCalories增加存储查询结果的变量,修正求和逻辑,增加空值与格式校验:
import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:my_plate/models/calories.dart'; class AllCalories with ChangeNotifier { List<Calories> myCalories = []; // 存储查询到的当日总卡路里,null代表未查询或无有效结果 double? dailyTotal; List<Calories> getMyCalories() { return myCalories; } void getTotal(String inputDate) { // 空输入直接重置结果 if(inputDate.trim().isEmpty) { dailyTotal = null; notifyListeners(); return; } try { var dateTime = DateFormat('d/M/y').parse(inputDate); var tmrDate = dateTime.add(const Duration(days: 1)); // 筛选指定日期的记录 List<Calories> dateList = myCalories.where((o) { return o.consumptionDate.isAfter(dateTime) && o.consumptionDate.isBefore(tmrDate); }).toList(); double sum = 0; // 仅对筛选后的当日记录求和 for (var item in dateList) { sum += item.numCalories; } dailyTotal = sum; } catch(e) { // 日期格式错误时重置结果,避免崩溃 dailyTotal = null; } // 通知监听的UI刷新数据 notifyListeners(); } }
2. 调整RecordsPage页面代码
将StatelessWidget改为StatefulWidget管理控制器生命周期,移除build阶段直接调用getTotal的逻辑,根据查询状态动态展示结果:
import 'package:flutter/material.dart'; import 'package:my_plate/models/calories.dart'; import 'package:my_plate/providers/all_calories.dart'; import 'package:my_plate/screens/home_screen.dart'; import 'package:my_plate/screens/login_screen.dart'; import 'package:my_plate/widgets/app_drawer.dart'; import 'package:provider/provider.dart'; class RecordsPage extends StatefulWidget { static String routeName = '/records'; @override State<RecordsPage> createState() => _RecordsPageState(); } class _RecordsPageState extends State<RecordsPage> { final controller = TextEditingController(); @override void dispose() { // 页面销毁时释放控制器,避免内存泄漏 controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { AllCalories myCalories = Provider.of<AllCalories>(context); return Scaffold( resizeToAvoidBottomInset: true, backgroundColor: Colors.white, appBar: AppBar( title: Text('Previous Records'), elevation: 0, brightness: Brightness.light, backgroundColor: Color(0xff588157), leading: IconButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => MainScreen(), )); }, icon: Icon( Icons.arrow_back, size: 20, color: Colors.white, ), ), ), body: SingleChildScrollView( child: Container( padding: EdgeInsets.symmetric(horizontal: 40), child: Column(children: [ Image.asset('images/date.png', alignment: Alignment.topCenter), TextField( controller: controller, decoration: InputDecoration( enabledBorder: UnderlineInputBorder( borderSide: BorderSide(color: Color(0xff588157)), ), focusedBorder: UnderlineInputBorder( borderSide: BorderSide(color: Color(0xff588157)), ), hintText: '请输入d/M/y格式日期,例如1/10/2024', suffixIcon: IconButton( onPressed: () { // 点击搜索时才触发计算 myCalories.getTotal(controller.text); // 自动收起键盘 FocusScope.of(context).unfocus(); }, icon: Icon(Icons.search))), ), SizedBox(height: 20), Row( children: [ Text( myCalories.dailyTotal == null ? '请输入日期后点击搜索查询' : '当日总摄入卡路里:${myCalories.dailyTotal!.toStringAsFixed(0)}', textAlign: TextAlign.center, style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500), ), ], ), ]), ), ), drawer: AppDrawer()); } }
关键修改说明
- 初始页面构建阶段不触发日期解析,从根源避免空字符串解析报错
- 仅在用户点击搜索按钮时执行计算逻辑,计算完成通过
notifyListeners()触发UI刷新 - 修正求和逻辑,仅遍历筛选后的当日记录,保证计算结果准确
- 增加异常捕获,用户输入格式错误时不会触发崩溃
- 控制器随页面生命周期销毁,避免内存泄漏
内容的提问来源于stack exchange,提问作者user19350273
相关产品推荐
相关产品推荐

