Integrating Firebase in Flutter, React Native, and .NET MAUI: A Practical Guide

Integrating Firebase in Flutter, React Native, and .NET MAUI: A Practical Guide

Jan Thalheim
Jan Thalheim
11 min read

Overview

Firebase has evolved into an essential backend solution for mobile app developers, providing a comprehensive suite of tools that eliminate much of the server-side complexity from the development process. For cross-platform developers working with Flutter, React Native, or .NET MAUI, Firebase offers consistent APIs and services that work seamlessly across iOS and Android. This guide walks through implementing core Firebase services in each framework, helping you choose the right approach for your project while avoiding common integration pitfalls.

Whether you're building your first cross-platform app or looking to enhance an existing project with Firebase capabilities, this practical guide will help you implement authentication, data storage, and analytics features with confidence. We'll focus on real-world implementation patterns that maintain code quality and performance across platforms.

Firebase Project Setup

Before diving into framework-specific implementations, you need to properly configure a Firebase project. This initial setup is largely consistent regardless of which development framework you'll use downstream.

First, navigate to the Firebase Console and create a new project. You'll need to:

  1. Provide a project name
  2. Configure Google Analytics (recommended)
  3. Accept the Firebase terms of service

After project creation, you'll register your applications. For cross-platform development, you'll need to add both iOS and Android apps to your Firebase project, even though you're writing a single codebase.

For each platform, you'll need to:

  • Register the app with its bundle ID/package name
  • Download configuration files (GoogleService-Info.plist for iOS, google-services.json for Android)
  • Place these files in the appropriate locations in your project structure:
    • Flutter: Place GoogleService-Info.plist in ios/Runner/ and google-services.json in android/app/.
    • React Native: Place GoogleService-Info.plist in ios/[YourAppName]/ and google-services.json in android/app/.
    • .NET MAUI: Place GoogleService-Info.plist in Platforms/iOS/ (set Build Action to BundleResource) and google-services.json in Platforms/Android/ (set Build Action to GoogleServicesJson).

With these preliminaries complete, you're ready to implement Firebase services in your cross-platform app.

Authentication Implementation

User authentication is often the first Firebase feature developers implement. Let's explore how to set up Firebase Authentication across Flutter, React Native, and .NET MAUI.

Setting Up Authentication in Flutter

Flutter's Firebase integration has become increasingly streamlined with the FlutterFire packages. Start by adding dependencies to your pubspec.yaml:

dependencies:
  # Add the latest stable versions from pub.dev
  firebase_core:
  firebase_auth:

Initialize Firebase in your main.dart:

import 'package:firebase_core/firebase_core.dart';

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

Implementing email/password authentication:

import 'package:firebase_auth/firebase_auth.dart';

final FirebaseAuth _auth = FirebaseAuth.instance;

// Sign up
Future<UserCredential> signUp(String email, String password) async {
  try {
    UserCredential userCredential = await _auth.createUserWithEmailAndPassword(
      email: email,
      password: password,
    );
    return userCredential;
  } on FirebaseAuthException catch (e) {
    if (e.code == 'weak-password') {
      throw 'The password provided is too weak.';
    } else if (e.code == 'email-already-in-use') {
      throw 'The account already exists for that email.';
    }
    throw e.message ?? 'An unknown error occurred';
  }
}

// Sign in
Future<UserCredential> signIn(String email, String password) async {
  try {
    UserCredential userCredential = await _auth.signInWithEmailAndPassword(
      email: email,
      password: password,
    );
    return userCredential;
  } on FirebaseAuthException catch (e) {
    if (e.code == 'user-not-found' || e.code == 'wrong-password') {
      throw 'Invalid email or password.';
    }
    throw e.message ?? 'An unknown error occurred';
  }
}

Setting Up Authentication in React Native

React Native uses the react-native-firebase packages. Install the required dependencies (npm will typically fetch the latest compatible versions):

npm install @react-native-firebase/app @react-native-firebase/auth
# Or using yarn:
# yarn add @react-native-firebase/app @react-native-firebase/auth

Initialize Firebase in your App.js:

import React, { useEffect } from "react";
import { SafeAreaView, StatusBar } from "react-native";
import auth from "@react-native-firebase/auth";

const App = () => {
  useEffect(() => {
    // Subscribe to auth state changes
    const subscriber = auth().onAuthStateChanged(onAuthStateChanged);
    return subscriber; // unsubscribe on unmount
  }, []);

  function onAuthStateChanged(user) {
    if (user) {
      // User is signed in
      console.log("User is signed in:", user.uid);
    } else {
      // User is signed out
      console.log("User is signed out");
    }
  }

  // Rest of your app code
};

export default App;

Email/password authentication implementation:

import auth from "@react-native-firebase/auth";

// Sign up function
const signUp = async (email, password) => {
  try {
    const userCredential = await auth().createUserWithEmailAndPassword(
      email,
      password
    );
    return userCredential;
  } catch (error) {
    if (error.code === "auth/email-already-in-use") {
      throw "That email address is already in use!";
    }
    if (error.code === "auth/invalid-email") {
      throw "That email address is invalid!";
    }
    if (error.code === "auth/weak-password") {
      throw "Password is too weak!";
    }
    throw error.message;
  }
};

// Sign in function
const signIn = async (email, password) => {
  try {
    const userCredential = await auth().signInWithEmailAndPassword(
      email,
      password
    );
    return userCredential;
  } catch (error) {
    if (
      error.code === "auth/user-not-found" ||
      error.code === "auth/wrong-password"
    ) {
      throw "Invalid email or password.";
    }
    throw error.message;
  }
};

Setting Up Authentication in .NET MAUI

.NET MAUI developers can leverage community plugins like Plugin.Firebase (based on Xamarin.Firebase) or use REST API calls to Firebase.

Note: Plugin.Firebase is a community-maintained project. Evaluate if it meets your project's requirements or consider other integration methods if needed.

To use the plugin, install the Firebase packages (ensure you get versions compatible with your MAUI setup):

<!-- In your .csproj file -->
<ItemGroup>
  <PackageReference Include="Plugin.Firebase" />
  <PackageReference Include="Plugin.Firebase.Auth" />
  <!-- Use appropriate versions -->
</ItemGroup>

Initialize Firebase in your App.xaml.cs:

using Plugin.Firebase;
using Plugin.Firebase.Auth;

public partial class App : Application
{
    public App()
    {
        InitializeComponent();

        // Initialize Firebase
        CrossFirebase.Initialize();

        MainPage = new AppShell();
    }
}

Email/password authentication:

using Plugin.Firebase.Auth;
using System.Threading.Tasks;

public class FirebaseAuthService
{
    private readonly IFirebaseAuthentication _auth;

    public FirebaseAuthService()
    {
        _auth = CrossFirebaseAuth.Current;
    }

    // Sign up
    public async Task<IFirebaseUser> SignUpAsync(string email, string password)
    {
        try
        {
            var result = await _auth.CreateUserWithEmailAndPasswordAsync(email, password);
            return result;
        }
        catch (FirebaseAuthException ex)
        {
            switch (ex.ErrorCode)
            {
                case "ERROR_EMAIL_ALREADY_IN_USE":
                    throw new Exception("The email address is already in use.");
                case "ERROR_WEAK_PASSWORD":
                    throw new Exception("The password is too weak.");
                default:
                    throw new Exception($"Authentication error: {ex.Message}");
            }
        }
    }

    // Sign in
    public async Task<IFirebaseUser> SignInAsync(string email, string password)
    {
        try
        {
            var result = await _auth.SignInWithEmailAndPasswordAsync(email, password);
            return result;
        }
        catch (FirebaseAuthException ex)
        {
            if (ex.ErrorCode == "ERROR_USER_NOT_FOUND" || ex.ErrorCode == "ERROR_WRONG_PASSWORD")
            {
                throw new Exception("Invalid email or password.");
            }
            throw new Exception($"Authentication error: {ex.Message}");
        }
    }
}

Cloud Firestore Integration

Firestore provides a flexible, scalable NoSQL cloud database to store and sync data. Let's see how to implement it across our frameworks.

Firestore in Flutter

Add the Firestore package to your pubspec.yaml:

dependencies:
  # Add the latest stable version from pub.dev
  cloud_firestore:

Basic CRUD operations:

import 'package:cloud_firestore/cloud_firestore.dart';

final FirebaseFirestore _firestore = FirebaseFirestore.instance;

// Create
Future<void> addUser(String userId, Map<String, dynamic> userData) async {
  await _firestore.collection('users').doc(userId).set(userData);
}

// Read
Future<DocumentSnapshot> getUser(String userId) async {
  return await _firestore.collection('users').doc(userId).get();
}

// Update
Future<void> updateUser(String userId, Map<String, dynamic> newData) async {
  await _firestore.collection('users').doc(userId).update(newData);
}

// Delete
Future<void> deleteUser(String userId) async {
  await _firestore.collection('users').doc(userId).delete();
}

// Query
Future<QuerySnapshot> getAdminUsers() async {
  return await _firestore.collection('users')
    .where('role', isEqualTo: 'admin')
    .get();
}

// Real-time updates
Stream<DocumentSnapshot> userStream(String userId) {
  return _firestore.collection('users').doc(userId).snapshots();
}

Firestore in React Native

Install the Firestore package:

npm install @react-native-firebase/firestore
# Or using yarn:
# yarn add @react-native-firebase/firestore

Basic CRUD operations:

import firestore from "@react-native-firebase/firestore";

// Create
const addUser = async (userId, userData) => {
  await firestore().collection("users").doc(userId).set(userData);
};

// Read
const getUser = async (userId) => {
  const documentSnapshot = await firestore()
    .collection("users")
    .doc(userId)
    .get();
  return documentSnapshot.data();
};

// Update
const updateUser = async (userId, newData) => {
  await firestore().collection("users").doc(userId).update(newData);
};

// Delete
const deleteUser = async (userId) => {
  await firestore().collection("users").doc(userId).delete();
};

// Query
const getAdminUsers = async () => {
  const querySnapshot = await firestore()
    .collection("users")
    .where("role", "==", "admin")
    .get();

  return querySnapshot.docs.map((doc) => doc.data());
};

// Real-time updates
const subscribeToUser = (userId, onUpdate) => {
  return firestore()
    .collection("users")
    .doc(userId)
    .onSnapshot((documentSnapshot) => {
      onUpdate(documentSnapshot.data());
    });
};

Firestore in .NET MAUI

Install the Firestore package for the community plugin:

<!-- In your .csproj file -->
<ItemGroup>
  <PackageReference Include="Plugin.Firebase.Firestore" />
  <!-- Use appropriate version -->
</ItemGroup>

Basic CRUD operations:

using Plugin.Firebase.Firestore;
using System.Collections.Generic;
using System.Threading.Tasks;

public class FirestoreService
{
    private readonly IFirebaseFirestore _firestore;

    public FirestoreService()
    {
        _firestore = CrossFirebaseFirestore.Current;
    }

    // Create
    public async Task AddUserAsync(string userId, Dictionary<string, object> userData)
    {
        await _firestore.Collection("users").Document(userId).SetDataAsync(userData);
    }

    // Read
    public async Task<IDictionary<string, object>> GetUserAsync(string userId)
    {
        var documentSnapshot = await _firestore.Collection("users").Document(userId).GetDocumentAsync();
        return documentSnapshot.Data;
    }

    // Update
    public async Task UpdateUserAsync(string userId, Dictionary<string, object> newData)
    {
        await _firestore.Collection("users").Document(userId).UpdateDataAsync(newData);
    }

    // Delete
    public async Task DeleteUserAsync(string userId)
    {
        await _firestore.Collection("users").Document(userId).DeleteDocumentAsync();
    }

    // Query
    public async Task<IEnumerable<IDictionary<string, object>>> GetAdminUsersAsync()
    {
        var querySnapshot = await _firestore.Collection("users")
            .WhereEqualsTo("role", "admin")
            .GetDocumentsAsync();

        var result = new List<IDictionary<string, object>>();
        foreach (var doc in querySnapshot.Documents)
        {
            result.Add(doc.Data);
        }

        return result;
    }

    // Real-time updates
    public IDisposable SubscribeToUser(string userId, Action<IDictionary<string, object>> onUpdate)
    {
        return _firestore.Collection("users").Document(userId)
            .AddSnapshotListener((snapshot, error) => {
                if (error != null || snapshot == null)
                    return;

                onUpdate(snapshot.Data);
            });
    }
}

Firebase Analytics Implementation

Analytics helps you understand user behavior in your app. Let's implement Firebase Analytics across our platforms.

Analytics in Flutter

Add the Analytics package to pubspec.yaml:

dependencies:
  # Add the latest stable version from pub.dev
  firebase_analytics:

Implementation:

import 'package:firebase_analytics/firebase_analytics.dart';

final FirebaseAnalytics analytics = FirebaseAnalytics.instance;

// Log custom event
Future<void> logScreenView(String screenName) async {
  await analytics.logScreenView(screenName: screenName);
}

// Log purchase event
Future<void> logPurchase({
  required double amount,
  required String currency,
  required String itemId,
  required String itemName,
}) async {
  await analytics.logPurchase(
    currency: currency,
    value: amount,
    items: [
      AnalyticsEventItem(
        itemId: itemId,
        itemName: itemName,
        price: amount,
      ),
    ],
  );
}

// Set user properties
Future<void> setUserProperties({required String userId, required String userRole}) async {
  await analytics.setUserId(id: userId);
  await analytics.setUserProperty(name: 'user_role', value: userRole);
}

Analytics in React Native

Install the Analytics package:

npm install @react-native-firebase/analytics
# Or using yarn:
# yarn add @react-native-firebase/analytics

Implementation:

import analytics from "@react-native-firebase/analytics";

// Log screen view
const logScreenView = async (screenName) => {
  await analytics().logScreenView({
    screen_name: screenName,
    screen_class: screenName,
  });
};

// Log purchase event
const logPurchase = async (amount, currency, itemId, itemName) => {
  await analytics().logPurchase({
    value: amount,
    currency: currency,
    items: [
      {
        item_id: itemId,
        item_name: itemName,
        price: amount,
      },
    ],
  });
};

// Set user properties
const setUserProperties = async (userId, userRole) => {
  await analytics().setUserId(userId);
  await analytics().setUserProperty("user_role", userRole);
};

Analytics in .NET MAUI

Install the Analytics package for the community plugin:

<!-- In your .csproj file -->
<ItemGroup>
  <PackageReference Include="Plugin.Firebase.Analytics" />
  <!-- Use appropriate version -->
</ItemGroup>

Implementation:

using Plugin.Firebase.Analytics;
using System.Collections.Generic;
using System.Threading.Tasks;

public class FirebaseAnalyticsService
{
    private readonly IFirebaseAnalytics _analytics;

    public FirebaseAnalyticsService()
    {
        _analytics = CrossFirebaseAnalytics.Current;
    }

    // Log screen view
    public async Task LogScreenViewAsync(string screenName)
    {
        await _analytics.LogEventAsync("screen_view", new Dictionary<string, object>
        {
            { "screen_name", screenName },
            { "screen_class", screenName }
        });
    }

    // Log purchase event
    public async Task LogPurchaseAsync(double amount, string currency, string itemId, string itemName)
    {
        await _analytics.LogPurchaseAsync(amount, currency, new Dictionary<string, object>
        {
            { "item_id", itemId },
            { "item_name", itemName }
        });
    }

    // Set user properties
    public async Task SetUserPropertiesAsync(string userId, string userRole)
    {
        await _analytics.SetUserIdAsync(userId);
        await _analytics.SetUserPropertyAsync("user_role", userRole);
    }
}

Performance Optimization

When integrating Firebase into cross-platform apps, performance optimizations are critical to ensure your app remains responsive and efficient. Here are key strategies for each framework:

For Flutter apps, consider these optimizations:

  • Use the FirebaseOptions parameter during initialization to customize Firebase behavior
  • Implement query pagination with Firestore's limit() and startAfter() methods
  • Enable offline persistence with appropriate cache size limits
  • Use Firebase Performance Monitoring to identify bottlenecks

For React Native implementations:

  • Configure the persistence settings for Firestore with enablePersistence()
  • Use the waitForPendingWrites() method to ensure critical data is synchronized
  • Implement efficient listeners and remember to unsubscribe from real-time updates when components unmount
  • Consider batch operations for multiple document updates

For .NET MAUI applications:

  • Use the async/await pattern consistently with Firebase operations
  • Implement caching strategies for frequently accessed data
  • Configure Firebase to use less bandwidth with appropriate settings
  • Consider using a repository pattern to abstract Firebase operations from your UI logic

Regardless of your chosen framework, always monitor your app's network usage and battery consumption after implementing Firebase services.

Summary

Integrating Firebase into cross-platform apps provides a powerful foundation for building feature-rich mobile applications that work seamlessly across iOS and Android. Whether you choose Flutter, React Native, or .NET MAUI as your development framework, Firebase offers a consistent set of tools that can accelerate your development process.

We've covered the essential Firebase services—Authentication, Firestore, and Analytics—with practical implementation examples for each framework. By following the patterns outlined in this guide, you can create a solid architecture that leverages Firebase's capabilities while maintaining good performance and code quality.

Remember that Firebase offers many additional services beyond what we've covered here, including Cloud Functions, Cloud Storage, Remote Config, and more. As your app grows, you can incrementally add these services to enhance functionality without having to redesign your backend infrastructure.

When developing cross-platform apps with Firebase, focus on creating abstraction layers that isolate platform-specific code and prioritize testing on both platforms throughout development. By taking this approach, you'll build robust applications that provide consistent experiences across devices while leveraging the full power of Firebase's cloud infrastructure.

Ready to streamline your internal app distribution?

Start sharing your app builds with your team and clients today.
No app store reviews, no waiting times.