Back to Blog
Development Tips10 min read

Firebase for Mobile Apps: Complete Tutorial 2026

Complete Firebase tutorial for mobile apps. Learn Authentication, Firestore, Cloud Storage, Push Notifications, and Analytics for iOS and Android.

Hevcode Team
January 21, 2026

Firebase provides a comprehensive backend solution for mobile apps, handling authentication, databases, storage, and more. This tutorial covers everything you need to integrate Firebase into your mobile applications.

Why Firebase?

  • Quick setup - Minutes to integrate
  • Scalable - Grows with your app
  • Real-time - Live data synchronization
  • Free tier - Generous free usage
  • Cross-platform - iOS, Android, Web, Flutter

Firebase Services Overview

Service Purpose Free Tier
Authentication User sign-in Unlimited users
Firestore NoSQL database 1GB storage, 50K reads/day
Realtime Database JSON database 1GB storage, 100 connections
Cloud Storage File storage 5GB storage
Cloud Functions Serverless code 2M invocations/month
Cloud Messaging Push notifications Unlimited
Analytics User analytics Unlimited
Crashlytics Crash reporting Unlimited

Setting Up Firebase

1. Create Firebase Project

  1. Go to console.firebase.google.com
  2. Click "Create Project"
  3. Enter project name
  4. Enable/disable Google Analytics
  5. Create project

2. Add Firebase to Your App

For React Native:

npm install @react-native-firebase/app
npm install @react-native-firebase/auth
npm install @react-native-firebase/firestore
npm install @react-native-firebase/storage

For Flutter:

flutter pub add firebase_core
flutter pub add firebase_auth
flutter pub add cloud_firestore
flutter pub add firebase_storage

Configure:

# Flutter
flutterfire configure

# React Native - download google-services.json (Android)
# and GoogleService-Info.plist (iOS) from console

Firebase Authentication

Email/Password Authentication

React Native:

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

// Sign up
const signUp = async (email, password) => {
  try {
    const userCredential = await auth().createUserWithEmailAndPassword(
      email,
      password
    );
    console.log('User created:', userCredential.user.uid);
    return userCredential.user;
  } catch (error) {
    if (error.code === 'auth/email-already-in-use') {
      throw new Error('Email already in use');
    }
    if (error.code === 'auth/invalid-email') {
      throw new Error('Invalid email address');
    }
    throw error;
  }
};

// Sign in
const signIn = async (email, password) => {
  try {
    const userCredential = await auth().signInWithEmailAndPassword(
      email,
      password
    );
    return userCredential.user;
  } catch (error) {
    if (error.code === 'auth/wrong-password') {
      throw new Error('Invalid password');
    }
    throw error;
  }
};

// Sign out
const signOut = async () => {
  await auth().signOut();
};

// Listen to auth state
const unsubscribe = auth().onAuthStateChanged(user => {
  if (user) {
    console.log('User signed in:', user.email);
  } else {
    console.log('User signed out');
  }
});

Flutter:

import 'package:firebase_auth/firebase_auth.dart';

class AuthService {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  // Sign up
  Future<User?> signUp(String email, String password) async {
    try {
      final credential = await _auth.createUserWithEmailAndPassword(
        email: email,
        password: password,
      );
      return credential.user;
    } on FirebaseAuthException catch (e) {
      if (e.code == 'email-already-in-use') {
        throw Exception('Email already in use');
      }
      throw e;
    }
  }

  // Sign in
  Future<User?> signIn(String email, String password) async {
    try {
      final credential = await _auth.signInWithEmailAndPassword(
        email: email,
        password: password,
      );
      return credential.user;
    } on FirebaseAuthException catch (e) {
      if (e.code == 'wrong-password') {
        throw Exception('Invalid password');
      }
      throw e;
    }
  }

  // Sign out
  Future<void> signOut() async {
    await _auth.signOut();
  }

  // Auth state stream
  Stream<User?> get authStateChanges => _auth.authStateChanges();
}

Social Authentication (Google)

React Native:

import auth from '@react-native-firebase/auth';
import { GoogleSignin } from '@react-native-google-signin/google-signin';

// Configure
GoogleSignin.configure({
  webClientId: 'YOUR_WEB_CLIENT_ID',
});

const signInWithGoogle = async () => {
  // Get user's ID token
  const { idToken } = await GoogleSignin.signIn();

  // Create Firebase credential
  const googleCredential = auth.GoogleAuthProvider.credential(idToken);

  // Sign in with credential
  return auth().signInWithCredential(googleCredential);
};

Flutter:

import 'package:google_sign_in/google_sign_in.dart';

Future<UserCredential> signInWithGoogle() async {
  final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();

  final GoogleSignInAuthentication? googleAuth =
      await googleUser?.authentication;

  final credential = GoogleAuthProvider.credential(
    accessToken: googleAuth?.accessToken,
    idToken: googleAuth?.idToken,
  );

  return await FirebaseAuth.instance.signInWithCredential(credential);
}

Cloud Firestore

Basic Operations

React Native:

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

// Reference to collection
const usersRef = firestore().collection('users');

// Add document (auto-generated ID)
const addUser = async (userData) => {
  const docRef = await usersRef.add({
    ...userData,
    createdAt: firestore.FieldValue.serverTimestamp(),
  });
  return docRef.id;
};

// Set document (specific ID)
const setUser = async (userId, userData) => {
  await usersRef.doc(userId).set({
    ...userData,
    createdAt: firestore.FieldValue.serverTimestamp(),
  });
};

// Get single document
const getUser = async (userId) => {
  const doc = await usersRef.doc(userId).get();
  if (doc.exists) {
    return { id: doc.id, ...doc.data() };
  }
  return null;
};

// Get all documents
const getAllUsers = async () => {
  const snapshot = await usersRef.get();
  return snapshot.docs.map(doc => ({
    id: doc.id,
    ...doc.data(),
  }));
};

// Update document
const updateUser = async (userId, updates) => {
  await usersRef.doc(userId).update({
    ...updates,
    updatedAt: firestore.FieldValue.serverTimestamp(),
  });
};

// Delete document
const deleteUser = async (userId) => {
  await usersRef.doc(userId).delete();
};

Flutter:

import 'package:cloud_firestore/cloud_firestore.dart';

class FirestoreService {
  final CollectionReference users =
      FirebaseFirestore.instance.collection('users');

  // Add document
  Future<String> addUser(Map<String, dynamic> userData) async {
    final docRef = await users.add({
      ...userData,
      'createdAt': FieldValue.serverTimestamp(),
    });
    return docRef.id;
  }

  // Get single document
  Future<Map<String, dynamic>?> getUser(String userId) async {
    final doc = await users.doc(userId).get();
    if (doc.exists) {
      return {'id': doc.id, ...doc.data() as Map<String, dynamic>};
    }
    return null;
  }

  // Get all documents
  Future<List<Map<String, dynamic>>> getAllUsers() async {
    final snapshot = await users.get();
    return snapshot.docs.map((doc) => {
      'id': doc.id,
      ...doc.data() as Map<String, dynamic>,
    }).toList();
  }

  // Update document
  Future<void> updateUser(String userId, Map<String, dynamic> updates) async {
    await users.doc(userId).update({
      ...updates,
      'updatedAt': FieldValue.serverTimestamp(),
    });
  }

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

Queries

// React Native
const usersRef = firestore().collection('users');

// Where clause
const activeUsers = await usersRef
  .where('isActive', '==', true)
  .get();

// Multiple conditions
const filteredUsers = await usersRef
  .where('age', '>=', 18)
  .where('age', '<=', 65)
  .get();

// Order by
const sortedUsers = await usersRef
  .orderBy('createdAt', 'desc')
  .limit(10)
  .get();

// Compound query
const results = await usersRef
  .where('city', '==', 'New York')
  .where('isActive', '==', true)
  .orderBy('name')
  .limit(20)
  .get();

// Array contains
const taggedPosts = await firestore()
  .collection('posts')
  .where('tags', 'array-contains', 'firebase')
  .get();

// In query
const specificUsers = await usersRef
  .where('status', 'in', ['active', 'pending'])
  .get();

Real-time Listeners

// React Native - Listen to document
useEffect(() => {
  const unsubscribe = firestore()
    .collection('users')
    .doc(userId)
    .onSnapshot(doc => {
      if (doc.exists) {
        setUser({ id: doc.id, ...doc.data() });
      }
    }, error => {
      console.error('Error:', error);
    });

  return () => unsubscribe();
}, [userId]);

// Listen to collection
useEffect(() => {
  const unsubscribe = firestore()
    .collection('messages')
    .where('chatId', '==', chatId)
    .orderBy('createdAt', 'desc')
    .limit(50)
    .onSnapshot(snapshot => {
      const messages = snapshot.docs.map(doc => ({
        id: doc.id,
        ...doc.data(),
      }));
      setMessages(messages);
    });

  return () => unsubscribe();
}, [chatId]);

Flutter:

// Stream builder for real-time data
StreamBuilder<QuerySnapshot>(
  stream: FirebaseFirestore.instance
      .collection('messages')
      .where('chatId', isEqualTo: chatId)
      .orderBy('createdAt', descending: true)
      .limit(50)
      .snapshots(),
  builder: (context, snapshot) {
    if (snapshot.hasError) {
      return Text('Error: ${snapshot.error}');
    }
    if (!snapshot.hasData) {
      return CircularProgressIndicator();
    }

    final messages = snapshot.data!.docs.map((doc) {
      return Message.fromFirestore(doc);
    }).toList();

    return ListView.builder(
      itemCount: messages.length,
      itemBuilder: (context, index) {
        return MessageTile(message: messages[index]);
      },
    );
  },
)

Cloud Storage

Upload Files

React Native:

import storage from '@react-native-firebase/storage';

const uploadImage = async (userId, uri) => {
  const filename = `users/${userId}/profile.jpg`;
  const reference = storage().ref(filename);

  // Upload file
  const task = reference.putFile(uri);

  // Monitor progress
  task.on('state_changed', snapshot => {
    const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
    console.log(`Upload is ${progress}% done`);
  });

  // Wait for completion
  await task;

  // Get download URL
  const downloadURL = await reference.getDownloadURL();
  return downloadURL;
};

// Upload from base64
const uploadBase64 = async (path, base64String) => {
  const reference = storage().ref(path);
  await reference.putString(base64String, 'base64');
  return await reference.getDownloadURL();
};

Flutter:

import 'package:firebase_storage/firebase_storage.dart';
import 'dart:io';

class StorageService {
  final FirebaseStorage _storage = FirebaseStorage.instance;

  Future<String> uploadImage(String userId, File file) async {
    final ref = _storage.ref('users/$userId/profile.jpg');

    final uploadTask = ref.putFile(file);

    // Monitor progress
    uploadTask.snapshotEvents.listen((event) {
      final progress = event.bytesTransferred / event.totalBytes;
      print('Upload progress: ${(progress * 100).toStringAsFixed(0)}%');
    });

    // Wait for completion
    await uploadTask;

    // Get download URL
    return await ref.getDownloadURL();
  }

  Future<void> deleteImage(String path) async {
    await _storage.ref(path).delete();
  }
}

Cloud Messaging (Push Notifications)

Setup and Token

React Native:

import messaging from '@react-native-firebase/messaging';

// Request permission (iOS)
const requestPermission = async () => {
  const authStatus = await messaging().requestPermission();
  const enabled =
    authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
    authStatus === messaging.AuthorizationStatus.PROVISIONAL;

  if (enabled) {
    console.log('Permission granted');
  }
};

// Get FCM token
const getFCMToken = async () => {
  const token = await messaging().getToken();
  console.log('FCM Token:', token);
  // Save to your backend
  return token;
};

// Listen for token refresh
messaging().onTokenRefresh(token => {
  console.log('Token refreshed:', token);
  // Update on your backend
});

Handle Notifications

// Foreground messages
useEffect(() => {
  const unsubscribe = messaging().onMessage(async remoteMessage => {
    console.log('Foreground message:', remoteMessage);
    // Show local notification
    Alert.alert(
      remoteMessage.notification?.title || 'Notification',
      remoteMessage.notification?.body
    );
  });

  return unsubscribe;
}, []);

// Background/quit messages
messaging().setBackgroundMessageHandler(async remoteMessage => {
  console.log('Background message:', remoteMessage);
});

// Notification opened app
messaging().onNotificationOpenedApp(remoteMessage => {
  console.log('Notification opened app:', remoteMessage);
  // Navigate to specific screen
});

// App opened from quit state
messaging()
  .getInitialNotification()
  .then(remoteMessage => {
    if (remoteMessage) {
      console.log('App opened from quit:', remoteMessage);
      // Navigate to specific screen
    }
  });

Flutter:

import 'package:firebase_messaging/firebase_messaging.dart';

class NotificationService {
  final FirebaseMessaging _messaging = FirebaseMessaging.instance;

  Future<void> initialize() async {
    // Request permission
    final settings = await _messaging.requestPermission();

    if (settings.authorizationStatus == AuthorizationStatus.authorized) {
      // Get token
      final token = await _messaging.getToken();
      print('FCM Token: $token');

      // Listen for token refresh
      _messaging.onTokenRefresh.listen((token) {
        print('Token refreshed: $token');
      });

      // Foreground messages
      FirebaseMessaging.onMessage.listen((message) {
        print('Foreground message: ${message.notification?.title}');
      });

      // Background/terminated messages
      FirebaseMessaging.onMessageOpenedApp.listen((message) {
        print('Notification opened app: ${message.data}');
      });
    }
  }
}

Firebase Analytics

// React Native
import analytics from '@react-native-firebase/analytics';

// Log event
await analytics().logEvent('purchase', {
  item_id: 'SKU_123',
  item_name: 'Product Name',
  value: 29.99,
  currency: 'USD',
});

// Log screen view
await analytics().logScreenView({
  screen_name: 'Home',
  screen_class: 'HomeScreen',
});

// Set user property
await analytics().setUserProperty('user_type', 'premium');

// Set user ID
await analytics().setUserId('user_123');

Flutter:

import 'package:firebase_analytics/firebase_analytics.dart';

final analytics = FirebaseAnalytics.instance;

// Log event
await analytics.logEvent(
  name: 'purchase',
  parameters: {
    'item_id': 'SKU_123',
    'item_name': 'Product Name',
    'value': 29.99,
    'currency': 'USD',
  },
);

// Log screen view
await analytics.logScreenView(
  screenName: 'Home',
  screenClass: 'HomeScreen',
);

Security Rules

Firestore Rules

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Users can only read/write their own data
    match /users/{userId} {
      allow read, write: if request.auth != null
                        && request.auth.uid == userId;
    }

    // Posts - anyone can read, only author can write
    match /posts/{postId} {
      allow read: if true;
      allow create: if request.auth != null;
      allow update, delete: if request.auth != null
                           && request.auth.uid == resource.data.authorId;
    }

    // Messages - only participants can read/write
    match /chats/{chatId}/messages/{messageId} {
      allow read, write: if request.auth != null
                        && request.auth.uid in get(/databases/$(database)/documents/chats/$(chatId)).data.participants;
    }
  }
}

Storage Rules

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    // User profile images
    match /users/{userId}/profile.jpg {
      allow read: if true;
      allow write: if request.auth != null
                  && request.auth.uid == userId
                  && request.resource.size < 5 * 1024 * 1024
                  && request.resource.contentType.matches('image/.*');
    }

    // Chat attachments
    match /chats/{chatId}/{fileName} {
      allow read, write: if request.auth != null;
    }
  }
}

Best Practices

1. Offline Persistence

// Enable offline persistence (React Native - enabled by default)
firestore().settings({
  persistence: true,
  cacheSizeBytes: firestore.CACHE_SIZE_UNLIMITED,
});

// Flutter
FirebaseFirestore.instance.settings = Settings(
  persistenceEnabled: true,
  cacheSizeBytes: Settings.CACHE_SIZE_UNLIMITED,
);

2. Batch Operations

// Batch writes
const batch = firestore().batch();

batch.set(userRef, userData);
batch.update(statsRef, { count: firestore.FieldValue.increment(1) });
batch.delete(oldRef);

await batch.commit();

3. Transactions

// Atomic transaction
await firestore().runTransaction(async transaction => {
  const doc = await transaction.get(accountRef);
  const newBalance = doc.data().balance - amount;

  if (newBalance < 0) {
    throw new Error('Insufficient funds');
  }

  transaction.update(accountRef, { balance: newBalance });
});

Conclusion

Firebase provides a powerful, scalable backend for mobile apps. Start with Authentication and Firestore, then add other services as needed. The real-time capabilities and generous free tier make it ideal for most mobile applications.

Need help integrating Firebase? Contact Hevcode for professional mobile app development with Firebase backend.

Related Articles

Tags:FirebaseMobile DevelopmentTutorialBackendBaaS

Need help with your project?

We've helped 534+ clients build successful apps. Let's discuss yours.

Ready to Build Your App?

534+ projects delivered • 4.9★ rating • 6+ years experience

Let's discuss your project — no obligations, just a straightforward conversation.