LoginSignup
6

More than 1 year has passed since last update.

posted at

updated at

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

要約

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

感想

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

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
What you can do with signing up
6