Formatter | CoUI
본문으로 건너뛰기
LogoCoUI

Formatter

텍스트 입력 포매팅 유틸리티 (정수·소수·대소문자·시간·수식 마스킹)

목차

TextField 의 inputFormatters 에 넘기는 입력 포매터 팩토리 모음입니다. 같은 변환 로직(CoreInputFormatters)을 양 플랫폼이 공유하며, 클래스 이름(TextInputFormatters)과 팩토리 표면(integerOnly·decimalOnly·toUpperCase·toLowerCase·time·mathExpression)이 동일합니다 — Flutter 는 TextInputFormatter 로, Web <input> 은 평문 문자열에 직접 적용합니다.

Live Preview#

Web
Flutter
Loading Flutter...
코드 보기
class FormatterDefaultExample extends StatelessComponent {
  const FormatterDefaultExample({super.key});

  @override
  Component build(BuildContext context) {
    return div(
      styles: Styles(raw: {'width': '280px'}),
      [
        TextField(
          placeholder: Text('숫자만 입력 (0–100)'),
          inputFormatters: [TextInputFormatters.integerOnly(min: 0, max: 100)],
        ),
      ],
    );
  }
}
class FormatterDefaultExample extends StatelessWidget {
  const FormatterDefaultExample({super.key});

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: 280,
      child: TextField(
        placeholder: const Text('숫자만 입력 (0–100)'),
        inputFormatters: [TextInputFormatters.integerOnly(min: 0, max: 100)],
      ),
    );
  }
}

사용법#

// Flutter / Web 동일 — TextInputFormatters
TextField(
  placeholder: const Text('숫자만 입력 (0–100)'),
  inputFormatters: [TextInputFormatters.integerOnly(min: 0, max: 100)],
)

Props / Parameters#

TextInputFormatters 는 위젯이 아니라 정적 팩토리 홀더라 생성자가 없습니다 (TextInputFormatters._() 는 private). 노출되는 표면은 아래 팩토리와 그 named 파라미터뿐이며, 각 팩토리가 돌려준 값을 TextField.inputFormatters 에 넘겨 씁니다.

팩토리파라미터기본값설명
integerOnly int? min, int? max 둘 다 null (무제한) 파싱 불가능한 입력은 이전 값, 범위 밖 정수는 min/max로 클램핑
decimalOnly int? decimalDigits, double? min, double? max 모두 null (자릿수·범위 무제한) 소수를 허용하고 소수점 이하 자릿수와 범위를 제한합니다
toUpperCase없음—입력을 대문자로 강제합니다
toLowerCase없음—입력을 소문자로 강제합니다
time required int length 필수 지정한 길이까지 앞을 0 으로 채우는 시간 형식 마스킹
mathExpression Map<String, Object?>? context null 입력을 수식으로 평가합니다. context 는 식에서 참조할 변수 맵
allow¹ String pattern 필수 정규식 소스에 매칭되는 문자만 남김
phoneText¹없음—숫자와 선두 + 를 허용

InputFormatter (Flutter 전용 어댑터)

파라미터타입기본값설명
formatter CoreInputFormatter 필수 (positional) 감쌀 플랫폼 무관 변환. Flutter TextInputFormatter 로 어댑트하며 재작성 후 캐럿 위치를 보존합니다

¹ 현재 Flutter는 두 팩토리를 노출하고 Web TextInputFormatters에는 노출이 빠져 있습니다. 공유 CoreInputFormatters에는 같은 변환이 있어 Web에서 직접 사용할 수 있습니다. 이는 수용된 플랫폼 전용 기능이 아니라 공개 팩토리 배선 결손입니다.

동작 스펙 (Behavior)#

  • 입력의 이전 문자열·새 문자열을 받아 변환하는 정적 팩토리입니다. 필드 렌더 · 콜백 · 자체 상태 · 비활성 기능은 없으며 반환값을 TextField.inputFormatters에 넣어 적용합니다.
  • integerOnly / decimalOnly의 min/max는 숫자 범위, decimalDigits는 소수 자릿수, time.length는 문자 길이입니다. 입력 변환은 검증 메시지를 만들지 않으므로 필수·도메인 규칙에는 FormField.validator를 사용합니다.
  • 마스크 자리·리터럴과 함께 단일 입력을 표시하려면 FormattedInput, 자유 입력의 변환만 필요하면 이 팩토리를 씁니다.

사용 가이드라인 (Usage Guidelines)#

✅ Do#

숫자 입력은 min/max로 범위를 clamp

TextField(
  placeholder: const Text('나이 (0–120)'),
  inputFormatters: [TextInputFormatters.integerOnly(min: 0, max: 120)],
)

min/max는 범위를 벗어난 값을 자동으로 경계값으로 보정합니다(예: 130 입력 시 120으로) — 범위가 있는 숫자 입력은 별도 검증 로직 없이 포매터만으로 값을 안전한 범위 안에 유지할 수 있습니다.


❌ Don't#

TextInputFormatters.allow가 Web에도 있다고 가정하지 않기

// ❌ Web 의 TextInputFormatters 에는 allow 가 없음 — 컴파일 에러
TextInputFormatters.allow(r'[0-9]')

// ✅ Web 은 공유 로직인 CoreInputFormatters.allow 를 직접 사용
CoreInputFormatters.allow(r'[0-9]')

allow 팩토리는 Flutter의 TextInputFormatters에만 있고 Web TextInputFormatters에는 없습니다 — 양 플랫폼에서 같은 코드를 쓰려면 Web에서는 공유 로직인 CoreInputFormatters.allow를 직접 호출해야 합니다.

접근성 (Accessibility)#

이 절은 Formatter 자신의 동작만 다룹니다. 전 컴포넌트에 공통으로 적용되는 축은 전역 접근성 축을 참고하세요.

Formatter 는 아무것도 그리지 않습니다. Flutter 쪽은 TextInputFormatter 를 상속한 InputFormatter 와 팩토리 홀더 TextInputFormatters 이고, Web 쪽은 CoreInputFormatter 를 돌려주는 팩토리 홀더 TextInputFormatters 하나뿐입니다. 어느 파일도 Widget / Component 도 build 도 선언하지 않으므로 role 을 실을 엘리먼트도 시맨틱 노드도 존재하지 않습니다 — 아래 네 축이 "없음" 인 이유는 전부 이것 하나입니다.

역할#

없음. 양 플랫폼 모두 Semantics 호출도, role / aria-* 를 붙일 엘리먼트도 없습니다. 접근성 트리에 나타나는 것은 포매터를 소비하는 호스트 필드(TextField)의 노드뿐이고, 그 노드의 역할은 필드가 정합니다.

키보드#

처리하는 키가 없습니다. 두 플랫폼 다 키 이벤트를 구독하지 않고, 이미 확정된 텍스트를 받아 문자열을 되돌려 줄 뿐입니다 — Flutter 는 플랫폼 텍스트 입력이 키를 처리한 뒤 프레임워크가 호출하는 formatEditUpdate(oldValue, newValue) 안에서, Web 은 호스트 TextField 가 <input> 의 input 이벤트에서 format(old, new) 를 목록 순서대로 돌려 실행합니다.

스크린 리더#

이 클래스들이 직접 읽히는 것은 없습니다. 간접적으로는 사용자가 입력하는 동안 필드의 텍스트를 다시 쓰므로 리더가 다시 읽는 것은 호스트 필드의 값 변경입니다. 포매터 자신은 live region 도, status 도, "문자가 거부되었다 / 다시 포맷되었다" 는 안내도 내보내지 않습니다.

포커스 관리#

없음. 포커스 가능한 노드를 만들지 않으므로 진입·이탈·트랩이 성립하지 않습니다 — Flutter 에 FocusNode / Focus 가 없고, Web 에는 tabindex 를 실을 엘리먼트 자체가 없습니다. 포커스는 처음부터 끝까지 호스트 필드가 갖고 있습니다.

알려진 제약#

  • 거부·재작성이 조용히 일어납니다. 허용되지 않는 문자를 입력한 스크린 리더 사용자는 무언가 버려졌다는 피드백을 전혀 받지 못합니다(양 플랫폼 동일). 입력 규칙을 알려야 한다면 호스트 필드 쪽에서 안내 문구나 live region 을 직접 제공해야 합니다.
  • Web 은 재작성이 일어날 때마다 캐럿이 문자열 끝으로 밀립니다. Flutter 의 InputFormatter 는 재작성 후 선택 범위를 새 길이에 clamp 해 캐럿 위치를 보존하지만, Web 은 결과 문자열을 <input> 의 value 에 직접 대입하므로 브라우저가 캐럿을 끝으로 옮깁니다. 그래서 Web 에서는 이미 입력한 값의 중간을 고칠 때마다 커서가 끝으로 튀어, 키보드·스크린 리더만 쓰는 사용자가 앞부분을 수정하기 어렵습니다. 대안은 없습니다 — 두 경로 모두 컴포넌트 내부이고 캐럿 정책을 여는 파라미터가 없습니다.
  • 접근성과 직접 관련은 없지만 팩토리 표면이 실제로는 완전히 같지 않습니다 — Flutter 에는 TextInputFormatters.allow(pattern) 이 있고 Web 의 TextInputFormatters 에는 없어, Web 호출자는 CoreInputFormatters.allow 를 직접 씁니다.

크로스 플랫폼 차이점 (Platform Differences)#

항목FlutterWeb
반환 타입TextInputFormatter 어댑터CoreInputFormatter
적용 값TextEditingValue의 텍스트·선택이전·새 String
재작성 후 선택기존 선택 offset을 새 길이에 클램핑실제 DOM 캐럿 보정은 입력 컴포넌트가 담당
공유 변환CoreInputFormatters에 위임같은 CoreInputFormatters에 위임