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时,该参数会强制将输入框内容重置为空,导致意外清空。
修改步骤
- 移除
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: "" ), ),
- 确认Provider逻辑:原
PinValidationProvider中仅在首次设置有效PIN时调用controller.clear(),匹配PIN的流程中无清空操作,无需修改。
效果验证
修改后:
- 首次设置有效PIN时,控制器正常清空,进入重新输入界面;
- 重新输入PIN匹配成功后,输入框内容保留,不会被意外清空。
内容的提问来源于stack exchange,提问作者Mansoor Malik
相关产品推荐
相关产品推荐

