4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Flutter】TextFormFieldで入力値をフォーマットする

Last updated at Posted at 2021-09-16

要約

  • inputFormattersTextInputFormatterを継承したCustomFormatterをセット
  • validator maxLength buildCounterにはフォーマット後の値を反映させること(できればもっと楽にしたい)

expiry_date.gif

きっかけ

クレジットカードの入力フォームを作ろうとしたときに入力フォームを工夫したかった。具体的には、カードナンバーで4桁ごとに空白を入れること、有効期限で/(スラッシュ)を入れること。

TextInputFormatterを継承したCustomTextInputFormatterの作成

formatEditUpdateにて oldValuenewValueが渡ってくるのでこの2つを比較して処理を決定します。
入力時だけでなく、削除時の実装も忘れずに。

これをTextFormFieldinputFormattersに格納します。

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;
  }
}

感想

カスタムフォーマッターは使い回せるので便利だ

4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?