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

Flutter ToggleButtons添加可编辑数值并同步至指定输入框的实现问询

实现方案

核心思路

  1. 为每个餐食(Breakfast/Lunch/Tea)单独创建输入框控制器,预设默认值
  2. 配置ToggleButtons为单选模式,选中时将对应餐食输入框的值同步到目标控制器numR3C2controller
  3. 限制输入框仅允许数字和小数输入,同时支持实时同步(选中状态下修改餐食值时自动更新目标输入框)

完整代码示例

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class MealCarbsPage extends StatefulWidget {
  const MealCarbsPage({super.key});

  @override
  State<MealCarbsPage> createState() => _MealCarbsPageState();
}

class _MealCarbsPageState extends State<MealCarbsPage> {
  // 控制ToggleButtons选中状态(单选)
  List<bool> isSelected = [false, false, false];
  // 餐食输入框控制器,预设默认值
  final TextEditingController breakfastController = TextEditingController(text: '1.0');
  final TextEditingController lunchController = TextEditingController(text: '0.6');
  final TextEditingController teaController = TextEditingController(text: '0.55');
  // 目标计算输入框控制器
  final TextEditingController numR3C2Controller = TextEditingController();

  @override
  void dispose() {
    // 销毁控制器避免内存泄漏
    breakfastController.dispose();
    lunchController.dispose();
    teaController.dispose();
    numR3C2Controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Meal Carbs Calculator')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // 餐食选择ToggleButtons
            ToggleButtons(
              isSelected: isSelected,
              onPressed: (index) {
                setState(() {
                  // 切换单选状态
                  for (int i = 0; i < isSelected.length; i++) {
                    isSelected[i] = i == index;
                  }
                  // 同步选中餐食的值到目标输入框
                  switch(index) {
                    case 0:
                      numR3C2Controller.text = breakfastController.text;
                      break;
                    case 1:
                      numR3C2Controller.text = lunchController.text;
                      break;
                    case 2:
                      numR3C2Controller.text = teaController.text;
                      break;
                  }
                });
              },
              children: const [
                Padding(
                  padding: EdgeInsets.symmetric(horizontal: 16),
                  child: Text('Breakfast'),
                ),
                Padding(
                  padding: EdgeInsets.symmetric(horizontal: 16),
                  child: Text('Lunch'),
                ),
                Padding(
                  padding: EdgeInsets.symmetric(horizontal: 16),
                  child: Text('Tea'),
                ),
              ],
            ),
            const SizedBox(height: 20),
            // Breakfast输入框
            TextField(
              controller: breakfastController,
              keyboardType: TextInputType.numberWithOptions(decimal: true),
              inputFormatters: [
                // 仅允许数字和小数点输入
                FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d*$')),
              ],
              decoration: const InputDecoration(
                labelText: 'Breakfast Ratio',
                border: OutlineInputBorder(),
              ),
              onChanged: (value) {
                // 如果当前选中Breakfast,实时同步到目标输入框
                if (isSelected[0]) {
                  numR3C2Controller.text = value;
                }
              },
            ),
            const SizedBox(height: 12),
            // Lunch输入框
            TextField(
              controller: lunchController,
              keyboardType: TextInputType.numberWithOptions(decimal: true),
              inputFormatters: [
                FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d*$')),
              ],
              decoration: const InputDecoration(
                labelText: 'Lunch Ratio',
                border: OutlineInputBorder(),
              ),
              onChanged: (value) {
                if (isSelected[1]) {
                  numR3C2Controller.text = value;
                }
              },
            ),
            const SizedBox(height: 12),
            // Tea输入框
            TextField(
              controller: teaController,
              keyboardType: TextInputType.numberWithOptions(decimal: true),
              inputFormatters: [
                FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d*$')),
              ],
              decoration: const InputDecoration(
                labelText: 'Tea Ratio',
                border: OutlineInputBorder(),
              ),
              onChanged: (value) {
                if (isSelected[2]) {
                  numR3C2Controller.text = value;
                }
              },
            ),
            const SizedBox(height: 20),
            // 目标计算输入框(Meal Carbs)
            TextField(
              controller: numR3C2Controller,
              keyboardType: TextInputType.numberWithOptions(decimal: true),
              decoration: const InputDecoration(
                labelText: 'Meal Carbs',
                border: OutlineInputBorder(),
                hintText: 'Selected meal ratio will sync here',
              ),
            ),
          ],
        ),
      ),
    );
  }
}

关键细节说明

  • 单选ToggleButtons:通过遍历isSelected列表,确保每次只有一个按钮处于选中状态,符合餐食单选的需求
  • 输入限制:使用FilteringTextInputFormatter配合正则表达式r'^\d+\.?\d*$',严格限制输入内容为数字和最多一个小数点;同时设置keyboardType为数字键盘,提升输入体验
  • 双向同步:不仅在选中按钮时同步值,当选中状态下修改餐食输入框内容时,也会实时更新目标输入框,保证数值一致性
  • 内存管理:在dispose方法中销毁所有控制器,避免Flutter内存泄漏问题

内容的提问来源于stack exchange,提问作者rnmuk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 08:35:20