Skip to content

Instantly share code, notes, and snippets.

@hectorAguero
Created March 13, 2025 14:04
Show Gist options
  • Save hectorAguero/0de10b2aec54a3f46944826498330302 to your computer and use it in GitHub Desktop.
Save hectorAguero/0de10b2aec54a3f46944826498330302 to your computer and use it in GitHub Desktop.
Sync and Async Button named constructors
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
spacing: 8,
children: [
CustomElevatedButton.sync(
child: Text('Sync Button'),
onPressed: syncMethod,
),
CustomElevatedButton.async(
child: Text('Async Button'),
onPressed: asyncMethod,
),
],
),
),
),
);
}
void syncMethod() {
print('Sync Hello');
}
Future<void> asyncMethod() async {
await Future.delayed(Duration(seconds: 1));
print('Async Hello');
}
}
class CustomElevatedButton extends StatefulWidget {
/// General constructor that allows both sync and async callbacks.
/// At least one callback must be non-null.
const CustomElevatedButton.sync({
required this.child,
required this.onPressed,
this.loadingChild,
super.key,
// ignore: avoid_field_initializers_in_const_classes
}) : onAsyncPressed = null;
const CustomElevatedButton.async({
required this.child,
required Future<void> Function()? onPressed,
this.loadingChild,
super.key,
}) : onPressed = null,
onAsyncPressed = onPressed;
final Widget child;
final Widget? loadingChild;
final Future<void> Function()? onAsyncPressed;
final VoidCallback? onPressed;
@override
State<CustomElevatedButton> createState() => _CustomElevatedButtonState();
}
class _CustomElevatedButtonState extends State<CustomElevatedButton> {
bool _isLoading = false;
Future<void> _handlePressed() async {
// If the async callback is provided, use it.
if (widget.onAsyncPressed != null) {
// Prevent multiple presses.
if (_isLoading) return;
setState(() => _isLoading = true);
try {
await widget.onAsyncPressed!();
} finally {
// Ensure that state is updated even if an exception occurs.
if (mounted) setState(() => _isLoading = false);
}
} else {
// Fallback to synchronous callback.
widget.onPressed?.call();
}
}
@override
Widget build(BuildContext context) => ElevatedButton(
onPressed: _isLoading ? null : _handlePressed,
child: switch (_isLoading) {
true =>
widget.loadingChild ??
const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
false => widget.child,
},
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment