material_async_button 3.1.0 copy "material_async_button: ^3.1.0" to clipboard
material_async_button: ^3.1.0 copied to clipboard

Drop-in loading state for Elevated, Filled, Outlined, Text, Icon and FloatingAction Material buttons — spinner while onPressed runs, AsyncButtonTheme, AsyncButtonController.

Changelog #

3.1.0 - 2026-10-04 #

Added #

  • IconAsyncButton (all four constructors) forwards onHover, onLongPress (dropped while loading) and statesController to its IconButton.
  • TextAsyncButton.isSemanticButton, forwarded to TextButton.
  • maintainSize on AsyncButtonTheme, AsyncButton and every wrapper: the loading view overlays the invisible idle child, so the button keeps its idle footprint while loading (.icon constructors keep their icon).
  • minLoadingDuration on AsyncButtonTheme, AsyncButton and every wrapper: an anti-flicker floor that holds the loading view at least that long; an error rethrows once the floor has elapsed.

Changed #

  • Requires Dart 3.13 / Flutter 3.47 (was Dart 3.10 / Flutter 3.38).
  • One AsyncButtonController bound to two mounted buttons now fails a debug assertion (release builds keep last-binder-wins).
  • AsyncButtonController.reset() abandons the in-flight run: the button is idle and re-armed at once, and the abandoned run's completion is ignored.
  • onPressed: null with an onLongPress keeps the button enabled for long-press, as in Flutter.
  • The agent skill moved to skills/material-async-button-usage/ for dart run skills@ get (was skills/flutter-material-async-button/).

Fixed #

  • Loading buttons no longer grow to fill their parent (every non-FAB wrapper since 2.0.0, and custom loadingBuilders returning AsyncButtonSpinner).
  • reset() mid-flight no longer lets the stale run clear a new run's loading state.
  • Swapping a button's controller mid-flight keeps its loading state: the incoming controller adopts the run that button is showing.
  • An external controller detaches when its button unmounts or is swapped out: canTrigger is false and trigger() is a no-op.
  • trigger() after dispose() never runs onPressed.
  • A selected IconAsyncButton (isSelected: true with a selectedIcon) shows the spinner while loading.
  • A collapsed extended FAB (FloatingActionAsyncButton.extended with isExtended: false) shows the spinner while loading.
  • Every .icon / .tonalIcon constructor accepts enabled.
  • The default spinner no longer leaks a TextPainter per line-box cache miss.
  • The default spinner re-measures after fonts load (e.g. google_fonts): the line-box cache is cleared on every system font change.

3.0.0 #

Breaking #

  • AsyncStandardMaterialButton is now sealed and can no longer be extended outside the package; compose AsyncButton for custom buttons.

2.1.2 #

Improved #

  • Smarter line-box cache eviction. The bounded text line-box cache now evicts only its least-recently-used entry when full, instead of clearing entirely — cycling through many text styles no longer re-measures every cached style.
  • Added test coverage for AsyncButtonSpinner.strokeWidth.

2.1.1 #

Fixed #

  • Avoid layout cache thrashing. Replaced the singleton TextPainter cache with a bounded Map. This fixes a performance issue where the cache would thrash (and incorrectly re-measure) if multiple buttons with different text styles were rendered in the same frame.
  • Dead code warning. Fixed a static analysis warning in AsyncButtonTheme.lerp by using covariant for the other parameter.
  • Added comprehensive test coverage for AsyncButtonController disposal during an active asynchronous operation and external controller swapping.

2.1.0 #

Added #

  • New FloatingActionAsyncButton wrapper. Adds drop-in async loading states to FloatingActionButton with support for all four core variants: standard, .small, .large, and .extended.
  • Improved accessibility for screen readers. Added semanticsLabel parameter to AsyncButtonSpinner (defaulting to 'Loading') to ensure screen readers actively announce the progress of asynchronous operations when buttons are clicked.

2.0.2 #

Fixed #

  • Buttons no longer shrink by a sub-pixel while loading. The default spinner now sizes to the label's line-box height (the idle content's vertical extent) rather than the raw fontSize, which is shorter than the rendered line. .icon buttons take max(iconSize, lineBox), text-only buttons take the line box, icon-only buttons keep iconSize (unchanged). The line box is measured with the ambient TextScaler, so the loading view also tracks text scaling.

2.0.1 #

Fixed #

  • Icon-bearing buttons no longer shrink vertically while loading. The default spinner sizes to the button's shape instead of always to the font size: iconSize for IconAsyncButton, max(iconSize, fontSize) for the .icon constructors (the idle row height), and fontSize for text-only buttons (unchanged). Sizes are read from the button's resolved IconTheme / DefaultTextStyle, so a per-widget iconSize and theme overrides flow through.

2.0.0 #

Breaking redesign. The button now does exactly one job — show a spinner while onPressed is in flight — and stays out of error handling, success feedback, and styling. State collapses to a single bool (loading), and the theme becomes a pure complement to ButtonStyle.

Breaking #

  • Loading-only: no success or error state. Removed AsyncButtonStatus entirely (the state is now a plain bool isLoading), along with onSuccess, onError, successBuilder, errorBuilder, feedbackDuration, cooldownDuration, markSuccess, markError, haptic / HapticOn, and announce / AsyncButtonAnnouncer / defaultAsyncButtonAnnouncer. An in-button error view is a Material anti-pattern; success is handled by what your action already does (navigate away, flip the label). Both belong to your state management, not the button.
  • onPressed throws now re-propagate. When onPressed throws, the button returns to idle and re-throws so the error reaches FlutterError.onError / your runZonedGuarded zone. controller.trigger() rethrows instead of completing normally.
  • AsyncButtonController is now a read-only ValueListenable<bool> (ChangeNotifier implements ValueListenable<bool>). There is no public value setter — drive it with trigger() / reset() and observe with addListener / ValueListenableBuilder<bool>. Builders receive bool isLoading instead of an AsyncButtonStatus.
  • AsyncButtonTheme carries only loadingBuilder + transitionBuilder. AsyncButtonTheme.empty (spinner-only) is the sole baseline; removed AsyncButtonTheme.material(). It complements ButtonStyle — no styling.
  • disabled flag → enabled (defaults to true). Affirmative naming that pairs with a tear-off onPressed. Either enabled: false or onPressed: null disables — both also no-op an external controller.trigger().
  • loadingChild (a Widget) → loadingBuilder (a WidgetBuilder) on both the widgets and AsyncButtonTheme.
  • Removed the loading-colour knobs (loadingForegroundColor, material(loadingColor:)). The spinner inherits each button variant's foreground; recolour a single button via AsyncButtonSpinner(color: ...).

Fixed #

  • Loading never disables the button. It keeps the button's themed enabled colours — being loading and being disabled are different things. The spinner inherits the enabled foreground for free (no more greyed indicator); taps that can't run are silently swallowed, and onLongPress is gated off while busy. The button shows the disabled look only when explicitly disabled via onPressed: null.
  • .icon buttons drop the icon while loading, rendering the spinner alone.

Improved #

  • The default AsyncButtonSpinner derives its size from the ambient font size, so it matches the button's label instead of a fixed 16px.
  • Guarded against state changes on an unmounted button.
  • Corrected the flutter SDK constraint.

1.0.1 #

  • Docs: use tear-off form (onPressed: api.save) in README examples.
  • CI: split workflow into format/analyze/test/pana/example, add tag-triggered pub.dev publish via OIDC trusted publishing.

1.0.0 #

Initial release. Renamed from async_button_builder to material_async_button with a redesigned, theme-aware API.

1
likes
160
points
252
downloads

Documentation

API reference

Publisher

verified publishermehmetesen.com

Weekly Downloads

Drop-in loading state for Elevated, Filled, Outlined, Text, Icon and FloatingAction Material buttons — spinner while onPressed runs, AsyncButtonTheme, AsyncButtonController.

Repository (GitHub)
View/report issues
Contributing

Topics

#button #loading #async #material #widget

License

MIT (license)

Dependencies

flutter

More

Packages that depend on material_async_button