140 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			140 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'dart:developer';
 | |
| 
 | |
| import 'package:dio/dio.dart';
 | |
| import 'package:easy_localization/easy_localization.dart';
 | |
| import 'package:flutter/material.dart';
 | |
| import 'package:flutter_platform_alert/flutter_platform_alert.dart';
 | |
| import 'package:gap/gap.dart';
 | |
| import 'package:styled_widget/styled_widget.dart';
 | |
| 
 | |
| // TODO support web here
 | |
| 
 | |
| String _parseRemoteError(DioException err) {
 | |
|   log('${err.requestOptions.method} ${err.requestOptions.uri} ${err.message}');
 | |
|   if (err.response?.data is String) return err.response?.data;
 | |
|   if (err.response?.data?['errors'] != null) {
 | |
|     final errors = err.response?.data['errors'] as Map<String, dynamic>;
 | |
|     return errors.values
 | |
|         .map(
 | |
|           (ele) =>
 | |
|               (ele as List<dynamic>).map((ele) => ele.toString()).join('\n'),
 | |
|         )
 | |
|         .join('\n');
 | |
|   }
 | |
|   return err.message ?? err.toString();
 | |
| }
 | |
| 
 | |
| void showErrorAlert(dynamic err) async {
 | |
|   final text = switch (err) {
 | |
|     String _ => err,
 | |
|     DioException _ => _parseRemoteError(err),
 | |
|     Exception _ => err.toString(),
 | |
|     _ => err.toString(),
 | |
|   };
 | |
|   FlutterPlatformAlert.showAlert(
 | |
|     windowTitle: 'somethingWentWrong'.tr(),
 | |
|     text: text,
 | |
|     alertStyle: AlertButtonStyle.ok,
 | |
|     iconStyle: IconStyle.error,
 | |
|   );
 | |
| }
 | |
| 
 | |
| void showInfoAlert(String message, String title) async {
 | |
|   FlutterPlatformAlert.showAlert(
 | |
|     windowTitle: title,
 | |
|     text: message,
 | |
|     alertStyle: AlertButtonStyle.ok,
 | |
|     iconStyle: IconStyle.information,
 | |
|   );
 | |
| }
 | |
| 
 | |
| Future<bool> showConfirmAlert(String message, String title) async {
 | |
|   final result = await FlutterPlatformAlert.showAlert(
 | |
|     windowTitle: title,
 | |
|     text: message,
 | |
|     alertStyle: AlertButtonStyle.okCancel,
 | |
|     iconStyle: IconStyle.question,
 | |
|   );
 | |
|   return result == AlertButton.okButton;
 | |
| }
 | |
| 
 | |
| 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();
 | |
| }
 |