Flutter项目运行触发FormatException: Invalid double错误求助
Flutter BMI计算器FormatException(Invalid double)错误解决
问题描述
运行Flutter BMI计算器项目时触发FormatException,提示“Invalid double”,错误定位在double.parse(_controllerone.text)执行位置。尝试通过判断TextEditingController非空来解决,但问题依旧。
错误提示:
Exception has occurred.
FormatException (FormatException: Invalid double)
错误栈核心代码:
@patch static double parse(String source, [@deprecated double onError(String source)?]) { var result = _parse(source); if (result == null) { if (onError == null) throw new FormatException("Invalid double", source); return onError(source); } return result; }
错误原因分析
- 控制器非空判断无效:初始化后的
_controllerone和_controllertwo永远不会为null,判断_controllerone != null无法校验输入内容的有效性,真正问题是控制器的text属性为空字符串或无法解析为合法double值。 - 按钮点击逻辑错误:
TextButton的onPressed直接赋值为calcuator(),会导致页面构建时立即执行计算逻辑,此时输入框为空,直接触发double.parse解析空字符串的异常,而非点击按钮时执行。 - 重复解析未做校验:计算逻辑中多次调用
double.parse,即使第一次校验通过,后续分支的解析也无异常处理,依然可能触发错误。
修复方案
- 修正按钮点击事件:将
onPressed: calcuator()改为onPressed: calcuator,传递函数引用而非立即执行。 - 使用安全解析方法:用
double.tryParse替代double.parse,解析失败时返回null,不会抛出异常。 - 增加输入校验:在TextFormField中添加
validator,提前拦截无效输入并提示用户。 - 重构计算逻辑:先校验输入有效性,再执行计算,避免重复解析和除以零的情况。
完整修正代码
import 'package:flutter/material.dart'; class Homepage extends StatelessWidget { const Homepage({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( backgroundColor: const Color.fromARGB(77, 62, 60, 60), appBar: AppBar( elevation: 0, backgroundColor: const Color.fromARGB(0, 35, 34, 34), title: const Center( child: Text( 'BMI Calculator', style: TextStyle(color: Colors.amber), ), ), ), body: const MyTextFormField(), ), ); } } class MyTextFormField extends StatefulWidget { const MyTextFormField({super.key}); @override State<MyTextFormField> createState() => _MyTextFormFieldState(); } class _MyTextFormFieldState extends State<MyTextFormField> { final GlobalKey<FormState> _formkey = GlobalKey<FormState>(); final TextEditingController _controllerone = TextEditingController(); final TextEditingController _controllertwo = TextEditingController(); String resulttext = ''; double result = 0; @override Widget build(BuildContext context) { return Center( child: SingleChildScrollView( child: Column( children: [ const SizedBox(height: 30), Form( key: _formkey, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( height: 50, width: 100, child: TextFormField( keyboardType: const TextInputType.numberWithOptions(decimal: true), controller: _controllerone, decoration: const InputDecoration( labelText: 'Height', labelStyle: TextStyle(fontSize: 30, color: Colors.white), ), validator: (value) { if (value == null || value.isEmpty) { return '请输入身高'; } if (double.tryParse(value) == null) { return '请输入有效数字'; } return null; }, ), ), const SizedBox(width: 30), SizedBox( height: 50, width: 100, child: TextFormField( keyboardType: const TextInputType.numberWithOptions(decimal: true), controller: _controllertwo, decoration: const InputDecoration( label: Text( 'Weight', style: TextStyle(fontSize: 30, color: Colors.white), ), ), validator: (value) { if (value == null || value.isEmpty) { return '请输入体重'; } if (double.tryParse(value) == null) { return '请输入有效数字'; } return null; }, ), ), ], ), ), const SizedBox(height: 40), TextButton( // 修复:传递函数引用,而非立即执行 onPressed: calcuator, child: const Text( 'Calculate', style: TextStyle( color: Colors.amber, fontSize: 25, fontWeight: FontWeight.bold, ), ), ), const SizedBox(height: 60), Text( result.isNaN ? '请输入有效数值' : '$result', style: const TextStyle( color: Colors.white, fontSize: 50, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 10), Text( resulttext, style: const TextStyle( color: Colors.white, fontSize: 50, fontWeight: FontWeight.bold, ), ), Column( children: [ const SizedBox(height: 115), Align( alignment: Alignment.centerRight, child: ContainerM.containerM( heightbox: 25, widthbox: 100, rightnum: 0, leftnum: 50, ), ), const SizedBox(height: 42), Align( alignment: Alignment.centerRight, child: ContainerM.containerM( heightbox: 25, widthbox: 45, rightnum: 0, leftnum: 50, ), ), const SizedBox(height: 42), Align( alignment: Alignment.centerRight, child: ContainerM.containerM( heightbox: 25, widthbox: 115, rightnum: 0, leftnum: 50, ), ), const SizedBox(height: 42), Align( alignment: Alignment.centerLeft, child: ContainerM.containerM( heightbox: 25, widthbox: 90, rightnum: 50, leftnum: 0, ), ), const SizedBox(height: 42), Align( alignment: Alignment.centerLeft, child: ContainerM.containerM( heightbox: 25, widthbox: 60, rightnum: 50, leftnum: 0, ), ), ], ) ], ), ), ); } void calcuator() { // 先校验表单有效性 if (!_formkey.currentState!.validate()) { setState(() { resulttext = ''; result = 0; }); return; } setState(() { double? heightnum = double.tryParse(_controllerone.text); double? weightnum = double.tryParse(_controllertwo.text); // 确保数值有效,避免null和除以零 if (heightnum == null || weightnum == null || heightnum <= 0) { resulttext = '请输入有效数值'; result = 0; return; } // 处理身高单位转换(米/厘米) if (heightnum > 4) { heightnum = heightnum / 100; } result = weightnum / (heightnum * heightnum); // 判断BMI区间 if (result <= 18.5) { resulttext = '需要多补充营养'; } else if (result <= 24.5) { resulttext = '身体很健康'; } else if (result <= 29.9) { resulttext = '需要加强运动'; } else if (result >= 30) { resulttext = '需要控制体重了'; } }); } } class ContainerM { late bool right; late bool left; static containerM({ required double heightbox, required double widthbox, required double rightnum, required double leftnum, }) { return Container( width: widthbox, height: heightbox, decoration: BoxDecoration( color: Colors.amber, shape: BoxShape.rectangle, borderRadius: BorderRadius.horizontal( left: Radius.circular(leftnum), right: Radius.circular(rightnum), ), ), ); } }
额外说明
- 移除了冗余的
onChanged回调:TextEditingController会自动同步输入内容,无需手动赋值。 - 优化了键盘类型:使用
TextInputType.numberWithOptions(decimal: true)支持小数输入。 - 增加了表单校验:通过Form的validate方法确保输入有效后再执行计算。
- 处理了除以零的情况:判断heightnum大于0后再计算,避免NaN结果。
内容的提问来源于stack exchange,提问作者Mahdi Abbasi
相关产品推荐
相关产品推荐

