on_process_button_widget 2.0.13
on_process_button_widget: ^2.0.13 copied to clipboard
A highly customizable Flutter button with built-in loading animations and status feedback (Success/Error). Perfect for asynchronous operations and form submissions.
⚡ OnProcessButtonWidget #
A production-ready Flutter button widget with built-in loading animations and status feedback (Success/Error). Perfect for asynchronous operations and form submissions.
✨ Features #
- ⏳ Loading State — Built-in progress indicator; button auto-disables during execution.
- ✅ Status Feedback — Success/Error icons shown based on operation result.
- 🎨 Deeply Customizable — Colors, radius, borders, shadows, animations, icons, and more.
- 🔄 Theme Support — Global defaults via
OnProcessButtonDefaultValuesorOnProcessButtonTheme. - 💫 Smooth Animations — Animated size transitions between states.
- 📱 Platform Ready — Android, iOS, Web, macOS, Windows, and Linux.
📦 Installation #
flutter pub add on_process_button_widget
Or add to pubspec.yaml:
dependencies:
on_process_button_widget: ^2.0.12
import 'package:on_process_button_widget/on_process_button_widget.dart';
🚀 Usage #
Basic Button #
onTap runs your async operation. Return true for success, false for error, or null to skip status display entirely.
OnProcessButtonWidget(
onTap: () async {
await submitForm();
return true;
},
child: const Text('Submit'),
)
Filled Button (Default) #
Uses Theme.of(context).colorScheme.primary as background and onPrimary for text/icons automatically. No manual color styling needed on Text() or Icon() children — the button controls all color.
OnProcessButtonWidget(
onTap: () async {
await Future.delayed(const Duration(seconds: 2));
return true;
},
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.download),
SizedBox(width: 8),
Text('Download'),
],
),
)
Outlined Button #
OnProcessButtonWidget(
expanded: false,
border: Border.all(color: Theme.of(context).colorScheme.outline),
backgroundColor: Colors.transparent,
iconColor: Theme.of(context).colorScheme.onSurface,
fontColor: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.normal,
onTap: () async => true,
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.sync, size: 18),
SizedBox(width: 8),
Text('Refresh'),
],
),
)
Colored Action Button (e.g. Danger) #
OnProcessButtonWidget(
expanded: false,
border: Border.all(color: Colors.red),
backgroundColor: Colors.red.withValues(alpha: 0.1),
iconColor: Colors.red,
fontColor: Colors.red,
fontWeight: FontWeight.normal,
onTap: () async {
await performDangerousAction();
return true;
},
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.delete, size: 18),
SizedBox(width: 8),
Text('Delete'),
],
),
)
Disabled Button #
OnProcessButtonWidget(
enable: false,
onTap: () async => true,
child: const Text('Disabled'),
)
Manual Running State #
Set isRunning: true to force the loading state without calling onTap. The callback won't be invoked.
OnProcessButtonWidget(
isRunning: isLoading,
child: const Text('Submit'),
)
Color Rules Summary #
| Style | backgroundColor |
border |
iconColor |
fontColor |
|---|---|---|---|---|
| Filled (default) | (theme primary) | (none) | (theme onPrimary) | (theme onPrimary) |
| Outlined | Colors.transparent |
Border.all(...) |
onSurface |
onSurface |
| Colored action | color.withAlpha(25) |
Border.all(color) |
same color |
same color |
⚠️ Important: Never set
color:orstyle:directly onText()orIcon()children. The button controls text/icon appearance via its own properties (fontColor,iconColor,textStyle,fontWeight). Manual colors will override the button's color system.
🎛️ Key Properties #
| Property | Type | Default | Description |
|---|---|---|---|
onTap |
Future<bool?> Function()? |
null |
Async handler. Return true=success, false=error, null=skip status. |
onDone |
void Function(bool?)? |
null |
Called after tap completes and status finishes displaying. |
onStatusChange |
void Function(BuildContext?, OnProcessButtonStatus)? |
null |
Called when button status changes (stable/running/success/error). |
child |
Widget? |
null |
Primary button content (Text, Row with Icon+Text, etc.). |
expanded |
bool |
true |
Whether button fills available width. |
backgroundColor |
Color? |
theme primary | Background color. |
iconColor |
Color? |
theme onPrimary | Color of status indicators AND child icons. |
fontColor |
Color? |
theme onPrimary | Color of button text. |
fontWeight |
FontWeight? |
bold | Font weight. |
textStyle |
TextStyle? |
titleMedium | Full text style override. |
border |
BoxBorder? |
null |
Border (set for outlined style). |
borderRadius |
BorderRadius? |
8.0 circular | Corner radius. |
boxShadow |
List<BoxShadow>? |
null |
Shadow under the button. |
contentPadding |
EdgeInsetsGeometry? |
horiz:12, vert:4 | Inner padding. |
enable |
bool? |
true |
Whether button is interactive. |
onRunningWidget |
Widget? |
CircularProgressIndicator |
Shown during loading. |
onSuccessWidget |
Widget? |
Icons.done |
Shown on success. |
onErrorWidget |
Widget? |
Icons.error |
Shown on error. |
showRunningStatusWidget |
bool? |
true |
Whether to show the running status widget. |
roundBorderWhenRunning |
bool? |
true |
Circular border during non-stable states. |
expandedIcon |
bool? |
null |
Whether status icon fills available width. |
statusShowingDuration |
Duration? |
2 seconds | How long success/error is displayed. |
isRunning |
bool |
false |
Manually force running/loading state. |
For the complete property reference including interaction callbacks, mouse/focus/highlight, animation, and text styling, see the API Reference section.
🎨 Styling with Properties #
The button provides dedicated properties for all visual aspects. Do not style Text() or Icon() children directly.
OnProcessButtonWidget(
backgroundColor: Colors.deepPurple,
iconColor: Colors.white,
fontColor: Colors.white,
borderRadius: BorderRadius.circular(12),
contentPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
onTap: () async => true,
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.save),
SizedBox(width: 8),
Text('Save Changes'),
],
),
)
🔄 Controller Pattern #
Your controller methods should return Future<bool?>:
class AuthController {
Future<bool?> login() async {
try {
await authService.login(email, password);
return true;
} catch (e) {
return false;
}
}
}
// In your view:
OnProcessButtonWidget(
onTap: controller.login,
child: const Text('Login'),
)
🔀 Conditional Flow #
Show a confirmation dialog before executing, or chain operations:
OnProcessButtonWidget(
onTap: () async {
final confirmed = await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Confirm'),
content: const Text('Proceed with this action?'),
actions: [
TextButton(onPressed: () => Navigator.pop(ctx, false), child: const Text('Cancel')),
TextButton(onPressed: () => Navigator.pop(ctx, true), child: const Text('OK')),
],
),
);
if (confirmed != true) return false;
await controller.dangerousAction();
return true;
},
onDone: (isSuccess) {
if (isSuccess == true) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Action completed')),
);
}
},
child: const Text('Dangerous Action'),
)
🌐 Global Configuration #
OnProcessButtonDefaultValues #
Set app-wide defaults before runApp. Properties are static and persist for the application lifetime.
void main() {
OnProcessButtonDefaultValues.borderRadius = BorderRadius.circular(12);
OnProcessButtonDefaultValues.expandedIcon = true;
OnProcessButtonDefaultValues.roundBorderWhenRunning = false;
OnProcessButtonDefaultValues.onStatusChange = (context, status) {
if (context == null) return;
if (status == OnProcessButtonStatus.running) {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => const AlertDialog(
title: Text('Processing'),
content: Text('Please wait...'),
),
);
} else if (status == OnProcessButtonStatus.stable) {
Navigator.of(context).pop();
}
};
runApp(const MyApp());
}
OnProcessButtonTheme #
Scoped defaults via inherited widget. Resolution order: widget param → OnProcessButtonTheme.of(context) → OnProcessButtonDefaultValues → default.
OnProcessButtonThemeProvider(
data: const OnProcessButtonThemeData(
borderRadius: BorderRadius.all(Radius.circular(16)),
iconColor: Colors.white,
),
child: MyWidget(),
)
📸 Screenshots #
| All Features | Hover Effects |
|---|---|
![]() |
![]() |
| Request Status | Double Process |
![]() |
![]() |
| Custom Styles | Card Mode |
![]() |
![]() |
📚 API Reference #
OnProcessButtonStatus enum #
enum OnProcessButtonStatus { stable, running, success, error }
| Value | Meaning |
|---|---|
stable |
Idle state, showing child. |
running |
onTap is executing, showing onRunningWidget. |
success |
onTap returned true, showing onSuccessWidget. |
error |
onTap returned false, showing onErrorWidget. |
OnProcessButtonWidget #
The main button widget. A StatefulWidget with all properties passed via the constructor.
State Machine
stable → (tap) → running → (result=true) → success → (delay) → stable
→ (result=false) → error → (delay) → stable
→ (result=null) → stable (skip)
Complete Property Reference
Core Callbacks
| Property | Type | Default |
|---|---|---|
onTap |
Future<bool?>? Function()? |
null |
onDone |
void Function(bool?)? |
null |
onStatusChange |
void Function(BuildContext?, OnProcessButtonStatus)? |
null |
Interaction Callbacks
| Property | Type | Default |
|---|---|---|
onLongPress |
void Function()? |
null |
onTapUp |
void Function(TapUpDetails)? |
null |
onTapDown |
void Function(TapDownDetails)? |
null |
onTapCancel |
void Function()? |
null |
onDoubleTap |
void Function()? |
null |
onSecondaryTap |
void Function()? |
null |
onSecondaryTapUp |
void Function(TapUpDetails)? |
null |
onSecondaryTapDown |
void Function(TapDownDetails)? |
null |
onSecondaryTapCancel |
void Function()? |
null |
Mouse / Focus / Highlight
| Property | Type | Default |
|---|---|---|
onHover |
void Function(bool isEnter)? |
null |
onHovering |
void Function(PointerHoverEvent)? |
null |
onFocusChange |
void Function(bool isFocused)? |
null |
onHighlightChanged |
void Function(bool isHighlighted)? |
null |
mouseCursor |
MouseCursor? |
null |
focusNode |
FocusNode? |
null |
Appearance
| Property | Type | Default | Notes |
|---|---|---|---|
backgroundColor |
Color? |
theme primary | Runtime theme fallback |
iconColor |
Color? |
theme onPrimary | Controls status icons AND child icons |
fontColor |
Color? |
theme onPrimary | |
fontWeight |
FontWeight? |
bold |
|
textStyle |
TextStyle? |
titleMedium |
Full override; if null, builds from fontColor + fontWeight |
border |
BoxBorder? |
null |
|
borderRadius |
BorderRadius? |
8.0 circular |
|
boxShadow |
List<BoxShadow>? |
null |
When set, background becomes surface color |
focusColor |
Color? |
null |
|
splashColor |
Color? |
null |
|
highlightColor |
Color? |
null |
|
hoverColor |
Color? |
null |
|
useMaterial3 |
bool? |
true |
Controls which theme colors are referenced |
Layout
| Property | Type | Default | Notes |
|---|---|---|---|
expanded |
bool |
true |
Fills width when true |
expandedIcon |
bool? |
null |
Falls back to expanded |
enable |
bool? |
true |
Disabled = onTap becomes null |
enableFeedback |
bool? |
true |
|
autofocus |
bool? |
true |
|
width |
double? |
null |
Prefer constraints |
height |
double? |
null |
Prefer constraints |
constraints |
BoxConstraints? |
theme derived | Min height = Theme.buttonTheme.height minus border widths |
contentPadding |
EdgeInsetsGeometry? |
horiz:12, vert:4 |
|
margin |
EdgeInsetsGeometry? |
null |
|
alignment |
AlignmentGeometry? |
Alignment.center |
|
iconHeight |
double? |
content-based |
Animation
| Property | Type | Default |
|---|---|---|
animationDuration |
Duration? |
500ms |
animationAlignment |
AlignmentGeometry? |
Alignment.center |
statusShowingDuration |
Duration? |
2s |
roundBorderWhenRunning |
bool? |
true |
Status Widgets
| Property | Type | Default |
|---|---|---|
onRunningWidget |
Widget? |
CircularProgressIndicator (themed) |
onSuccessWidget |
Widget? |
Icon(Icons.done) (themed) |
onErrorWidget |
Widget? |
Icon(Icons.error) (themed) |
showRunningStatusWidget |
bool? |
true |
Text Styling
| Property | Type | Default |
|---|---|---|
textAlign |
TextAlign? |
TextAlign.center |
textOverflow |
TextOverflow? |
TextOverflow.clip |
textHeightBehavior |
TextHeightBehavior? |
null |
textMaxLines |
int? |
null |
textWrap |
bool? |
true |
textWidthBasis |
TextWidthBasis? |
TextWidthBasis.parent |
Misc
| Property | Type | Default |
|---|---|---|
isRunning |
bool |
false |
splashFactory |
InteractiveInkFeatureFactory? |
null |
child |
Widget? |
SizedBox() |
OnProcessButtonDefaultValues #
Static class with nullable fields for all OnProcessButtonWidget properties. Set before runApp(). Values persist for the application lifetime — there is no reset() method.
OnProcessButtonDefaultValues.borderRadius = BorderRadius.circular(12);
OnProcessButtonDefaultValues.expandedIcon = true;
OnProcessButtonDefaultValues.roundBorderWhenRunning = false;
OnProcessButtonThemeData #
Immutable theme data class holding all default values. Use with OnProcessButtonTheme.
const OnProcessButtonThemeData(
borderRadius: BorderRadius.all(Radius.circular(16)),
iconColor: Colors.white,
)
Includes a copyWith() method for deriving modified instances:
final theme = const OnProcessButtonThemeData(iconColor: Colors.white);
final modified = theme.copyWith(iconColor: Colors.black);
OnProcessButtonTheme #
Inherited widget providing scoped defaults. Retrievable via OnProcessButtonTheme.of(context).
OnProcessButtonThemeProvider #
Convenience StatelessWidget wrapping OnProcessButtonTheme.
OnProcessButtonThemeProvider(
data: const OnProcessButtonThemeData(borderRadius: ...),
child: MyWidget(),
)
📄 License #
BSD 3-Clause License — see LICENSE for details.
Made with ❤️ by Shajedur Rahman Panna






