flutter_thinking_orbs 0.1.0
flutter_thinking_orbs: ^0.1.0 copied to clipboard
Dotted thought-orb loading indicators for AI & agent UIs. Six hand-tuned animated states, monochrome and theme-aware, drawn with plain Canvas circles.
flutter_thinking_orbs #
Dotted thought-orb loading indicators for AI & agent UIs. Six hand-tuned animated states, monochrome and theme-aware, drawn with plain Canvas circles — no shaders, no filters, no image assets, so they render identically everywhere and stay cheap on low-end devices.
A Flutter port of thinking-orbs by Jakub Antalik. The animation math and tunings are ported faithfully; the API is reshaped to be idiomatic Flutter.
Features #
- Six distinct states — one animation per verb an agent can be doing.
- Two tuned designs — an inline-text scale and a chat-avatar scale, picked automatically from the rendered size. They are separate tunings (dot count, dot size, speed), not one design scaled up.
- Monochrome & theme-aware — light ink on dark surfaces, dark ink on light ones.
autofollows the ambientThemeand updates live. - In phase — every mounted orb shares one clock, so a wall of orbs pulses together.
- Accessible — ships as a labelled
Semanticsimage; honours reduced-motion with a static frame. - Cheap — filled circles only; pauses itself when its route is inactive (
TickerMode).
Install #
dependencies:
flutter_thinking_orbs: ^0.1.0
import 'package:flutter_thinking_orbs/flutter_thinking_orbs.dart';
Quick start #
const ThinkingOrb(state: OrbState.searching, size: 64)
That is the whole surface for the common case. Everything else is optional tuning.
States #
Six verbs an agent can be doing, each a distinct animation. Left is the dark-substrate ink, right is the light-substrate ink — the same frame, mirrored.
| State | Dark | Light | What it shows |
|---|---|---|---|
working |
![]() |
![]() |
Particles run on tilted orbits. |
searching |
![]() |
![]() |
A scan meridian sweeps a dotted globe. |
solving |
![]() |
![]() |
Bands scramble in quarter turns, then click back solved. |
listening |
![]() |
![]() |
A waveform rolls through the latitude rings. |
composing |
![]() |
![]() |
An undulating multi-band sash. |
shaping |
![]() |
![]() |
A dotted outline morphs circle → triangle → square. |
The still frames above are the representative frame each state shows to reduced-motion users. In an app they animate continuously.
ThinkingOrb(state: OrbState.working) // particles on tilted orbits
ThinkingOrb(state: OrbState.searching) // a scan meridian sweeps a dotted globe
ThinkingOrb(state: OrbState.solving) // bands scramble, then click back solved
ThinkingOrb(state: OrbState.listening) // a waveform rolls through the rings
ThinkingOrb(state: OrbState.composing) // an undulating multi-band sash
ThinkingOrb(state: OrbState.shaping) // dotted outline: circle → triangle → square
Sizes #
size is a plain logical-pixel dimension (default 64). Under the hood, two designs ship — one tuned at inline-text scale, one at chat-avatar scale — and the tuning is chosen from the rendered size:
size < 40→ the small, inline-text design;size >= 40→ the large, chat-avatar design.
The chosen design is then drawn at the exact size you asked for, so any value works:
ThinkingOrb(state: OrbState.working, size: 64) // chat-avatar scale
ThinkingOrb(state: OrbState.working, size: 20) // inline-text scale
Dropping an orb inline with text:
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text('Thinking'),
SizedBox(width: 6),
ThinkingOrb(state: OrbState.working, size: 18),
],
)
Theme #
The orbs are strictly monochrome. theme picks which ink to paint:
ThinkingOrb(theme: OrbTheme.auto) // default — follows the ambient Theme brightness
ThinkingOrb(theme: OrbTheme.dark) // pin light ink, for dark backgrounds
ThinkingOrb(theme: OrbTheme.light) // pin dark ink, for light backgrounds
OrbTheme.auto reads Theme.of(context).brightness, so it flips automatically whenever your app toggles light/dark — no work on your side. Pin dark or light when the orb sits on a surface whose color is fixed regardless of the app theme (a always-dark toolbar, say).
All props #
| Prop | Type | Default | Description |
|---|---|---|---|
state |
OrbState |
working |
Which animation to show. |
size |
double |
64 |
Rendered side length in logical pixels. |
theme |
OrbTheme |
auto |
Ink selection: auto, dark, or light. |
speed |
double |
1 |
Multiplier on top of the state's baked speed. |
paused |
bool |
false |
Freeze on the current frame. |
semanticLabel |
String? |
per-state | Accessibility label; overrides the default. |
ThinkingOrb(
state: OrbState.solving,
size: 20,
speed: 1.5,
paused: false,
semanticLabel: 'Analysing repository…',
)
Accessibility & performance #
- Semantics — each orb is a
Semanticsimage with a sensible per-state label (Working…,Searching…, …), overridable viasemanticLabel. - Reduced motion — when the platform requests reduced motion (
MediaQuery.disableAnimations), the orb paints a single static representative frame instead of animating, and still follows the live theme. - Auto-pause — animation is driven by a
Ticker, so it pauses automatically when the orb's route becomes inactive (Flutter'sTickerMode) and resumes in phase. - In phase — all orbs read one shared clock; a grid of them pulses together rather than drifting apart.
- Cheap to draw — every state is filled circles and nothing else. No
ImageFilter, no shaders, no rasterised assets.
Example #
A runnable gallery with live controls for size, speed, pause and theme is in example/:
cd example
flutter run
How it works #
Each state is a cloud of depth-shaded dots. A point field is rotated and tilted in 3D, projected orthographically, then z-sorted and painted back-to-front as flat grey circles — depth is carried entirely by dot size and ink weight, so no lighting or blur is needed. On a dark substrate the ink value is mirrored, so near dots read bright. The six states differ only in how their dot field moves each frame.
Credit #
This is a Flutter port of thinking-orbs by Jakub Antalik, used under the MIT License. The orb animation algorithms and per-state tunings originate from that project.
License #
MIT — see LICENSE.











