Countdown | CoUI
LogoCoUI

Countdown

숫자 값을 표시하는 카운트다운 디스플레이 컴포넌트

Countdown#

숫자 값(0-99)을 표시하는 디스플레이 컴포넌트입니다. 값이 변경될 때 전환 애니메이션이 적용됩니다. 타이머 로직은 부모 컴포넌트에서 관리합니다.

Live Preview#

사용 시기 (When to Use)#

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

  • 카운트다운 숫자를 크게 표시할 때
  • 시간 단위별 숫자를 개별 표시할 때 (시:분:초 각각)
  • 세션 타임아웃 등 남은 시간을 숫자로 표시할 때

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

  • NumberTicker: 일반적인 숫자 애니메이션이 필요할 때
  • Progress: 남은 시간을 비율로 시각화할 때

기본 사용법 (Basic Usage)#

// 기본 카운트다운
Countdown(value: 42)

// 크기 조절
Countdown(
  value: 42,
  countdownStyle: CoreCountdownStyle(fontSize: 64),
)

// 시:분:초 조합
Row(
  children: [
    Countdown(value: hours),
    Text(':'),
    Countdown(value: minutes),
    Text(':'),
    Countdown(value: seconds),
  ],
)
// 기본 카운트다운
Countdown(value: 42)

// 크기 조절
Countdown(
  value: 42,
  countdownStyle: CoreCountdownStyle(fontSize: 64),
)

// 시:분:초 조합
div([
  Countdown(value: hours),
  Text(':'),
  Countdown(value: minutes),
  Text(':'),
  Countdown(value: seconds),
], classes: 'flex items-center gap-${CoreSpace.scale.space4}')

빠른 오버라이드 (Chain)#

이미 만든 Countdown 인스턴스에 스타일을 빠르게 덧붙이고 싶다면 withStyle 체인을 쓸 수 있습니다. 생성자의 style 슬롯 인자와 동일하게 동작하지만, 이미 구성된 위젯 위에서 바로 이어 쓸 수 있습니다.

class CountdownChainExample extends StatefulWidget {
  const CountdownChainExample({super.key});

  @override
  State<CountdownChainExample> createState() => _CountdownChainExampleState();
}

class _CountdownChainExampleState extends State<CountdownChainExample> {
  @override
  Widget build(BuildContext context) {
    return const Countdown(value: 42).withStyle(
      const CoreCountdownStyle(
        fontSize: CoreFontSize.s48,
        color: CoreColor.token(CoreColors.primary),
        animationDuration: Duration(milliseconds: CoreDuration.moderate),
      ),
    );
  }
}
class CountdownChainExample extends StatefulComponent {
  const CountdownChainExample({super.key});

  @override
  State<CountdownChainExample> createState() => _CountdownChainExampleState();
}

class _CountdownChainExampleState extends State<CountdownChainExample> {
  @override
  Component build(BuildContext context) {
    return Countdown(value: 42).withStyle(
      const CoreCountdownStyle(
        fontSize: CoreFontSize.s48,
        color: CoreColor.token(CoreColors.primary),
        animationDuration: Duration(milliseconds: CoreDuration.moderate),
      ),
    );
  }
}

Props / Parameters#

속성타입기본값설명
value int 필수 표시할 숫자 값 (min~max 로 클램핑)
minint0클램핑 하한
maxint99클램핑 상한
countdownStyle CoreCountdownStyle? null 폰트/색상/애니메이션 등 chrome override

스타일 시스템 (Style System)#

모든 chrome / 애니메이션 override 는 countdownStyle 슬롯 하나로 흐릅니다.

CoreCountdownStyle 필드#

필드타입설명
fontSize double? Display font size override (logical px). No default* , and it must not get one. Both resolvers fold this flat field into an overlay that merges ON TOP of [defaultLabelStyle] ⊕ [labelStyle], so whatever sits here outranks both — and the resting font size is already stated, by the headlineLarge role inside [defaultLabelStyle]. A static const would therefore (a) make the [labelStyle] slot's own fontSize unreachable and (b) replace a token role with a raw scalar, which on Flutter also drops out of the text-scaling axis the role is materialised through. The resolvers rely on the overlay being null-only to stay a no-op; on Web a non-null one turns emitTypography from "role class, no inline" into a permanent inline font-size that beats the class.
color CoreColor? Display text colour override. No default* — same overlay position as [fontSize], and the resting colour is likewise already stated: onSurface , inside [defaultLabelStyle]. A default here would win over labelStyle.color , inverting the documented precedence, and would emit a standing inline color on Web where only the token class renders today.
animationDuration Duration? Digit transition / fade animation duration override. NOT the total countdown length — that is a semantic widget parameter (see contract value ).
labelStyle CoreTextStyle? Label text style override (applied to the numeric child).

fontSize / colorlabelStyle 슬롯 위에 덧씌워지므로 flat 필드가 이깁니다.

Resolve chain#

CoreCountdownStyle.defaultX (static const, 단일 출처)
  → CoreCountdownTheme.style                      // 프로젝트 공통
  → widget.countdownStyle                         // 인스턴스별

동작 스펙 (Behavior)#

인터랙션#

  • Countdown은 표시 전용 컴포넌트입니다. 인터랙션 없음.
  • 부모 컴포넌트에서 Timer로 value를 업데이트해야 합니다.

애니메이션#

  • Flutter: AnimatedSwitcher + FadeTransition으로 값 변경 시 페이드 전환
  • Web: CSS transition으로 값 변경 시 페이드 전환
  • 양쪽 모두 countdownStyle.animationDuration(기본 CoreDuration.fast 150ms)을 씁니다

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

✅ Do#

시:분:초처럼 자릿수 단위로 인스턴스를 나눠서 표시

Row(
  children: [
    Countdown(value: hours),
    Text(':'),
    Countdown(value: minutes),
    Text(':'),
    Countdown(value: seconds),
  ],
)

Countdownmin(기본 0)~max(기본 99) 범위의 두 자릿수 값 하나만 표시하도록 설계되어 있습니다 — 더 큰 시간 단위를 표현하려면 단위별로 인스턴스를 나눠야 합니다.


❌ Don't#

Countdown 내부에 타이머 로직이 있다고 기대하지 않기

// ❌ value를 한 번만 넘기고 자동으로 줄어들 거라 기대
Countdown(value: 60)

Countdown은 표시 전용 컴포넌트로 인터랙션도 내부 타이머도 없습니다 — 부모 컴포넌트가 Timervalue를 직접 갱신해야 카운트다운이 동작합니다.

접근성 (Accessibility)#

스크린 리더#

  • Flutter: Semantics(liveRegion: true)로 값 변경 알림
  • Web: aria-live="polite" + aria-label로 값 변경 알림

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

항목FlutterWeb
클래스명CountdownCountdown
애니메이션AnimatedSwitcher fadeCSS transition
숫자 정렬FontFeature.tabularFigures()tabular-nums
  • NumberTicker: 일반 숫자 카운터 애니메이션
  • Stat: 레이블 + 값 형식의 통계 표시