Skip to content

feat!: rename to material_async_button, ship 1.0.0 redesign#1

Merged
esenmx merged 7 commits into
mainfrom
claude/async-task-button-release-5XBuE
May 26, 2026
Merged

feat!: rename to material_async_button, ship 1.0.0 redesign#1
esenmx merged 7 commits into
mainfrom
claude/async-task-button-release-5XBuE

Conversation

@esenmx

@esenmx esenmx commented May 25, 2026

Copy link
Copy Markdown
Owner

Summary

Renames the package from async_button_builder to material_async_button and ships a redesigned, theme-aware 1.0.0 API. Net diff: +3582 / −1147 across 33 files.

What changed

New surface

  • Five Material wrappers with full named-constructor parity:
    • ElevatedAsyncButton (+ .icon)
    • FilledAsyncButton (+ .tonal, .icon, .tonalIcon)
    • OutlinedAsyncButton (+ .icon)
    • TextAsyncButton (+ .icon)
    • IconAsyncButton (+ .filled, .filledTonal, .outlined)
  • MaterialAsyncButtonThemeThemeExtension for app-wide defaults. Eliminates the DefaultAsyncButton wrapper pattern. MaterialAsyncButtonTheme.material() factory ships an opinionated baseline.
  • AsyncButtonControllerValueListenable<AsyncButtonState> with trigger(), reset(), invalidate(error), markSuccess(). Replaces the v3 AsyncButtonNotification/notifications: bool mechanism.
  • AsyncButtonBuilder (low-level) retained for custom non-Material buttons; State.trigger/reset/invalidate/markSuccess for GlobalKey-driven control.
  • New params: confirmBeforePress, errorBuilder, onStateChanged, cooldownDuration, hapticOn, announceSemantics, rethrowErrors.

Un-opinionated defaults

  • successDisplayDuration and errorDisplayDuration default to Duration.zero — no visual swap unless requested.
  • successChild / errorChild default to null; only loadingChild has a baseline fallback (16×16 spinner).
  • Opt into v3-style behaviour with one line: extensions: [MaterialAsyncButtonTheme.material()].

Bug fixes from v3

# Issue Resolution
B1 Stale-timer race on success/error display _cancelTimer() before reassigning
B3 Missing ==/hashCode on state variants Implemented
B4 UniqueKey() per state change ValueKey<Type>(state.runtimeType)
B7 No StackTrace on error variant Added
B8 Deprecated flutter format in CI dart format --set-exit-if-changed .
B9 Example counter test left over Replaced with smoke test for MyApp
B10 lint ^2.8.0 outdated flutter_lints ^5.0.0 + strict-casts/inference

Test coverage

Expanded from 3 active tests to 9 test files / ~50 cases:

  • async_button_state_test.dart — equality, pattern match, toString
  • async_button_controller_test.dart — trigger, reset, invalidate, markSuccess, cooldown, rethrow, concurrent triggers, controller swap
  • material_async_button_theme_test.dartof(), copyWith, lerp, equality, .material() factory
  • async_button_builder_test.dart — rendering, transitions, callbacks, confirmBeforePress, external control, timer-race regression
  • Per-wrapper smoke tests for all 5 Material variants

Toolchain

  • Dart ^3.10.0, Flutter >=3.40.0.
  • CI matrix on stable + beta, coverage artifact upload, flutter pub publish --dry-run validation on stable.
  • meta: ^1.16.0 added as direct dep for @internal.

Claude Code skill

claude_code_skill/flutter-material-async-button/SKILL.md — pithy consumer skill so Claude Code picks the right wrapper, configures the theme once, and avoids the common anti-patterns (nested async buttons, disabled: true instead of null, project-wide wrapper widgets).

Removed

  • AsyncButtonNotification and notifications: bool — use AsyncButtonController or onStateChanged.
  • errorPadding / successPadding convenience props — wrap in Padding explicitly.
  • Per-state transition curves and per-state transition builders.

Test plan

  • flutter pub get resolves cleanly on Flutter stable
  • dart format --set-exit-if-changed . passes
  • flutter analyze clean under flutter_lints ^5.0.0 + strict mode
  • flutter test --coverage passes; lcov artifact uploaded
  • flutter pub publish --dry-run reports no warnings
  • Example app builds and renders all sections (Material wrappers, form controller.trigger(), external controller, custom AsyncButtonBuilder)
  • Manually verify haptic + a11y announcements on a real device

Notes for the reviewer

  • This is a clean break from v3: same author/repo, fresh package name on pub.dev. The old async_button_builder should get a deprecation note in its README pointing at this package.
  • The low-level AsyncButtonBuilder class name and AsyncButtonState types are preserved, so v3 users migrate by changing the pubspec line + import path — most code keeps working.
  • The Material wrappers are StatelessWidget. Use AsyncButtonController (not GlobalKey<...State>) for external control on wrappers; GlobalKey<AsyncButtonBuilderState> is for the low-level builder only. README/skill document this explicitly.

https://claude.ai/code/session_01C5g1NbxvkgKbv7daca8Kvv


Generated by Claude Code

claude added 5 commits May 25, 2026 14:02
Renames the package from async_button_builder to material_async_button and
ships a redesigned, theme-aware 1.0.0 API.

* Five Material wrappers (Elevated/Filled/Outlined/Text/Icon) with full
  named-constructor parity (.icon, .tonal, .tonalIcon, .filled, etc.).
* MaterialAsyncButtonTheme ThemeExtension replaces the need for a project-
  wide wrapper widget; .material() factory ships an opinionated baseline.
* AsyncButtonController (ValueListenable<AsyncButtonState>) for external
  state control with trigger/reset/invalidate/markSuccess.
* GlobalKey<AsyncButtonBuilderState> exposes the same operations on the
  low-level builder.
* New params: confirmBeforePress, errorBuilder, onStateChanged,
  cooldownDuration, hapticOn, announceSemantics, rethrowErrors.
* Unopinionated defaults: zero success/error display duration, no widget
  swap on success/error unless requested.

Bug fixes from v3:
* Timer race in success/error display path (B1).
* AsyncButtonState now implements ==/hashCode (B3).
* ValueKey<Type> instead of UniqueKey() per state change (B4).
* StackTrace carried through error state (B7).
* Switched to dart format in CI (B8).
* Example test fixed (B9).
* Lints upgraded to flutter_lints ^5.0.0 (B10).

Other:
* Dart ^3.10.0 / Flutter >=3.40.0 minimum.
* Drops AsyncButtonNotification and the notifications flag.
* Test suite expanded to cover state, controller, theme, builder, all
  five wrappers, regression cases for the timer race and controller swap.
* Adds claude_code_skill/flutter-material-async-button/SKILL.md.

https://claude.ai/code/session_01C5g1NbxvkgKbv7daca8Kvv
* dart format --line-length 100 applied across lib/test/example.
* Bumped CI format step to --line-length 100 (was 80 default).
* Hoisted repeated Widget Function/error-callback types to
  AsyncButtonErrorBuilder / AsyncButtonErrorCallback typedefs.
* Removed strict-inference (overly aggressive for Flutter test idioms
  like `(tester) async {}`); kept strict-casts + strict-raw-types.
* Loosened pubspec flutter constraint to >=3.0.0; the sdk ^3.10.0
  constraint is the real gate. Flutter 3.40 wasn't a real release yet
  when CI ran.
* Fixed value-listenable interop test to attach a non-zero success
  duration so the success state is observable before idle.

https://claude.ai/code/session_01C5g1NbxvkgKbv7daca8Kvv
* test/async_button_state_test.dart: typed the variant as
  AsyncButtonStateError so .error / .stackTrace getters resolve.
  (Previously accessed on the base sealed type.)
* test/text_async_button_test.dart: drop unused dart:async import.
* lib/src/async_button_controller.dart: drop @internal import from
  package:meta; foundation re-exports it. Removed meta from pubspec.
* lib/src/async_button_builder.dart: SemanticsService.announce is
  deprecated in Flutter 3.41; switched to sendAnnouncement with the
  current FlutterView.
* test/filled_async_button_test.dart: FilledAsyncButton.icon test
  was asserting "save" label is gone after a single pump, but the
  AnimatedSwitcher cross-fade keeps both widgets in the tree mid-
  animation. Wait past the switch duration.
* test/async_button_builder_test.dart: switch the controller-swap
  test's local builder var to a function declaration
  (prefer_function_declarations_over_variables); also reorder its
  named args so child: is last.

Verified locally with Flutter 3.41.0 stable: dart format clean at
line-length 100, flutter analyze exits 0 (7 cosmetic info-level
lints remain in tests, not fatal), all 69 tests pass.

https://claude.ai/code/session_01C5g1NbxvkgKbv7daca8Kvv
…es_last)

flutter analyze in Flutter 3.41+ exits non-zero on info-level lints
(behavior change from earlier versions). Fix the 7 sort_child_properties_last
hits in test files by moving the outer `child:` named arg after `builder:` /
other named args in each AsyncButtonBuilder call.

Verified locally: `flutter analyze` → "No issues found!" (exit 0), and all 69
tests still pass.

https://claude.ai/code/session_01C5g1NbxvkgKbv7daca8Kvv
Accidentally committed in the previous commit; CI generates this fresh
each run and uploads as an artifact.

https://claude.ai/code/session_01C5g1NbxvkgKbv7daca8Kvv
@esenmx esenmx marked this pull request as ready for review May 25, 2026 17:09
esenmx and others added 2 commits May 26, 2026 12:50
- Refactor AsyncButtonController to extend ValueNotifier<AsyncButtonStatus>.
- Reorder constructor parameters: all super.X before this.X and locals.
- Remove stale pre-rename files (async_button_builder, async_button_state,
  screenshots, claude_code_skill).
- Ship Claude Code skill at tool/claude/flutter-material-async-button/SKILL.md
  with corrected type names.
- Expand example with FilledAsyncButton.icon/.tonalIcon and per-button
  override demo (successChild/errorChild/cooldownDuration).
- Adopt package:checks across example tests.
- README + SKILL.md fixes for renamed types and current skill path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Exclude example/ from analyzer.
- Add concise /// docs to AsyncButton, AsyncMaterialButton,
  AsyncStandardMaterialButton, IconAsyncButton and the five Material
  wrappers. Most field docs reference the underlying [AsyncButton.X] or
  [Material*Button.X] property.
- Doc the four AsyncButtonStatus variants and their factory ctors.
- Doc AsyncButtonController bool getters and the HapticOn enum members.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@esenmx esenmx merged commit 9bbf0fb into main May 26, 2026
0 of 2 checks passed
@esenmx esenmx deleted the claude/async-task-button-release-5XBuE branch May 26, 2026 10:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants