FlutterDB
FlutterDB is a lightweight, SQLite-based document database for Flutter applications. It provides a MongoDB-like API for managing collections and documents while leveraging the performance benefits of SQLite.
Why do I build this?
So I am using MongoDB in my NodeJS application. Now I am building a chat application in Flutter, and for rendering performance, I thought I should store the messages locally and render a sliding window on it. Still, I have no luck finding a good local database that is flexible enough and also fast enough. And so I thought, why not build a MongoDB-like database in Flutter, which gets the job done. It's not a full-fledged MongoDB running locally, but it is similar to it, and most of the common tasks are going very well, as I tested.
Features
- Document-oriented database with collections and documents
- MongoDB-style query syntax and operations
- Built on top of SQLite for performance and reliability
- Unique ID generation for documents
- Support for complex queries, updates, and aggregations
- Batch operations for better performance
Installation
Add the following dependencies to your pubspec.yaml
:
dependencies:
flutterdb: ^0.0.1
Getting Started
Import the package:
import 'package:flutterdb/flutterdb.dart';
Initialize the database:
final db = FlutterDB();
Basic Usage
Working with Collections
// Create or get a collection
final users = await db.collection('users');
// List all collections
final collections = await db.listCollections();
// Drop a collection
await db.dropCollection('users');
CRUD Operations
Create
// Insert a single document
final userId = await users.insert({
'name': 'John Doe',
'email': '[email protected]',
'age': 30
});
// Insert multiple documents
final ids = await users.insertMany([
{
'name': 'Jane Smith',
'email': '[email protected]',
'age': 25
},
{
'name': 'Bob Johnson',
'email': '[email protected]',
'age': 40
}
]);
Read
// Find all documents in a collection
final allUsers = await users.find({});
// Find documents with a simple query
final adults = await users.find({'age': {'$gte': 18}});
// Find a document by ID
final user = await users.findById(userId);
// Count documents
final userCount = await users.count({'age': {'$gt': 30}});
Update
// Update a document by ID
await users.updateById(userId, {'status': 'active'});
// Update multiple documents
final updatedCount = await users.updateMany(
{'age': {'$lt': 30}},
{'status': 'young'}
);
Delete
// Delete a document by ID
await users.deleteById(userId);
// Delete multiple documents
final deletedCount = await users.deleteMany({'status': 'inactive'});
Advanced Queries
Comparison Operators
// Greater than
await users.find({'age': {'$gt': 30}});
// Less than or equal
await users.find({'age': {'$lte': 25}});
// Not equal
await users.find({'status': {'$ne': 'inactive'}});
// In array
await users.find({'role': {'$in': ['admin', 'moderator']}});
Logical Operators
// AND
await users.find({
'$and': [
{'age': {'$gte': 18}},
{'status': 'active'}
]
});
// OR
await users.find({
'$or': [
{'role': 'admin'},
{'permissions': {'$in': ['write', 'delete']}}
]
});
// NOR
await users.find({
'$nor': [
{'status': 'banned'},
{'role': 'guest'}
]
});
Text Search
// Regex search
await users.find({'name': {'$regex': '^Jo'}});
// Simple text search
await users.find({'bio': {'$like': 'flutter developer'}});
Aggregation
final results = await users.aggregate([
{'$match': {'status': 'active'}},
{'$sort': {'age': -1}},
{'$skip': 10},
{'$limit': 20},
{'$project': {
'name': 1,
'age': 1,
'city': 1,
'_id': 0,
}
},
{'$group': {
'_id': '\$city',
'count': {'\$sum': 1}
}
},
]);
Best Practices
Performance Optimization
- Use batch operations for multiple insertions or updates:
await users.insertMany(manyUsers); // Better than inserting one by one
-
Keep document size reasonable:
- Large documents can slow down performance
- Consider storing large binary data (images, files) separately
-
Create indexes for frequently queried fields:
- Currently, FlutterDB automatically indexes collection names
- Future versions may support custom indexes
-
Use appropriate queries:
- Finding by ID is faster than complex queries
- Limit results when possible using
$limit
Data Structure
-
Use consistent schemas:
- While FlutterDB is schemaless, consistent document structures improve code maintainability
-
Choose good document IDs:
- Let FlutterDB generate IDs unless you have specific requirements
- Custom IDs should be unique and consistent
-
Handle relationships thoughtfully:
- For one-to-many relationships, consider embedding related data or using references
Error Handling
Always implement error handling:
try {
await users.insert({'name': 'John'});
} catch (e) {
print('Error inserting document: $e');
// Handle error appropriately
}
Limitations
-
Not a full MongoDB replacement:
- Limited subset of MongoDB query operators
- Some complex aggregation operations not supported
-
Performance with large datasets:
- Best suited for mobile apps with moderate data size
- Consider pagination or limiting queries for large collections
-
No built-in encryption:
- Data is stored in plain text
- Consider additional encryption for sensitive data
-
Limited indexing options:
- Custom indexes not yet supported
- Consider query performance for large collections
-
No network synchronization:
- Local database only
- Implement your own sync solution if needed
Complete Example
import 'package:flutter/material.dart';
import 'package:flutterdb/flutterdb.dart';
class UserRepository {
final FlutterDB _db = FlutterDB();
late Future<Collection> _users;
UserRepository() {
_users = _db.collection('users');
}
Future<String> addUser(String name, String email, int age) async {
final collection = await _users;
return await collection.insert({
'name': name,
'email': email,
'age': age,
'createdAt': DateTime.now().toIso8601String(),
});
}
Future<List<Map<String, dynamic>>> getAdultUsers() async {
final collection = await _users;
return await collection.find({
'age': {'$gte': 18},
});
}
Future<void> updateUserStatus(String id, String status) async {
final collection = await _users;
await collection.updateById(id, {
'status': status,
'updatedAt': DateTime.now().toIso8601String(),
});
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final userRepo = UserRepository();
// Add a user
final userId = await userRepo.addUser('Alice', '[email protected]', 28);
print('Added user with ID: $userId');
// Update user status
await userRepo.updateUserStatus(userId, 'premium');
// Get all adult users
final adults = await userRepo.getAdultUsers();
for (var user in adults) {
print('User: ${user['name']}, Age: ${user['age']}, Status: ${user['status']}');
}
}
Future Enhancements
- Custom indexing support
- More aggregation pipeline operators
- Full-text search capabilities
- Data encryption options
- Schema validation
- Observable queries
- Migration support
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the GNU AGPLv3 License - see the LICENSE file for details.
If you are searching for this keywords
"offline-first Flutter apps"
"local storage in Flutter"
"best local database Flutter"
your search ends here
I'd love your thoughts. Try FlutterDB in your next project and let me know how it goes! You can ⭐️ the GitHub repo or open an issue if you hit any snags. 🙌