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

Flutter中TextFormField键盘事件检测:PIN输入屏退格跳转实现

实现Flutter PIN输入框的键盘事件处理(删除键跳转)

这个需求在PIN码输入场景里太常见了,我来给你一套完整的实现方案,完美解决你说的「删除最后一个字符后再次按下删除键,光标跳转到上一个TextFormField」的需求,同时实现类似你提到的PIN输入界面效果。

核心思路

要实现这个功能,关键在于用FocusNode管理每个输入框的焦点,并监听输入框的键盘事件和内容变化:

  • 当用户输入字符时,自动跳转到下一个输入框
  • 当用户在空输入框按下删除键时,自动跳转到上一个输入框

完整代码实现

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

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

  @override
  State<PinInputWidget> createState() => _PinInputWidgetState();
}

class _PinInputWidgetState extends State<PinInputWidget> {
  late List<FocusNode> _focusNodes;
  late List<TextEditingController> _controllers;
  // 可以根据需求修改PIN码长度
  final int _pinLength = 4;

  @override
  void initState() {
    super.initState();
    // 为每个输入框创建FocusNode和TextEditingController
    _focusNodes = List.generate(_pinLength, (index) => FocusNode());
    _controllers = List.generate(_pinLength, (index) => TextEditingController());
    
    // 页面加载完成后,自动让第一个输入框获取焦点
    WidgetsBinding.instance.addPostFrameCallback((_) {
      FocusScope.of(context).requestFocus(_focusNodes[0]);
    });
  }

  @override
  void dispose() {
    // 销毁资源,避免内存泄漏
    for (var node in _focusNodes) {
      node.dispose();
    }
    for (var controller in _controllers) {
      controller.dispose();
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      padding: const EdgeInsets.symmetric(horizontal: 20),
      children: List.generate(_pinLength, (index) {
        return SizedBox(
          width: 60,
          height: 60,
          child: TextFormField(
            controller: _controllers[index],
            focusNode: _focusNodes[index],
            // 限制每个输入框只能输入1个字符
            maxLength: 1,
            // 只允许输入数字
            keyboardType: TextInputType.number,
            textAlign: TextAlign.center,
            style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            decoration: InputDecoration(
              counterText: '', // 隐藏默认的长度计数器
              border: OutlineInputBorder(
                borderRadius: BorderRadius.circular(8),
              ),
              // 焦点状态下的边框样式,提升用户体验
              focusedBorder: OutlineInputBorder(
                borderRadius: BorderRadius.circular(8),
                borderSide: const BorderSide(color: Colors.blue, width: 2),
              ),
            ),
            onChanged: (value) {
              // 输入字符后,自动跳转到下一个输入框(如果不是最后一个)
              if (value.isNotEmpty && index < _pinLength - 1) {
                FocusScope.of(context).requestFocus(_focusNodes[index + 1]);
              }
            },
            onKeyEvent: (node, event) {
              // 监听删除键的按下事件
              if (event.logicalKey == LogicalKeyboardKey.backspace && event is KeyDownEvent) {
                // 如果当前输入框为空,且不是第一个输入框,跳转到上一个
                if (_controllers[index].text.isEmpty && index > 0) {
                  FocusScope.of(context).requestFocus(_focusNodes[index - 1]);
                  return KeyEventResult.handled; // 标记事件已处理,避免默认行为
                }
              }
              return KeyEventResult.ignored;
            },
          ),
        );
      }),
    );
  }
}

关键代码解释

  1. FocusNode与Controller管理:

    • 为每个PIN输入框创建独立的FocusNode和TextEditingController,方便单独控制焦点和内容
    • 在dispose方法中销毁这些资源,避免内存泄漏
  2. 输入自动跳转:

    • 通过onChanged回调,当用户输入字符后,自动将焦点切换到下一个输入框,提升输入流畅度
  3. 删除键跳转逻辑:

    • 使用onKeyEvent(Flutter 3.3+支持)监听键盘事件,当检测到删除键按下时:
      • 如果当前输入框为空,说明用户想要删除上一个输入框的内容,此时将焦点切换到上一个输入框
      • 返回KeyEventResult.handled标记事件已处理,避免触发默认的删除行为
  4. 用户体验优化:

    • 设置keyboardType: TextInputType.number,只弹出数字键盘
    • 自定义输入框的焦点边框样式,让用户清晰看到当前输入位置
    • 页面加载后自动让第一个输入框获取焦点,用户无需手动点击

额外优化建议

  • 如果需要隐藏输入内容(比如密码类型的PIN),可以设置obscureText: true
  • 可以添加输入完成后的回调,比如当所有输入框都有内容时,自动提交PIN码
  • 可以处理粘贴事件,支持一次性粘贴完整的PIN码并自动分配到各个输入框

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:05:06