Flutter Post请求报错但仍创建费用对象,金额类型转换存疑
问题分析与解决方案
一、"Failed to create expense"错误但数据已添加的原因
这个问题核心是后端已成功创建费用数据,但返回的HTTP状态码不是代码中判断的201,导致前端抛出异常,但数据实际已写入数据库。
排查与解决步骤:
- 确认后端真实返回内容:在
createExpense方法中添加日志,打印响应状态码和响应体,明确后端的实际返回:Future<Expenses> createExpense(String category,int amount,String date,String paymentType) async { final response = await http.post(...); // 添加日志排查 print('状态码:${response.statusCode}'); print('响应体:${response.body}'); if (response.statusCode == 201) { return Expenses.fromJson(jsonDecode(response.body)); } else { throw Exception('Failed to create expense.'); } } - 调整状态码判断逻辑:如果后端用200表示创建成功(部分后端的标准),将判断条件改为
response.statusCode == 200,或根据后端文档要求修改。 - 检查后端逻辑:确认后端是否存在"先写入数据库,再返回错误响应"的逻辑漏洞,比如数据插入成功后,后续校验或处理失败导致返回非成功状态码。
二、金额字段处理的优化
当前流程(输入字符串→int.parse→toString)冗余且无校验,易引发异常:
优化方案:
- 限制输入为整数:给金额输入框添加键盘类型和过滤器,避免非数字输入:
TextField( controller: _amountInput, decoration: const InputDecoration(hintText: 'Enter amount'), keyboardType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], // 仅允许输入数字 ) - 减少不必要的类型转换:如果后端接受字符串格式的整数,直接传递输入内容即可:
- 修改
createExpense参数与请求体:Future<Expenses> createExpense(String category, String amount, String date, String paymentType) async { final response = await http.post( Uri.parse('****************'), headers: <String, String>{ 'Content-Type': 'application/json; charset=UTF-8', }, body: jsonEncode(<String, String>{ 'category': category, 'amount': amount, // 直接传输入的字符串 'date': date, 'paymentType': paymentType }), ); // 状态码判断逻辑不变 } - 修改按钮调用逻辑:
ElevatedButton( onPressed: () { final amount = _amountInput.text.trim(); // 添加空值校验 if (amount.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('请输入金额')), ); return; } setState(() { _futureExpense = createExpense(_controller.text, amount, _dateInput.text, _typeInput.text); }); }, child: const Text('Create Data'), )
- 修改
- 添加格式校验:若需确保金额是整数,用
int.tryParse替代int.parse避免异常:onPressed: () { final amountStr = _amountInput.text.trim(); final amount = int.tryParse(amountStr); if (amount == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('请输入有效的整数金额')), ); return; } // 后续调用逻辑 }
三、额外体验优化
在buildFutureBuilder中,请求完成后添加按钮让用户回到输入界面:
FutureBuilder<Expenses> buildFutureBuilder() { return FutureBuilder<Expenses>( future: _futureExpense, builder: (context, snapshot) { if (snapshot.hasData) { return Column( children: [ Text(snapshot.data!.category!), ElevatedButton( onPressed: () => setState(() => _futureExpense = null), child: const Text('添加新费用'), ), ], ); } else if (snapshot.hasError) { return Column( children: [ Text('${snapshot.error}'), ElevatedButton( onPressed: () => setState(() => _futureExpense = null), child: const Text('重新尝试'), ), ], ); } return const CircularProgressIndicator(); }, ); }
内容的提问来源于stack exchange,提问作者User G Amsterdam
相关产品推荐
相关产品推荐

