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

Flutter技术问题:如何避免确认匹配PIN时清空输入控制器

问题修复:仅初始设置PIN时清空控制器,避免匹配成功时意外清空

问题描述

需求是仅在用户首次设置有效PIN时清空TextEditingController,当前该功能正常,但在重新输入PIN并匹配成功的界面中,控制器被意外清空,需要阻止此行为。

现有代码

SetPinScreen代码

class _SetPinScreenState extends State<SetPinScreen> {
  TextEditingController setPinController;

  PinValidationProvider pinValidationProvider;
  bool isPinLengthValid = false;

  @override
  void initState() {
    super.initState();
    setPinController = TextEditingController();
    pinValidationProvider = context.read<PinValidationProvider>();
    pinValidationProvider.resetPinValidation();
  }

  @override
  void dispose() {
    setPinController.dispose();
    super.dispose();
  }

  void _validatePin(String value) {
    if (value.isNotEmpty && value.length <= 4) {
      pinValidationProvider.validatePin(value, setPinController);
    }
  }

  @override
  Widget build(BuildContext context) {
    final isPinSet =
        context.select((PinValidationProvider provider) => provider.isPinSet);
    final isPinMatched = context
        .select((PinValidationProvider provider) => provider.isPinMatched);

    return MainTemplate(
      isFloatingButtonVisible: isPinSet,
      isFloatingActionButtonDisabled: !isPinMatched,
      floatingButtonAction: () {
        if (isPinMatched) {
          print('Select');
        }
      },
      child: Padding(
        padding: const EdgeInsets.all(15.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Align(
              alignment: Alignment.centerLeft,
              child: Consumer<PinValidationProvider>(
                builder: (context, pinProvider, child) {
                  return Text(
                    pinProvider.isPinSet
                        ? 'Re-enter your PIN code to Confirm'
                        : 'Set PIN to Activate your Card Ending With XXXX',
                    style: TextStyle(fontWeight: FontWeight.w700, fontSize: 30),
                  );
                },
              ),
            ),
            SizedBox(height: 20),
            Center(
              child: PinFieldAutoFill(
                decoration: UnderlineDecoration(
                  textStyle: Theme.of(context).textTheme.subtitle1.copyWith(
                        fontWeight: FontWeight.bold,
                        fontSize: 15,
                      ),
                  gapSpace: 8,
                  lineHeight: 1.2,
                  colorBuilder:
                      PinListenColorBuilder(Colors.black, Colors.grey.shade400),
                ),
                onCodeChanged: _validatePin,
                codeLength: 4,
                cursor: Cursor(color: Colors.black, enabled: true),
                controller: setPinController,
                keyboardType: TextInputType.number,
                currentCode: "",
              ),
            ),
            SizedBox(height: 20),
            Consumer<PinValidationProvider>(
              builder: (context, provider, child) {
                final textColor = provider.isPinSet == false
                    ? provider.isInvalidPin ||
                            (!provider.isPinMatched && provider.isPinSet)
                        ? Colors.red
                        : Colors.black
                    : provider.isInvalidPin && !provider.isPinMatched
                        ? Colors.red
                        : Colors.black;

                if (!provider.isPinMatched &&
                    provider.isPinSet &&
                    setPinController.text.isNotEmpty &&
                    setPinController.text.length == 4) {
                  return Center(
                    child: const Text(
                      'PIN does not match.',
                      style: TextStyle(
                        color: Colors.red,
                      ),
                    ),
                  );
                } else {
                  return Text(
                    'Pin must be at least 4 digits. It must not be single digits like'
                    ' 1111 or digits in numerical order such as 1234',
                    style: TextStyle(
                      color: textColor,
                    ),
                  );
                }
              },
            ),
          ],
        ),
      ),
    );
  }
}

PinValidationProvider代码

import 'package:flutter/material.dart';

class PinValidationProvider extends ChangeNotifier {
  bool _isInvalidPin = false;

  bool get isInvalidPin => _isInvalidPin;

  bool isPinMatched = false;
  bool isPinSet = false;
  String pin = '';

  void validatePin(String pin, TextEditingController controller) {
    if (isPinSet) {
      matchPin(pin);
      return;
    }

    if (pin.length != 4) {
      _isInvalidPin = false;
    } else {
      _isInvalidPin = _isSimpleSequence(pin) || _isRepeatedDigit(pin);
      if (!_isInvalidPin) {
        isPinSet = true;
        this.pin = pin;
        controller.clear();
      }
    }
    _notifyListeners();
  }

  void matchPin(String pin) {
    isPinMatched = pin.length == 4 && pin == this.pin;
    _notifyListeners();
  }

  bool _isSimpleSequence(String pin) {
    for (int i = 0; i < pin.length - 1; i++) {
      int digit1 = int.parse(pin[i]);
      int digit2 = int.parse(pin[i + 1]);

      if (digit2 - digit1 == 1) {
        return true;
      }
    }
    return false;
  }

  bool _isRepeatedDigit(String pin) {
    for (int i = 0; i < pin.length - 1; i++) {
      if (pin[i] == pin[i + 1]) {
        return true;
      }
    }
    return false;
  }

  void _notifyListeners() {
    Future.microtask(() {
      notifyListeners();
    });
  }

  void resetPinValidation() {
    _isInvalidPin = false;
    isPinSet = false;
    pin = '';
    isPinMatched = false;
    _notifyListeners();
  }
}

解决方案

问题根源在于PinFieldAutoFill组件的currentCode: ""参数:当PIN匹配成功后,界面因状态变化重新build时,该参数会强制将输入框内容重置为空,导致意外清空。

修改步骤

  1. 移除currentCode参数:由于已经绑定了setPinController,无需额外设置固定初始值,避免每次build时重置输入框内容。

修改后的PinFieldAutoFill代码片段:

Center(
  child: PinFieldAutoFill(
    decoration: UnderlineDecoration(
      textStyle: Theme.of(context).textTheme.subtitle1.copyWith(
            fontWeight: FontWeight.bold,
            fontSize: 15,
          ),
      gapSpace: 8,
      lineHeight: 1.2,
      colorBuilder:
          PinListenColorBuilder(Colors.black, Colors.grey.shade400),
    ),
    onCodeChanged: _validatePin,
    codeLength: 4,
    cursor: Cursor(color: Colors.black, enabled: true),
    controller: setPinController,
    keyboardType: TextInputType.number,
    // 移除currentCode: ""
  ),
),
  1. 确认Provider逻辑:原PinValidationProvider中仅在首次设置有效PIN时调用controller.clear(),匹配PIN的流程中无清空操作,无需修改。

效果验证

修改后:

  • 首次设置有效PIN时,控制器正常清空,进入重新输入界面;
  • 重新输入PIN匹配成功后,输入框内容保留,不会被意外清空。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 15:40:54