Motion Counter

A production-grade Flutter package for animating numeric value changes with physics-driven, per-digit transitions.

Each digit behaves as an independent animated entity. Only the digits that actually change will animate, while unchanged digits remain perfectly still.


Animation Styles Preview

Odometer Spring Slot Mechanical
Odometer Style Spring Style Slot Style Mechanical Style

Features

  • Physics-Based Transitions: Individual digit controllers ensure smooth, realistic animations.
  • Cascading Stagger: Optional cascade delays where animations roll from right-to-left or left-to-right.
  • Rich Formatting Options: Integrates with the intl package to support grouping separators, decimals, prefixes, and suffixes.
  • Fixed-Width Padding (minDigits): Pad numbers with leading zeros (ideal for countdowns, timers, or fixed-width scoreboards).
  • Dynamic Animation Styles:
    • odometer: Classic mechanical rolling digits.
    • spring: Elastic slide animation with physics overshoot and bounce.
    • slot: Rapid multi-revolution slot machine spin.
    • mechanical: Sharp snap-into-place transitions.

Installation

Add motion_counter to your pubspec.yaml dependencies:

dependencies:
  motion_counter: ^1.0.1

Usage

Basic Usage

Simply pass a num value (integer or double) to animate transitions whenever the value updates:

import 'package:motion_counter/motion_counter.dart';

MotionCounter(
  value: _counterValue,
)

Choosing Animation Styles

Use named constructors to switch between transition physics and curves:

// Rolling mechanical wheels (Default)
MotionCounter.odometer(value: 1234)

// Bounce & overshoot spring transition
MotionCounter.spring(value: 1234)

// Spin multiple times like slot machine reels
MotionCounter.slot(value: 1234)

// Snap industrial style
MotionCounter.mechanical(value: 1234)

Zero-Padding / Fixed Digit Width (minDigits)

Define a minDigits length so the integer part always maintains a fixed number of digits, padded with leading zeros (perfect for countdowns):

MotionCounter.odometer(
  value: 42,
  minDigits: 5, // Renders as "00042"
)

Formatting & Specialized Factories

MotionCounter includes built-in factories to quickly format currencies, percentages, and large numbers:

// Currency (renders e.g. $1,234.56)
MotionCounter.currency(
  value: 1234.56,
  symbol: '\$',
  decimalDigits: 2,
)

// Percentages (renders e.g. 97.4%)
MotionCounter.percent(
  value: 97.4,
  decimalDigits: 1,
)

// Compact notation (renders e.g. 1.5M or 2.3K)
MotionCounter.compact(
  value: 1500000,
)

Staggered Animations

Provide a cascade effect across digit updates (e.g. rightmost digits animate first) by setting a stagger delay:

MotionCounter.odometer(
  value: _value,
  stagger: const Duration(milliseconds: 50),
)

Additional Information

For custom formatting, you can pass a custom NumberFormat from the intl package:

MotionCounter(
  value: 1234.56,
  numberFormat: NumberFormat.scientificPattern(),
)

Libraries

motion_counter