Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      The Double-Edged Sustainability Sword Of AI In Web Design

      August 20, 2025

      Top 12 Reasons Enterprises Choose Node.js Development Services for Scalable Growth

      August 20, 2025

      GitHub’s coding agent can now be launched from anywhere on platform using new Agents panel

      August 20, 2025

      Stop writing tests: Automate fully with Generative AI

      August 19, 2025

      I’m a diehard Pixel fan, but I’m not upgrading to the Pixel 10. Here’s why

      August 21, 2025

      Google Pixel Watch 4 vs. Samsung Galaxy Watch 8: I compared the two best Androids, and here’s the winner

      August 21, 2025

      Get a free Amazon gift card up to $300 when you preorder a new Google Pixel 10 phone – here’s how

      August 21, 2025

      Everything announced at Made by Google 2025: Pixel 10 Pro, Fold, Watch 4, and more

      August 21, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      Copy Errors as Markdown to Share With AI in Laravel 12.25

      August 21, 2025
      Recent

      Copy Errors as Markdown to Share With AI in Laravel 12.25

      August 21, 2025

      Deconstructing the Request Lifecycle in Sitecore Headless – Part 2: SSG and ISR Modes in Next.js

      August 20, 2025

      Susan Etlinger, AI Analyst and Industry Watcher on Building Trust

      August 20, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      TerraMaster D1 SSD Plus Review: Experience a Faster External SSD

      August 20, 2025
      Recent

      TerraMaster D1 SSD Plus Review: Experience a Faster External SSD

      August 20, 2025

      Microsoft is investigating Windows 11 KB5063878 SSD data corruption/failure issue

      August 20, 2025

      Microsoft Surface Won’t Turn On: 6 Tested Solutions to Fix

      August 20, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»Development»How to Set Up Firebase Crashlytics in a Flutter App (iOS and Android)

    How to Set Up Firebase Crashlytics in a Flutter App (iOS and Android)

    August 20, 2025

    When you’re building mobile applications, one of the biggest challenges you might face is ensuring stability in real-world usage. No matter how much testing you do, unexpected crashes are bound to occur.

    This is where Firebase Crashlytics becomes an essential tool. Crashlytics is a lightweight, real-time crash reporter that helps you understand why your app is crashing and how widespread the problem is among users. With this knowledge, you can fix bugs faster and improve your app’s reliability.

    In this article, we’ll walk through setting up Firebase Crashlytics in a Flutter app for both iOS and Android platforms. Along the way, you’ll learn not only how to integrate Crashlytics, but also the reasoning behind each step, so you fully understand how it works.

    Table of Contents:

    1. Prerequisites

    2. Set Up the Flutter Project

    3. Connect Flutter to Firebase

    4. Add the Required Dependencies

    5. Initialize Crashlytics in main.dart

    6. Build a Simple Test Screen

    7. Run and Test the App

    8. Understanding the Crashlytics Dashboard

    9. Advanced Firebase Crashlytics in Flutter: Going Beyond the Basics

      • How to Log Non-Fatal Errors

      • How to Add Custom Keys for Context

      • How to Log Custom Events and Breadcrumbs

      • How to Associate Crashes with Users

      • How to Ensure Proper Symbolication

      • How to Controll Data Collection

    10. Best Practices for Production

    11. Conclusion

    12. References

    Prerequisites

    Before jumping into the setup, make sure you have the following requirements ready. These prerequisites ensure your environment is properly configured and you won’t get stuck midway through the integration.

    First, you need a working Flutter installation on your system. Flutter must be correctly installed and configured so you can run apps on both iOS and Android. If you haven’t set this up yet, follow the official Flutter installation guide to prepare your development environment.

    Next, you need a Firebase account. Firebase provides a web-based console where you’ll create a project that links to your Flutter app. You can sign up for free at the Firebase Console.

    For a smoother integration process, I also highly recommend installing the Firebase CLI. The CLI enables the flutterfire configure command, which automatically links your Flutter project to Firebase and generates a firebase_options.dart file with all your platform-specific configurations. This step is optional, but it saves you time compared to manually adding configuration files. You can install the CLI by following Firebase CLI setup instructions.

    Finally, ensure you have either an iOS simulator (via Xcode on macOS) or an Android emulator (via Android Studio or the command line) to test the integration. Crashlytics will only log crashes once the app has run on a real or simulated device.

    With these prerequisites in place, you’re ready to move on to the actual integration steps.

    Set Up the Flutter Project

    The journey begins by creating a Flutter project. If you don’t already have one, run the following command from your terminal:

    flutter create my_crashlytics_app
    cd my_crashlytics_app
    

    This generates the boilerplate structure for your Flutter app, giving us a foundation where we can add Firebase and Crashlytics.

    Connect Flutter to Firebase

    Before Crashlytics can work, your app must be connected to a Firebase project. Head over to the Firebase Console and create a new project. Think of the Firebase project as the “backend container” that manages all services, including analytics, authentication, and crash reporting.

    Once the project is created, you need to register your Flutter apps with Firebase. Flutter supports both iOS and Android, so you’ll add both platforms.

    On the iOS side, Firebase will guide you through adding an iOS app, downloading the GoogleService-Info.plist configuration file, and placing it inside the ios/Runner directory of your Flutter project. On Android, you’ll do something similar by downloading the google-services.json file and adding it to the android/app directory.

    If you prefer a more streamlined approach, the Firebase CLI provides a flutterfire configure command. Running this will allow you to select your Firebase project and automatically generate a firebase_options.dart file for your Flutter app. This file centralizes your Firebase configuration and reduces manual setup.

    Add the Required Dependencies

    With Firebase linked, the next step is to bring in the necessary packages that enable Crashlytics. Flutter integrates with Firebase through plugins, which are small libraries that bridge Flutter and native SDKs. Open your pubspec.yaml file and add the following:

    dependencies:
      firebase_core: ^4.0.0
      firebase_crashlytics: ^5.0.0
    

    The firebase_core package initializes communication with Firebase, while firebase_crashlytics is the library that captures and reports crashes. Run flutter pub get to download and install these dependencies.

    Initialize Crashlytics in main.dart

    Now that the dependencies are installed, we need to initialize Firebase when the app starts and configure Crashlytics to capture both synchronous and asynchronous errors. Replace the contents of your lib/main.dart file with the following code:

    import 'dart:ui';
    import 'package:firebase_core/firebase_core.dart';
    import 'package:firebase_crashlytics/firebase_crashlytics.dart';
    import 'firebase_options.dart';
    import 'presentation/home_screen.dart';
    import 'package:flutter/material.dart';
    
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      await Firebase.initializeApp(
        options: DefaultFirebaseOptions.currentPlatform,
      );
    
      // Capture Flutter framework errors
      FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError;
    
      // Capture uncaught asynchronous errors
      PlatformDispatcher.instance.onError = (error, stack) {
        FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
        return true;
      };
    
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Firebase Crashlytics Demo',
          theme: ThemeData(
            colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
            useMaterial3: true,
          ),
          home: const HomeScreen(),
        );
      }
    }
    

    Let’s pause to unpack this. The FlutterError.onError line ensures that any error that occurs inside Flutter’s widget tree is reported as a fatal crash. The PlatformDispatcher.instance.onError captures errors outside of the widget tree, such as asynchronous exceptions, and reports them to Crashlytics as well. Together, these configurations ensure that virtually all unexpected issues are sent to Firebase.

    Build a Simple Test Screen

    To verify that Crashlytics works, let’s create a test screen where we can deliberately throw errors. Create a new folder called presentation in your lib directory, then inside it, create a file named home_screen.dart with the following content:

    import 'package:flutter/material.dart';
    
    class HomeScreen extends StatefulWidget {
      const HomeScreen({super.key});
    
      @override
      State<HomeScreen> createState() => _HomeScreenState();
    }
    
    class _HomeScreenState extends State<HomeScreen> {
      int _counter = 0;
    
      void _incrementCounter() {
        setState(() {
          _counter++;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            backgroundColor: Theme.of(context).colorScheme.inversePrimary,
            title: const Text('Firebase Crashlytics App'),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text('You have pushed the button this many times:'),
                Text(
                  '$_counter',
                  style: Theme.of(context).textTheme.headlineMedium,
                ),
                const SizedBox(height: 15),
                ElevatedButton(
                  onPressed: () => throw Exception('Test Exception'),
                  child: const Text('Throw Exception'),
                ),
                const SizedBox(height: 10),
                ElevatedButton(
                  onPressed: () {
                    throw const FormatException('Custom format error occurred');
                  },
                  child: const Text('Throw Exception with Feedback'),
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'Increment',
            child: const Icon(Icons.add),
          ),
        );
      }
    }
    

    This screen provides two buttons: one that throws a general exception and another that throws a format exception. When clicked, these crashes are reported to Crashlytics. This makes it easy to test whether your setup is working correctly.

    General and format exception buttons

    Run and Test the App

    At this stage, run the app on an iOS simulator or Android emulator. Interact with the screen and press the buttons that throw exceptions. Even though the app will crash or display an error, Crashlytics will silently log the details and send them to Firebase once the app restarts and regains network connectivity.

    Crashes usually take a couple of minutes to appear in the Firebase Console. Navigate to your project in the console, then go to Release & Monitor > Crashlytics. There, you will see a dashboard listing all recorded crashes, complete with stack traces, device information, and frequency of occurrence. The screenshots below showcase what you’ll be able to see on Crashlytics.

    Crashlytics Dashboard

    Checking Logs

    Viewing Data

    Detailed log of error

    Stack trace of error

    Understanding the Crashlytics Dashboard

    The Crashlytics dashboard is more than just a list of crashes. It groups issues together so you can see how many users are affected by a specific bug. It highlights trends such as whether a particular crash is new, increasing, or decreasing. It also integrates with alerts, allowing you to get notified when a severe issue affects a significant portion of your users.

    This means you don’t just learn that your app crashed, you also get actionable insights to prioritize which bugs need immediate attention.

    Advanced Firebase Crashlytics in Flutter: Going Beyond the Basics

    Once Crashlytics is successfully integrated into your Flutter app, the next step is to take full advantage of its advanced features. While catching crashes is useful, real-world debugging often requires context, deeper insights, and error handling strategies that go beyond simply knowing an app has failed. Let’s explore these advanced concepts.

    How to Log Non-Fatal Errors

    Not every problem in an application leads to a crash. Sometimes you’ll encounter recoverable errors, such as a failed API call, a parsing issue, or a user action that leads to unexpected behavior. These issues don’t crash your app but still affect user experience. Crashlytics allows you to record them as non-fatal errors.

    In Flutter, you can use:

    try {
      // Some code that might fail
      final result = int.parse("invalid_number");
    } catch (e, stack) {
      FirebaseCrashlytics.instance.recordError(
        e,
        stack,
        fatal: false,
        reason: 'Number parsing failed in profile setup',
      );
    }
    

    Here, the fatal: false flag ensures the error is logged without being treated as a full app crash. The optional reason parameter provides extra human-readable context in the Crashlytics dashboard. This feature is invaluable for tracking silent failures that degrade performance but don’t necessarily kill your app.

    How to Add Custom Keys for Context

    One of the challenges with debugging crashes in production is reproducing the problem. A stack trace alone often doesn’t tell you enough about the user’s journey. Custom keys allow you to attach extra metadata to Crashlytics reports, such as the user’s app state, preferences, or which feature they were using when the crash occurred.

    For example:

    FirebaseCrashlytics.instance.setCustomKey('screen', 'CheckoutScreen');
    FirebaseCrashlytics.instance.setCustomKey('cart_items', 3);
    FirebaseCrashlytics.instance.setCustomKey('payment_method', 'Card');
    

    With these keys set, any crash or non-fatal error that occurs while the user is on the checkout screen will carry this context. When you open the report in the Firebase Console, you’ll immediately see these values, which makes debugging significantly easier.

    How to Log Custom Events and Breadcrumbs

    In addition to custom keys, Crashlytics allows you to log custom messages that act as breadcrumbs. These are small logs that tell you what the app was doing leading up to a crash.

    FirebaseCrashlytics.instance.log('User tapped "Place Order" button');
    FirebaseCrashlytics.instance.log('API request started: /checkout');
    FirebaseCrashlytics.instance.log('Payment process initialized');
    

    If a crash happens afterward, you’ll have a trail of events that explain the sequence leading up to the failure. This is often the missing piece in diagnosing complex crashes.

    How to Associate Crashes with Users

    Crashlytics supports user identifiers, allowing you to link crashes back to specific users. While you should avoid storing sensitive data, you can safely attach unique identifiers such as user IDs, emails, or usernames.

    FirebaseCrashlytics.instance.setUserIdentifier('user_12345');
    

    With this, you can investigate whether specific users or groups of users are disproportionately affected by a bug. This also helps customer support teams quickly link bug reports from users to real data in Crashlytics.

    How to Ensure Proper Symbolication

    When you run your app in debug mode, stack traces are human-readable. But in release builds, especially on iOS and Android, stack traces can be obfuscated or stripped of symbols. Symbolication is the process of mapping these stripped traces back to meaningful method and class names.

    On iOS, you’ll need to upload dSYM files (debug symbol files) to Firebase. These files are generated when you build your iOS app for release. You can automate the upload by adding a Run Script in Xcode under your project’s build settings:

    "${PODS_ROOT}/FirebaseCrashlytics/upload-symbols" 
    -gsp "${PROJECT_DIR}/Runner/GoogleService-Info.plist" 
    -p ios "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}"
    

    This ensures that whenever you build a release, symbol files are automatically uploaded to Firebase.

    On Android, if you’re using ProGuard or R8 for code shrinking and obfuscation, you’ll need to upload mapping files. In your app/build.gradle, enable the Crashlytics Gradle plugin:

    apply plugin: 'com.google.firebase.crashlytics'
    

    This plugin takes care of uploading the mapping files automatically when you build a release.

    Without symbolication, your crash reports will contain unreadable stack traces, making debugging almost impossible. Ensuring proper symbol upload is critical for production-level monitoring.

    How to Controll Data Collection

    In some cases, such as adhering to GDPR or other data privacy laws, you may want to control when Crashlytics starts collecting data. Flutter gives you a way to enable or disable collection dynamically:

    await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(false);
    

    You can turn this on once the user has given consent. This flexibility is especially useful in regions with strict user privacy requirements.

    Best Practices for Production

    1. Test before release: Always trigger crashes in debug mode and confirm they appear in the Crashlytics dashboard before deploying your app.

    2. Use non-fatal logs liberally: Many silent issues can be caught this way before they escalate into widespread crashes.

    3. Automate symbol uploads: Make sure your CI/CD or build pipeline uploads dSYM (iOS) and mapping files (Android) consistently.

    4. Add context with custom keys and logs: The more context you attach, the faster you can reproduce and fix bugs.

    5. Respect privacy: Never log personally identifiable or sensitive information.

    Conclusion

    Integrating Firebase Crashlytics into a Flutter app is a straightforward process, but its impact is massive. By providing real-time crash reporting and detailed analytics, Crashlytics helps you maintain stability, build user trust, and ultimately deliver a better app experience. From setting up the Firebase project to capturing both synchronous and asynchronous errors, we’ve gone through everything you need to get started.

    Crashlytics goes far beyond crash reporting. By leveraging features like non-fatal error logging, custom keys, breadcrumbs, and user identifiers, you can transform raw crash data into meaningful insights that directly improve your debugging process.

    With proper symbolication in place, you’ll always have readable stack traces, making it much easier to fix issues in production. With this advanced setup, Crashlytics becomes not just a safety net, but a core part of your development workflow, helping you ship stable apps, respond quickly to issues, and build trust with your users.

    The next step is to deploy your app to real devices and monitor crashes as they happen in the wild. Over time, Crashlytics will become one of your most valuable tools in maintaining app quality.

    References

    • Flutter Documentation – Install Flutter

    • Firebase Documentation – Firebase Console

    • Firebase Documentation – Add Firebase to your Flutter App

    • Firebase Crashlytics for Flutter – firebase_crashlytics Package

    • Firebase Core for Flutter – firebase_core Package

    • Firebase Documentation – Firebase Crashlytics Overview

    • Firebase Documentation – Upload dSYM Files (iOS)

    • Firebase Documentation – Upload ProGuard/R8 Mapping Files (Android)

    • Firebase CLI – Install and Configure Firebase CLI

    Source: freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleDeconstructing the Request Lifecycle in Sitecore Headless – Part 2: SSG and ISR Modes in Next.js
    Next Article How to Build a Tic Tac Toe Game with Phaser.js

    Related Posts

    Development

    Copy Errors as Markdown to Share With AI in Laravel 12.25

    August 21, 2025
    Artificial Intelligence

    Scaling Up Reinforcement Learning for Traffic Smoothing: A 100-AV Highway Deployment

    August 21, 2025
    Leave A Reply Cancel Reply

    For security, use of Google's reCAPTCHA service is required which is subject to the Google Privacy Policy and Terms of Use.

    Continue Reading

    Samsung’s 500Hz gaming monitor isn’t just wicked fast — it overcomes a big OLED flaw

    News & Updates

    Dev Hackathon: Reusable Creativity on Wix Studio

    Development

    CVE-2025-44896 – Fujitsu WGS-804HPT Stack Overflow Vulnerability

    Common Vulnerabilities and Exposures (CVEs)

    CVE-2025-32463 – Sudo Local Command Injection Vulnerability

    Common Vulnerabilities and Exposures (CVEs)

    Highlights

    Security

    WordPress Security Alert: CVE-2025-6043 Enables Remote File Deletion via Malcure Plugin

    July 17, 2025

    WordPress Security Alert: CVE-2025-6043 Enables Remote File Deletion via Malcure Plugin

    A new vulnerability, CVE-2025-6043, has been discovered in the Malcure Malware Scanner plugin for WordPress, a popular security tool used by over 10,000 websites to detect and remove malware. Security …
    Read more

    Published Date:
    Jul 16, 2025 (17 hours, 58 minutes ago)

    Vulnerabilities has been mentioned in this article.

    CVE-2025-6043

    CVE-2025-4389

    CVE-2025-29824

    CVE-2025-30406

    Beyond the Hype: Google’s Practical AI Guide Every Startup Founder Should Read

    April 30, 2025

    KontextAIPrecision AI Image and Video Editing and Generation Harness the power of Flux Kontext AI technology.

    August 7, 2025
    T* and LV-Haystack: A Spatially-Guided Temporal Search Framework for Efficient Long-Form Video Understanding

    T* and LV-Haystack: A Spatially-Guided Temporal Search Framework for Efficient Long-Form Video Understanding

    April 10, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.