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; return errors.values .map( (ele) => (ele as List).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 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(); }