Firebase: Your Complete Backend Solution for Flutter
Backend Development
10 min read
September 5, 2025

Firebase: Your Complete Backend Solution for Flutter

Explore how Firebase transforms Flutter development by handling all the complex backend stuff, so you can focus on creating amazing user experiences.

Muhammad Nabi Rahmani

Muhammad Nabi Rahmani

Flutter Developer passionate about creating beautiful mobile experiences

Firebase: Your Complete Backend Solution for Flutter

Firebase gives you authentication, a real-time database, file storage, cloud functions, and analytics — all wired together and ready to use from your Flutter app. No server setup, no DevOps. Here's how to actually use each service.

Setup

Install the FlutterFire CLI and initialize:

# Install the CLI globally
dart pub global activate flutterfire_cli

# Initialize Firebase in your Flutter project
flutterfire configure

This generates firebase_options.dart. Then initialize in your app:

import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(const MyApp());
}

Authentication

Firebase Auth handles email/password, Google Sign-In, Apple Sign-In, and phone number verification. Here's the practical implementation:

class AuthService {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  // Current user as a stream — drives your UI reactively
  Stream<User?> get authStateChanges => _auth.authStateChanges();

  // Email + Password sign up
  Future<UserCredential> signUp(String email, String password) async {
    return await _auth.createUserWithEmailAndPassword(
      email: email,
      password: password,
    );
  }

  // Email + Password sign in
  Future<UserCredential> signIn(String email, String password) async {
    return await _auth.signInWithEmailAndPassword(
      email: email,
      password: password,
    );
  }

  // Google Sign-In
  Future<UserCredential> signInWithGoogle() async {
    final googleUser = await GoogleSignIn().signIn();
    if (googleUser == null) throw Exception('Google sign-in cancelled');

    final googleAuth = await googleUser.authentication;
    final credential = GoogleAuthProvider.credential(
      accessToken: googleAuth.accessToken,
      idToken: googleAuth.idToken,
    );

    return await _auth.signInWithCredential(credential);
  }

  Future<void> signOut() => _auth.signOut();
}

Listen to auth state changes in your app to redirect between login and home:

StreamBuilder<User?>(
  stream: authService.authStateChanges,
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const SplashScreen();
    }
    return snapshot.hasData ? const HomeScreen() : const LoginScreen();
  },
)

Cloud Firestore

Firestore is a NoSQL document database with real-time sync and offline support built in. Data is organized in collections and documents.

Writing Data

class TaskRepository {
  final FirebaseFirestore _db = FirebaseFirestore.instance;

  Future<void> createTask(Task task) async {
    await _db.collection('users')
      .doc(task.userId)
      .collection('tasks')
      .doc(task.id)
      .set({
        'title': task.title,
        'isCompleted': task.isCompleted,
        'createdAt': FieldValue.serverTimestamp(),
        'priority': task.priority.name,
      });
  }

  Future<void> toggleComplete(String userId, String taskId) async {
    final ref = _db.collection('users').doc(userId).collection('tasks').doc(taskId);
    final doc = await ref.get();
    await ref.update({
      'isCompleted': !(doc.data()?['isCompleted'] ?? false),
      'updatedAt': FieldValue.serverTimestamp(),
    });
  }
}

Reading Data in Real-Time

// One-time fetch
Future<List<Task>> getTasks(String userId) async {
  final snapshot = await _db
    .collection('users')
    .doc(userId)
    .collection('tasks')
    .orderBy('createdAt', descending: true)
    .get();

  return snapshot.docs.map((doc) => Task.fromFirestore(doc)).toList();
}

// Real-time stream — UI updates automatically when data changes
Stream<List<Task>> watchTasks(String userId) {
  return _db
    .collection('users')
    .doc(userId)
    .collection('tasks')
    .orderBy('createdAt', descending: true)
    .snapshots()
    .map((snapshot) =>
      snapshot.docs.map((doc) => Task.fromFirestore(doc)).toList(),
    );
}

Security Rules

Firestore Security Rules control who can read and write. Without them, your data is wide open:

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

Cloud Storage

For user-uploaded files — profile pictures, documents, images:

class StorageService {
  final FirebaseStorage _storage = FirebaseStorage.instance;

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

    // Upload with metadata
    await ref.putFile(
      file,
      SettableMetadata(contentType: 'image/jpeg'),
    );

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

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

Cloud Functions

Server-side logic that runs in response to events. Write in TypeScript, deploy to Google Cloud:

// functions/src/index.ts
import { onDocumentCreated } from 'firebase-functions/v2/firestore';
import { getMessaging } from 'firebase-admin/messaging';

export const onNewTask = onDocumentCreated(
  'users/{userId}/tasks/{taskId}',
  async (event) => {
    const task = event.data?.data();
    const userId = event.params.userId;

    // Send push notification when a task is created
    await getMessaging().sendToTopic(userId, {
      notification: {
        title: 'New Task',
        body: task?.title ?? 'You have a new task',
      },
    });
  }
);

Offline Support

Firestore has offline support enabled by default on mobile. When the user loses connection:

  1. Writes are queued locally
  2. Reads come from the local cache
  3. When connection returns, queued writes sync automatically

You don't write any extra code for this. It just works.

// This works even offline
await _db.collection('tasks').add({
  'title': 'Buy groceries',
  'isCompleted': false,
});
// Firestore queues the write and syncs when back online

When to Use Firebase

Use CaseFirebase Service
User login/signupFirebase Auth
App data (CRUD)Cloud Firestore
File uploadsCloud Storage
Push notificationsCloud Messaging
Server logicCloud Functions
Crash trackingCrashlytics
User analyticsGoogle Analytics

When NOT to Use Firebase

  • Complex relational data: If your data has many join tables and complex relationships, PostgreSQL (via Supabase) is better.
  • Full SQL access: Firestore is NoSQL. No JOINs, no aggregations beyond basic counts.
  • Self-hosting requirement: Firebase is Google Cloud only.
  • Predictable pricing at scale: Firestore charges per read/write. High-traffic apps can get expensive fast.

The Bottom Line

Firebase is the fastest way to go from zero to a production backend. Auth, database, storage, push notifications — it's all there, wired together, with offline support out of the box. Start with Auth + Firestore, add services as you need them.

Share:

Keep Reading

More articles you might enjoy

© 2026 Mohammad Nabi RahmaniBuilt with Next.js & Tailwind