crash_manager 1.0.1
crash_manager: ^1.0.1 copied to clipboard
A comprehensive Flutter crash manager package with Firebase Crashlytics and Sentry support.
Flutter Crash Manager #
A comprehensive Flutter crash reporting package that provides a unified interface for multiple crash reporting services including Firebase Crashlytics and Sentry.
Features #
- Unified Interface: Single API for multiple crash reporting services
- Firebase Crashlytics Support: Complete integration with Firebase Crashlytics
- Sentry Support: Full Sentry integration for cross-platform error tracking
- Multi-Service Support: Use multiple crash reporting services simultaneously
- Rich Context: User context, device information, and custom attributes
- Breadcrumb Logging: Detailed debugging trails
- Crash Filtering: Custom filters to control which crashes are reported
- Privacy Controls: Built-in sensitive data filtering
- Type Safety: Comprehensive type safety with proper null handling
- Production Ready: Built for high-performance production applications
Installation #
Add this package to your pubspec.yaml
:
dependencies:
crash_manager:
git:
url: https://github.com/bahricanyesil/flutter-crash-manager.git
Quick Start #
1. Basic Setup #
import 'package:crash_manager/crash_manager.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Create and initialize your preferred crash manager (already initialized!)
final crashManager = await FirebaseCrashManager.create();
// Set user context
await crashManager.setUserContext(UserContext(
userId: 'user123',
email: 'user@example.com',
name: 'John Doe',
));
runApp(MyApp());
}
2. Recording Crashes #
try {
// Your code that might throw
riskyOperation();
} catch (error, stackTrace) {
// Record fatal crash
await crashManager.recordFatalCrash(error, stackTrace);
// Or record non-fatal error
await crashManager.recordNonFatalError(error, stackTrace);
}
3. Adding Context and Breadcrumbs #
// Add custom attributes
await crashManager.setCustomAttribute('feature', 'user_profile');
await crashManager.setCustomAttribute('experiment_id', 'exp_123');
// Add breadcrumbs for debugging
await crashManager.addBreadcrumb('User clicked login button');
await crashManager.addBreadcrumb('API call started', data: {'endpoint': '/login'});
Firebase Crashlytics Setup #
Android Setup #
-
Add Firebase to your Android app:
- Go to Firebase Console
- Create a new project or use existing one
- Add your Android app to the project
- Download
google-services.json
and place it inandroid/app/
-
Configure build.gradle files:
Project-level
android/build.gradle
:buildscript { dependencies { classpath 'com.google.gms:google-services:4.4.0' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.9' } }
App-level
android/app/build.gradle
:apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.firebase.crashlytics' android { compileSdkVersion 34 defaultConfig { multiDexEnabled true } } dependencies { implementation 'com.google.firebase:firebase-crashlytics' implementation 'com.google.firebase:firebase-analytics' }
iOS Setup #
-
Add Firebase to your iOS app:
- In Firebase Console, add your iOS app
- Download
GoogleService-Info.plist
- Add the file to your Xcode project in
ios/Runner/
-
Configure Xcode:
- Open
ios/Runner.xcworkspace
- Ensure
GoogleService-Info.plist
is added to the Runner target - No additional configuration needed for Crashlytics
- Open
Firebase Initialization Scenarios #
The Firebase crash manager handles multiple initialization scenarios automatically:
Scenario 1: Auto-Initialize Firebase (Simplest)
import 'package:crash_manager/crash_manager.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Auto-initializes Firebase if not already done
final crashManager = await FirebaseCrashManager.create();
runApp(MyApp());
}
Scenario 2: User Already Initialized Firebase (Most Common)
import 'package:firebase_core/firebase_core.dart';
import 'package:crash_manager/crash_manager.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// User initializes Firebase for other services (Auth, Firestore, etc.)
await Firebase.initializeApp();
// Crash manager detects existing Firebase and uses it
final crashManager = await FirebaseCrashManager.create();
runApp(MyApp());
}
Scenario 3: Custom Firebase Options
import 'package:firebase_core/firebase_core.dart';
import 'package:crash_manager/crash_manager.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize with custom Firebase options
final crashManager = await FirebaseCrashManager.createWithOptions(
firebaseOptions: FirebaseOptions(
apiKey: 'your-api-key',
appId: 'your-app-id',
messagingSenderId: 'your-sender-id',
projectId: 'your-project-id',
// ... other options
),
);
runApp(MyApp());
}
Scenario 4: Multiple Firebase Apps
import 'package:firebase_core/firebase_core.dart';
import 'package:crash_manager/crash_manager.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize multiple Firebase apps
await Firebase.initializeApp(); // Default app
await Firebase.initializeApp(
name: 'secondary',
options: secondaryOptions,
);
// Use specific Firebase app for crash reporting
final crashManager = await FirebaseCrashManager.create(
firebaseAppName: 'secondary',
);
runApp(MyApp());
}
Scenario 5: Explicit Control (No Auto-Initialize)
import 'package:firebase_core/firebase_core.dart';
import 'package:crash_manager/crash_manager.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// You control Firebase initialization
await Firebase.initializeApp();
// Crash manager assumes Firebase is already initialized
final crashManager = await FirebaseCrashManager.createWithExistingFirebase();
runApp(MyApp());
}
Error Handling for Missing Configuration
If Firebase configuration files are missing, you'll get a helpful error:
Firebase configuration files not found. Please ensure you have:
• Android: google-services.json in android/app/
• iOS: GoogleService-Info.plist in ios/Runner/
Or provide FirebaseOptions manually.
See: https://firebase.flutter.dev/docs/overview#installation
Sentry Setup #
1. Create Sentry Account and Project #
- Go to Sentry.io and create an account
- Create a new Flutter project
- Copy your DSN from the project settings
2. Initialize Sentry with Configuration #
Unlike Firebase, Sentry requires configuration to be passed directly to the crash manager:
import 'package:crash_manager/crash_manager.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Create and initialize Sentry crash manager with configuration (already initialized!)
final crashManager = await SentryCrashManager.create(
config: SentryConfig(
dsn: 'YOUR_SENTRY_DSN_HERE', // Required - get from Sentry dashboard
environment: 'production', // or 'development', 'staging'
release: '1.0.0', // Your app version
debug: kDebugMode, // Enable debug logs in debug mode
tracesSampleRate: 1.0, // Performance monitoring sample rate
maxBreadcrumbs: 100, // Maximum breadcrumbs to keep
enableAutoSessionTracking: true, // Track user sessions
attachScreenshot: true, // Attach screenshots on crashes (iOS/Android)
attachViewHierarchy: true, // Attach view hierarchy (iOS/Android)
tags: {
'team': 'mobile',
'platform': 'flutter',
},
inAppIncludes: ['com.yourcompany.yourapp'], // Filter stack traces
),
);
runApp(MyApp());
}
3. Advanced Configuration Options #
The SentryConfig
class provides comprehensive configuration options:
const SentryConfig(
dsn: 'YOUR_SENTRY_DSN_HERE', // Required
// Environment settings
environment: 'production', // deployment environment
release: '1.0.0+1', // app version/build
// Performance monitoring
tracesSampleRate: 1.0, // 0.0 to 1.0 (0% to 100%)
// Debug settings
debug: false, // enable debug logging
// Breadcrumbs
maxBreadcrumbs: 100, // max breadcrumbs to keep in memory
// Session tracking
enableAutoSessionTracking: true, // automatically track sessions
// Screenshots and view hierarchy (mobile only)
attachScreenshot: false, // attach screenshots on crashes
attachViewHierarchy: false, // attach view hierarchy on crashes
// Stack trace filtering
inAppIncludes: ['com.yourcompany.yourapp'], // include patterns
inAppExcludes: ['flutter', 'dart'], // exclude patterns
// Global tags
tags: {
'team': 'mobile',
'platform': 'flutter',
'version': '1.0.0',
},
);
Multi-Service Setup #
Use both Firebase Crashlytics and Sentry simultaneously:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Create multi crash manager with both services (already initialized!)
final crashManager = await MultiCrashManager.create();
// Create and add Firebase Crashlytics
final firebaseManager = await FirebaseCrashManager.create();
crashManager.addManager(firebaseManager);
// Create and add Sentry with configuration
final sentryManager = await SentryCrashManager.create(
config: SentryConfig(
dsn: 'YOUR_SENTRY_DSN_HERE',
environment: 'production',
debug: kDebugMode,
tracesSampleRate: 0.1, // Lower sample rate for production
enableAutoSessionTracking: true,
tags: {
'service': 'multi_crash_manager',
'platform': 'flutter',
},
),
);
crashManager.addManager(sentryManager);
runApp(MyApp());
}
Advanced Usage #
Custom Crash Filtering #
// Set up custom crash filter
crashManager.setCrashFilter((crashReport) {
// Don't report network errors in debug mode
if (kDebugMode && crashReport.message.contains('SocketException')) {
return false;
}
// Don't report crashes from specific users
if (crashReport.userId == 'test_user') {
return false;
}
return true;
});
Comprehensive Context Setup #
// Set device information
final deviceInfo = await DeviceInfoCollector.collect();
await crashManager.setDeviceInfo(deviceInfo);
// Set user context with custom attributes
await crashManager.setUserContext(UserContext(
userId: 'user123',
email: 'user@example.com',
name: 'John Doe',
role: 'premium',
customAttributes: {
'subscription_tier': 'premium',
'feature_flags': ['new_ui', 'beta_features'],
'last_login': DateTime.now().toIso8601String(),
},
));
// Set session ID
await crashManager.setSessionId('session_abc123');
Manual Crash Reporting #
// Create custom crash report
final crashReport = CrashUtils.createCrashReport(
error: Exception('Custom error occurred'),
stackTrace: StackTrace.current,
isFatal: false,
customAttributes: {
'user_action': 'button_click',
'screen': 'profile_page',
},
userId: 'user123',
sessionId: 'session_abc123',
);
await crashManager.recordCrashReport(crashReport);
Performance Monitoring #
// Add performance breadcrumbs
await crashManager.addBreadcrumb(
'API call started',
data: {
'endpoint': '/api/users',
'method': 'GET',
'timestamp': DateTime.now().toIso8601String(),
},
level: CrashLogLevel.info,
);
// Measure operation performance
final stopwatch = Stopwatch()..start();
try {
await performOperation();
await crashManager.addBreadcrumb(
'Operation completed successfully',
data: {'duration_ms': stopwatch.elapsedMilliseconds},
);
} catch (error, stackTrace) {
await crashManager.recordNonFatalError(
error,
stackTrace,
customAttributes: {
'operation_duration_ms': stopwatch.elapsedMilliseconds,
'operation_type': 'api_call',
},
);
}
API Reference #
CrashManager Interface #
abstract class CrashManager {
Future<void> initialize();
Future<void> setUserContext(UserContext userContext);
Future<void> recordFatalCrash(Object error, StackTrace stackTrace);
Future<void> recordNonFatalError(Object error, StackTrace? stackTrace);
Future<void> addBreadcrumb(String message, {Map<String, Object?>? data});
Future<void> setCustomAttribute(String key, Object? value);
void setCrashFilter(bool Function(CrashReport report) filter);
// ... and more
}
Data Models #
CrashReport
class CrashReport extends BaseDataModel<CrashReport> {
final String id;
final String message;
final String stackTrace;
final String errorType;
final DateTime timestamp;
final bool isFatal;
final Map<String, Object?> userContext;
final Map<String, Object?> customAttributes;
// ... and more
}
UserContext
class UserContext extends BaseDataModel<UserContext> {
final String? userId;
final String? email;
final String? name;
final String? role;
final Map<String, Object?> customAttributes;
final bool isAuthenticated;
// ... and more
}
DeviceInfo
class DeviceInfo extends BaseDataModel<DeviceInfo> {
final String platform;
final String model;
final String manufacturer;
final String osVersion;
final String appVersion;
final String buildNumber;
// ... and more
}
Best Practices #
1. Privacy and Security #
- Always sanitize sensitive data before reporting
- Use the built-in data sanitization features
- Implement custom filters for sensitive operations
2. Performance #
- Use non-fatal error reporting for non-critical issues
- Limit breadcrumb frequency for high-traffic operations
- Implement rate limiting for error reporting
3. Testing #
- Use test crashes sparingly and only in development
- Implement proper error boundaries in your app
- Test crash reporting in staging environments
4. Monitoring #
- Regularly review crash reports and patterns
- Set up alerts for critical crash thresholds
- Monitor crash-free user percentages
Troubleshooting #
Firebase Crashlytics Issues #
Problem: Crashes not appearing in Firebase Console
- Solution: Ensure debug builds are configured to send crashes
- Check: Verify
google-services.json
andGoogleService-Info.plist
are correctly placed - Wait: It can take up to 15 minutes for crashes to appear
Problem: Build errors with Crashlytics
- Solution: Ensure all gradle plugins are up to date
- Check: Verify minimum SDK versions are met
Sentry Issues #
Problem: Events not appearing in Sentry
- Solution: Verify DSN is correct and project is active
- Check: Ensure network connectivity and no firewall blocking
Problem: High event volume
- Solution: Implement proper sampling rates and filters
- Configure: Set appropriate
tracesSampleRate
values
Contributing #
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Submit a pull request
License #
This project is licensed under the MIT License - see the LICENSE file for details.
Support #
For support and questions:
- Create an issue on GitHub
- Check the documentation
- Review the example project
Changelog #
See CHANGELOG.md for a detailed list of changes and updates.