NumberTicker
숫자가 부드럽게 변경되는 애니메이션 숫자 표시 컴포넌트
목차
숫자가 부드럽게 보간되며 변경되는 애니메이션 효과를 가진 숫자 표시 컴포넌트입니다. 통계, 카운터, 대시보드 수치 표시에 적합합니다.
기본 사용법 (Basic Usage)#
// 기본 숫자 틱커
NumberTicker(value: 1234)
// 커스텀 포맷터 (통화)
NumberTicker(
value: 98765,
formatter: (n) => '\$${n.toStringAsFixed(2)}',
numberTickerStyle: CoreNumberTickerStyle(
duration: Duration(milliseconds: 800),
),
)
// 커스텀 빌더 (완전한 표시 제어)
NumberTicker.builder(
value: score,
builder: (context, value, child) {
return Text('${value.toInt()} points').headlineSmall.onSurface;
},
)
// 기본 숫자 틱커
NumberTicker(value: 1234)
// 커스텀 포맷터 (통화)
NumberTicker(
value: 98765,
formatter: (n) => '\$${n.toStringAsFixed(2)}',
numberTickerStyle: CoreNumberTickerStyle(
duration: Duration(milliseconds: 800),
),
)
// 커스텀 빌더 (완전한 표시 제어)
NumberTicker.builder(
value: score,
builder: (context, value, child) {
return Text('${value.toInt()} points').headlineSmall.onSurface;
},
)
빠른 오버라이드 (Chain)#
이미 만든 NumberTicker 인스턴스에 스타일을 빠르게 덧붙이고 싶다면 withStyle 체인을 쓸 수 있습니다. 생성자의 style 슬롯 인자와 동일하게 동작하지만, 이미 구성된 위젯 위에서 바로 이어 쓸 수 있습니다.
class NumberTickerChainExample extends StatefulWidget {
const NumberTickerChainExample({super.key});
@override
State<NumberTickerChainExample> createState() => _NumberTickerChainExampleState();
}
class _NumberTickerChainExampleState extends State<NumberTickerChainExample> {
@override
Widget build(BuildContext context) {
return NumberTicker(
value: 1234,
initialValue: 0,
).withStyle(
const CoreNumberTickerStyle(
duration: Duration(milliseconds: CoreDuration.slow),
textStyle: CoreTextStyle.token(
CoreTextStyles.headlineSmall,
color: CoreColor.token(CoreColors.primary),
),
),
);
}
}
class NumberTickerChainExample extends StatefulComponent {
const NumberTickerChainExample({super.key});
@override
State<NumberTickerChainExample> createState() => _NumberTickerChainExampleState();
}
class _NumberTickerChainExampleState extends State<NumberTickerChainExample> {
@override
Component build(BuildContext context) {
return NumberTicker(value: 1234, initialValue: 0).withStyle(
const CoreNumberTickerStyle(
duration: Duration(milliseconds: CoreDuration.slow),
textStyle: CoreTextStyle.token(
CoreTextStyles.headlineSmall,
color: CoreColor.token(CoreColors.primary),
),
),
);
}
}
Live Preview#
코드 보기
class NumberTickerDefaultExample extends StatefulComponent {
const NumberTickerDefaultExample({super.key});
@override
State<NumberTickerDefaultExample> createState() =>
_NumberTickerDefaultExampleState();
}
class _NumberTickerDefaultExampleState
extends State<NumberTickerDefaultExample> {
@override
Component build(BuildContext context) {
return NumberTicker(value: 1234, initialValue: 0);
}
}
class NumberTickerDefaultExample extends StatefulWidget {
const NumberTickerDefaultExample({super.key});
@override
State<NumberTickerDefaultExample> createState() =>
_NumberTickerDefaultExampleState();
}
class _NumberTickerDefaultExampleState
extends State<NumberTickerDefaultExample> {
@override
Widget build(BuildContext context) {
return NumberTicker(
value: 1234,
initialValue: 0,
);
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 대시보드에서 실시간으로 변하는 수치를 시각적으로 강조하고 싶을 때
- 랜딩 페이지에서 사용자 수, 매출 등 인상적인 통계 수치를 애니메이션으로 표시할 때
- 게임이나 포인트 시스템에서 점수 변화를 생동감 있게 표현할 때
대신 다른 컴포넌트를 사용하세요:
Countdown: 남은 시간을 초 단위로 표시할 때Progress: 완료율이나 진행률을 바 형태로 표시할 때Text: 단순 숫자 표시로 충분하고 애니메이션이 필요 없을 때
Props / Parameters#
NumberTicker는 두 생성자를 갖습니다 — 포맷된 텍스트를 그리는 기본 생성자와,
표시를 완전히 제어하는 NumberTicker.builder. 두 표시 경로는 상호 배타적이라
formatter는 기본 생성자에만, builder / child는
.builder에만 있습니다.
W는 빌더가 반환하는 콘텐츠 타입이며 Flutter의 Widget, Web의 Component입니다.
NumberTicker (기본 생성자)#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
value | num | 필수 | 애니메이션할 대상 숫자 값 |
formatter |
String Function(num)? |
null |
커스텀 숫자 포맷 함수. 같은 표기가 필요하면 명시 |
initialValue |
num? |
null |
초기 애니메이션 시작 값 |
curve |
double Function(double)? |
null |
사용자 정의 이징 함수. 입력은 0–1 진행 비율 |
numberTickerStyle |
CoreNumberTickerStyle? |
null |
chrome 슬롯 (아래 표) |
NumberTicker.builder#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
value | num | 필수 | 애니메이션할 대상 숫자 값 |
builder |
W Function(BuildContext, num, W?) |
필수 | 보간된 값으로 표시를 직접 구성 |
child |
W? |
null |
빌더에 최적화용으로 전달되는 자식 |
initialValue |
num? |
null |
초기 애니메이션 시작 값 |
curve |
double Function(double)? |
null |
사용자 정의 이징 함수. 입력은 0–1 진행 비율 |
numberTickerStyle |
CoreNumberTickerStyle? |
null |
chrome 슬롯 (아래 표) |
CoreNumberTickerStyle 필드#
| 필드 | 타입 | 설명 |
|---|---|---|
duration |
Duration? |
Value interpolation animation duration override. |
textStyle |
CoreTextStyle? |
Text style override (applied to the formatted numeric child). Includes the display text colour via
CoreTextStyle.color
.
|
동작 스펙 (Behavior)#
애니메이션#
value가 변경되면 이전 값에서 새 값으로 부드럽게 보간- 기본 500ms ease-in-out 커브
- 애니메이션 중 새 값이 들어오면 현재 값에서 새 값으로 전환
사용 가이드라인 (Usage Guidelines)#
값 변경을 보간하는 통계 숫자에는 NumberTicker를 사용합니다. 숫자의 단일 단계 교체는 Countdown, 제목·설명·변화량까지 묶은 지표 카드는 Stat를 선택합니다. 두 플랫폼에 같은 소수 자릿수가 필요하면 formatter를 명시하세요. NumberTicker 자체는 시간을 세는 타이머가 아닙니다.
Do#
적절한 포맷터 사용
NumberTicker(
value: revenue,
formatter: (n) => '\$${n.toStringAsFixed(0)}',
)
숫자의 맥락에 맞는 포맷터를 제공하세요.
Don't#
너무 빈번한 값 변경 금지
매 프레임마다 값을 변경하면 애니메이션이 부자연스럽습니다. 의미 있는 변경만 전달하세요.
접근성 (Accessibility)#
NumberTicker 는 값이 애니메이션되는 동안 텍스트를 프레임마다 다시 그립니다.
시맨틱은 어느 쪽 플랫폼에도 붙어 있지 않아 아래 다섯 축 대부분이 "없음" 이고,
그게 현재 구현의 사실입니다.
역할#
-
Flutter — 없음.
number_ticker.dart전체에Semantics가 한 번도 나오지 않습니다. -
Web — 없음. 루트
<span>은role도aria-*도 emit 하지 않고, caller 가 넘긴attributes만 그대로 통과시킵니다.
키보드#
없음. 양 플랫폼 모두 포커스를 받지 않습니다(Web 루트에 tabindex 가 붙지 않습니다).
Web 생성자가 onKeyDown / onKeyUp 을 받지만 루트가 포커스 대상이 아니라 키
이벤트가 도달하지 않습니다.
스크린 리더#
-
이름 — 포맷된 숫자 텍스트 자체입니다. Flutter 는
Text(formatter(값)), Web 은<span>의 텍스트 노드이고 별도 라벨은 없습니다. - 통보 — 없습니다. 어느 쪽도 live region 이 아니라, 값이 올라가는 동안 텍스트가 바뀌어도 리더는 아무것도 알리지 않습니다.
-
필요하면 호출부가 붙일 수 있습니다 — Web 은
attributes: {'role': 'status'}(루트가 callerattributes를 그대로 통과시킵니다), Flutter 는 위젯을Semantics(liveRegion: true)로 감쌉니다.
포커스 관리#
없음. 오버레이가 아니고 포커스 가능 요소도 아니라 진입·이탈·트랩이 모두 해당되지 않습니다.
알려진 제약#
-
builder모드에서는 Web 의attributes경로 자체가 사라집니다.builder가 있으면 상태가 builder 결과를 그대로 반환하고 루트<span>을 만들지 않아id/classes/css/attributes/ 이벤트가 전부 버려집니다. 대안: builder 안에서 직접attributes를 단 요소를 반환합니다. -
Web 의 값 보간에는 '동작 줄이기' 설정이 닿지 않습니다. Web 은 CSS transition 이
아니라 16 ms
Timer.periodic으로 보간하고 그 duration 은 CSS 변수를 거치지 않습니다. Flutter 는resolveMotionDuration을 거쳐 기본 500 ms(전환 tier)가 0 이 되어 곧바로 최종값으로 점프합니다. 대안: 호출부가 그 설정을 읽어numberTickerStyle.duration을 직접 줄이는 방법뿐입니다. - 애니메이션 중에는 텍스트가 프레임마다 바뀝니다. 위 방법으로 live region 을 붙이면 최종값 한 번이 아니라 보간되는 중간값까지 통보될 수 있습니다. 대안: 애니메이션과 별개인 노드에 최종값만 한 번 실어 알립니다.
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| 애니메이션 | AnimatedValueBuilder |
Timer.periodic (~60fps) + ease-in-out |
현재 fallback 제약과 우회#
현재 formatter 생략 경로는 Flutter가 정수로, Web이 target이 비정수면 소수 2자리로 표시합니다. 승인된 포맷 정책 차이가 아니라 같은 숫자의 표기가 갈리는 코드 갭이며 같은 소수 자릿수가 필요하면 formatter 또는 .builder를 명시하세요. 기본 이징도 Flutter의 CoreEasing.move와 Web의 로컬 _easeInOut 경로가 다르므로 동일 전환이 필요한 호출은 curve를 명시합니다.
이징 함수의 입력은 0–1 진행 비율, 스타일 duration은 Duration이며 숫자의 단위는 호출자가 formatter/콘텐츠에서 정합니다.