UI & Widgetsbeginner
Loading Overlay
Reusable loading overlay widget that blocks interactions while async operations run.
#loading#overlay#ux
dart
| 1 | class LoadingOverlay extends StatelessWidget { |
| 2 | const LoadingOverlay({ |
| 3 | super.key, |
| 4 | required this.isLoading, |
| 5 | required this.child, |
| 6 | this.message, |
| 7 | this.opacity = 0.5, |
| 8 | }); |
| 9 | |
| 10 | final bool isLoading; |
| 11 | final Widget child; |
| 12 | final String? message; |
| 13 | final double opacity; |
| 14 | |
| 15 | @override |
| 16 | Widget build(BuildContext context) { |
| 17 | return Stack( |
| 18 | children: [ |
| 19 | child, |
| 20 | if (isLoading) |
| 21 | Positioned.fill( |
| 22 | child: IgnorePointer( |
| 23 | child: AnimatedOpacity( |
| 24 | opacity: isLoading ? 1.0 : 0.0, |
| 25 | duration: const Duration(milliseconds: 200), |
| 26 | child: Container( |
| 27 | color: Colors.black.withOpacity(opacity), |
| 28 | child: Center( |
| 29 | child: Container( |
| 30 | padding: const EdgeInsets.all(24), |
| 31 | decoration: BoxDecoration( |
| 32 | color: Theme.of(context).colorScheme.surface, |
| 33 | borderRadius: BorderRadius.circular(12), |
| 34 | ), |
| 35 | child: Column( |
| 36 | mainAxisSize: MainAxisSize.min, |
| 37 | children: [ |
| 38 | const CircularProgressIndicator(), |
| 39 | if (message != null) ...[ |
| 40 | const SizedBox(height: 12), |
| 41 | Text(message!, style: Theme.of(context).textTheme.bodyMedium), |
| 42 | ], |
| 43 | ], |
| 44 | ), |
| 45 | ), |
| 46 | ), |
| 47 | ), |
| 48 | ), |
| 49 | ), |
| 50 | ), |
| 51 | ], |
| 52 | ); |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | // Usage |
| 57 | LoadingOverlay( |
| 58 | isLoading: controller.isLoading, |
| 59 | message: 'Saving...', |
| 60 | child: YourPageContent(), |
| 61 | ) |