Files
AutoJudge-Flutter/lib/utils/error_handler.dart
2025-11-13 09:14:49 +08:00

175 lines
4.8 KiB
Dart

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<void> runAppWithErrorHandling(
Future<void> 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();
},
),
),
);
}
}