Popular Searches
Popular Course Categories
Popular Courses

Storing and retrieving application data

Storing and retrieving application data

Firebase with Flutter

Storing and Retrieving Application Data in Flutter


Storing and retrieving application data is an essential part of Flutter application development. Most real-world applications need to save information such as user profiles, products, orders, messages, tasks, settings, and other application data.


Flutter applications can work with different types of storage depending on the requirements. For cloud-based application data, Cloud Firestore is a popular Firebase solution. Firestore is a NoSQL document database that stores data in collections and documents and supports one-time reads, queries, real-time listeners, and offline capabilities.




1. What is Application Data?


Application data is information created, entered, or used by an application during its operation.


Examples include:



  • User names and email addresses

  • User profiles

  • Products and categories

  • Orders and payments

  • Messages and comments

  • Tasks and notes

  • Course information

  • Application settings

  • Favorites and bookmarks


For example, an e-commerce application may store products like this:


products
├── product001
│   ├── name: "Laptop"
│   ├── price: 65000
│   └── category: "Electronics"
└── product002
    ├── name: "Phone"
    ├── price: 35000
    └── category: "Electronics"



2. Why Do Applications Need Data Storage?


Without persistent storage, information entered by users may disappear when the application is closed or the application state is recreated.


Data storage allows applications to:



  • Save user information.

  • Retrieve previously stored information.

  • Update existing information.

  • Delete information when required.

  • Synchronize information between devices.

  • Provide personalized experiences.

  • Maintain application state and records.




3. Types of Data Storage in Flutter


Different applications require different storage solutions.










Storage Type Common Use
Shared Preferences Small key-value settings and preferences
SQLite Structured local relational data
Hive Local NoSQL-style storage
Secure Storage Sensitive local values such as tokens
Cloud Firestore Cloud-based NoSQL application data
Firebase Realtime Database Real-time JSON-based application data

This topic focuses primarily on storing and retrieving application data using Cloud Firestore with Flutter.




4. What is Cloud Firestore?


Cloud Firestore is a flexible, scalable NoSQL document database provided by Firebase. It stores data in documents organized into collections. Documents can contain fields, nested objects, and subcollections.


Firestore also supports queries, real-time listeners, and offline capabilities on supported platforms.


Cloud Firestore Documentation




5. Firestore Data Model


Firestore uses a collection and document structure rather than traditional SQL tables and rows.


Firestore
   |
   +-- Collection
         |
         +-- Document
               |
               +-- Fields
               |
               +-- Subcollections

Example


users
  |
  +-- user001
       |
       +-- name: "John Doe"
       +-- email: "[email protected]"
       +-- age: 25
       +-- isActive: true



6. Collections


A collection is a group of related documents.


Examples:


users
products
orders
messages
courses
tasks

A collection can contain many documents.




7. Documents


A document is a record containing fields and values.


users
  |
  +-- user001
       |
       +-- name: "John Doe"
       +-- email: "[email protected]"
       +-- age: 25

Every document has an identifier. Firestore can generate an ID automatically, or your application can specify one.




8. Fields


Fields contain the actual data stored inside a document.










Field Example Type
name John Doe String
age 25 Number
isActive true Boolean
skills ["Flutter","Dart"] Array
address {city: "Mumbai"} Map
createdAt Timestamp Timestamp



9. Prerequisites


Before storing and retrieving application data with Cloud Firestore, you should have:



  • Flutter SDK installed.

  • A Flutter project.

  • A Firebase project.

  • Firebase configured with the Flutter application.

  • Cloud Firestore enabled in Firebase Console.


For Firebase configuration, follow the official Flutter setup documentation.


Firebase Setup for Flutter


10. Add Cloud Firestore to Flutter


Add the Cloud Firestore Flutter plugin using:


flutter pub add cloud_firestore

Then import it into the Dart file:


import 'package:cloud_firestore/cloud_firestore.dart';



11. Access Firestore


The Firestore instance can be accessed using FirebaseFirestore.instance.


final db = FirebaseFirestore.instance;

You can then use the instance to access collections and documents.




12. Firebase Initialization


Firebase must be initialized before Firestore operations are performed.


void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Firebase.initializeApp();


  runApp(const MyApp());
}


In a FlutterFire-configured application, Firebase is commonly initialized using generated configuration options.


await Firebase.initializeApp(
  options: DefaultFirebaseOptions.currentPlatform,
);



13. Storing Application Data


Storing data means writing information from the Flutter application into a database.


Firestore provides methods such as:



  • add()

  • set()

  • update()




14. Store Data with add()


The add() method creates a new document with an automatically generated document ID.


await FirebaseFirestore.instance
    .collection('users')
    .add({
  'name': 'John Doe',
  'email': '[email protected]',
  'age': 25,
});

Firestore generates a unique document ID for the new document.




15. Store Data with a Custom Document ID


Use doc() and set() when you want to specify the document ID.


await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .set({
  'name': 'John Doe',
  'email': '[email protected]',
  'age': 25,
});

The resulting structure is:


users
  |
  +-- user001
       |
       +-- name: John Doe
       +-- email: [email protected]
       +-- age: 25



16. Storing Different Data Types


Firestore documents can contain different types of values.


await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .set({
  'name': 'John Doe',
  'age': 25,
  'isActive': true,
  'skills': ['Flutter', 'Dart', 'Firebase'],
  'address': {
    'city': 'Mumbai',
    'country': 'India',
  },
});



17. Store Date and Time


Firestore supports timestamp values.


await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .set({
  'name': 'John Doe',
  'createdAt': Timestamp.now(),
});

You can also use a server-side timestamp:


await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .set({
  'name': 'John Doe',
  'createdAt': FieldValue.serverTimestamp(),
});



18. Store Application Data from a Form


A common Flutter pattern is collecting information from a form and storing it in Firestore.


Controllers


final nameController = TextEditingController();
final emailController = TextEditingController();

Save Function


Future saveUser() async {
  final name = nameController.text.trim();
  final email = emailController.text.trim();

  await FirebaseFirestore.instance
      .collection('users')
      .add({
    'name': name,
    'email': email,
    'createdAt': FieldValue.serverTimestamp(),
  });
}


Save Button


ElevatedButton(
  onPressed: saveUser,
  child: const Text('Save'),
)



19. Form Validation Before Storing Data


Always validate important user input before storing it.


Future saveUser() async {
  final name = nameController.text.trim();
  final email = emailController.text.trim();

  if (name.isEmpty || email.isEmpty) {
    return;
  }


  await FirebaseFirestore.instance
      .collection('users')
      .add({
    'name': name,
    'email': email,
  });
}


For larger forms, Flutter's Form and TextFormField widgets can be used for structured validation.




20. Retrieve a Single Document


Use get() to retrieve a document once.


final doc = await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .get();

if (doc.exists) {
  print(doc.data());
}


The retrieved document contains its fields and values.




21. Retrieve Document Data


final doc = await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .get();

if (doc.exists) {
  final data = doc.data();


  print(data?['name']);
  print(data?['email']);
  print(data?['age']);
}




22. Check Whether a Document Exists


final doc = await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .get();

if (doc.exists) {
  print('Document found');
} else {
  print('Document not found');
}




23. Retrieve All Documents


Use get() on a collection reference to retrieve multiple documents.


final snapshot = await FirebaseFirestore.instance
    .collection('users')
    .get();

for (final doc in snapshot.docs) {
  print('ID: ${doc.id}');
  print('Data: ${doc.data()}');
}




24. Retrieve Data as a List


Firestore query results can be converted into a list that can be displayed in Flutter.


final snapshot = await FirebaseFirestore.instance
    .collection('users')
    .get();

final users = snapshot.docs.map((doc) {
  return {
    'id': doc.id,
    ...doc.data(),
  };
}).toList();




25. Display Stored Data in ListView


Firestore data can be displayed using Flutter's ListView.builder.


final snapshot = await FirebaseFirestore.instance
    .collection('users')
    .get();

final users = snapshot.docs;


ListView.builder(
  itemCount: users.length,
  itemBuilder: (context, index) {
    final user = users[index].data();


    return ListTile(
      title: Text(user['name'] ?? ''),
      subtitle: Text(user['email'] ?? ''),
    );
  },
)




26. FutureBuilder for Retrieving Data


FutureBuilder is useful when retrieving Firestore data once and displaying the result in the Flutter UI.


FutureBuilder(
  future: FirebaseFirestore.instance
      .collection('users')
      .get(),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const Center(
        child: CircularProgressIndicator(),
      );
    }

    if (snapshot.hasError) {
      return const Center(
        child: Text('Unable to load data'),
      );
    }


    if (!snapshot.hasData || snapshot.data!.docs.isEmpty) {
      return const Center(
        child: Text('No data found'),
      );
    }


    final users = snapshot.data!.docs;


    return ListView.builder(
      itemCount: users.length,
      itemBuilder: (context, index) {
        final user = users[index].data();


        return ListTile(
          title: Text(user['name'] ?? ''),
          subtitle: Text(user['email'] ?? ''),
        );
      },
    );
  },
)




27. Real-Time Data Retrieval


Cloud Firestore supports real-time listeners. Instead of retrieving data only once, an application can listen for changes and update the UI when the listened-to data changes.


FirebaseFirestore.instance
    .collection('users')
    .snapshots()
    .listen((snapshot) {
  for (final doc in snapshot.docs) {
    print(doc.data());
  }
});



28. StreamBuilder for Real-Time Data


StreamBuilder can automatically rebuild the Flutter UI when Firestore data changes.


StreamBuilder>>(
  stream: FirebaseFirestore.instance
      .collection('users')
      .snapshots(),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const Center(
        child: CircularProgressIndicator(),
      );
    }

    if (snapshot.hasError) {
      return const Center(
        child: Text('Error loading data'),
      );
    }


    final users = snapshot.data?.docs ?? [];


    if (users.isEmpty) {
      return const Center(
        child: Text('No users found'),
      );
    }


    return ListView.builder(
      itemCount: users.length,
      itemBuilder: (context, index) {
        final user = users[index].data();


        return ListTile(
          title: Text(user['name'] ?? ''),
          subtitle: Text(user['email'] ?? ''),
        );
      },
    );
  },
)




29. Difference Between get() and snapshots()









Feature get() snapshots()
Purpose Retrieve data once Listen for data changes
Return Type Future Stream
Typical Widget FutureBuilder StreamBuilder
Real-Time Updates No continuous listener Yes
Common Use One-time data loading Live data screens



30. Retrieve Data Using Queries


Firestore queries allow the application to retrieve only documents that match specific conditions.


final snapshot = await FirebaseFirestore.instance
    .collection('users')
    .where(
      'isActive',
      isEqualTo: true,
    )
    .get();

for (final doc in snapshot.docs) {
  print(doc.data());
}




31. Retrieve Data by Email


final snapshot = await FirebaseFirestore.instance
    .collection('users')
    .where(
      'email',
      isEqualTo: '[email protected]',
    )
    .get();

for (final doc in snapshot.docs) {
  print(doc.data());
}




32. Retrieve Data by Category


final snapshot = await FirebaseFirestore.instance
    .collection('products')
    .where(
      'category',
      isEqualTo: 'Electronics',
    )
    .get();



33. Retrieve Data Using Multiple Conditions


Firestore supports compound queries when the selected query structure is supported.


final snapshot = await FirebaseFirestore.instance
    .collection('products')
    .where(
      'category',
      isEqualTo: 'Electronics',
    )
    .where(
      'isAvailable',
      isEqualTo: true,
    )
    .get();



34. Sort Retrieved Data


The orderBy() method can be used to specify the sort order.


final snapshot = await FirebaseFirestore.instance
    .collection('products')
    .orderBy('price')
    .get();

Descending Order


final snapshot = await FirebaseFirestore.instance
    .collection('products')
    .orderBy(
      'price',
      descending: true,
    )
    .get();



35. Limit Retrieved Data


The limit() method restricts the number of documents returned.


final snapshot = await FirebaseFirestore.instance
    .collection('products')
    .limit(10)
    .get();

Limiting data is useful when the application does not need to retrieve an entire collection at once.




36. Store and Retrieve Product Data


Store Product


await FirebaseFirestore.instance
    .collection('products')
    .doc('product001')
    .set({
  'name': 'Laptop',
  'price': 65000,
  'category': 'Electronics',
  'isAvailable': true,
});

Retrieve Product


final doc = await FirebaseFirestore.instance
    .collection('products')
    .doc('product001')
    .get();

if (doc.exists) {
  final product = doc.data();


  print(product?['name']);
  print(product?['price']);
}




37. Updating Stored Data


Application data often needs to be modified after it has been stored.


Use update() to modify selected fields.


await FirebaseFirestore.instance
    .collection('products')
    .doc('product001')
    .update({
  'price': 62000,
});



38. Update Multiple Fields


await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .update({
  'name': 'John Smith',
  'age': 26,
  'isActive': true,
});



39. Delete Stored Data


Use delete() to remove a document.


await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .delete();



40. CRUD Operations


CRUD represents the four basic database operations:








Operation Firestore Method Purpose
Create add() / set() Store new data
Read get() / snapshots() Retrieve data
Update update() Modify existing data
Delete delete() Remove data



41. Storing User Data with Firebase Authentication


Cloud Firestore is commonly used with Firebase Authentication. Authentication identifies the user, while Firestore stores additional application-specific user information.


A common structure is:


users
  |
  +-- USER_UID
       |
       +-- name
       +-- email
       +-- phone
       +-- createdAt

Example


final user = FirebaseAuth.instance.currentUser;

if (user != null) {
  await FirebaseFirestore.instance
      .collection('users')
      .doc(user.uid)
      .set({
    'name': 'John Doe',
    'email': user.email,
    'createdAt': FieldValue.serverTimestamp(),
  });
}




42. Retrieve Current User Data


final user = FirebaseAuth.instance.currentUser;

if (user != null) {
  final doc = await FirebaseFirestore.instance
      .collection('users')
      .doc(user.uid)
      .get();


  if (doc.exists) {
    final data = doc.data();


    print(data?['name']);
    print(data?['email']);
  }
}




43. Store Nested Data


Firestore documents can contain nested maps.


await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .set({
  'name': 'John Doe',
  'address': {
    'city': 'Mumbai',
    'state': 'Maharashtra',
    'country': 'India',
  },
});



44. Retrieve Nested Data


final doc = await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .get();

final data = doc.data();


if (data != null) {
  final address = data['address'];


  print(address['city']);
  print(address['state']);
}




45. Store Data in Subcollections


Subcollections can be used when data belongs to a particular document.


await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .collection('orders')
    .doc('order001')
    .set({
  'product': 'Flutter Course',
  'price': 5000,
  'status': 'completed',
});

The structure becomes:


users
  |
  +-- user001
       |
       +-- orders
            |
            +-- order001
                 |
                 +-- product
                 +-- price
                 +-- status



46. Retrieve Subcollection Data


final snapshot = await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .collection('orders')
    .get();

for (final doc in snapshot.docs) {
  print(doc.data());
}




47. Convert Firestore Data into Dart Models


For small applications, working directly with maps may be sufficient. For larger applications, Dart model classes provide a cleaner and more maintainable approach.


User Model


class UserModel {
  final String id;
  final String name;
  final String email;

  UserModel({
    required this.id,
    required this.name,
    required this.email,
  });


  factory UserModel.fromFirestore(
    DocumentSnapshot> doc,
  ) {
    final data = doc.data() ?? {};


    return UserModel(
      id: doc.id,
      name: data['name'] ?? '',
      email: data['email'] ?? '',
    );
  }
}




48. Retrieve Data Using a Model


final snapshot = await FirebaseFirestore.instance
    .collection('users')
    .get();

final users = snapshot.docs
    .map(UserModel.fromFirestore)
    .toList();


for (final user in users) {
  print(user.name);
  print(user.email);
}




49. Store Data Using a Model


A model can also provide a method for converting Dart objects into Firestore-compatible maps.


class UserModel {
  final String id;
  final String name;
  final String email;

  UserModel({
    required this.id,
    required this.name,
    required this.email,
  });


  Map toFirestore() {
    return {
      'name': name,
      'email': email,
    };
  }
}


Save Model


final user = UserModel(
  id: 'user001',
  name: 'John Doe',
  email: '[email protected]',
);

await FirebaseFirestore.instance
    .collection('users')
    .doc(user.id)
    .set(user.toFirestore());




50. Firestore Service Class


Database operations can be moved into a separate service class.


class UserService {
  final FirebaseFirestore _db = FirebaseFirestore.instance;

  Future createUser({
    required String id,
    required String name,
    required String email,
  }) async {
    await _db.collection('users').doc(id).set({
      'name': name,
      'email': email,
      'createdAt': FieldValue.serverTimestamp(),
    });
  }


  Future>> getUser(
    String id,
  ) {
    return _db.collection('users').doc(id).get();
  }


  Future updateUser(
    String id,
    Map data,
  ) {
    return _db.collection('users').doc(id).update(data);
  }


  Future deleteUser(String id) {
    return _db.collection('users').doc(id).delete();
  }
}




51. Loading State


Database operations are asynchronous, so the UI should display a loading indicator while data is being retrieved.


bool isLoading = false;

Future loadUsers() async {
  setState(() {
    isLoading = true;
  });


  try {
    final snapshot = await FirebaseFirestore.instance
        .collection('users')
        .get();


    // Process data
  } finally {
    if (mounted) {
      setState(() {
        isLoading = false;
      });
    }
  }
}




52. Error Handling


Firestore operations can fail because of network problems, permission errors, invalid queries, or other conditions. Always handle possible errors.


try {
  final snapshot = await FirebaseFirestore.instance
      .collection('users')
      .get();

  print(snapshot.docs);
} on FirebaseException catch (e) {
  print('Firestore error: ${e.code}');
  print('Message: ${e.message}');
} catch (e) {
  print('Unexpected error: $e');
}




53. Empty State


An application should handle the case where no documents are returned.


if (snapshot.docs.isEmpty) {
  return const Center(
    child: Text('No data available'),
  );
}

Empty-state messages provide useful feedback instead of displaying a blank screen.




54. Complete Data State Flow


Request Data
     |
     v
   Loading
     |
     v
Firestore Request
     |
     +---- Error ----> Error State
     |
     +---- No Data --> Empty State
     |
     +---- Data ----> Success State
                         |
                         v
                    Display Data



55. Real-Time User List Example


class UsersScreen extends StatelessWidget {
  const UsersScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Users'),
      ),
      body: StreamBuilder<
          QuerySnapshot>>(
        stream: FirebaseFirestore.instance
            .collection('users')
            .snapshots(),
        builder: (context, snapshot) {
          if (snapshot.connectionState ==
              ConnectionState.waiting) {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }


          if (snapshot.hasError) {
            return const Center(
              child: Text('Unable to load users'),
            );
          }


          final users = snapshot.data?.docs ?? [];


          if (users.isEmpty) {
            return const Center(
              child: Text('No users found'),
            );
          }


          return ListView.builder(
            itemCount: users.length,
            itemBuilder: (context, index) {
              final user = users[index].data();


              return ListTile(
                title: Text(user['name'] ?? ''),
                subtitle: Text(user['email'] ?? ''),
              );
            },
          );
        },
      ),
    );
  }
}




56. Storing Application Settings


Firestore can also store settings that need to be available across devices.


await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .set({
  'settings': {
    'darkMode': true,
    'notifications': true,
    'language': 'English',
  },
}, SetOptions(merge: true));



57. Retrieve Application Settings


final doc = await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .get();

final data = doc.data();


if (data != null) {
  final settings = data['settings'];


  print(settings['darkMode']);
  print(settings['notifications']);
  print(settings['language']);
}




58. Storing Orders


An e-commerce application can store order information in Firestore.


await FirebaseFirestore.instance
    .collection('orders')
    .add({
  'userId': 'user001',
  'productId': 'product001',
  'quantity': 2,
  'totalAmount': 130000,
  'status': 'pending',
  'createdAt': FieldValue.serverTimestamp(),
});



59. Retrieve Orders for a User


final snapshot = await FirebaseFirestore.instance
    .collection('orders')
    .where(
      'userId',
      isEqualTo: 'user001',
    )
    .get();

for (final doc in snapshot.docs) {
  print(doc.data());
}




60. Store Messages


Firestore can be used to store messages for chat or communication features.


await FirebaseFirestore.instance
    .collection('messages')
    .add({
  'senderId': 'user001',
  'message': 'Hello!',
  'createdAt': FieldValue.serverTimestamp(),
});



61. Retrieve Messages in Real Time


StreamBuilder(
  stream: FirebaseFirestore.instance
      .collection('messages')
      .orderBy('createdAt')
      .snapshots(),
  builder: (context, snapshot) {
    if (!snapshot.hasData) {
      return const CircularProgressIndicator();
    }

    final messages = snapshot.data!.docs;


    return ListView.builder(
      itemCount: messages.length,
      itemBuilder: (context, index) {
        final message = messages[index].data();


        return ListTile(
          title: Text(message['message'] ?? ''),
          subtitle: Text(message['senderId'] ?? ''),
        );
      },
    );
  },
)




62. Pagination


When a collection contains a large number of documents, retrieving everything at once may not be appropriate. Firestore supports query cursors that can be used to retrieve data in pages.


A typical pagination flow is:


First Request
     |
     v
Load First Page
     |
     v
Remember Last Document
     |
     v
Request Next Page
     |
     v
Append New Data
     |
     v
Continue Until No More Data

Pagination should be designed together with appropriate ordering and filtering.




63. Limit and Order Data for Pagination


final snapshot = await FirebaseFirestore.instance
    .collection('products')
    .orderBy('createdAt', descending: true)
    .limit(10)
    .get();

The last document from one query can be used as a cursor for a subsequent query when implementing cursor-based pagination.




64. Offline Data


Cloud Firestore provides offline capabilities on supported client platforms. Firestore can cache data that the application is actively using and synchronize local changes with the backend when connectivity returns.


This can help applications remain responsive when network connectivity is temporarily unavailable.


Firestore Offline Data Documentation


65. Firestore Security Rules


Application data should not be left publicly accessible in production. Firestore Security Rules can be used to control access to documents and collections.


For example, a user-specific document can be protected using the authenticated user's UID.


rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {


    match /users/{userId} {
      allow read, write: if request.auth != null
                         && request.auth.uid == userId;
    }
  }
}


This rule allows an authenticated user to access the user document whose document ID matches their Firebase Authentication UID.




66. Security and Data Validation



  • Use Firebase Authentication when user identity is required.

  • Protect Firestore data with Security Rules.

  • Do not store sensitive information unnecessarily.

  • Validate important data before storing it.

  • Use UID-based document structures for user-specific data.

  • Design queries that are compatible with your Security Rules.

  • Do not assume client-side validation is sufficient for database security.

  • Review Security Rules before deploying the application.




67. Firestore Data Architecture


A clean application architecture can separate the UI, business logic, and database operations.


Flutter UI
    |
    v
Controller / ViewModel
    |
    v
Repository / Service
    |
    v
Cloud Firestore
    |
    v
Firebase Backend

This approach makes the application easier to maintain and test.




68. Recommended Project Structure


lib/
├── main.dart
├── firebase_options.dart
├── models/
│   ├── user_model.dart
│   ├── product_model.dart
│   └── order_model.dart
├── services/
│   ├── firestore_service.dart
│   └── auth_service.dart
├── repositories/
│   └── user_repository.dart
├── screens/
│   ├── home_screen.dart
│   ├── users_screen.dart
│   ├── products_screen.dart
│   └── orders_screen.dart
└── widgets/
    ├── user_card.dart
    ├── product_card.dart
    └── loading_widget.dart



69. Common Mistakes













Mistake Better Approach
Loading all documents unnecessarily Use queries, limits, and pagination where appropriate.
Ignoring errors Handle Firestore exceptions.
Ignoring empty results Display an appropriate empty state.
No loading indicator Show loading feedback during asynchronous operations.
Public database rules in production Use appropriate Security Rules.
Mixing database code with every widget Use services or repositories.
Using raw maps everywhere Use Dart model classes for larger applications.
Retrieving data repeatedly Use appropriate state management and listeners.
Ignoring query limitations Design supported queries and indexes carefully.



70. Best Practices



  • Design your Firestore data structure before writing application code.

  • Use meaningful collection and document names.

  • Use model classes for complex applications.

  • Separate Firestore operations from UI widgets.

  • Handle loading, success, error, and empty states.

  • Use queries instead of downloading unnecessary data.

  • Use limit() and pagination for large datasets.

  • Use real-time listeners only where real-time updates are required.

  • Use server timestamps for server-generated timestamps when appropriate.

  • Protect data using Firebase Authentication and Security Rules.

  • Test Firestore Security Rules before production.

  • Keep sensitive information out of client-side code.

  • Use offline support thoughtfully when your application requires it.




71. Complete CRUD Example


Create


Future createUser() async {
  await FirebaseFirestore.instance
      .collection('users')
      .doc('user001')
      .set({
    'name': 'John Doe',
    'email': '[email protected]',
    'createdAt': FieldValue.serverTimestamp(),
  });
}

Read


Future readUser() async {
  final doc = await FirebaseFirestore.instance
      .collection('users')
      .doc('user001')
      .get();

  print(doc.data());
}


Update


Future updateUser() async {
  await FirebaseFirestore.instance
      .collection('users')
      .doc('user001')
      .update({
    'name': 'John Smith',
  });
}

Delete


Future deleteUser() async {
  await FirebaseFirestore.instance
      .collection('users')
      .doc('user001')
      .delete();
}



72. Mini Project: User Data Management App


Create a Flutter application that stores and retrieves user information using Cloud Firestore.


Features



  • Add a new user.

  • Display all users.

  • View individual user details.

  • Update user information.

  • Delete users.

  • Search or filter users.

  • Display loading state.

  • Display error state.

  • Display empty state.

  • Use Firebase Authentication for user identity.

  • Protect data with Firestore Security Rules.


Suggested Firestore Structure


users
  |
  +-- USER_UID
       |
       +-- name
       +-- email
       +-- phone
       +-- createdAt
       +-- updatedAt



73. Mini Project: Product Management App


Create a product management application that uses Firestore as its cloud database.


Product Structure


products
  |
  +-- product001
       |
       +-- name
       +-- price
       +-- category
       +-- description
       +-- isAvailable
       +-- createdAt

Required Operations



  1. Add product.

  1. Display products.

  1. Search or filter products.

  1. Sort products by price.

  1. Update product.

  1. Delete product.

  1. Display loading state.

  1. Handle errors.

  1. Handle empty results.




74. Interview Questions



  1. What is application data?

  1. Why do Flutter applications need persistent storage?

  1. What is Cloud Firestore?

  1. What is the Firestore data model?

  1. What is a collection?

  1. What is a document?

  1. What are Firestore fields?

  1. How do you add Cloud Firestore to a Flutter application?

  1. What is FirebaseFirestore.instance?

  1. What is the difference between add() and set()?

  1. How do you store data with a custom document ID?

  1. How do you retrieve a single document?

  1. How do you retrieve all documents from a collection?

  1. What is the difference between get() and snapshots()?

  1. How do you display Firestore data using FutureBuilder?

  1. How do you display real-time data using StreamBuilder?

  1. How do you filter Firestore data?

  1. What is where()?

  1. What is orderBy()?

  1. What is limit()?

  1. How do you update a Firestore document?

  1. How do you delete a Firestore document?

  1. How do Firebase Authentication and Firestore work together?

  1. What are Firestore Security Rules?

  1. Why should model classes be used in larger applications?

  1. What is a Firestore subcollection?

  1. What is real-time data retrieval?

  1. What is offline support in Firestore?




75. Quick Revision




















Concept Firestore API
Firestore Instance FirebaseFirestore.instance
Collection collection()
Document doc()
Store with Auto ID add()
Store with Custom ID set()
Update Data update()
Retrieve Once get()
Real-Time Retrieval snapshots()
Filter Data where()
Sort Data orderBy()
Limit Data limit()
Delete Data delete()
Server Timestamp FieldValue.serverTimestamp()
Increment Value FieldValue.increment()
Array Add FieldValue.arrayUnion()
Array Remove FieldValue.arrayRemove()



76. Learning Outcomes


After completing this topic, you should be able to:



  • Explain application data and persistent storage.

  • Understand the Cloud Firestore data model.

  • Configure Firestore in a Flutter application.

  • Store data in Firestore.

  • Retrieve individual documents.

  • Retrieve multiple documents.

  • Update stored application data.

  • Delete application data.

  • Use Firestore queries.

  • Filter and sort retrieved data.

  • Display Firestore data using Flutter widgets.

  • Use FutureBuilder for one-time data retrieval.

  • Use StreamBuilder for real-time data.

  • Store nested data and subcollections.

  • Connect Firestore with Firebase Authentication.

  • Convert Firestore documents into Dart models.

  • Create Firestore service classes.

  • Handle loading, error, empty, and success states.

  • Apply Firestore Security Rules.

  • Understand pagination and offline data concepts.




77. Official Firebase Resources















78. JustAcademy Flutter Resources


For structured Flutter learning, practical development training, and additional Flutter topics, explore the following resources:








79. Summary


Storing and retrieving application data is a fundamental requirement for real-world Flutter applications. Cloud Firestore provides a flexible NoSQL database where application data can be organized into collections and documents.


Flutter applications can use Firestore to create, read, update, delete, query, and listen to application data in real time. One-time reads can be performed with get(), while real-time updates can be handled with snapshots(). Queries such as where(), orderBy(), and limit() help retrieve the required data efficiently.


For production applications, Firestore should be combined with proper data modeling, loading and error handling, Firebase Authentication where appropriate, Security Rules, and a clean service or repository architecture.

whatsapp