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

Flutter:如何为多个TextFormField实现共用屏幕数字键盘

解决方案

要实现同一页面多个输入框共用自定义数字键盘,核心是跟踪当前激活的输入框控制器,让键盘操作指向这个激活的控制器。以下是具体实现步骤和修改后的代码:

1. 页面层实现:管理多个输入框与焦点

在你的页面State类中,为每个TextFormField创建独立的TextEditingController和FocusNode,并监听焦点变化来记录当前激活的控制器:

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

  @override
  State<MultiInputPage> createState() => _MultiInputPageState();
}

class _MultiInputPageState extends State<MultiInputPage> {
  // 为每个输入框创建独立控制器和焦点节点
  final TextEditingController _controller1 = TextEditingController();
  final TextEditingController _controller2 = TextEditingController();
  final TextEditingController _controller3 = TextEditingController();

  final FocusNode _focusNode1 = FocusNode();
  final FocusNode _focusNode2 = FocusNode();
  final FocusNode _focusNode3 = FocusNode();

  // 当前激活的输入框控制器
  TextEditingController? _activeController;

  @override
  void initState() {
    super.initState();
    // 监听每个焦点节点的变化,更新激活的控制器
    _focusNode1.addListener(_updateActiveController);
    _focusNode2.addListener(_updateActiveController);
    _focusNode3.addListener(_updateActiveController);
  }

  void _updateActiveController() {
    setState(() {
      if (_focusNode1.hasFocus) {
        _activeController = _controller1;
      } else if (_focusNode2.hasFocus) {
        _activeController = _controller2;
      } else if (_focusNode3.hasFocus) {
        _activeController = _controller3;
      } else {
        _activeController = null;
      }
    });
  }

  // 退格键逻辑:删除当前激活控制器的最后一个字符
  void _onDelete() {
    if (_activeController != null && _activeController!.text.isNotEmpty) {
      _activeController!.text = _activeController!.text.substring(0, _activeController!.text.length - 1);
    }
  }

  // 提交逻辑(根据需求自定义)
  void _onSubmit() {
    // 处理提交操作,比如获取所有输入框的值
    print("输入1: ${_controller1.text}, 输入2: ${_controller2.text}, 输入3: ${_controller3.text}");
  }

  @override
  void dispose() {
    // 销毁控制器和焦点节点,避免内存泄漏
    _controller1.dispose();
    _controller2.dispose();
    _controller3.dispose();
    _focusNode1.dispose();
    _focusNode2.dispose();
    _focusNode3.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("多输入框数字键盘")),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            // 第一个输入框
            TextFormField(
              controller: _controller1,
              focusNode: _focusNode1,
              keyboardType: TextInputType.none, // 禁用系统键盘
              decoration: const InputDecoration(labelText: "输入框1"),
            ),
            const SizedBox(height: 16),
            // 第二个输入框
            TextFormField(
              controller: _controller2,
              focusNode: _focusNode2,
              keyboardType: TextInputType.none,
              decoration: const InputDecoration(labelText: "输入框2"),
            ),
            const SizedBox(height: 16),
            // 第三个输入框
            TextFormField(
              controller: _controller3,
              focusNode: _focusNode3,
              keyboardType: TextInputType.none,
              decoration: const InputDecoration(labelText: "输入框3"),
            ),
            const SizedBox(height: 32),
            // 自定义数字键盘
            NumPadCheckout(
              activeController: _activeController,
              delete: _onDelete,
              onSubmit: _onSubmit,
            ),
          ],
        ),
      ),
    );
  }
}

2. 修改自定义数字键盘组件

调整NumPadCheckout及其子组件,使其依赖当前激活的控制器,并修正原代码中的回调执行错误:

// 定义键盘按钮的宽高(根据你的需求调整)
const double _keyWidth = 100;
const double _keyHeight = 60;

class NumPadCheckout extends StatelessWidget {
  final TextEditingController? activeController;
  final VoidCallback delete;
  final VoidCallback onSubmit;

  const NumPadCheckout({
    super.key,
    required this.delete,
    required this.onSubmit,
    required this.activeController,
  });

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const SizedBox(height: 20),
        Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: [
            NumberButtonCheckout(number: "1", controller: activeController),
            NumberButtonCheckout(number: "2", controller: activeController),
            NumberButtonCheckout(number: "3", controller: activeController),
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: [
            NumberButtonCheckout(number: "4", controller: activeController),
            NumberButtonCheckout(number: "5", controller: activeController),
            NumberButtonCheckout(number: "6", controller: activeController),
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: [
            NumberButtonCheckout(number: "7", controller: activeController),
            NumberButtonCheckout(number: "8", controller: activeController),
            NumberButtonCheckout(number: "9", controller: activeController),
          ],
        ),
        Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: [
            NumberButtonCheckout(number: "00", controller: activeController),
            NumberButtonCheckout(number: "0", controller: activeController),
            CustomKey(
              iconData: Icons.keyboard_backspace,
              onPressed: delete, // 直接传递删除回调
            )
          ],
        ),
      ],
    );
  }
}

class NumberButtonCheckout extends StatelessWidget {
  final String number;
  final TextEditingController? controller;

  const NumberButtonCheckout({
    super.key,
    required this.number,
    required this.controller,
  });

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: _keyWidth,
      height: _keyHeight,
      child: ElevatedButton(
        onPressed: () {
          // 仅当控制器存在时追加数字
          if (controller != null) {
            controller!.text += number;
          }
        },
        child: Center(child: Text(number)),
      ),
    );
  }
}

class CustomKey extends StatelessWidget {
  final IconData iconData;
  final VoidCallback? onPressed;

  const CustomKey({
    super.key,
    required this.iconData,
    required this.onPressed,
  });

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: _keyWidth,
      height: _keyHeight,
      child: ElevatedButton(
        onPressed: onPressed, // 修正原代码的回调执行错误
        child: Center(child: Icon(iconData)),
      ),
    );
  }
}

关键说明

  • 焦点监听:通过FocusNode的监听器,实时更新当前激活的TextEditingController,确保键盘操作指向正确的输入框。
  • 组件解耦:修改后的数字键盘不再绑定固定控制器,而是接收当前激活的控制器,实现了多输入框复用。
  • 错误修正:原代码中CustomKey的onPressed存在执行错误(() => onPressed只是返回函数,未执行),现在直接传递回调函数即可触发操作。
  • 内存管理:页面中必须销毁所有TextEditingController和FocusNode,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 19:32:34