← Files FirebaseARCHIVED FILE
skills/firebase-firestore/references/enterprise/flutter_setup.md
4.65 KB · Oct 4, 2026 · 12:18 UTC
# Cloud Firestore in Flutter
This guide covers basic CRUD operations, type-safe data modeling, and real-time
streams when using Cloud Firestore in a Flutter application via the
`cloud_firestore` package.
## 1. Setup
Ensure you have added the required dependency:
```bash
flutter pub add cloud_firestore
```
Also, ensure FlutterFire is configured properly for your target platforms.
______________________________________________________________________
## 2. Best Practices: Type-Safe Models
Instead of passing raw `Map<String, dynamic>` maps throughout your UI layer,
define a domain model class with `fromFirestore` and `toFirestore` converters to
maintain type safety.
```dart
import 'package:cloud_firestore/cloud_firestore.dart';
class Item {
final String id;
final String name;
final String ownerId;
final DateTime createdAt;
Item({
required this.id,
required this.name,
required this.ownerId,
required this.createdAt,
});
factory Item.fromFirestore(DocumentSnapshot doc) {
final data = doc.data() as Map<String, dynamic>? ?? {};
return Item(
id: doc.id,
name: data['name'] as String? ?? '',
ownerId: data['ownerId'] as String? ?? '',
createdAt: data['createdAt'] is Timestamp
? (data['createdAt'] as Timestamp).toDate()
: DateTime.now(),
);
}
Map<String, dynamic> toFirestore() {
return {
'name': name,
'ownerId': ownerId,
'createdAt': Timestamp.fromDate(createdAt),
};
}
}
```
______________________________________________________________________
## 3. The Service Layer
Encapsulate all database interactions within a dedicated service class to keep
your UI code clean and testable.
### Initialization & References
```dart
class ItemService {
// For Enterprise Native Mode, you often need to specify a non-default database ID:
final FirebaseFirestore _db = FirebaseFirestore.instanceFor(
app: Firebase.app(),
databaseId: 'my-database-id',
);
// Define your collection reference
CollectionReference get _itemsRef => _db.collection('items');
// 1. Create Data
Future<void> createItem(Item item) async {
try {
await _itemsRef.add(item.toFirestore());
} catch (e) {
print("Error creating document: $e");
}
}
// 2. Read Data (One-Time Fetch)
Future<List<Item>> fetchItems(String ownerId) async {
try {
final querySnapshot = await _itemsRef
.where('ownerId', isEqualTo: ownerId)
.orderBy('createdAt', descending: true)
.get();
return querySnapshot.docs.map((doc) => Item.fromFirestore(doc)).toList();
} catch (e) {
print("Error fetching documents: $e");
return [];
}
}
// 3. Read Data (Real-Time Stream)
Stream<List<Item>> streamItems(String ownerId) {
return _itemsRef
.where('ownerId', isEqualTo: ownerId)
.snapshots()
.map((snapshot) {
// If a custom composite index is missing during prototyping, apply sorting client-side:
final items = snapshot.docs.map((doc) => Item.fromFirestore(doc)).toList();
items.sort((a, b) => b.createdAt.compareTo(a.createdAt));
return items;
});
}
// 4. Update Data
Future<void> updateItemName(String id, String newName) async {
try {
await _itemsRef.doc(id).update({'name': newName});
} catch (e) {
print("Error updating document: $e");
}
}
// 5. Delete Data
Future<void> deleteItem(String id) async {
try {
await _itemsRef.doc(id).delete();
} catch (e) {
print("Error deleting document: $e");
}
}
}
```
______________________________________________________________________
## 4. Listening to Streams in the UI (`StreamBuilder`)
Use Flutter's `StreamBuilder` to rebuild the interface reactively whenever data
changes in your database collection.
```dart
StreamBuilder<List<Item>>(
stream: itemService.streamItems(currentUser.uid),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(child: Text('Failed to load data'));
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
final items = snapshot.data ?? [];
if (items.isEmpty) {
return const Center(child: Text('No items found.'));
}
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return ListTile(
title: Text(item.name),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () => itemService.deleteItem(item.id),
),
);
},
);
},
);
```
SHA-256: 56915ee88796b65656e5dbbf2fce433615231b4705d07e41ebc998e19eef5e8d