One Firebase
A comprehensive Firebase service wrapper for Flutter with Riverpod integration, providing type-safe CRUD operations, batch updates, pagination, and real-time data streaming for Firestore.
Features
β¨ Type-safe operations - Generic methods with compile-time type checking
π Riverpod integration - Built-in providers for dependency injection
π¦ Batch operations - Efficient batch create, update, and delete operations
π Pagination support - Built-in pagination with PaginatedResult
π΄ Real-time streaming - Live data updates with Firestore streams
π‘οΈ Error handling - Comprehensive error handling with custom exceptions
π Logging - Built-in error logging for debugging
Getting started
Prerequisites
- Flutter SDK
- Firebase project configured
- Firestore enabled in your Firebase project
Installation
Add this package to your pubspec.yaml
:
dependencies:
one_firebase: ^0.0.1
flutter_riverpod: ^2.6.1
cloud_firestore: ^5.6.0
Firebase Setup
- Configure Firebase in your Flutter project
- Initialize Firebase in your
main.dart
:
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(ProviderScope(child: MyApp()));
}
Usage
Basic Setup
Import the package and use the provided service:
import 'package:one_firebase/one_firebase.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class MyWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final firebaseService = ref.watch(firebaseServiceProvider);
// Use the service...
}
}
Creating Documents
// Define your model
class User {
final String id;
final String name;
final String email;
User({required this.id, required this.name, required this.email});
Map<String, dynamic> toMap() => {
'id': id,
'name': name,
'email': email,
};
factory User.fromMap(Map<String, dynamic> map) => User(
id: map['id'],
name: map['name'],
email: map['email'],
);
}
// Create a document
final user = User(id: 'user1', name: 'John Doe', email: 'john@example.com');
final docRef = firebaseService.getDoc('users', user.id);
await firebaseService.create(ref: docRef, data: user.toMap());
Fetching Documents
// Fetch a single document
final docRef = firebaseService.getDoc('users', 'user1');
final user = await firebaseService.fetchModel<User>(
ref: docRef,
fromMap: User.fromMap,
);
// Fetch multiple documents with pagination
final query = FirebaseFirestore.instance
.collection('users')
.where('active', isEqualTo: true)
.orderBy('createdAt', descending: true);
final result = await firebaseService.fetchModels<User>(
query: query,
fromMap: User.fromMap,
collectionName: 'users',
limit: 10,
);
print('Fetched ${result.items.length} users');
// Use result.lastDocument for pagination
Real-time Data Streaming
// Listen to a single document
final docRef = firebaseService.getDoc('users', 'user1');
final userStream = firebaseService.listenModel<User>(
ref: docRef,
fromMap: User.fromMap,
);
userStream.listen((user) {
if (user != null) {
print('User updated: ${user.name}');
}
});
// Listen to multiple documents
final query = FirebaseFirestore.instance.collection('users');
final usersStream = firebaseService.listenModels<User>(
query: query,
fromMap: User.fromMap,
collectionName: 'users',
limit: 20,
);
usersStream.listen((result) {
print('Users updated: ${result.items.length} items');
});
Updating Documents
final docRef = firebaseService.getDoc('users', 'user1');
await firebaseService.update(
ref: docRef,
data: {'name': 'John Smith', 'updatedAt': FieldValue.serverTimestamp()},
);
Batch Operations
final batch = [
FirebaseServiceModel(
ref: firebaseService.getDoc('users', 'user1'),
data: {'name': 'Updated Name'},
type: BatchTypeEnum.update,
),
FirebaseServiceModel(
ref: firebaseService.getDoc('users', 'user2'),
data: {'name': 'New User', 'email': 'new@example.com'},
type: BatchTypeEnum.create,
),
FirebaseServiceModel(
ref: firebaseService.getDoc('users', 'user3'),
data: {},
type: BatchTypeEnum.delete,
),
];
await firebaseService.batchUpdate(batch);
Error Handling
The service provides comprehensive error handling with FirebaseServiceException
:
try {
await firebaseService.create(ref: docRef, data: userData);
} on FirebaseServiceException catch (e) {
print('Firebase operation failed: ${e.message}');
print('Method: ${e.method}');
print('Original error: ${e.error}');
} catch (e) {
print('Unexpected error: $e');
}
API Reference
FirebaseServiceInterface
Methods
getDoc(String collection, String? id)
- Get document referencecreate({required DocumentReference ref, required Map<String, dynamic> data})
- Create documentupdate({required DocumentReference ref, required Map<String, dynamic> data})
- Update documentbatchUpdate(List<FirebaseServiceModel> models)
- Batch operationsfetchModel<T>({required DocumentReference ref, required T Function(Map<String, dynamic>) fromMap})
- Fetch single documentfetchModels<T>({required Query query, required T Function(Map<String, dynamic>) fromMap, required String collectionName, int? limit, DocumentSnapshot? lastDocumentSnapshot})
- Fetch multiple documents with paginationlistenModel<T>({required DocumentReference ref, required T Function(Map<String, dynamic>) fromMap})
- Stream single documentlistenModels<T>({required Query query, required T Function(Map<String, dynamic>) fromMap, required String collectionName, int? limit})
- Stream multiple documents
Models
PaginatedResult
class PaginatedResult<T> {
final List<T> items;
final DocumentSnapshot? lastDocument;
}
FirebaseServiceModel
class FirebaseServiceModel {
final Map<String, dynamic> data;
final DocumentReference ref;
final BatchTypeEnum type;
}
BatchTypeEnum
enum BatchTypeEnum { create, update, delete }
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.