rolly 0.1.0
rolly: ^0.1.0 copied to clipboard
Numbers and text that animate the way they do on iOS. Digits roll, the line reflows, glyphs are born and die in place. No dependencies beyond Flutter's own.
rolly
Numbers and text that animate the way they do on iOS.
Digits roll, the line reflows, glyphs are born and die in place.
Install #
dependencies:
rolly: ^0.1.0
Use #
A number that animates whenever it changes:
NumericText(
value: total,
format: const NumericTextFormat.currency(),
style: const TextStyle(fontSize: 64, fontWeight: FontWeight.w700),
)
The same transition over any string:
RollingText(
status, // 'Connecting…' -> 'Connected'
style: const TextStyle(fontSize: 28, fontWeight: FontWeight.w600),
)
Both behave like Text in layout, take the ambient DefaultTextStyle, and honour the platform's
reduced-motion setting. Nothing else is required — no controller, no ticker, no builder.
Formats #
const NumericTextFormat.integer() // 1,234
const NumericTextFormat.decimal(maximumFractionDigits: 2) // 1,234.5
const NumericTextFormat.currency(symbol: r'$') // $1,234.50
const NumericTextFormat.percent() // 12.5%
Separators are arguments, not locale lookups, so any convention works:
const NumericTextFormat.decimal(
groupSeparator: '.',
decimalSeparator: ',',
minimumFractionDigits: 2,
) // 1.234,50
And for real locale data — per-country currency symbols, compact notation, anything else —
NumericTextFormat.custom takes a formatting function, intl's included:
final de = NumberFormat.currency(locale: 'de_DE');
NumericTextFormat.custom(
de.format,
groupSeparator: de.symbols.GROUP_SEP,
decimalSeparator: de.symbols.DECIMAL_SEP,
minusSign: de.symbols.MINUS_SIGN,
);
That is the reason this package depends on nothing beyond what Flutter itself ships (its one
dependency, characters, is already pinned by the Flutter SDK): intl is yours to add if you want
it, not a version constraint you inherit.
Options #
Both widgets take:
style |
merged over DefaultTextStyle, as Text does |
duration |
320 ms by default; other values stretch every channel by the same factor |
direction |
automatic reads it from the change itself, or force up / down |
reduceMotion |
system, always (snap), or never |
textAlign |
which edge the line is pinned to while its width changes |
edgeFade |
softens the top and bottom of the rolling area |
NumericText adds value, format and fractionColor — a second colour for the decimal mark
and everything after it, for de-emphasising cents. RollingText adds split, which animates the
line by character or by word.
How it works #
- Four channels, two springs. A glyph moves, grows, fades and comes into focus at once, on different clocks. Position gets a bouncy spring that overshoots; scale and alpha share a critically damped one; blur is slowest of all, which is most of what separates this from a hard digit flip.
- A column holds two glyphs, not a drum.
4 → 6crossfades a settled 4 with a settled 6. Nothing spins through 5. - Births and deaths are not rolls. When
999becomes1,000the new digit appears in place, small and blurred, and grows. Surviving glyphs slide to their new positions; dying ones fade where they stand rather than riding the layout inward. - The cascade is a fixed budget. Columns start left to right across 150 ms total, however many of them change, so a long number does not take proportionally longer.
- Interruption is free. A transition is never cancelled or retargeted. A glyph stores its starting conditions and the instant it began, so a value changing mid-flight costs nothing to absorb — hold the stepper down and columns degrade into a soft pair, then recover.
Text works the same way: the model operates on columns, and a column is whatever identifies a glyph. Numbers are keyed from their own structure; a string is diffed against the one on screen, so matched characters reflow, replaced ones roll, and the rest are born or die.
doc/animation_model.md has the full model, constant by constant.
Example #
example/ is a playground covering every feature: each format, a stepper that accelerates while
held, a scripted stress run, the text transition, and live controls for duration, size, direction,
reduced motion and edge fade.
cd example && flutter run
Built with AI #
This package — the implementation, its tests, the documentation and the example app — was generated with AI assistance (Claude), then reviewed and tested before release. The test suite is the honest description of what is verified: 100 tests covering the curves, the keying, the engine's behaviour under interruption, and full-sequence integrity checks.
Credits #
The transition is modelled on SwiftUI's .contentTransition(.numericText()). The animation
constants were derived from the open-source measurements published by
react-native-numeric-text, which
decomposed the original transition frame by frame.