flow_button 1.0.0
flow_button: ^1.0.0 copied to clipboard
A customizable animated button widget for Flutter with built-in loading, success and error states, smooth transitions, and a tap scale effect.
import 'package:flutter/material.dart';
import 'package:flow_button/flow_button.dart';
void main() => runApp(const ExampleApp());
/// Demonstrates the full range of [AnimatedButton] configurations.
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'flow_button demo',
theme: ThemeData(
colorSchemeSeed: Colors.indigo,
brightness: Brightness.light,
),
darkTheme: ThemeData(
colorSchemeSeed: Colors.indigo,
brightness: Brightness.dark,
),
themeMode: ThemeMode.system,
home: const ExampleHomePage(),
);
}
}
class ExampleHomePage extends StatefulWidget {
const ExampleHomePage({super.key});
@override
State<ExampleHomePage> createState() => _ExampleHomePageState();
}
class _ExampleHomePageState extends State<ExampleHomePage> {
late final AnimatedButtonController _controlledController =
AnimatedButtonController();
@override
void dispose() {
_controlledController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('AnimatedButton Showcase')),
body: ListView(
padding: const EdgeInsets.all(24),
children: [
const _SectionTitle('Normal'),
AnimatedButton(onPressed: () {}, child: const Text('Continue')),
const SizedBox(height: 24),
const _SectionTitle('Loading (auto, from a Future)'),
AnimatedButton(
onPressed: () async {
await Future.delayed(const Duration(seconds: 2));
},
child: const Text('Login'),
),
const SizedBox(height: 24),
const _SectionTitle('Success'),
AnimatedButton(
resetDelay: const Duration(seconds: 2),
onPressed: () async {
await Future.delayed(const Duration(seconds: 1));
},
child: const Text('Save changes'),
),
const SizedBox(height: 24),
const _SectionTitle('Error'),
AnimatedButton(
resetDelay: const Duration(seconds: 2),
backgroundColor: Colors.deepOrange,
onPressed: () async {
await Future.delayed(const Duration(seconds: 1));
throw Exception('Network error');
},
child: const Text('Submit payment'),
),
const SizedBox(height: 24),
const _SectionTitle('Disabled'),
AnimatedButton(
enabled: false,
onPressed: () {},
child: const Text('Unavailable'),
),
const SizedBox(height: 24),
const _SectionTitle('With icon'),
AnimatedButton(
icon: const Icon(Icons.cloud_upload_outlined),
onPressed: () async {
await Future.delayed(const Duration(seconds: 1));
},
child: const Text('Upload'),
),
const SizedBox(height: 24),
const _SectionTitle('Custom colors & border'),
AnimatedButton(
backgroundColor: Colors.white,
foregroundColor: Colors.black87,
border: Border.all(color: Colors.black12, width: 1.5),
borderRadius: const BorderRadius.all(Radius.circular(8)),
onPressed: () {},
child: const Text('Outlined style'),
),
const SizedBox(height: 24),
const _SectionTitle('Gradient background'),
AnimatedButton(
gradient: const LinearGradient(
colors: [Colors.purple, Colors.pinkAccent],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
onPressed: () async {
await Future.delayed(const Duration(seconds: 1));
},
child: const Text('Upgrade to Pro'),
),
const SizedBox(height: 24),
const _SectionTitle('Custom loading indicator'),
AnimatedButton(
loadingIndicator: const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
strokeWidth: 3,
color: Colors.amber,
),
),
onPressed: () async {
await Future.delayed(const Duration(seconds: 2));
},
child: const Text('Sync now'),
),
const SizedBox(height: 24),
const _SectionTitle('Controller-driven'),
Row(
children: [
Expanded(
child: AnimatedButton(
controller: _controlledController,
onPressed: () {},
child: const Text('Controlled'),
),
),
],
),
const SizedBox(height: 8),
Wrap(
spacing: 8,
children: [
OutlinedButton(
onPressed: _controlledController.setLoading,
child: const Text('Set loading'),
),
OutlinedButton(
onPressed: _controlledController.setSuccess,
child: const Text('Set success'),
),
OutlinedButton(
onPressed: _controlledController.setError,
child: const Text('Set error'),
),
OutlinedButton(
onPressed: _controlledController.setIdle,
child: const Text('Reset'),
),
],
),
const SizedBox(height: 24),
const _SectionTitle('Sizes'),
Wrap(
spacing: 12,
runSpacing: 12,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
AnimatedButton(
height: 36,
onPressed: () {},
child: const Text('Small'),
),
AnimatedButton(
height: 48,
onPressed: () {},
child: const Text('Medium'),
),
AnimatedButton(
height: 60,
onPressed: () {},
child: const Text('Large'),
),
AnimatedButton(
width: 240,
height: 48,
onPressed: () {},
child: const Text('Fixed width'),
),
],
),
const SizedBox(height: 24),
],
),
);
}
}
class _SectionTitle extends StatelessWidget {
const _SectionTitle(this.title);
final String title;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Text(title, style: Theme.of(context).textTheme.titleMedium),
);
}
}