92 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			92 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'package:easy_localization/easy_localization.dart';
 | |
| import 'package:flutter/material.dart';
 | |
| import 'package:gap/gap.dart';
 | |
| import 'package:styled_widget/styled_widget.dart';
 | |
| 
 | |
| export 'content/alert.native.dart'
 | |
|     if (dart.library.html) 'content/alert.web.dart';
 | |
| 
 | |
| void showSnackBar(BuildContext context, String message) {
 | |
|   showSnackBar(context, message);
 | |
| }
 | |
| 
 | |
| OverlayEntry? _loadingOverlay;
 | |
| GlobalKey<_FadeOverlayState> _loadingOverlayKey = GlobalKey();
 | |
| 
 | |
| class _FadeOverlay extends StatefulWidget {
 | |
|   const _FadeOverlay({super.key, required this.child});
 | |
|   final Widget child;
 | |
| 
 | |
|   @override
 | |
|   State<_FadeOverlay> createState() => _FadeOverlayState();
 | |
| }
 | |
| 
 | |
| class _FadeOverlayState extends State<_FadeOverlay> {
 | |
|   bool _visible = false;
 | |
| 
 | |
|   @override
 | |
|   void initState() {
 | |
|     super.initState();
 | |
|     WidgetsBinding.instance.addPostFrameCallback((_) {
 | |
|       setState(() => _visible = true);
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     return AnimatedOpacity(
 | |
|       opacity: _visible ? 1.0 : 0.0,
 | |
|       duration: const Duration(milliseconds: 200),
 | |
|       child: widget.child,
 | |
|     );
 | |
|   }
 | |
| }
 | |
| 
 | |
| void showLoadingModal(BuildContext context) {
 | |
|   if (_loadingOverlay != null) return;
 | |
| 
 | |
|   _loadingOverlay = OverlayEntry(
 | |
|     builder:
 | |
|         (context) => _FadeOverlay(
 | |
|           key: _loadingOverlayKey,
 | |
|           child: Material(
 | |
|             color: Colors.black54,
 | |
|             child: Center(
 | |
|               child: Material(
 | |
|                 color: Colors.white,
 | |
|                 borderRadius: BorderRadius.circular(8),
 | |
|                 elevation: 4,
 | |
|                 child: Column(
 | |
|                   mainAxisSize: MainAxisSize.min,
 | |
|                   children: [
 | |
|                     CircularProgressIndicator(year2023: true),
 | |
|                     const Gap(24),
 | |
|                     Text('loading'.tr()),
 | |
|                   ],
 | |
|                 ).padding(all: 32),
 | |
|               ),
 | |
|             ),
 | |
|           ),
 | |
|         ),
 | |
|   );
 | |
| 
 | |
|   Overlay.of(context).insert(_loadingOverlay!);
 | |
| }
 | |
| 
 | |
| void hideLoadingModal(BuildContext context) async {
 | |
|   if (_loadingOverlay == null) return;
 | |
| 
 | |
|   final entry = _loadingOverlay!;
 | |
|   _loadingOverlay = null;
 | |
| 
 | |
|   final state = entry.mounted ? _loadingOverlayKey.currentState : null;
 | |
| 
 | |
|   if (state != null) {
 | |
|     // ignore: invalid_use_of_protected_member
 | |
|     state.setState(() => state._visible = false);
 | |
|     await Future.delayed(const Duration(milliseconds: 200));
 | |
|   }
 | |
| 
 | |
|   entry.remove();
 | |
| }
 |