要約
-
inputFormatters
にTextInputFormatter
を継承したCustomFormatterをセット -
validator
maxLength
buildCounter
にはフォーマット後の値を反映させること(できればもっと楽にしたい)
きっかけ
クレジットカードの入力フォームを作ろうとしたときに入力フォームを工夫したかった。具体的には、カードナンバーで4桁ごとに空白を入れること、有効期限で/
(スラッシュ)を入れること。
TextInputFormatter
を継承したCustomTextInputFormatterの作成
formatEditUpdate
にて oldValue
とnewValue
が渡ってくるのでこの2つを比較して処理を決定します。
入力時だけでなく、削除時の実装も忘れずに。
これをTextFormField
のinputFormatters
に格納します。
class CustomTextInputFormatter extends TextInputFormatter {
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
if ('フォーマットを変更する時') {
final newText = 'hoge'; // フォーマット後の文字
return newValue.copyWith(
text: newText,
// カーソルの位置を端へ
selection: TextSelection.collapsed(offset: newText.length),
);
}
// フォーマットしない場合はそのままnewValueを返す
return newValue;
}
}
バリデーションや文字数カウントへの対応
フォーマットのやり方によって文字数が変わってしまうので、バリデーションや文字数のカウントにも対応する必要があります。
クレジットカードの有効期限だと
04/23
打ち込むのは4文字分、でも表示は5文字分。
具体的にはvalidator
maxLength
buildCounter
に反映させます。
※これをもうちょっとオシャレに、楽にやる方法知ってる方はご教授くださると嬉しいです。
有効期限のフォームのサンプルです。
サンプルコード
class ExpiryDateTextField extends StatelessWidget {
ExpiryDateTextField();
@override
Widget build(BuildContext context) {
return TextFormField(
// '/'を含めた5文字
maxLength: 5,
buildCounter: (context,
{int currentLength,
bool isFocused,
int? maxLength}) {
// `/`を抜いた文字数でカウント
final length = currentLength < 3 ? currentLength : currentLength - 1;
return CustomText.caption('$length/4');
},
// 配列の中に格納
inputFormatters: [_ExpiryDateInputFormatter()],
validator: (String? value) {
if (value?.length == 0) return '有効期限が入力されていません';
// '/'を含めた5文字
if (value?.length != 5) return '有効期限が不正です';
},
);
}
}
class CustomTextInputFormatter extends TextInputFormatter {
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
// 入力している時
if (oldValue.text.length == 2 && newValue.text.length == 3) {
final newText = '${oldValue.text}/${newValue.text.substring(2)}';
return newValue.copyWith(
text: newText,
selection: TextSelection.collapsed(offset: newText.length),
);
}
// 削除している時
if (oldValue.text.length == 4 && newValue.text.length == 3) {
final newText = newValue.copyWith().text.substring(0, 2);
return newValue.copyWith(
text: newText,
selection: TextSelection.collapsed(offset: newText.length),
);
}
return newValue;
}
}
感想
カスタムフォーマッターは使い回せるので便利だ