Firebase Firestore in Flutter: Complete Guide to Save & Read Cloud Data

Flutter developers often need a reliable and scalable cloud database for storing user data, app records, chat messages, or real-time updates. Firebase Firestore is one of the best choices because it offers fast performance, offline support, instant synchronization, and an easy integration experience.

In this guide, you’ll learn how to set up Firestore in Flutter and perform the most important operations including saving, reading, updating, and deleting data. We will also walk through a real-world example to show you how Firestore works inside a complete app.

✔️ What Is Firebase Firestore?

Firebase Firestore is a NoSQL cloud database by Google. Instead of storing information in tables, it stores data in:

  • Collections

  • Documents

  • Fields

This structure makes it flexible and perfect for mobile apps where data needs to sync quickly.

✔️ Why Use Firestore in Flutter?

Here are a few reasons why Flutter developers prefer Firestore:

🔥 Real-time database updates

🌐 Cloud-hosted and scalable

📱 Offline mode support

Fast query performance

🔒 Secure access rules

🔄 Easy integration with Firebase Authentication

🟦 Step 1: Add Firebase to Your Flutter App

Run the command in your Flutter project (Terminal):

  • flutterfire configure
🟦 Step 2: Add Required Packages

In pubspec.yaml:

dependencies:
flutter:
sdk: flutter
firebase_core: ^3.0.0
cloud_firestore: ^5.0.0
google_fonts: ^6.0.0

 

🟦 Step 3 — Initialize Firebase

main.dart

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

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

class StudentApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: "Student Manager",
      home: HomeScreen(),
    );
  }
}


🎨 Step 4: App Theme (Modern UI)

core/theme.dart

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

class AppTheme {
  static ThemeData lightTheme = ThemeData(
    primaryColor: Colors.deepPurple,
    scaffoldBackgroundColor: Colors.white,
    textTheme: GoogleFonts.poppinsTextTheme(),
    appBarTheme: AppBarTheme(
      elevation: 1,
      backgroundColor: Colors.deepPurple,
      titleTextStyle: TextStyle(
          fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white
      ),
    ),
  );
}

🧩 Step 5:  Student Model

models/student_model.dart

class Student {
  final String id;
  final String name;
  final String rollNo;
  final int marks;

  Student({
    required this.id,
    required this.name,
    required this.rollNo,
    required this.marks,
  });

  Map<String, dynamic> toMap() {
    return {
      "name": name,
      "rollNo": rollNo,
      "marks": marks,
    };
  }

  factory Student.fromMap(Map<String, dynamic> data, String id) {
    return Student(
      id: id,
      name: data["name"],
      rollNo: data["rollNo"],
      marks: data["marks"],
    );
  }
}

🔥 Step 6: Firestore Service (CRUD Operations)

services/firestore_service.dart

import 'package:cloud_firestore/cloud_firestore.dart';
import '../models/student_model.dart';

class FirestoreService {
  final CollectionReference students =
  FirebaseFirestore.instance.collection("students");

  Future<void> addStudent(Student student) {
    return students.add(student.toMap());
  }

  Stream<List<Student>> getStudents() {
    return students.snapshots().map((snapshot) =>
        snapshot.docs.map((doc) => Student.fromMap(doc.data() as Map<String, dynamic>, doc.id)).toList());
  }

  Future<void> updateStudent(Student student) {
    return students.doc(student.id).update(student.toMap());
  }

  Future<void> deleteStudent(String id) {
    return students.doc(id).delete();
  }
}
📄 Reusable Input Field

core/widgets/custom_input.dart

import 'package:flutter/material.dart';

class CustomInput extends StatelessWidget {
  final String hint;
  final TextEditingController controller;
  final TextInputType keyboard;

  CustomInput({
    required this.hint,
    required this.controller,
    this.keyboard = TextInputType.text,
  });

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: controller,
      keyboardType: keyboard,
      decoration: InputDecoration(
        labelText: hint,
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(12),
        ),
      ),
    );
  }
}
🔘 Reusable Button

core/widgets/custom_button.dart

import 'package:flutter/material.dart';

class CustomButton extends StatelessWidget {
  final String text;
  final VoidCallback onPressed;

  CustomButton({required this.text, required this.onPressed});

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      height: 55,
      child: ElevatedButton(
        style: ElevatedButton.styleFrom(
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(12),
          ),
          backgroundColor: Colors.deepPurple,
          foregroundColor: Colors.white,
        ),
        onPressed: onPressed,
        child: Text(text, style: TextStyle(fontSize: 16)),
      ),
    );
  }
}
🏠 Home Screen: Real-Time Student List

screens/home_screen.dart

import 'package:flutter/material.dart';
import '../services/firestore_service.dart';
import '../models/student_model.dart';
import 'add_student_screen.dart';
import 'edit_student_screen.dart';

class HomeScreen extends StatelessWidget {
  final FirestoreService service = FirestoreService();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Student Manager")),
      floatingActionButton: FloatingActionButton(
        backgroundColor: Colors.deepPurple,
        child: Icon(Icons.add, color: Colors.white),
        onPressed: () => Navigator.push(context,
            MaterialPageRoute(builder: (_) => AddStudentScreen())),
      ),

      body: StreamBuilder<List<Student>>(
        stream: service.getStudents(),
        builder: (context, snapshot) {
          if (snapshot.hasError) return Center(child: Text("Error loading data"));
          if (!snapshot.hasData) return Center(child: CircularProgressIndicator());

          final students = snapshot.data!;

          return ListView.builder(
            padding: EdgeInsets.all(16),
            itemCount: students.length,
            itemBuilder: (context, index) {
              final s = students[index];

              return Card(
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
                child: ListTile(
                  title: Text(s.name),
                  subtitle: Text("Roll No: ${s.rollNo} | Marks: ${s.marks}"),
                  trailing: PopupMenuButton(
                    onSelected: (value) {
                      if (value == "edit") {
                        Navigator.push(context, MaterialPageRoute(
                          builder: (_) => EditStudentScreen(student: s),
                        ));
                      } else {
                        service.deleteStudent(s.id);
                      }
                    },
                    itemBuilder: (_) => [
                      PopupMenuItem(value: "edit", child: Text("Edit")),
                      PopupMenuItem(value: "delete", child: Text("Delete")),
                    ],
                  ),
                ),
              );
            },
          );
        },
      ),
    );
  }
}
Add Student Screen

screens/add_student_screen.dart

import 'package:flutter/material.dart';
import '../core/widgets/custom_input.dart';
import '../core/widgets/custom_button.dart';
import '../services/firestore_service.dart';
import '../models/student_model.dart';

class AddStudentScreen extends StatelessWidget {
  final nameC = TextEditingController();
  final rollC = TextEditingController();
  final marksC = TextEditingController();

  final FirestoreService service = FirestoreService();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Add Student")),
      body: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          children: [
            CustomInput(hint: "Name", controller: nameC),
            SizedBox(height: 12),
            CustomInput(hint: "Roll Number", controller: rollC),
            SizedBox(height: 12),
            CustomInput(
              hint: "Marks",
              controller: marksC,
              keyboard: TextInputType.number,
            ),
            SizedBox(height: 20),
            CustomButton(
              text: "Save Student",
              onPressed: () {
                if (nameC.text.isEmpty || rollC.text.isEmpty || marksC.text.isEmpty) {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text("All fields are required")),
                  );
                  return;
                }

                final student = Student(
                  id: "",
                  name: nameC.text.trim(),
                  rollNo: rollC.text.trim(),
                  marks: int.parse(marksC.text),
                );

                service.addStudent(student);

                Navigator.pop(context);
              },
            )
          ],
        ),
      ),
    );
  }
}
✏️ Edit Student Screen

screens/edit_student_screen.dart

import 'package:flutter/material.dart';
import '../models/student_model.dart';
import '../services/firestore_service.dart';
import '../core/widgets/custom_button.dart';
import '../core/widgets/custom_input.dart';

class EditStudentScreen extends StatefulWidget {
  final Student student;

  EditStudentScreen({required this.student});

  @override
  _EditStudentScreenState createState() => _EditStudentScreenState();
}

class _EditStudentScreenState extends State<EditStudentScreen> {
  late TextEditingController nameC;
  late TextEditingController rollC;
  late TextEditingController marksC;

  final FirestoreService service = FirestoreService();

  @override
  void initState() {
    super.initState();

    nameC = TextEditingController(text: widget.student.name);
    rollC = TextEditingController(text: widget.student.rollNo);
    marksC = TextEditingController(text: widget.student.marks.toString());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Edit Student")),
      body: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          children: [
            CustomInput(hint: "Name", controller: nameC),
            SizedBox(height: 12),
            CustomInput(hint: "Roll Number", controller: rollC),
            SizedBox(height: 12),
            CustomInput(
              hint: "Marks",
              controller: marksC,
              keyboard: TextInputType.number,
            ),
            SizedBox(height: 20),
            CustomButton(
              text: "Update Student",
              onPressed: () {
                final updatedStudent = Student(
                  id: widget.student.id,
                  name: nameC.text.trim(),
                  rollNo: rollC.text.trim(),
                  marks: int.parse(marksC.text),
                );

                service.updateStudent(updatedStudent);

                Navigator.pop(context);
              },
            ),
          ],
        ),
      ),
    );
  }
}

🎉 Your Student Management App Is Ready!

You now have:

✔ Clean and scalable architecture
✔ Firebase Firestore CRUD
✔ Elegant UI with Google Fonts
✔ Validation + Snackbars
✔ Real-time updates
✔ Modern card-based layout

🙏 Thank You for Reading!

Thank you so much for taking out your valuable time to read this guide. I truly hope it helped you understand how to build a complete and functional Student Management App using Flutter and Firebase Firestore.

If you enjoyed this article and want to stay updated with more practical Flutter tutorials, real-world app guides, and step-by-step development tips,  don’t forget to subscribe to the blog. Your support motivates me to create even more helpful content for the community!

Stay tuned — more exciting tutorials are coming soon! 🚀📲

Author

Write A Comment