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

NumberInput

버튼으로 올리고 내리는 한 줄 숫자 입력 — 인원 · 수량처럼 작은 수

목차

NumberInput 은 [ − ] 값 [ + ] 한 줄로 숫자를 올리고 내리는 입력입니다. 인원 · 수량처럼 작은 수를 고를 때 씁니다. 값은 직접 입력할 수도 있고, 두 버튼과 위 · 아래 화살표 키가 step 만큼 움직입니다. 값은 늘 min ~ max 안에 머물고, 끝에 닿으면 그 방향 버튼이 비활성이 됩니다.

필드는 TextField 를, 양 끝은 Button 을 합성해 그립니다. 테두리 · 바탕 · hover · 포커스 · 오류 · 라벨 · 설명은 TextField 와 같은 값이라 나란히 놓아도 같은 말을 합니다.

기본 사용법 (Basic Usage)#

NumberInput(
  label: '인원',
  description: '최대 8명까지 예약할 수 있어요',
  initialValue: 2,
  min: 1,
  max: 8,
  size: CoreNumberInputSize.lg,
  onChanged: (value) => print(value),
)
NumberInput(
  label: '인원',
  description: '최대 8명까지 예약할 수 있어요',
  initialValue: 2,
  min: 1,
  max: 8,
  size: CoreNumberInputSize.lg,
  onChanged: (value) => print(value),
)

Live Preview#

구조 (Anatomy)#

label                                  // 선택
┌────────────────────────────────┐
│ [ − ]        value        [ + ] │     // TextField(centred) · Button(plain · square) × 2
└────────────────────────────────┘
description                            // 선택
크기필드버튼모서리
sm90 × 32248
md114 × 403210
lg138 × 484012

Figma: CoUI 라이브러리 ❖ NumberInput 페이지.

사용 시기 (When to Use)#

이 컴포넌트를 사용하세요:

  • 인원 · 수량 · 개수처럼 작은 범위의 수를 한두 번 눌러 바꿀 때
  • 값의 범위(min ~ max)가 정해져 있어 넘어가지 않게 막아야 할 때

대신 다른 컴포넌트를 사용하세요:

  • TextField: 자릿수가 많은 숫자나 단위가 붙는 값을 자유롭게 입력할 때
  • Slider: 범위 안에서 대략적인 값을 끌어서 고를 때
  • Stepper: 숫자 입력이 아니라 단계 이동을 보여 줄 때

Props / Parameters#

속성타입기본값설명
value double? null 현재 값(제어). null 이면 initialValue 에서 시작합니다
initialValue double? null 시작 값. null 이면 min, 그것도 없으면 0
onChanged ValueChanged<double>? null 값이 바뀔 때 — 버튼 · 키 · 직접 입력
min double? null 가장 작은 값. 닿으면 − 버튼이 비활성
max double? null 가장 큰 값. 닿으면 + 버튼이 비활성
step double 1 버튼 한 번 · 화살표 한 번에 움직이는 양. 양수
variant CoreNumberInputVariant defaultVariant defaultVariant · filled · error
size CoreNumberInputSize md sm · md · lg
enabled bool? null null 이면 켜짐
labelString?null필드 위 이름
description String? null 필드 아래 도움말
numberInputStyle CoreNumberInputStyle? null 인스턴스 스타일

CoreNumberInputStyle 필드#

필드타입설명
width double? The whole field's width (logical px), border to border. null → the size row's width.
buttonStyle CoreButtonStyle? Chrome of the two step buttons, forwarded raw to each Button.
fieldStyle CoreTextFieldStyle? Chrome of the composed TextField, forwarded raw.

스타일 시스템 (Style System)#

CoreNumberInputStyle 은 두 가지 크기(width · buttonSize)와 두 개의 중첩 슬롯(fieldStyle · buttonStyle)만 가집니다. 필드의 테두리 · 바탕 · 포커스는 TextField 의 스타일이 정하고, fieldStyle 이 그대로 넘겨집니다.

Resolve chain#

design system default (defaultsBySize[size])
  → CoreNumberInputTheme.style
  → CoreNumberInputTheme.variantStyles[variant]
  → widget.numberInputStyle

테마로 덮어쓰기#

CoreNumberInputTheme(
  variantStyles: {
    CoreNumberInputVariant.error: CoreNumberInputStyle(
      fieldStyle: CoreTextFieldStyle(errorBorderColor: ...),
    ),
  },
)

변형 (Variants)#

variant: defaultVariant#

표면 바탕에 outline-variant 테두리. hover 는 outline, 포커스는 primary 1.5.

variant: filled#

surface-container 바탕. 테두리가 바탕과 같은 색이라 면으로 읽힙니다.

variant: error#

테두리가 오류 색입니다. 굵기는 쉴 때 1, 포커스에서만 1.5 입니다.

동작 스펙 (Behavior)#

인터랙션#

  • − · + 버튼은 step 만큼 값을 움직이고 onChanged 를 부릅니다.
  • 위 · 아래 화살표는 step, Page Up · Page Down 은 step × 10 만큼 움직입니다.
  • 값 칸에 직접 입력할 수 있습니다. 범위 안의 숫자는 입력하는 즉시 반영하고, 범위 밖이거나 아직 숫자가 아닌 입력(-)은 포커스가 빠지거나 Enter 를 누를 때 확정합니다 — 범위 밖은 가장 가까운 끝으로, 숫자가 아니면 이전 값으로 돌아갑니다.
  • 값은 min ~ max 를 넘지 않습니다.

상태 전환#

  • 끝에 닿으면 그 방향 버튼이 비활성이 되고 반대쪽 버튼은 그대로 눌립니다.
  • enabled: false 이면 필드와 두 버튼이 모두 비활성이고 키도 반응하지 않습니다.

소수 값#

step 이 소수여도 0.1 + 0.2 가 0.3 으로 보이도록 step 과 현재 값의 소수 자릿수에서 반올림합니다.

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

Do#

  • 범위가 정해진 값에는 min · max 를 함께 줍니다 — 끝의 버튼이 자동으로 비활성이 됩니다.
  • 라벨을 붙여 무엇의 수인지 말합니다. 단위는 description 에 적습니다.

Don't#

  • 자릿수가 많은 값(전화번호 · 금액)에 쓰지 않습니다 — TextField 를 씁니다.
  • step 에 0 이나 음수를 주지 않습니다.

접근성 (Accessibility)#

역할#

  • Web 은 값 칸이 type="number" 입력이라 브라우저가 spinbutton 으로 알리고 min · max · 현재 값을 읽습니다.
  • Flutter 는 값과 함께 늘리기 · 줄이기 동작을 시맨틱에 싣습니다.
  • 두 버튼의 접근성 이름은 로케일의 "줄이기" · "늘리기" 입니다(numberInputDecrease · numberInputIncrease).

키보드#

  • 위 · 아래 화살표: step 만큼 증감.
  • Page Up · Page Down: step × 10 만큼 증감.
  • Enter: 입력한 값을 확정.
  • Tab: 값 칸과 두 버튼을 차례로 지납니다.

스크린 리더#

  • 값 칸은 현재 값과 범위를, 두 버튼은 "줄이기" · "늘리기" 이름을 읽어 줍니다.
  • Flutter 는 늘리기 · 줄이기 동작을 시맨틱에 실어, 보조 기술의 스와이프로 값을 움직일 수 있습니다.

스크린 리더#

  • 값 칸은 현재 값과 범위를, 두 버튼은 "줄이기" · "늘리기" 이름을 읽어 줍니다.
  • Flutter 는 늘리기 · 줄이기 동작을 시맨틱에 실어, 보조 기술의 스와이프로 값을 움직일 수 있습니다.

스크린 리더#

  • 값 칸은 현재 값과 범위를, 두 버튼은 "줄이기" · "늘리기" 이름을 읽어 줍니다.
  • Flutter 는 늘리기 · 줄이기 동작을 시맨틱에 실어, 보조 기술의 스와이프로 값을 움직일 수 있습니다.

포커스 관리#

바깥 포커스 링은 키보드 포커스일 때만 그립니다. 마우스 · 터치로 값 칸을 누르면 테두리가 primary 1.5 로만 바뀝니다.

알려진 제약#

  • 비활성 버튼은 포커스를 받지 않습니다. 끝에 닿아 버튼이 비활성이 되는 순간 포커스가 그 버튼에 있었다면 값 칸으로 돌아오지 않습니다.

전역 축(동작 줄이기 · 고대비 · 색 강제 모드 · 시맨틱 역할 · 최소 터치 타겟)은 접근성 페이지가 한 곳에서 다룹니다.

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

항목FlutterWeb
값 칸 글자 입력만 받는 TextField, 0-9 . + - 만 통과 type="number" 입력 — 브라우저가 문자를 거릅니다
키 처리 포커스 부모에서 KeyEvent 를 받음 래퍼 <div> 의 keydown
확정 시점포커스가 빠지거나 onSubmittedfocusout 이나 Enter
접근성 역할Semantics 의 늘리기 · 줄이기 동작입력의 spinbutton 역할
캐럿 합성한 TextField 의 기본값 그대로 — 굵기 2 · 모서리 2 · primary , 값 바로 뒤(간격 0), 높이는 값 줄 상자( bodyMedium 14 × 1.5 = 21). iOS · macOS 는 프레임워크가 줄 상자보다 2 높게 그립니다 브라우저가 그리는 기본 캐럿 — CSS 로 정할 수 있는 것은 색(caret-color: primary)뿐이라 굵기 · 모서리 · 높이는 브라우저 몫입니다

이름 · 기본값 · 순서는 두 플랫폼이 같습니다.

  • TextField: 필드의 테두리 · 포커스 · 오류 기준
  • Button: 양 끝 버튼(plain · square)
  • Stepper: 단계 이동

조합 예제#

NumberInput(
  label: '수량',
  variant: CoreNumberInputVariant.filled,
  size: CoreNumberInputSize.sm,
  initialValue: 1,
  min: 1,
)