Integrating with Crashlytics for Automatic Reporting of Crashes and Errors in Flutter

Ensuring the stability of your Flutter application is paramount for providing a seamless user experience. Crashlytics, a Firebase service, offers robust crash reporting, enabling you to identify, track, and fix issues quickly. Integrating Crashlytics with your Flutter app allows automatic reporting of crashes and errors, significantly streamlining your debugging process.

What is Crashlytics?

Crashlytics is a real-time crash reporting tool that helps you track, prioritize, and fix stability issues in your apps. As part of Firebase, Crashlytics offers seamless integration with other Firebase services, providing a comprehensive solution for monitoring app health.

Why Integrate Crashlytics with Flutter?

  • Automatic Crash Reporting: Automatically collects crash reports, even for the rarest of crashes.
  • Real-Time Monitoring: Provides real-time insights into your app’s stability.
  • Prioritization of Issues: Helps prioritize issues based on impact.
  • Detailed Crash Reports: Offers detailed stack traces and device information for effective debugging.
  • User Impact Assessment: Provides insights into the number of users affected by each crash.

How to Integrate Crashlytics with Flutter

Integrating Crashlytics involves several steps, including setting up Firebase, adding necessary dependencies, and initializing Crashlytics in your Flutter app.

Step 1: Set up a Firebase Project

First, create a new Firebase project or use an existing one:

  1. Go to the Firebase Console.
  2. Click on "Add project" and follow the instructions to create a new project.
  3. Register your Android and iOS apps within the Firebase project by following the console’s prompts.

Step 2: Add Firebase to Your Flutter Project

Install the firebase_core and firebase_crashlytics plugins.

dependencies:
  firebase_core: ^2.24.2
  firebase_crashlytics: ^3.4.8

Run the following command to install the packages:

flutter pub get

Step 3: Initialize Firebase

In your Flutter app, initialize Firebase using the firebase_core plugin:

import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Crashlytics Example'),
        ),
        body: Center(
          child: Text('Hello, Crashlytics!'),
        ),
      ),
    );
  }
}

Step 4: Configure Crashlytics

Add the firebase_crashlytics import to your main.dart and configure Crashlytics to handle errors.

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  // Pass all uncaught errors from the framework to Crashlytics.
  FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError;

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Crashlytics Example'),
        ),
        body: Center(
          child: Text('Hello, Crashlytics!'),
        ),
      ),
    );
  }
}

Step 5: Handling Asynchronous Errors

To catch errors that occur outside the Flutter framework (e.g., in asynchronous code), use runZonedGuarded:

import 'dart:async';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError;

  runZonedGuarded(() {
    runApp(MyApp());
  }, (error, stack) {
    FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
  });
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Crashlytics Example'),
        ),
        body: Center(
          child: Text('Hello, Crashlytics!'),
        ),
      ),
    );
  }
}

Step 6: Force a Test Crash

To test if Crashlytics is correctly set up, you can force a test crash.

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError;

  runZonedGuarded(() {
    runApp(MyApp());
  }, (error, stack) {
    FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
  });
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Crashlytics Example'),
        ),
        body: Center(
          child: ElevatedButton(
            child: Text('Test Crash'),
            onPressed: () {
              FirebaseCrashlytics.instance.crash();
            },
          ),
        ),
      ),
    );
  }
}

When you press the “Test Crash” button, the app will crash. Check the Firebase Crashlytics dashboard to see the crash report.

Step 7: Handle Non-Fatal Errors

To record non-fatal exceptions, you can use FirebaseCrashlytics.instance.recordError:

try {
  // Your code that might throw an exception
  throw Exception('This is a non-fatal exception');
} catch (e, stackTrace) {
  FirebaseCrashlytics.instance.recordError(e, stackTrace);
}

Best Practices for Using Crashlytics

  • Handle All Errors: Use try-catch blocks to handle potential exceptions gracefully and report them.
  • Use Meaningful Messages: Add meaningful error messages to the exceptions for better context in crash reports.
  • Test Thoroughly: Ensure that your integration works correctly by inducing test crashes and verifying the reports.
  • Monitor Regularly: Regularly check the Crashlytics dashboard for new issues and address them promptly.

Conclusion

Integrating Crashlytics into your Flutter applications provides a streamlined approach to automatically report crashes and errors, enabling faster debugging and improving overall app stability. By setting up Firebase, adding the necessary dependencies, and correctly initializing Crashlytics, you can monitor and address issues effectively. Regularly monitoring the Crashlytics dashboard and implementing best practices will significantly enhance the user experience of your Flutter app.