Skip to content

Instantly share code, notes, and snippets.

@callmephil
Created April 21, 2025 07:05
Show Gist options
  • Save callmephil/52e79c158002254762bee6acb0183da8 to your computer and use it in GitHub Desktop.
Save callmephil/52e79c158002254762bee6acb0183da8 to your computer and use it in GitHub Desktop.
Base Firestore Service
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/foundation.dart';
/// Base service class for handling CRUD operations with Firestore
abstract class BaseFirestoreService<T> {
/// Constructor
BaseFirestoreService(String collectionPath) {
_collection = _firestore.collection(collectionPath);
}
/// The Firestore instance
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
/// The collection reference
late final CollectionReference _collection;
/// Convert a document snapshot to a model instance
T fromSnapshot(DocumentSnapshot snapshot);
/// Convert a model instance to a map for Firestore
Map<String, dynamic> toMap(T item);
/// Get the ID of an item
String? getId(T item);
/// Set the ID of an item
T setId(T item, String id);
/// Get all items from the collection
Future<List<T>> getAll() async {
final querySnapshot = await _collection.get();
return querySnapshot.docs.map(fromSnapshot).toList();
}
/// Get an item by its ID
Future<T?> getById(String id) async {
final docSnapshot = await _collection.doc(id).get();
if (!docSnapshot.exists) {
return null;
}
return fromSnapshot(docSnapshot);
}
/// Add a new item
Future<T> add(T item) async {
final docRef = _collection.doc(); // Create a new doc ref to get the ID
final itemWithId = setId(item, docRef.id);
await docRef.set(toMap(itemWithId));
return itemWithId;
}
// bulk add with id
Future<List<T>> bulkAdd(List<T> items) async {
final batch = _firestore.batch();
final docRefs = <DocumentReference>[];
final itemsWithId = <T>[];
for (final item in items) {
final docRef = _collection.doc();
final itemWithId = setId(item, docRef.id);
batch.set(docRef, toMap(itemWithId));
docRefs.add(docRef);
itemsWithId.add(itemWithId);
}
await batch.commit();
return itemsWithId;
}
/// Set an item with a specific ID
Future<T> set(String id, T item) async {
final itemWithId = setId(item, id);
await _collection.doc(id).set(toMap(itemWithId));
return itemWithId;
}
/// Update an existing item
Future<T?> update(String id, T item) async {
await _collection.doc(id).update(toMap(setId(item, id)));
return getById(id);
}
/// Delete an item by its ID
Future<bool> delete(String id) async {
try {
await _collection.doc(id).delete();
return true;
} on Exception catch (e, trace) {
// TODO: must handle excepetion properly
debugPrint([e, trace].toString());
return false;
}
}
/// Get a stream of all items
Stream<List<T>> getAllStream() {
return _collection.snapshots().map(
(snapshot) => snapshot.docs.map(fromSnapshot).toList(),
);
}
/// Get a stream of a specific item
Stream<T?> getByIdStream(String id) {
return _collection
.doc(id)
.snapshots()
.map((snapshot) => snapshot.exists ? fromSnapshot(snapshot) : null);
}
/// Query items with a filter
Future<List<T>> query(
String field, {
Object? isEqualTo,
Object? isNotEqualTo,
Object? isLessThan,
Object? isLessThanOrEqualTo,
Object? isGreaterThan,
Object? isGreaterThanOrEqualTo,
Object? arrayContains,
List<Object>? arrayContainsAny,
List<Object>? whereIn,
List<Object>? whereNotIn,
bool? isNull,
}) async {
final query = _collection.where(
field,
isEqualTo: isEqualTo,
isNotEqualTo: isNotEqualTo,
isLessThan: isLessThan,
isLessThanOrEqualTo: isLessThanOrEqualTo,
isGreaterThan: isGreaterThan,
isGreaterThanOrEqualTo: isGreaterThanOrEqualTo,
arrayContains: arrayContains,
arrayContainsAny: arrayContainsAny,
whereIn: whereIn,
whereNotIn: whereNotIn,
isNull: isNull,
);
return (await query.get()).docs.map(fromSnapshot).toList();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment