import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../providers/error_provider.dart'; /// 全局错误处理组件 class GlobalErrorHandler extends ConsumerWidget { final Widget child; const GlobalErrorHandler({super.key, required this.child}); @override Widget build(BuildContext context, WidgetRef ref) { final errorState = ref.watch(errorProvider); // 监听错误状态变化 ref.listen(errorProvider, (previous, next) { if (next.currentError != null) { _showErrorDialog(context, next.currentError!, ref); } }); return child; } void _showErrorDialog(BuildContext context, AppError error, WidgetRef ref) { showDialog( context: context, barrierDismissible: false, builder: (context) => ErrorDialog( error: error, onDismiss: () { Navigator.of(context).pop(); ref.read(errorProvider.notifier).removeError(error.id); }, onRetry: null, ), ); } } /// 错误对话框 class ErrorDialog extends StatelessWidget { final AppError error; final VoidCallback onDismiss; final VoidCallback? onRetry; const ErrorDialog({ super.key, required this.error, required this.onDismiss, this.onRetry, }); @override Widget build(BuildContext context) { return AlertDialog( title: Row( children: [ Icon( _getErrorIcon(), color: _getErrorColor(), ), const SizedBox(width: 8), Text(_getErrorTitle()), ], ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(error.message), if (error.details != null) ...[ const SizedBox(height: 8), Text( error.details!, style: Theme.of(context).textTheme.bodySmall, ), ], ], ), actions: [ if (onRetry != null) TextButton( onPressed: () { onDismiss(); onRetry!(); }, child: const Text('重试'), ), TextButton( onPressed: onDismiss, child: const Text('确定'), ), ], ); } IconData _getErrorIcon() { switch (error.type) { case ErrorType.network: return Icons.wifi_off; case ErrorType.authentication: return Icons.lock; case ErrorType.validation: return Icons.warning; case ErrorType.server: return Icons.error; case ErrorType.unknown: default: return Icons.help_outline; } } Color _getErrorColor() { switch (error.severity) { case ErrorSeverity.critical: return Colors.red; case ErrorSeverity.error: return Colors.orange; case ErrorSeverity.warning: return Colors.yellow[700]!; case ErrorSeverity.info: return Colors.blue; } } String _getErrorTitle() { switch (error.type) { case ErrorType.network: return '网络错误'; case ErrorType.authentication: return '认证错误'; case ErrorType.validation: return '验证错误'; case ErrorType.server: return '服务器错误'; case ErrorType.unknown: default: return '未知错误'; } } } /// 错误横幅组件 class ErrorBanner extends ConsumerWidget { const ErrorBanner({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final errorState = ref.watch(errorProvider); if (!errorState.hasErrors) { return const SizedBox.shrink(); } final lowSeverityErrors = errorState.errors .where((error) => error.severity == ErrorSeverity.info) .toList(); if (lowSeverityErrors.isEmpty) { return const SizedBox.shrink(); } return Container( width: double.infinity, padding: const EdgeInsets.all(12), color: Colors.blue[50], child: Row( children: [ Icon( Icons.info_outline, color: Colors.blue[700], size: 20, ), const SizedBox(width: 8), Expanded( child: Text( lowSeverityErrors.first.message, style: TextStyle( color: Colors.blue[700], fontSize: 14, ), ), ), IconButton( onPressed: () { ref.read(errorProvider.notifier).removeError(lowSeverityErrors.first.id); }, icon: Icon( Icons.close, color: Colors.blue[700], size: 20, ), ), ], ), ); } } /// 错误重试组件 class ErrorRetryWidget extends StatelessWidget { final String message; final VoidCallback onRetry; final IconData? icon; const ErrorRetryWidget({ super.key, required this.message, required this.onRetry, this.icon, }); @override Widget build(BuildContext context) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( icon ?? Icons.error_outline, size: 64, color: Colors.grey[400], ), const SizedBox(height: 16), Text( message, style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: Colors.grey[600], ), textAlign: TextAlign.center, ), const SizedBox(height: 16), ElevatedButton.icon( onPressed: onRetry, icon: const Icon(Icons.refresh), label: const Text('重试'), ), ], ), ); } }