1.1.0: thinking-orb indicators, controller API, and a full correctness/docs audit - #9
Merged
Merged
Conversation
…ness pass
Adds a family of nine animated "thinking orb" loading indicators for AI and
agent interfaces, a controller/theme/sizing API for LoadingButton, and fixes
the correctness, accessibility and packaging defects found by a full audit.
Thinking orbs
- lib/src/orbs/ is a Dart port of the MIT-licensed thinking-orbs engine by
Jakub Antalik. Nine states, two hand-tuned size tiers, one shared clock so
several orbs stay in step, TickerMode-aware, and a representative still
frame under reduced motion. Filled circles only: no shaders or assets.
- Verified against upstream's published golden vectors dot by dot --
90 tests over 11,288 dots and 341 lines at 1e-4 tolerance.
- Usable standalone as ThinkingOrb, or in any button family via
LoadingIndicator.orb(), or app-wide via LoadingButtonThemeData.indicator.
Correctness
- AutoLoadingButtonState: a throwing callback left the button a permanently
disabled spinner, hung every awaiter, and escaped as an unhandled async
error. The loading flag is now cleared on both paths; tap-path errors go to
FlutterError and awaited doPress() callers receive the error themselves.
- LoadingButton: the double-tap guard read a state that was not written until
~200ms after the tap, so a fast double tap ran onPressed twice. Replaced
with a synchronous latch taken before the first await.
- onPressed no longer waits on haptics and the press animation before running.
- The uncancellable Future.delayed reset is now a cancelled-on-dispose Timer.
- ArgonButton/ArgonTimerButton no longer force-unwrap a nullable onTap or
loader, and their escaping start/stopLoading closures are mounted-guarded.
- Dropped rxdart: the package now has zero runtime dependencies.
API (all additive; nothing removed)
- LoadingButtonController + LoadingButtonValue for external control.
- LoadingButtonTheme / LoadingButtonThemeData, deprecating LoadingButtonConfig.
- LoadingButtonSizing: intrinsic, expand and fixed alongside the legacy box.
- Determinate progress, LoadingIndicator, Material 3 colour resolution,
debounce/cooldown, buttonStyle, tooltip, focusNode and enabled.
- onFailure(Object, StackTrace) deprecates onError.
Accessibility
- Removed the duplicate Semantics button node that shadowed the real button's
label; state changes now ride on the child as a live region.
Packaging and docs
- environment corrected to Dart >=3.4.0 / Flutter >=3.22.0. The declared
Flutter 1.17 floor had been fiction since WidgetStatesController was adopted.
- Added topics, platforms, .pubignore, a stronger lint set and GitHub Actions
CI (analyze, test, format, pana, publish --dry-run).
- README, MIGRATION and CHANGELOG rewritten: every previous README sample
failed to compile and the property tables documented parameters removed
before 1.0.0.
- Example app rebuilt as a five-page gallery with no third-party dependencies.
- Test suite grew from `void main() {}` to 336 tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A four-lens review of the release commit produced 25 confirmed findings (4 refuted). None were crashes, but several were wrong behaviour or false documentation, and eight were in code added by this release. State machine - A throwing onSuccess was caught by the same handler as onPressed, so a successful run reported itself as ActionState.error and called onFailure. The guarded region is now the user's action alone; onSuccess, onFailure, onError and onStateChanged run outside it and route their own throws to FlutterError instead of corrupting the state machine. - `enabled` no longer writes ActionState.disabled into the value. A disabled button sharing a controller was permanently disabling its enabled siblings, and the controller-less cooldown timer then forced idle regardless of `enabled`, leaving a button painted enabled that ignored every tap. The disabled phase is now layered on at render time, so the painted state can no longer disagree with interactivity. - initState no longer writes to the notifier. Mounting with `enabled: false` dispatched onStateChanged mid-build, and a listener that marked an ancestor dirty tripped a framework assertion. - onStateChanged and the reset window now react to real transitions only. Progress ticks share the value, so a determinate upload fired a transition per tick and pushed its own success window back forever. Controller - dispose() no longer clears its bindings, which made attachmentCount report zero while buttons were still bound. - setActionState preserves progress unless asked to clear it. - press() documents what it actually does: with several buttons attached the shared value means exactly one run happens. Orbs - The shared clock re-anchors when the frame clock moves backwards, instead of emitting negative, test-order-dependent time. - An orb mounted into a running app joins the clock on its first frame rather than painting one frame at t = 0. - A translucent tint keeps its alpha. Done with Color.lerp because the channel accessors differ across the supported range: `.a` needs Flutter 3.27 and `.alpha` is deprecated in current stable. AutoLoadingButton - One FlutterError report per failed run, not one per tap that joined it. Docs - Corrected the IconTheme inheritance claim (it does not hold for LoadingButton), the indicator precedence chain (loadingText replaces the indicator rather than labelling it), the package-wide framing of the accessibility section (it was LoadingButton-only, and the other families lose their accessible name while busy), the builder-key hazard, a phantom IconLoadingButton in MIGRATION, and the pubspec claim of no platform channels. - Disclosed the one break the release had not admitted: ArgonTimerButton.loader narrowed from Function(int) to Widget Function(int). - coverage/ is excluded from the published archive and from git. Adds test/regression_v110_test.dart, locking down each fix; verified that each test fails without it. 347 tests pass, analyze is clean at --fatal-infos, and pana scores 160/160. Also adds doc/thinking-orbs.png, a contact sheet of the nine states at both tuned sizes, generated by tool/generate_orb_sheet.dart and used by the README and as a pub.dev screenshot. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both passed locally and failed on GitHub Actions, for environment reasons rather than defects in the package. - format: `dart format` chooses its style from the package's RESOLVED language version, so running it before `pub get` made resolution fail, fell back to the SDK's own version, and reformatted the whole tree in the newer style. Resolve the root and the example first. - pana: screenshot validation shells out to libwebp (webpinfo, cwebp, dwebp, gif2webp, webpmux). pub.dev's analysis environment has those and a bare runner does not, so all 8 screenshot checks failed with "No such file or directory" and cost the 10 example/screenshot points — 150/160 on CI against 160/160 with libwebp present. Install it before scoring. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Screenshots - Replace generated-image.png, a 2.5 MB promotional graphic rather than a screenshot, whose code (`LoadingButton(icon:, onTap:)`, an `AutoLoadingButton` class) matched no release of this package. - Add real renders of the real widgets, produced by tool/generate_screenshots.dart and packed by tool/pack_animations.sh: an animated hero (spinner, thinking orb and determinate upload running, then succeeding), an animated strip of the nine orbs, a button-state matrix in light and dark, and a sheet of the button families. - Rendering goes through the golden-file pipeline because RenderRepaintBoundary.toImage rasterises once per test process and then hangs. Scenes are scaled at paint time because that pipeline always captures at 1x. - Only the three pubspec screenshots ship in the archive; the README loads the rest from GitHub. The archive dropped from 2 MB to 879 KB. Library - The default loading, success and error widgets inherit the button's foreground colour instead of hard-coding white, which was invisible on a light container. Rendering under the legacy colour strategy is unchanged. Docs - README restructured: hero first, a table of contents, an at-a-glance family comparison, and screenshots placed next to the sections they illustrate. Existing content is about 9% shorter with no factual loss. - New "LLM and AI assistant support" section with a pasteable context block, six task prompts and a table of common mistakes. - New llms.txt, a machine-readable API digest built from lib/. It lists the 0.0.x parameters removed in 1.0.0 that assistants still emit, each with its replacement. - Every README Dart sample and every llms.txt signature was compiled, and five prompts were followed literally and compiled. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Full audit and update of the package, plus the headline new feature.
Thinking orbs (new)
Nine animated "thought orb" loading indicators for AI and agent interfaces.
lib/src/orbs/is a Dart port of the MIT-licensedthinking-orbs engine by Jakub Antalik (credited in
the README alongside Bright Sunu's Flutter port).
Filled circles over a rotated, z-sorted 3D point field — no shaders, blurs or image assets, so it
renders identically on every Flutter target.
TickerMode-aware, pauses on request, and paints a representative still frame under reduced motion.ThinkingOrb, in any button family viaLoadingIndicator.orb(), or app-widethrough
LoadingButtonThemeData.indicator.Verified numerically against upstream's published golden vectors — 90 tests over 11,288 dots and
341 lines at 1e-4 tolerance, so this is a port rather than an approximation.
Fixed
*AutoLoadingButtonstuck forever. A throwingonPressedleft the button a permanentlydisabled spinner, hung every awaiter, and escaped as an unhandled async error. Affected all five
families. Now clears on both paths; tap-path errors go to
FlutterErroranddoPress()callersreceive the error.
fast double tap ran
onPressedtwice — a charged-twice bug on a payment button. Replaced with asynchronous latch.
onPressedno longer waits on haptics and the press animation before running (which also madeevery
LoadingButtonuntestable).Future.delayedreset is now aTimercancelled on dispose.ArgonButton/ArgonTimerButtonno longer force-unwrap a nullableonTaporloader, and theirescaping
startLoading/stopLoadingclosures aremounted-guarded.Semanticsbutton node that shadowed the real button's accessible name.rxdart: zero runtime dependencies.Added (all additive)
LoadingButtonController+LoadingButtonValue,LoadingButtonTheme/LoadingButtonThemeData(deprecating the global
LoadingButtonConfigsingleton),LoadingButtonSizing(intrinsic / expand /fixed — the old fixed 200x50 stays the default), determinate
progress,LoadingIndicator,Material 3 colour resolution,
debounce/cooldown,buttonStyle,tooltip,focusNode,enabled, andonFailure(Object, StackTrace)deprecatingonError.Breaking
Only the SDK floor: Dart >=3.4.0 / Flutter >=3.22.0. This is a correction, not a removal — the
package has used
WidgetStatesControllersince 1.0.0, so the declaredflutter: ">=1.17.0"had beenfiction for several releases and users below 3.22 were getting a confusing compile error instead of a
clean pub resolution message. One small typing break is disclosed in MIGRATION.md
(
ArgonTimerButton.loader).Docs
README, MIGRATION and CHANGELOG rewritten. The previous README was actively misleading: every
LoadingButtonsample failed to compile, and 10 of 13 rows in its main properties table documentedparameters removed before 1.0.0. MIGRATION told readers to depend on
loading_button— a differentpackage.
Verification
void main() {}.flutter analyze --fatal-infosclean under a strengthened lint set (strict-casts / strict-inference/ strict-raw-types).
flutter pub publish --dry-run: 0 warnings.Work was audited across eight dimensions and then adversarially reviewed across four lenses; the 25
confirmed review findings are fixed in 656329b, each with a regression test verified to fail without
its fix.
Merges #8 (thanks @alr2413).
🤖 Generated with Claude Code