import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'app_logger.dart'; import 'exceptions.dart'; /// Global error handler for the application class ErrorHandler { static final ErrorHandler _instance = ErrorHandler._internal(); factory ErrorHandler() => _instance; ErrorHandler._internal(); final AppLogger _logger = AppLogger(); bool _isInitialized = false; /// Initialize global error handling void initialize() { if (_isInitialized) return; // Capture Flutter framework errors FlutterError.onError = (FlutterErrorDetails details) { _handleFlutterError(details); }; // Capture errors in platform-specific code PlatformDispatcher.instance.onError = (error, stack) { _handlePlatformError(error, stack); return true; }; _isInitialized = true; _logger.info('Global error handler initialized'); } /// Run the app with error zone guarding static Future runAppWithErrorHandling( Future Function() appRunner, ) async { final errorHandler = ErrorHandler(); errorHandler.initialize(); // Run app in a guarded zone to catch async errors await runZonedGuarded( () async { await appRunner(); }, (error, stackTrace) { errorHandler._handleZoneError(error, stackTrace); }, ); } /// Handle Flutter framework errors void _handleFlutterError(FlutterErrorDetails details) { // Log the error _logger.error( 'Flutter Error', error: details.exception, stackTrace: details.stack, ); // In debug mode, show the red error screen if (kDebugMode) { FlutterError.presentError(details); } // Optionally report to crash reporting service _reportError(details.exception, details.stack); } /// Handle platform-specific errors bool _handlePlatformError(Object error, StackTrace stackTrace) { _logger.error('Platform Error', error: error, stackTrace: stackTrace); _reportError(error, stackTrace); return true; } /// Handle errors from runZonedGuarded void _handleZoneError(Object error, StackTrace stackTrace) { _logger.error('Async Error', error: error, stackTrace: stackTrace); _reportError(error, stackTrace); } /// Report error to external service (optional) void _reportError(Object error, StackTrace? stackTrace) { // In production, you could send errors to services like: // - Firebase Crashlytics // - Sentry // - Custom error reporting endpoint if (kReleaseMode) { // TODO: Implement error reporting to external service // Example: FirebaseCrashlytics.instance.recordError(error, stackTrace); } } /// Handle and display user-friendly error messages static String getUserFriendlyMessage(Object error) { if (error is AppException) { return error.message; } else if (error is NetworkException) { return '网络连接失败,请检查网络设置'; } else if (error is AuthenticationException) { return '登录失败,请检查账号密码'; } else if (error is ParseException) { return '数据解析失败,请稍后重试'; } else if (error is ValidationException) { return '输入验证失败:${error.message}'; } else if (error is TimeoutException) { return '请求超时,请稍后重试'; } else if (error is FormatException) { return '数据格式错误'; } else { return '发生未知错误,请稍后重试'; } } /// Show error dialog to user static void showErrorDialog( BuildContext context, Object error, { VoidCallback? onRetry, }) { final message = getUserFriendlyMessage(error); showDialog( context: context, builder: (context) => AlertDialog( title: const Text('错误'), content: Text(message), actions: [ if (onRetry != null) TextButton( onPressed: () { Navigator.of(context).pop(); onRetry(); }, child: const Text('重试'), ), TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('确定'), ), ], ), ); } /// Show error snackbar to user static void showErrorSnackBar( BuildContext context, Object error, { Duration duration = const Duration(seconds: 3), }) { final message = getUserFriendlyMessage(error); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), duration: duration, backgroundColor: Colors.red, action: SnackBarAction( label: '关闭', textColor: Colors.white, onPressed: () { ScaffoldMessenger.of(context).hideCurrentSnackBar(); }, ), ), ); } }