Flutter中用GetX实现amount、rate、total字段动态计算及自定义键盘
Flutter GetX实现金额、汇率、总计的动态联动计算
GetX控制器核心实现
控制器负责处理响应式变量管理、联动计算逻辑以及自定义键盘输入处理,核心代码如下:
import 'package:get/get.dart'; class ForexController extends GetxController { // 响应式变量存储字段值,适配自定义键盘的字符串输入 final RxString amount = ''.obs; final RxString rate = ''.obs; final RxString total = ''.obs; // 标记当前更新字段,避免循环计算触发死循环 String? updatingField; // 记录当前焦点字段,用于自定义键盘输入定位 final RxString currentFocusField = 'amount'.obs; @override void onInit() { super.onInit(); // 监听金额/汇率变化,自动计算总计 amount.listen((_) => updatingField != 'total' ? calculateTotal() : null); rate.listen((_) => updatingField != 'total' ? calculateTotal() : null); // 监听总计变化,自动计算金额 total.listen((_) => updatingField != 'amount' ? calculateAmount() : null); } // 计算总计:Total = Amount * Rate void calculateTotal() { final num? amountNum = num.tryParse(amount.value); final num? rateNum = num.tryParse(rate.value); if (amountNum != null && rateNum != null && rateNum != 0) { updatingField = 'total'; total.value = (amountNum * rateNum).toStringAsFixed(2); // 保留两位小数 updatingField = null; } else { total.value = ''; } } // 计算金额:Amount = Total / Rate void calculateAmount() { final num? totalNum = num.tryParse(total.value); final num? rateNum = num.tryParse(rate.value); if (totalNum != null && rateNum != null && rateNum != 0) { updatingField = 'amount'; amount.value = (totalNum / rateNum).toStringAsFixed(2); updatingField = null; } else { amount.value = ''; } } // 自定义键盘输入处理 void onKeyboardInput(String input) { switch (input) { case 'delete': if (currentFocusField.value == 'amount') { amount.value = amount.value.isNotEmpty ? amount.value.substring(0, amount.value.length - 1) : ''; } else if (currentFocusField.value == 'rate') { rate.value = rate.value.isNotEmpty ? rate.value.substring(0, rate.value.length - 1) : ''; } else if (currentFocusField.value == 'total') { total.value = total.value.isNotEmpty ? total.value.substring(0, total.value.length - 1) : ''; } break; case '.': // 限制每个字段仅能输入一个小数点 if (currentFocusField.value == 'amount' && !amount.value.contains('.')) { amount.value += '.'; } else if (currentFocusField.value == 'rate' && !rate.value.contains('.')) { rate.value += '.'; } else if (currentFocusField.value == 'total' && !total.value.contains('.')) { total.value += '.'; } break; default: // 数字输入处理 if (currentFocusField.value == 'amount') { amount.value += input; } else if (currentFocusField.value == 'rate') { rate.value += input; } else if (currentFocusField.value == 'total') { total.value += input; } } } }
ForexForm页面组件实现
页面负责UI渲染、绑定控制器变量以及自定义键盘交互:
import 'package:flutter/material.dart'; import 'package:get/get.dart'; class ForexForm extends StatelessWidget { final ForexController controller = Get.put(ForexController()); ForexForm({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('外汇计算')), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ _buildInputField( label: '金额', value: controller.amount, onTap: () => controller.currentFocusField.value = 'amount', ), const SizedBox(height: 16), _buildInputField( label: '汇率', value: controller.rate, onTap: () => controller.currentFocusField.value = 'rate', ), const SizedBox(height: 16), _buildInputField( label: '总计', value: controller.total, onTap: () => controller.currentFocusField.value = 'total', ), const SizedBox(height: 24), _buildCustomKeyboard(), ], ), ), ); } // 构建输入字段容器 Widget _buildInputField({ required String label, required RxString value, required VoidCallback onTap, }) { return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16), decoration: BoxDecoration( border: Border.all(color: Colors.grey), borderRadius: BorderRadius.circular(8), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(label), Obx(() => Text(value.value.isEmpty ? '0' : value.value)), ], ), ), ); } // 构建自定义数字键盘 Widget _buildCustomKeyboard() { return GridView.count( shrinkWrap: true, crossAxisCount: 3, childAspectRatio: 1.5, children: [ _buildKeyboardButton('1'), _buildKeyboardButton('2'), _buildKeyboardButton('3'), _buildKeyboardButton('4'), _buildKeyboardButton('5'), _buildKeyboardButton('6'), _buildKeyboardButton('7'), _buildKeyboardButton('8'), _buildKeyboardButton('9'), _buildKeyboardButton('.'), _buildKeyboardButton('0'), _buildKeyboardButton('delete', icon: Icons.backspace), ], ); } // 构建键盘按钮 Widget _buildKeyboardButton(String text, {IconData? icon}) { return InkWell( onTap: () => controller.onKeyboardInput(text), child: Center( child: icon != null ? Icon(icon) : Text(text, style: const TextStyle(fontSize: 20)), ), ); } }
关键注意事项
- 除数为0校验:计算时必须判断汇率不为0,避免抛出异常
- 循环计算拦截:通过
updatingField标记当前更新的字段,防止变量互相触发计算导致死循环 - 输入合法性控制:自定义键盘限制小数点仅能输入一次,避免无效格式
- 精度处理:使用
toStringAsFixed(2)保留两位小数,符合金融场景需求 - 空值友好处理:输入为空时显示0,提升用户体验
内容的提问来源于stack exchange,提问作者Abdirahman Abdullahi
相关产品推荐
相关产品推荐

