Created
March 13, 2025 14:04
-
-
Save hectorAguero/0de10b2aec54a3f46944826498330302 to your computer and use it in GitHub Desktop.
Sync and Async Button named constructors
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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