humm_error_handler 0.3.0
humm_error_handler: ^0.3.0 copied to clipboard
A comprehensive Flutter error handling library that provides centralized error tracking, logging, storage and display capabilities.
humm_error_handler #
A Flutter library for comprehensive error handling, tracking, and reporting across your applications.
Features #
- ✅ Centralized error handling
- ✅ Error log storage with SharedPreferences
- ✅ Plugin-based architecture for error tracking
- ✅ Integration with Fimber for advanced logging capabilities
- ✅ Zone errors and Flutter errors handling
- ✅ Easily extendable with custom trackers (e.g., Crashlytics, Sentry)
- ✅ User-facing error display with customizable messages
- ✅ Built-in error log viewer UI
Installation #
Add this to your package's pubspec.yaml
file:
dependencies:
humm_error_handler: ^0.2.0
Usage #
Basic Setup #
import 'package:flutter/material.dart';
import 'package:humm_error_handler/humm_error_handler.dart';
import 'package:fimber/fimber.dart';
void main() {
// Setup basic logging
Fimber.plantTree(DebugTree());
runZonedGuarded<Future<void>>(
() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize error handler
await HummErrorHandler.setup(
trackers: [FimberTracker()],
errorDisplayCallback: (String message, {Map<String, dynamic>? additionalData}) {
// Show a toast, dialog, snackbar, etc.
showToast(message);
},
);
runApp(MyApp());
},
(Object error, StackTrace stack) async {
// Delegate zone error handling to HummErrorHandler
HummErrorHandler().handleError(error, stack, source: 'Zone');
},
);
}
Error Display and Translation #
You can easily add user-friendly error messages by providing display and translation callbacks:
await HummErrorHandler.setup(
trackers: [FimberTracker()],
// This will be called when errors occur
errorDisplayCallback: (String message, {Map<String, dynamic>? additionalData}) {
// Show a toast, dialog, snackbar, etc.
showToast(message);
},
// Custom error translation
errorTranslationCallback: (error, stackTrace, source) {
// Translate specific errors to user-friendly messages
if (error is FirebaseFunctionsException) {
return translateFirebaseError(error.message);
}
// Return null to use default error message
return null;
},
// Default error message
defaultErrorMessage: 'Something went wrong',
);
You can also update these settings later:
final errorHandler = HummErrorHandler();
// Set or update display callback
errorHandler.setErrorDisplayCallback((message, {additionalData}) {
showCustomDialog(message);
});
// Set or update translation function
errorHandler.setErrorTranslationCallback((error, stackTrace, source) {
// Your translation logic
return 'User-friendly message';
});
// Update default message
errorHandler.setDefaultErrorMessage('Oops! An error occurred');
// Control which errors are displayed to users
errorHandler.setShouldDisplayErrorCallback((error, stackTrace) {
// Only show critical errors to users
return isCriticalError(error);
});
Integration with Firebase and runZonedGuarded #
Here's a complete example showing how to integrate with Firebase in a Flutter app:
void main() {
// Setup basic logging
Fimber.plantTree(DebugTree());
runZonedGuarded<Future<void>>(
() async {
// Flutter initialization
WidgetsFlutterBinding.ensureInitialized();
// Initialize all app resources
await _initializeApp();
// Run the app
final FirebaseAnalyticsObserver observer = FirebaseAnalyticsObserver(
analytics: FirebaseAnalytics.instance
);
runApp(MyApp(observer: observer));
},
(Object error, StackTrace stack) async {
// Use HummErrorHandler to handle Zone errors
HummErrorHandler().handleError(error, stack, source: 'Zone');
},
);
}
Future<void> _initializeApp() async {
// Setup Firebase
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
// Setup other resources
// ...
// Initialize HummErrorHandler with trackers
final bool crashlogAgreement = await getUserAgreement() ?? false;
await HummErrorHandler.setup(
trackers: <HummErrorTracker>[
FimberTracker(),
CrashlyticsTracker(
crashlytics: FirebaseCrashlytics.instance,
crashlogAgreement: crashlogAgreement,
shouldReportToCrashlytics: !isDevEnvironment || crashlogAgreement,
),
],
errorDisplayCallback: (String message, {Map<String, dynamic>? additionalData}) {
showErrorMessage(message);
},
errorTranslationCallback: (error, stackTrace, source) {
if (error is FirebaseFunctionsException) {
return translateFirebaseError(error.message);
}
return null;
},
defaultErrorMessage: 'Something went wrong',
);
}
Manual Error Handling #
void someFunction() {
try {
// Your code that might throw
throw Exception('Something went wrong');
} catch (e, stackTrace) {
// Get the singleton instance and handle the error
HummErrorHandler().handleError(
e,
stackTrace,
source: 'someFunction',
additionalData: {
'userId': 'user123',
'action': 'login',
},
);
}
}
Adding Custom Trackers #
Firebase Crashlytics Tracker Example #
Create a Crashlytics tracker that respects user preferences:
class CrashlyticsTracker implements HummErrorTracker {
CrashlyticsTracker({
FirebaseCrashlytics? crashlytics,
required this.crashlogAgreement,
required this.shouldReportToCrashlytics,
}) : _crashlytics = crashlytics ?? FirebaseCrashlytics.instance;
final FirebaseCrashlytics _crashlytics;
final bool crashlogAgreement;
final bool shouldReportToCrashlytics;
@override
Future<void> trackError({
required dynamic error,
required StackTrace stackTrace,
String? source,
Map<String, dynamic>? additionalData,
}) async {
// Add additional data as custom keys
if (additionalData != null && additionalData.isNotEmpty) {
for (final MapEntry<String, dynamic> entry in additionalData.entries) {
await _crashlytics.setCustomKey(entry.key, entry.value.toString());
}
}
// Add source as a custom key
if (source != null) {
await _crashlytics.setCustomKey('error_source', source);
}
// Record error to Crashlytics
await _crashlytics.recordError(
error,
stackTrace,
reason: 'Error from ${source ?? 'unknown'}',
fatal: true,
);
// Add a log
await _crashlytics.log('Error: $error');
}
@override
bool shouldHandleCrashlog() {
// Only report to Crashlytics if:
// 1. User has given consent or we're in production
// 2. Reporting hasn't been explicitly disabled
return shouldReportToCrashlytics;
}
}
Migration from runZonedGuarded Direct Implementation #
If you're migrating from a direct implementation of runZonedGuarded
to using the humm_error_handler
library, follow these steps:
- Keep your
runZonedGuarded
block, but update the error handler to useHummErrorHandler().handleError
- Move your app initialization to a separate method for clarity
- Configure
HummErrorHandler
with appropriate trackers
Before: #
void main() async {
runZonedGuarded<Future<void>>(
() async {
WidgetsFlutterBinding.ensureInitialized();
// ... app initialization
runApp(MyApp());
},
(Object error, StackTrace stack) async => handleError(error, stack),
);
}
After: #
void main() {
// Basic logging setup
Fimber.plantTree(DebugTree());
runZonedGuarded<Future<void>>(
() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize app resources
await _initializeApp();
// Run the app
runApp(MyApp());
},
(Object error, StackTrace stack) async {
// Use HummErrorHandler
HummErrorHandler().handleError(error, stack, source: 'Zone');
},
);
}
Future<void> _initializeApp() async {
// App initialization
// ...
// Configure HummErrorHandler
await HummErrorHandler.setup(
trackers: [
FimberTracker(),
CrashlyticsTracker(),
// Other trackers
],
// ...
);
}
License #
This project is licensed under the MIT License - see the LICENSE file for details.