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

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;
}

错误原因分析

  1. 控制器非空判断无效:初始化后的_controllerone和_controllertwo永远不会为null,判断_controllerone != null无法校验输入内容的有效性,真正问题是控制器的text属性为空字符串或无法解析为合法double值。
  2. 按钮点击逻辑错误:TextButton的onPressed直接赋值为calcuator(),会导致页面构建时立即执行计算逻辑,此时输入框为空,直接触发double.parse解析空字符串的异常,而非点击按钮时执行。
  3. 重复解析未做校验:计算逻辑中多次调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 04:55:21