【Flutter】TextFieldで3桁区切り整数6桁小数点第一まで入力できるようにする

結論から書くとこれ

    class DecimalTextInputFormatter extends TextInputFormatter {
      @override
      TextEditingValue formatEditUpdate(
          TextEditingValue oldValue, TextEditingValue newValue) {
        // ユーザーが新しいテキストを入力したときの処理
    
        // テキストからカンマを除去
        final unformatted = newValue.text.replaceAll(',', '');
    
        // テキストが空の場合、最初の文字を削除できるようにする
        if (unformatted.isEmpty) {
          return TextEditingValue(
            text: '',
            selection: TextSelection.collapsed(offset: 0),
          );
        }
    
        // 正規表現を使用して整数部と小数部を確認
        final regExp = RegExp(r'^\d{0,6}(\.\d{0,1})?$');
        if (regExp.hasMatch(unformatted)) {
          // カンマを除去
          final parts = unformatted.split('.');
          String integerPart = parts[0];
          String decimalPart = parts.length > 1 ? '.' + parts[1] : '';
    
          // 整数部を3桁ごとにカンマで区切る
          integerPart = _formatWithCommas(integerPart);
    
          // 新しいテキストを整形
          String formattedValue = integerPart + decimalPart;
    
          // 新しいテキストが".0"で終わる場合、それを削除
          if (formattedValue.endsWith('.0')) {
            formattedValue = formattedValue.substring(0, formattedValue.length - 2);
          }
    
          return TextEditingValue(
            text: formattedValue,
            selection: TextSelection.collapsed(offset: formattedValue.length),
          );
        }
    
        // 条件を満たさない場合、変更を受け入れず、元のテキストを維持
        return oldValue;
      }
    
      String _formatWithCommas(String value) {
        // 整数部を3桁ごとにカンマで区切る
        if (value.length <= 3) {
          return value;
        }
    
        final integerPart = value.substring(0, value.length - 3);
        final decimalPart = value.substring(value.length - 3);
    
        return _formatWithCommas(integerPart) + ',' + decimalPart;
      }
    }
    

TextField部分

    Container(
      width: 180,
      height: 45,
      decoration: BoxDecoration(
        color: Colors.grey.withOpacity(0.1),
        borderRadius: BorderRadius.circular(5),
      ),

      child: Padding(
        padding: const EdgeInsets.symmetric(vertical: 7),
        child: TextField(
            style: const TextStyle(
              fontSize: 16,
              color: Colors.black, // 黒色に設定
            ),
            controller: controller,
            decoration: const InputDecoration(
              filled: true,
              fillColor: Colors.transparent,
              border: InputBorder.none,
            ),
            keyboardType: const TextInputType.numberWithOptions(decimal: true),
            inputFormatters: [DecimalTextInputFormatter()],
            onChanged: widget.onChanged),
      ),
    ),

init部分

      @override
      void initState() {
        super.initState();
    
        controller = TextEditingController.fromValue(
          DecimalTextInputFormatter().formatEditUpdate(
            TextEditingValue.empty,
            TextEditingValue(text: widget.value),
          ),
        );
      }

dispose部分

      @override
      void dispose() {
        controller.dispose(); // Dispose the TextEditingController
        super.dispose();
      }

ちなみにChatgptに使ったメモはこれ

    整数は6桁まで入力可能
    小数点は第一まで入力可能
    3桁区切りを追加
    4桁以上入力できないプログラムは作成するな
    カンマが二つつかないようにしろ
    999,999.0のような文字が桁が含まれていても消せるようにしろ
    最初の文字も必ず削除できるようにしろ
    3桁区切りがちゃんと動作するようにしろ
    新しく入力されるときに最後が.0の場合はそれを消すようにして

# まとめ

整数で3桁区切りをするって内容はあったけど、小数点込みで情報を探して見つからなくて途方に暮れてたんですが、ネットの情報探せなかったからもうヤケクソでChatgptに必要な内容をメモに追加しつつ問題点を書き込んでひたすら実装させたらそれっぽい動作する感じに動いたので情報共有も兼ねて投稿です。

多分同じとこでつまづく人がいるかなって思ったんでそんな方達の参考になれば幸いです。