flow_button

A customizable, animated button widget for Flutter with built-in loading, success and error states, smooth transitions between them, and a subtle scale animation on tap.

Features

  • Idle, loading, success, error and disabled states
  • Automatic state handling: return a Future from onPressed and the button animates through loading → success/error → idle on its own
  • Optional AnimatedButtonController for driving state programmatically from outside the widget
  • Smooth cross-fade transitions between states and a tap scale effect
  • Optional leading icon and fully custom child content
  • Custom width, height, border radius, background color, text/icon color
  • Custom loading, success and error indicator widgets
  • Optional gradient background and border
  • Disabled state with muted appearance
  • Works with light and dark themes out of the box
  • Accessible: exposes proper Semantics (button role, label, enabled state)
  • Null-safe, dependency-free (only flutter)

Installation

Add the package to your pubspec.yaml:

dependencies:
  flow_button: ^1.0.0

Then run:

flutter pub get

Basic usage

import 'package:flow_button/flow_button.dart';

AnimatedButton(
  onPressed: () async {
    await Future.delayed(const Duration(seconds: 2));
  },
  child: const Text('Login'),
)

If onPressed returns a Future, the button automatically shows a loading indicator while it's pending, then briefly shows a success or error indicator depending on whether the future completes normally or throws, before returning to idle.

Configuration

AnimatedButton(
  width: 220,
  height: 52,
  borderRadius: const BorderRadius.all(Radius.circular(12)),
  backgroundColor: Colors.indigo,
  foregroundColor: Colors.white,
  successColor: Colors.green,
  errorColor: Colors.red,
  gradient: const LinearGradient(colors: [Colors.purple, Colors.pinkAccent]),
  border: Border.all(color: Colors.black12),
  icon: const Icon(Icons.login),
  loadingIndicator: const SizedBox(
    width: 18,
    height: 18,
    child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white),
  ),
  enabled: true,
  autoResetState: true,
  resetDelay: const Duration(seconds: 1),
  animationDuration: const Duration(milliseconds: 250),
  scaleFactor: 0.95,
  onPressed: () async {
    await signIn();
  },
  child: const Text('Sign in'),
)
Property Description
child Content shown in the idle state, usually a Text.
onPressed Tap callback. Return a Future to enable automatic loading/success/error handling.
controller Optional AnimatedButtonController for external state control.
icon Optional leading widget shown next to child in the idle state.
loadingIndicator / successIndicator / errorIndicator Override the default indicator shown for each state.
width / height Button dimensions. width is optional (sizes to content by default).
borderRadius Corner radius of the button.
backgroundColor / foregroundColor Idle state colors. Default to the current Theme's ColorScheme.
disabledBackgroundColor / disabledForegroundColor Colors used when enabled is false.
successColor / errorColor Background colors for the success/error states.
gradient Optional gradient painted instead of backgroundColor.
border Optional border around the button.
padding Padding around the idle content.
elevation Drop shadow elevation.
enabled Whether the button accepts input.
autoResetState / resetDelay Whether/when to automatically return to idle after success or error.
animationDuration Duration of state and color/size transitions.
scaleFactor How much the button shrinks while pressed (0–1).
semanticLabel Accessibility label; defaults to the text content of child.

Controller usage

Use an AnimatedButtonController to drive the button's state from outside, for example after a form validation result computed elsewhere:

final controller = AnimatedButtonController();

AnimatedButton(
  controller: controller,
  onPressed: () {},
  child: const Text('Submit'),
)

// Anywhere else in your code:
controller.setLoading();
controller.setSuccess();
controller.setError();
controller.setIdle();
controller.setDisabled();

// Dispose when done, e.g. in State.dispose():
controller.dispose();

The button still auto-manages state for a Future-returning onPressed, reporting the resulting state through the attached controller so external listeners stay in sync.

Screenshots

Add screenshots or a GIF of the example app here before publishing.

API overview

  • AnimatedButton — the button widget.
  • AnimatedButtonController — optional external state controller.
  • AnimatedButtonState — enum: idle, loading, success, error, disabled.

See the example app for a complete showcase of all supported configurations.

Testing

The package ships with widget and unit tests covering state transitions, callbacks, controller behavior and custom properties. Run them with:

flutter test

Contributing

Issues and pull requests are welcome. Please include tests for any new behavior and run flutter analyze and flutter test before submitting.

License

MIT — see LICENSE. # flow_button_package

Libraries

flow_button
A customizable, animated button widget with built-in loading, success and error states.