Flutter中如何使用Input Formatter将用户输入格式化为MM/DD/YYYY格式
MM/DD/YYYY格式输入实时校验实现方案
需求说明
- 输入过程中自动将内容格式化为
MM/DD/YYYY格式 - 实时校验月、日、年合法性,非法输入不会被录入,例如输入月份14时,末尾的4会被自动拦截
- 现有代码月份格式化功能正常,仅需优化日期相关校验逻辑
原始问题代码
class _DateFormatter extends TextInputFormatter { @override TextEditingValue formatEditUpdate( TextEditingValue prevText, TextEditingValue currText) { int selectionIndex; // Get the previous and current input strings String pText = prevText.text; String cText = currText.text; // Abbreviate lengths int cLen = cText.length; int pLen = pText.length; //Date if (cLen == 1) { // Can only be 0, 1, 2 or 3 if (int.parse(cText) > 1) { // Remove char cText = ''; } } else if (cLen == 2 && pLen == 1) { // Can only be 0, 1, 2 or 3 if (int.parse(cText) == 0 || int.parse(cText) > 12) { // Remove char cText = cText.substring(0, 1); } else { cText += '/'; } } else if (cLen == 2 && pLen == 3) { cText = cText.substring(0, 1); } else if (cLen == 2 && pLen == 4) { cText += '/'; } else if (cLen == 3 && pLen == 2) { cText = cText.substring(0, 2) + '/' + cText.substring(2, 3); } // Month else if (cLen == 4 && pLen == 2) { print('s'); if (int.parse(cText.substring(3, 4)) > 3) { cText = cText; } } else if (cLen == 4 && pLen == 3) { if (int.parse(cText.substring(3, 4)) > 3) { cText = cText.substring(0, 3); } // // Can only be 0, 1, 2 or 3 // if (int.parse(cText) > 3) { // // Remove char // cText = ' // } // // Can only be 0 or 1 // if (int.parse(cText.substring(3, 4)) > 1) { // // Remove char // cText = cText.substring(0, 3); // } } else if (cLen == 5 && pLen == 4) { // Month cannot be greater than 12 int mm = int.parse(cText.substring(3, 5)); if (mm == 0 || mm > 12) { // Remove char cText = cText.substring(0, 4); } else { // Add a / char cText += '/'; } } else if ((cLen == 3 && pLen == 4) || (cLen == 6 && pLen == 7)) { // Remove / char cText = cText.substring(0, cText.length - 1); } else if (cLen == 3 && pLen == 2) { if (int.parse(cText.substring(2, 3)) > 1) { // Replace char cText = cText.substring(0, 2) + '/'; } else { // Insert / char cText = cText.substring(0, pLen) + '/' + cText.substring(pLen, pLen + 1); } } //Year else if (cLen == 6 && pLen == 5) { // Can only be 1 or 2 - if so insert a / char int y1 = int.parse(cText.substring(5, 6)); if (y1 < 1 || y1 > 2) { // Replace char cText = cText.substring(0, 5) + '/'; } else { // Insert / char cText = cText.substring(0, 5) + '/' + cText.substring(5, 6); } } else if (cLen == 7) { // Can only be 1 or 2 int y1 = int.parse(cText.substring(6, 7)); if (y1 < 1 || y1 > 2) { // Remove char cText = cText.substring(0, 6); } } else if (cLen == 8) { // Can only be 19 or 20 int y2 = int.parse(cText.substring(6, 8)); if (y2 < 19 || y2 > 20) { // Remove char cText = cText.substring(0, 7); } } selectionIndex = cText.length; print(selectionIndex); return TextEditingValue( text: cText, selection: TextSelection.collapsed(offset: selectionIndex), ); } }
优化后完整代码
优化点说明:
- 理清了月份、日期、年份的分段校验逻辑,修正了原代码中日期和月份校验混淆的问题
- 日期段支持01~31的合法性校验,非法输入自动拦截
- 优化了退格场景的处理,光标不会错位
- 年份默认支持1900~2099区间,可自行修改校验规则调整范围
class _DateFormatter extends TextInputFormatter { @override TextEditingValue formatEditUpdate( TextEditingValue prevText, TextEditingValue currText) { String pText = prevText.text; String cText = currText.text.replaceAll('/', ''); int cLen = cText.length; int pLen = pText.replaceAll('/', '').length; StringBuffer formatted = StringBuffer(); // 处理退格场景 if (cLen < pLen) { int selectionOffset = currText.selection.baseOffset; if (selectionOffset > 0 && pText[selectionOffset - 1] == '/') { selectionOffset--; } return TextEditingValue( text: pText.substring(0, selectionOffset), selection: TextSelection.collapsed(offset: selectionOffset), ); } // 月份处理 if (cLen >= 1) { int firstMonthDigit = int.parse(cText[0]); if (firstMonthDigit > 1) { // 月份第一位只能是0或1 cText = cText.substring(0, 0); cLen = 0; } else { formatted.write(cText[0]); if (cLen >= 2) { int month = int.parse(cText.substring(0, 2)); if (month == 0 || month > 12) { // 月份非法,只保留第一位 cText = cText.substring(0, 1); cLen = 1; } else { formatted.write(cText[1]); formatted.write('/'); } } } } // 日期处理 if (cLen >= 3) { int firstDayDigit = int.parse(cText[2]); if (firstDayDigit > 3) { // 日期第一位只能是0、1、2、3 cText = cText.substring(0, 2); cLen = 2; } else { formatted.write(cText[2]); if (cLen >= 4) { int day = int.parse(cText.substring(2, 4)); if (day == 0 || day > 31) { // 日期非法,只保留第一位 cText = cText.substring(0, 3); cLen = 3; } else { formatted.write(cText[3]); formatted.write('/'); } } } } // 年份处理 if (cLen >= 5) { int firstYearDigit = int.parse(cText[4]); if (firstYearDigit < 1 || firstYearDigit > 2) { // 年份第一位只能是1或2 cText = cText.substring(0, 4); cLen = 4; } else { formatted.write(cText[4]); if (cLen >= 6) { int secondYearDigit = int.parse(cText[5]); if ((firstYearDigit == 1 && secondYearDigit != 9) || (firstYearDigit == 2 && secondYearDigit != 0)) { // 年份前两位只能是19或20 cText = cText.substring(0, 5); cLen = 5; } else { formatted.write(cText[5]); // 年份后两位无强制校验,直接拼接 if (cLen >=7) formatted.write(cText[6]); if (cLen >=8) formatted.write(cText[7]); } } } } String result = formatted.toString(); return TextEditingValue( text: result, selection: TextSelection.collapsed(offset: result.length), ); } }
使用方式
直接将该 formatter 配置给 TextField 即可:
TextField( inputFormatters: [_DateFormatter()], keyboardType: TextInputType.number, decoration: InputDecoration(hintText: 'MM/DD/YYYY'), )
内容的提问来源于stack exchange,提问作者imgkl
相关产品推荐
相关产品推荐

