如何实现双向联动且可编辑的加密货币TextField?
加密货币APP双向联动输入框实现方案
需求说明
我正在开发一款加密货币移动端APP,需要实现两个可编辑的TextField(USDT和BTC),要求双向联动:
- 假设BTC单价为15美元,USDT单价为1美元
- 在BTC输入框输入1时,USDT输入框自动显示15
- 在USDT输入框输入30时,BTC输入框自动显示2
- 删除USDT输入框的0时,BTC输入框需实时更新为对应的小数数值
目前已实现USDT输入、BTC输出的单向联动,希望改为双向均可输入输出,现有代码如下:
现有USDTInput代码
import 'package:cryptx/Constants/app_colors.dart'; import 'package:cryptx/Providers/basic_providers.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class USDTInput extends ConsumerWidget { const USDTInput({ Key? key, }) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0), child: TextField( decoration: InputDecoration( icon: SizedBox( height: 30, child: Image.network( "https://assets.coingecko.com/coins/images/325/small/Tether.png?1668148663")), hintText: "USDT", border: InputBorder.none, ), onChanged: (value) { ref .read(usdProvider.notifier) .update((state) => value != "" ? num.parse(value) : 0); }, autocorrect: false, keyboardType: const TextInputType.numberWithOptions(decimal: true), ), ); } }
现有CoinOutput代码
import 'package:cryptx/Objects/coin.dart'; import 'package:cryptx/Providers/basic_providers.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class CoinOutput extends ConsumerWidget { const CoinOutput({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { Coin coin = ref.watch(coinDetailProvider) as Coin; num usd = ref.watch(usdProvider); num amount = usd != 0 ? usd / coin.current_price : 0; //return Text(amount.toString()); return Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0), child: TextField( decoration: InputDecoration( icon: SizedBox(height: 30, child: Image.network(coin.image)), hintText: "Coin", border: InputBorder.none, ), controller: TextEditingController(text: "$amount ${coin.symbol.toUpperCase()}"), readOnly: true, autocorrect: false, keyboardType: const TextInputType.numberWithOptions(decimal: true), onChanged: (value) { ref.watch(coin_usdProvider.notifier).update((state) => value != "" ? num.parse(value) / coin.current_price : 0); }, ), ); } }
修改方案
1. 调整状态管理(basic_providers.dart)
添加双向联动所需的状态和价格常量:
import 'package:flutter_riverpod/flutter_riverpod.dart'; // USDT金额状态 final usdtAmountProvider = StateProvider<num>((ref) => 0); // BTC金额状态 final btcAmountProvider = StateProvider<num>((ref) => 0); // 当前编辑的输入框标识,避免循环更新 final editingFieldProvider = StateProvider<String?>((ref) => null); // BTC单价(实际可从API动态获取) final btcPriceProvider = Provider<num>((ref) => 15);
2. 修改USDTInput为可联动组件
改为ConsumerStatefulWidget,使用控制器监听状态变化:
import 'package:cryptx/Constants/app_colors.dart'; import 'package:cryptx/Providers/basic_providers.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class USDTInput extends ConsumerStatefulWidget { const USDTInput({Key? key}) : super(key: key); @override ConsumerState<USDTInput> createState() => _USDTInputState(); } class _USDTInputState extends ConsumerState<USDTInput> { final TextEditingController _controller = TextEditingController(); @override void initState() { super.initState(); // 监听USDT状态变化,同步输入框内容(非当前编辑时) ref.listen<num>(usdtAmountProvider, (previous, next) { if (ref.read(editingFieldProvider) != 'usdt') { _controller.text = next.toString(); } }); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0), child: TextField( controller: _controller, decoration: InputDecoration( icon: SizedBox( height: 30, child: Image.network( "https://assets.coingecko.com/coins/images/325/small/Tether.png?1668148663")), hintText: "USDT", border: InputBorder.none, ), onChanged: (value) { final usdtAmount = value.isNotEmpty ? num.tryParse(value) ?? 0 : 0; ref.read(usdtAmountProvider.notifier).state = usdtAmount; // 标记当前编辑USDT,避免BTC输入框反向触发更新 ref.read(editingFieldProvider.notifier).state = 'usdt'; // 计算并更新BTC金额 final btcPrice = ref.read(btcPriceProvider); ref.read(btcAmountProvider.notifier).state = usdtAmount / btcPrice; // 延迟重置编辑标识,确保状态更新完成 Future.delayed(const Duration(milliseconds: 100), () { ref.read(editingFieldProvider.notifier).state = null; }); }, autocorrect: false, keyboardType: const TextInputType.numberWithOptions(decimal: true), ), ); } }
3. 修改CoinOutput为可编辑的BTCInput组件
同样改为ConsumerStatefulWidget,实现反向联动:
import 'package:cryptx/Objects/coin.dart'; import 'package:cryptx/Providers/basic_providers.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class BTCInput extends ConsumerStatefulWidget { const BTCInput({super.key}); @override ConsumerState<BTCInput> createState() => _BTCInputState(); } class _BTCInputState extends ConsumerState<BTCInput> { final TextEditingController _controller = TextEditingController(); @override void initState() { super.initState(); // 监听BTC状态变化,同步输入框内容(非当前编辑时) ref.listen<num>(btcAmountProvider, (previous, next) { if (ref.read(editingFieldProvider) != 'btc') { _controller.text = next.toString(); } }); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { Coin coin = ref.watch(coinDetailProvider) as Coin; final btcPrice = ref.read(btcPriceProvider); return Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0), child: TextField( controller: _controller, decoration: InputDecoration( icon: SizedBox(height: 30, child: Image.network(coin.image)), hintText: "BTC", border: InputBorder.none, ), onChanged: (value) { final btcAmount = value.isNotEmpty ? num.tryParse(value) ?? 0 : 0; ref.read(btcAmountProvider.notifier).state = btcAmount; // 标记当前编辑BTC,避免USDT输入框反向触发更新 ref.read(editingFieldProvider.notifier).state = 'btc'; // 计算并更新USDT金额 ref.read(usdtAmountProvider.notifier).state = btcAmount * btcPrice; // 延迟重置编辑标识 Future.delayed(const Duration(milliseconds: 100), () { ref.read(editingFieldProvider.notifier).state = null; }); }, autocorrect: false, keyboardType: const TextInputType.numberWithOptions(decimal: true), ), ); } }
关键修改点
- 使用
ConsumerStatefulWidget和TextEditingController,避免build时重复创建控制器导致光标异常 - 添加
editingFieldProvider标记当前编辑的输入框,防止双向更新时出现循环触发的问题 - 用
num.tryParse处理无效输入,避免解析崩溃 - 通过延迟重置编辑标识,确保状态更新完成后再允许双向同步
内容的提问来源于stack exchange,提问作者Arjein
相关产品推荐
相关产品推荐

