import 'package:flutter/material.dart'; import '../../core/app_keys.dart'; import '../../core/theme/app_colors.dart'; import '../../core/theme/app_dimens.dart'; import '../../core/theme/app_typography.dart'; /// `uni.showToast({ title, icon })` 的等价实现。 /// 不依赖调用方的 BuildContext,可在任意异步回调里安全使用。 void showAppToast(String title, {bool success = false}) { final messenger = AppKeys.scaffoldMessenger.currentState; if (messenger == null) return; messenger ..clearSnackBars() ..showSnackBar( SnackBar( content: Row( children: [ if (success) ...[ const Icon( Icons.check_circle, size: 18, color: AppColors.success, ), const SizedBox(width: 8), ], Expanded( child: Text( title, style: AppTypography.bodyMd.copyWith(color: AppColors.surface), ), ), ], ), backgroundColor: AppColors.inverseSurface, behavior: SnackBarBehavior.floating, margin: const EdgeInsets.all(AppSpacing.md), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppRadius.lg), ), duration: Duration(milliseconds: success ? 1500 : 2200), ), ); } /// `uni.showLoading({ title })` 的等价实现,返回关闭函数。 VoidCallback showAppLoading([String title = '加载中...']) { final navigator = AppKeys.rootNavigator.currentState; if (navigator == null) return () {}; var closed = false; showDialog( context: navigator.context, barrierDismissible: false, builder: (_) => PopScope( canPop: false, child: Center( child: Container( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 20), decoration: BoxDecoration( color: const Color(0xCC000000), borderRadius: BorderRadius.circular(AppRadius.lg), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ const SizedBox( width: 26, height: 26, child: CircularProgressIndicator( strokeWidth: 2.4, valueColor: AlwaysStoppedAnimation(Colors.white), ), ), const SizedBox(height: 12), Text( title, style: AppTypography.bodyMd.copyWith(color: Colors.white), ), ], ), ), ), ), ); return () { if (closed) return; closed = true; final current = AppKeys.rootNavigator.currentState; if (current != null && current.canPop()) current.pop(); }; } /// `uni.showModal({ title, content })` 的等价实现,返回用户是否点了确认。 Future showAppConfirm({ required String title, required String content, String confirmText = '确定', String cancelText = '取消', bool destructive = false, }) async { final navigator = AppKeys.rootNavigator.currentState; if (navigator == null) return false; final result = await showDialog( context: navigator.context, builder: (ctx) => AlertDialog( backgroundColor: AppColors.surfaceContainerLowest, surfaceTintColor: Colors.transparent, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppRadius.xl), ), title: Text( title, textAlign: TextAlign.center, style: AppTypography.bodyLg.copyWith(fontWeight: FontWeight.w700), ), content: Text( content, textAlign: TextAlign.center, style: AppTypography.bodyMd.copyWith(color: AppColors.textMuted), ), actionsAlignment: MainAxisAlignment.spaceEvenly, actions: [ TextButton( onPressed: () => Navigator.of(ctx).pop(false), child: Text( cancelText, style: AppTypography.bodyLg.copyWith(color: AppColors.textMuted), ), ), TextButton( onPressed: () => Navigator.of(ctx).pop(true), child: Text( confirmText, style: AppTypography.bodyLg.copyWith( color: destructive ? AppColors.tertiaryContainer : AppColors.primary, ), ), ), ], ), ); return result ?? false; }