一、为什么需要"简易单位换算器"?
在 OpenHarmony 的 IoT 和生活场景中,用户常需进行简单单位换算:
- 装修时将"厘米"转为"米";
- 购物时比较"150cm 衣服"与"1.6m 身高";
- 教育场景中学习公制单位关系。
虽然手机自带计算器可完成,但专用界面能降低认知负荷------用户无需记住"1 米 = 100 厘米",只需选择单位、输入数字、点击转换。
更重要的是,此类功能完全基于确定性数学运算,不依赖网络、不读取传感器、不申请权限,是展示"纯逻辑 UI"的理想范例。
本文将构建一个极简页面:「简易单位换算器」。它只包含:
- 一个数字输入框(用于输入数值);
- 两个 Radio 按钮("米 → 厘米" 或 "厘米 → 米");
- 一个"转换"按钮;
- 一行结果显示区(如 "1.5 米 = 150 厘米")。
所有逻辑基于 乘除 100,无浮点精度处理(保留默认格式),确保简单可靠。
二、完整可运行代码
dart
// lib/main.dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '单位换算',
debugShowCheckedModeBanner: false,
theme: ThemeData(useMaterial3: true, colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo)),
home: const UnitConverterPage(),
);
}
}
enum ConvertDirection { meterToCm, cmToMeter }
class UnitConverterPage extends StatefulWidget {
const UnitConverterPage({super.key});
@override
State<UnitConverterPage> createState() => _UnitConverterPageState();
}
class _UnitConverterPageState extends State<UnitConverterPage> {
final TextEditingController _controller = TextEditingController();
ConvertDirection _direction = ConvertDirection.meterToCm;
String _result = '';
void _convert() {
final inputText = _controller.text;
if (inputText.isEmpty) {
setState(() {
_result = '请输入数字';
});
return;
}
double? value = double.tryParse(inputText);
if (value == null) {
setState(() {
_result = '请输入有效数字';
});
return;
}
String output;
if (_direction == ConvertDirection.meterToCm) {
output = '${value} 米 = ${value * 100} 厘米';
} else {
output = '${value} 厘米 = ${value / 100} 米';
}
setState(() {
_result = output;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('简易单位换算器')),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
TextField(
controller: _controller,
keyboardType: TextInputType.numberWithOptions(decimal: true),
decoration: const InputDecoration(labelText: '输入数值'),
),
const SizedBox(height: 16),
Row(
children: [
Text('转换方向: '),
Radio<ConvertDirection>(
value: ConvertDirection.meterToCm,
groupValue: _direction,
onChanged: (ConvertDirection? v) {
if (v != null) {
setState(() {
_direction = v;
});
}
},
),
const Text('米 → 厘米'),
const SizedBox(width: 24),
Radio<ConvertDirection>(
value: ConvertDirection.cmToMeter,
groupValue: _direction,
onChanged: (ConvertDirection? v) {
if (v != null) {
setState(() {
_direction = v;
});
}
},
),
const Text('厘米 → 米'),
],
),
const SizedBox(height: 24),
ElevatedButton(onPressed: _convert, child: const Text('转换')),
const SizedBox(height: 24),
Text(_result, style: const TextStyle(fontSize: 18)),
],
),
),
);
}
}
三、设计原则:简单,即是可靠
单位换算是确定性计算------给定输入,必有唯一输出。因此,UI 应聚焦于:
- 输入明确:数字键盘 + 清晰标签;
- 方向清晰:Radio 按钮避免歧义;
- 反馈即时:错误提示(空/非数字)与成功结果共用同一区域。
这种"输入-操作-输出"三段式结构,是工具类应用的黄金模板。
四、安全数字解析与转换逻辑:
我们首先看核心转换函数:
dart
void _convert() {
final inputText = _controller.text;
if (inputText.isEmpty) {
setState(() {
_result = '请输入数字';
});
return;
}
double? value = double.tryParse(inputText);
if (value == null) {
setState(() {
_result = '请输入有效数字';
});
return;
}
String output;
if (_direction == ConvertDirection.meterToCm) {
output = '${value} 米 = ${value * 100} 厘米';
} else {
output = '${value} 厘米 = ${value / 100} 米';
}
setState(() {
_result = output;
});
}
这段代码实现了健壮的输入处理与换算 。



- 空输入检查 :先判断
isEmpty,避免tryParse对空串返回null; double.tryParse:- 是 Dart 标准库方法,安全解析字符串为
double?; - 支持整数("5")和小数("1.5");
- 若失败(如 "abc"),返回
null;
- 是 Dart 标准库方法,安全解析字符串为
- 分支逻辑 :
- 使用
enum值比较,类型安全; - 乘 100 或除 100,符合公制单位定义;
- 使用
- 字符串插值 :
- 直接使用
${value},Dart 自动格式化浮点数; - 虽未处理精度(如 1.0 显示为 "1.0"),但对简易工具足够。
- 直接使用
💡 此设计不隐藏错误,而是明确告知用户"哪里错了",提升可用性。
五、Radio 按钮状态管理:
再看方向选择 UI:
dart
Radio<ConvertDirection>(
value: ConvertDirection.meterToCm,
groupValue: _direction,
onChanged: (ConvertDirection? v) {
if (v != null) {
setState(() {
_direction = v;
});
}
},
),

这里展示了 类型安全的单选控件用法。
enum ConvertDirection:- 定义两种转换方向,避免魔法字符串;
- 编译期检查,防止拼写错误;
groupValue: _direction:- 当前选中值由状态变量
_direction控制;
- 当前选中值由状态变量
onChanged回调 :- 参数
v可能为null(理论上不会,但 API 设计如此); - 显式检查
if (v != null)是良好实践; - 调用
setState更新状态,触发 Radio 重绘。
- 参数
📌 值得注意的是,未使用
RadioListTile以减少嵌套,保持布局扁平,更适合水平排列。
六、为何这个微工具值得存在?
1. 教育价值
- 向初学者展示
TextField、Radio、StatefulWidget的组合使用; - 演示
double.tryParse的安全数字处理模式。
2. 实用场景
- 快速换算装修尺寸;
- 辅助儿童学习单位关系;
- 作为更大换算器(如长度、重量、温度)的原型。
3. 工程示范
- 展示如何用最少代码实现完整输入-验证-输出流程;
- 体现"错误即反馈"的 UX 原则。
更重要的是,它完全自包含------不联网、不存档、不依赖外部状态,关闭即忘,符合隐私优先理念。
七、扩展与限制
可安全扩展的方向:
- 增加单位:如毫米、千米;
- 格式化输出 :
value.toStringAsFixed(2)保留两位小数; - 历史记录 :用
ListView显示最近 5 次结果。
当前限制(有意为之):
- 不支持负数(生活场景通常为正);
- 不自动清空输入(用户可连续转换);
- 不记忆上次方向(每次启动默认"米→厘米")。
这些限制确保核心功能极致简单、无认知负担。
八、结语:可靠的工具,源于确定的逻辑
本文的页面完整实现了一个实用、安全、直观的单位换算器。它没有炫技,没有复杂架构,只有清晰的输入、确定的计算、明确的输出。
在 OpenHarmony 的智慧世界中,我们常追求"智能"与"协同",但不应忘记:最值得信赖的功能,往往是那些简单到不会出错的那一个。
让我们继续用这样的微工具,让技术真正服务于日常。
🌐 欢迎加入开源鸿蒙跨平台社区 :
在这里,您将获得:
- OpenHarmony 工具类应用设计规范;
- Flutter 输入验证与状态管理模板;
- 无依赖实用组件实战指南。
用简单,成就可靠。