kinetic_text library
Kinetic typography for Flutter — per-letter, per-word and per-line text motion from one shaped paragraph.
Start with KineticText (a label with effects), TextMorph (a label that rewrites itself in place) and TickerText (a figure whose digits turn like an odometer). Effects: Rise, Glint, Blur, Typewriter, Bounce, Squeeze, Outline, Scramble, Shimmer, Sparkle, Float, Wave, Pulse, Spotlight, Flicker, GradientInk, Tint, Highlight, Underline. Build your own on TextEffect over a ShapedText.
Classes
- Blur
- Units resolve out of a blur, fading in. Each blurred unit costs a layer while its sigma is non-zero, so this is a word- or line-unit effect; on a paragraph of letters it would be a layer per glyph per frame.
- Bounce
- Letters drop in from above and land with one soft overshoot — the settle dips a hair below the line and squashes, then stands. Playful where Rise is quiet; keep it for a headline, not a list.
- CrossfadeMorph
- A plain cross-fade. See MorphStyle.crossfade.
- Flicker
- A few letters stutter dark and recover, like a sign with a loose contact. For error and offline states: short, rare, one or two letters at a time.
- Float
- A slow, tiny vertical drift, each unit a little behind the last — the idle of a hero line that should feel alive rather than pinned. The default amplitude is a pixel and a half; past three or four it becomes a wave, and a wave is a different, louder thing.
- FoldMorph
- The departure board. See MorphStyle.fold.
- Glint
- A sheen reveal: each unit grows from scaleFrom and fades in while a glint of sheen rides its wavefront — a soft light that draws the word on. The title idiom: one steady sweep, start to end.
- GradientInk
-
A gradient in place of the ink on a slice — a rainbow on one word, a
two-tone across a headline — optionally flowing along the reading
direction. The gradient is stretched over the slice's own bounds, so
centerStart → centerEndspans exactly the recoloured word. - Highlight
- A rounded marker behind a slice — a highlighter stroke — that sweeps in from the reading start as the host's progress plays. A wrapped slice gets one stroke per line, the second starting a beat after the first.
- InkPass
- A recolouring of glyph INK — painted inside the text's own layer with a blend mode that only touches pixels the glyphs already own. BlendMode.srcIn replaces the ink (a gradient fill); BlendMode.srcATop tints it (a sheen that keeps the base colour showing through).
- KineticController
- Drives a KineticText's one-shot progress from outside: play, replay, finish, reverse. Attach one to a widget through KineticText.controller.
- KineticEase
- The named easings every effect defaults to — so re-tuning the library's "feel" is one edit, and nothing moves on an anonymous curve.
- KineticPainter
- A CustomPainter over a ShapedText — the compositor as a painter, for a host that owns its own layout. KineticText uses a render object with the same pipeline (plus intrinsics and a baseline); this is for custom hosts.
- KineticText
- Text with motion — shaped once, animated per letter, word or line.
- LineBand
- One line of a shaped paragraph — the band a unit's glyphs are clipped to while it moves, and where an underline sits.
- MorphDiff
- Which units of two texts are shared (a common prefix and suffix, by unit text) and which differ.
- MorphStyle
- How a TextMorph exchanges the letters that differ. The letters both texts share stay put (or glide to their new position); only the changed span leaves and arrives, in reading order.
- Outline
- A stroke sweeps around each letter's outline like a clock hand, then the fill rises inside it and the stroke lets go. The outline is the SAME paragraph laid out again with a stroked ink, so every join and ligature is traced exactly as it is set.
- Pulse
- A slow glow breathes through the letters' ink — for a "live" or "waiting" label that should hold attention without moving. One ink pass, no pose, no layer per letter.
- Rise
- The cascade: each unit rises into place from a little below (or any side), fading in, one after another along the reading order. The motion most list rows and labels want — short travel, a clean settle, no bounce.
- RollMorph
- The odometer. See MorphStyle.roll.
- Scramble
- Every letter shows a run of random glyphs, then locks onto the real one, in order — the terminal reveal. A stand-in glyph is set in the unit's own style and sits on its baseline, centred in its cell.
- ShapedText
- A paragraph shaped ONCE — one TextPainter for the whole text, so cursive joins, ligatures, kerning and bidi ordering are all resolved by the engine that knows how — plus the box of every unit inside it, so an effect can move, fade, scale or recolour a single cluster by clipping to its cell and re-drawing the same painter. That is what keeps an animated Kurdish or Arabic label joined: nothing is ever laid out as a separate string.
- SheenMorph
- Dissolve out and draw in under a travelling glint. See MorphStyle.sheen.
- Shimmer
- A soft band of light glides across the text in reading order while the letters under it lift a hair and settle — then the text rests until the next sweep. One clock drives both the sheen and the pop, so they never drift apart.
- SlideMorph
- Leave one way, arrive from the other. See MorphStyle.slide.
- Sparkle
- A few small four-point glints twinkling on the glyphs — each on its own phase, each resting most of its cycle, so at any moment one or two are alive. Few, small and slow is what keeps it a highlight rather than a firework: the defaults are tuned for a single word.
- Spotlight
- The Shimmer's band, turned into light: the text sits at dim and the letters under the band are full ink — the band itself is the sweep, the feather and the ease the shimmer has, so a Kurdish label is lit in its reading order. Optionally the band also draws a focus outline round the letters it passes, so the light reads as a focus ring travelling the word.
- Squeeze
- Letters start wide and flat and stretch upright as they land — a stamp pressed into the line. Uses the vertical squash, so the glyph's width is barely disturbed and neighbours are never crossed.
- StaggeredEffect
- A one-shot, staggered effect: the shared skeleton of every reveal. Subclasses implement poseUnit for one unit at its local eased progress.
- TextEffect
- One text effect. Effects are plain, immutable descriptions; the compositor hands each one the current TextFrame and the UnitSlice it is bound to, and the effect writes poses, ink passes and decor into the frame.
- TextFrame
- Everything the effects have decided for one painted frame: a pose per unit, the ink passes, and the decor to draw behind and over the glyphs. Built fresh by the compositor every frame and handed to each effect in order.
- TextMorph
- A label that rewrites itself in place: when text changes, the letters the two values share stay (gliding to their new position if the width moved), and the letters that differ leave and arrive in the chosen morph style. The box's width eases between the two widths underneath.
- TextRun
- A piece of a KineticText.rich label: text, an optional style override, and effects that apply to THIS run's units only — a rainbow on one word, a highlight under another.
- TickerText
-
A label whose characters turn like an odometer when its text changes —
the SwiftUI
.contentTransition(.numericText())model, on a real drum. - Tint
- A flat recolour of a slice's ink — cheaper than a second TextStyle and animatable from the outside (rebuild with a lerped colour). Not motion.
- Typewriter
- Units appear one at a time, hard-cut, with an optional cursor bar sitting after the last one — solid while typing, blinking once the line is done.
- Underline
- A stroke drawn on along the reading direction under (or through) a slice. Behind the ink for an underline, so descenders stay clean; over it for a strike, so it reads as crossing the letters out.
- UnitBox
-
One unit of a ShapedText: its code-unit range, its physical box, and the
line it sits on. Units are numbered in READING order (by text offset), so a
stagger over
indexreads first-to-last in every script, whilerectis physical — the leftmost box of an Arabic line is its LAST unit. - UnitPose
- Where one unit is on this frame, relative to its resting layout. Effects COMPOSE by accumulating into the same pose: a cascade adds a rise, a shimmer adds a scale bump, a float adds a drift.
- UnitSlice
- The units one effect acts on — the whole text, or one run of a rich label. Staggers count within the slice, so a cascade on one word starts at that word's first letter.
- Wave
- A rounded ripple runs through the line in reading order, each letter a beat behind the last. Louder than Float — a wave is a gesture, not an idle — so keep it to a word or a short hero line.
- WipeMorph
- The quiet swap. See MorphStyle.wipe.
Enums
- RollDirection
- Which way a TickerText turns when its text changes.
- StaggerOrder
- The order a stagger walks the units of a slice.
- TextUnit
- The grain of a piece of kinetic text — what a stagger counts, what a pose moves, what an ink pass paints.
- TickerAnchor
- Which physical edge a TickerText keys its character slots from.
- UnderlinePosition
- Where an Underline sits.
Functions
-
colorAlong(
List< Color> colors, double t) → Color -
Interpolate along a colour list at
t∈ 0..1. -
composeFrame(
{required ShapedText shaped, required List< (TextEffect, UnitSlice)> effects, required double progress, required double time, required bool reduced, required List<UnitPose> poses}) → TextFrame -
Build the TextFrame for one paint: reset
poses, let every bound effect write into it (skipping motion underreduced). -
paintFrame(
Canvas canvas, TextFrame frame, {Offset origin = Offset.zero}) → void -
Paint a composed
frame: decor behind, the glyph layer, decor over. -
paintGlyphs(
Canvas canvas, TextFrame frame, {Offset origin = Offset.zero}) → void -
The glyph layer for
frame: resting units in one draw, moved units one by one, then the ink passes — all inside one layer so the ink blends only with glyph pixels. The layer is a singlesaveLayeronly when something is posed or recoloured; a text at rest with no ink pass is one plain paint, so a finished reveal costs nothing more than a Text. -
paintInk(
Canvas canvas, TextFrame frame, InkPass pass, {Offset origin = Offset.zero}) → void - Draw one ink pass over the glyph layer: per unit, under that unit's own pose transform, so a recolour follows a moving letter. When none of the targeted units is posed the pass is ONE draw through one clip.
-
paintResting(
Canvas canvas, ShapedText shaped, Iterable< UnitBox> moved, {Offset origin = Offset.zero}) → void - Paint every unit that is at rest — and every whitespace between them — in ONE draw, with the moved units' cells cut out of the clip. Each cell is a DIFFERENCE clip, never a hole in an even-odd path: two adjacent cells that overlap by a kerned hair would otherwise cancel each other out and let a sliver of resting ink show through under a moving letter.
-
paintUnit(
Canvas canvas, ShapedText shaped, UnitBox unit, UnitPose pose, {Offset origin = Offset.zero}) → void - Paints one unit of a ShapedText in a UnitPose.
-
staggered(
double progress, int rank, int n, double stagger) → double -
Where unit
rankofnis in its own local timeline when the whole slice is atprogress: units start one after another across the firststaggerfraction of the run and each moves over the remaining1 - stagger.
Typedefs
- DecorPainter = void Function(Canvas canvas, TextFrame frame)
- Something drawn on the canvas outside the glyph layer — a marker behind the ink, an underline, a sparkle over it.