Choosing the Right Local Database for Flutter: Beyond Isar's End-of-Life
The Flutter news you actually need
No spam, ever. Unsubscribe in one click.
So, you built your Flutter app with a snappy local database, your data layer is humming along, and then you get the news: your chosen database, Isar, is reaching its end-of-life. This scenario has left many developers in a tough spot, facing the non-trivial task of migrating their app’s foundational data layer. The good news? The Flutter ecosystem is robust, and there are excellent, well-maintained alternatives ready for you. Let’s move beyond the panic and evaluate two of the strongest contenders: ObjectBox and Drift.
The core lesson here is about sustainability. Choosing a database is a long-term architectural decision. While features and benchmarks are important, the health of the project—active maintenance, a clear roadmap, and community support—is paramount. Your new choice needs to be a dependable foundation for years to come.
Contender 1: ObjectBox – High-Performance NoSQL
ObjectBox is a superfast, NoSQL object database built for high performance on devices. If you loved Isar’s NoSQL approach and its speed, ObjectBox will feel familiar and is arguably its spiritual successor in the Flutter space. It’s maintained by a dedicated company, which bodes well for its future.
Key Features:
- Blazing Speed: Benchmarks often show it outperforming SQLite for many operations.
- Object-Oriented: You work directly with your Dart objects; no ORM mapping needed.
- Live Queries: Get reactive streams that automatically update when the underlying data changes.
- Strong Relations: Supports To-One, To-Many, and even Many-to-Many relationships.
Integration & Code Example:
First, add the dependencies to your pubspec.yaml:
dependencies:
objectbox: ^1.8.1
objectbox_flutter_libs: ^1.8.1
dev_dependencies:
objectbox_generator: ^1.8.1
build_runner: ^2.4.0
Define your entity. Notice the @Entity() annotation and the obligatory id field.
// customer.dart
import 'package:objectbox/objectbox.dart';
@Entity()
class Customer {
int id = 0; // ObjectBox requires an int id field (0 for new objects).
String name;
String email;
Customer({this.id = 0, required this.name, required this.email});
}
Run the code generator to create the necessary binding code:
flutter pub run build_runner build
Using the database is straightforward:
// main.dart (simplified snippet)
import 'package:objectbox/objectbox.dart';
void main() async {
final store = await openStore(); // Opens the ObjectBox store.
final customerBox = store.box<Customer>();
// Put (Insert/Update)
final newCustomer = Customer(name: 'Alice Smith', email: 'alice@example.com');
final customerId = customerBox.put(newCustomer);
print('Saved customer with ID: $customerId');
// Query
final query = customerBox.query(Customer_.name.equals('Alice Smith')).build();
final results = query.find();
query.close();
// Live Query (reactive stream)
final liveQuery = customerBox.query().watch();
liveQuery.stream.listen((query) {
print('Customers updated: ${query.find()}');
});
}
Contender 2: Drift – The Power of SQL, Simplified
Drift (formerly Moor) is a reactive persistence library built on SQLite. It lets you write queries in both typesafe Dart and traditional SQL, giving you the full power and battle-tested reliability of SQLite with a modern, fluent API.
Key Features:
- SQLite Power: Leverages one of the most deployed databases in the world.
- Compile-Time Safety: Queries are checked for errors at compile time, not at runtime.
- Reactive by Design: Easily convert queries into
Streams that update when data changes. - Flexible: Write pure SQL or use a fluent Dart API.
Integration & Code Example:
Add Drift and its generator to your pubspec.yaml:
dependencies:
drift: ^2.19.0
sqlite3_flutter_libs: ^0.5.0
path_provider: ^2.1.0
dev_dependencies:
drift_dev: ^2.19.0
build_runner: ^2.4.0
Define your tables and data access objects:
// database.dart
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';
part 'database.g.dart'; // This will be generated
class TaskItems extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().withLength(min: 1, max: 100)();
BoolColumn get isCompleted => boolean().withDefault(const Constant(false))();
}
@DriftDatabase(tables: [TaskItems])
class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection());
@override
int get schemaVersion => 1;
}
LazyDatabase _openConnection() {
return LazyDatabase(() async {
final dbFolder = await getApplicationDocumentsDirectory();
final file = File('${dbFolder.path}/my_app_db.sqlite');
return NativeDatabase(file);
});
}
Generate the code with flutter pub run build_runner build. Then, use it in your app:
// Using the database
final db = AppDatabase();
// Insert a task
await db.into(db.taskItems).insert(
TaskItemsCompanion.insert(title: 'Migrate database'),
);
// Query all incomplete tasks (reactive stream)
Stream<List<TaskItem>> watchIncompleteTasks() {
return (db.select(db.taskItems)
..where((t) => t.isCompleted.equals(false)))
.watch();
}
// Use raw SQL for complex operations
Future<void> archiveOldTasks() async {
await db.customStatement('''
UPDATE task_items SET title = '[ARCHIVED] ' || title
WHERE is_completed = 1 AND date_created < ?
''', [DateTime.now().subtract(Duration(days: 30))]);
}
How to Choose?
- Choose ObjectBox if: Your app demands maximum read/write performance, you prefer a NoSQL/Object-oriented model, and you need built-in reactive live queries without extra setup. It’s excellent for data-heavy, performance-critical applications.
- Choose Drift if: You value the stability and universality of SQLite, need to execute complex queries or migrations, or your team already has strong SQL knowledge. It offers unparalleled flexibility and control.
Common Migration Mistake to Avoid: Don’t try to write a one-shot converter that moves all data from Isar to your new database on the first run. This can block the UI and cause timeouts. Instead, design a gradual migration strategy. Initialize your new database, and lazily move old data as it’s accessed, or run the migration in a background isolate, showing a progress indicator to the user.
Ultimately, both ObjectBox and Drift are fantastic, production-ready choices backed by active maintenance. Your decision hinges on your app’s specific needs and your team’s preference for NoSQL versus SQL.
This blog is produced with the assistance of AI by a human editor. Learn more
Related Posts
Flutter's Hidden Gem: Building Powerful Linux Desktop Apps with Ease
Flutter is gaining traction as a robust framework for Linux desktop development, offering a smoother experience compared to traditional toolkits like GTK and Qt. This post will explore why Flutter excels for Linux apps, highlight its advantages, and provide practical insights for developers looking to leverage Flutter for their desktop projects.
Simplifying Flutter Desktop Deployment: Signing and Distribution for Windows
Deploying Flutter desktop apps on Windows can be challenging, especially regarding code signing and preventing Windows from blocking installations. This post will guide developers through the process of signing their Flutter desktop applications, exploring alternatives to the Microsoft Store like creating executable installers, and covering the necessary steps to ensure a smooth user experience.
Mastering Flutter Tooling: Streamlining SDK Management and Installation on Windows
Developers frequently voice frustrations about Flutter SDK version management and installation processes, particularly on Windows. This post will explore best practices for managing multiple Flutter versions using tools like FVM, discuss integrating Flutter with package managers like Winget for a smoother Windows setup, and offer strategies to ensure a consistent development environment across projects and teams.