Flutter ObjectBox
High-performance object database for Flutter
📦 What is ObjectBox?
ObjectBox is a super-fast object-oriented database for Flutter and Dart. It stores Dart objects directly without SQL, offers exceptional performance with minimal battery consumption, and provides reactive data synchronization for modern mobile applications.
// Add to pubspec.yaml
dependencies:
objectbox: ^2.3.0
objectbox_flutter_libs: any
// Store objects directly
box.put(Person(name: 'Alice', age: 28));
Key Features
Ultra Fast
10x faster than SQLite
box.put(object);
// Blazing fast!
Object-Oriented
Store Dart objects directly
Person person =
box.get(id);
Powerful Queries
Fast queries without SQL
box.query(Person_.age
.greaterThan(18)).build();
Reactive
Automatic UI updates
box.query().watch()
.listen(...);
🔹 Installation
Add ObjectBox to your Flutter project:
# pubspec.yaml
dependencies:
flutter:
sdk: flutter
objectbox: ^2.3.0
objectbox_flutter_libs: any
dev_dependencies:
build_runner: ^2.4.6
objectbox_generator: any
Install ObjectBox CLI:
bash <(curl -s https://raw.githubusercontent.com/objectbox/objectbox-dart/main/install.sh)
🔹 Define Entity
Create entity classes with annotations:
import 'package:objectbox/objectbox.dart';
@Entity()
class Person {
@Id()
int id = 0;
String name;
@Property(type: PropertyType.date)
DateTime? birthDate;
int age;
String? email;
Person({
required this.name,
required this.age,
this.birthDate,
this.email,
});
}
@Entity()
class Task {
@Id()
int id = 0;
String title;
bool isCompleted;
// Relation to Person
final person = ToOne<Person>();
Task({
required this.title,
this.isCompleted = false,
});
}
Run
flutter pub run build_runner build
to generate ObjectBox code
🔹 Initialize ObjectBox
Set up ObjectBox store in your app:
import 'package:objectbox/objectbox.dart';
import 'objectbox.g.dart'; // Generated file
class ObjectBox {
late final Store store;
late final Box<Person> personBox;
late final Box<Task> taskBox;
ObjectBox._create(this.store) {
personBox = Box<Person>(store);
taskBox = Box<Task>(store);
}
static Future<ObjectBox> create() async {
final store = await openStore();
return ObjectBox._create(store);
}
}
// In main.dart
late ObjectBox objectBox;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
objectBox = await ObjectBox.create();
runApp(MyApp());
}
🔹 CRUD Operations
Create, read, update, and delete objects:
// CREATE - Add a person
Person person = Person(name: 'Alice', age: 28, email: '[email protected]');
int id = objectBox.personBox.put(person);
print('Person added with ID: $id');
// READ - Get person by ID
Person? retrievedPerson = objectBox.personBox.get(id);
print('Name: ${retrievedPerson?.name}');
// READ - Get all persons
List<Person> allPersons = objectBox.personBox.getAll();
// UPDATE - Modify and save
person.age = 29;
objectBox.personBox.put(person);
// DELETE - Remove person
objectBox.personBox.remove(id);
// DELETE - Remove all
objectBox.personBox.removeAll();
Output:
Person added with ID: 1
Name: Alice
🔹 Queries
Build powerful queries without SQL:
// Simple query - Find adults
Query<Person> query = objectBox.personBox
.query(Person_.age.greaterOrEqual(18))
.build();
List<Person> adults = query.find();
query.close(); // Always close queries
// Multiple conditions
Query<Person> complexQuery = objectBox.personBox
.query(
Person_.age.greaterThan(18)
.and(Person_.name.startsWith('A'))
)
.order(Person_.name)
.build();
List<Person> results = complexQuery.find();
complexQuery.close();
// Contains query
Query<Person> emailQuery = objectBox.personBox
.query(Person_.email.contains('@example.com'))
.build();
// Count results
int count = emailQuery.count();
print('Found $count users with @example.com');
Output:
Found 5 users with @example.com
🔹 Reactive Queries
Watch for data changes automatically:
// Watch query results
Query<Person> query = objectBox.personBox
.query()
.build();
Stream<List<Person>> personStream = query
.watch(triggerImmediately: true)
.map((query) => query.find());
// Use in StreamBuilder
class PersonListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<List<Person>>(
stream: personStream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
final persons = snapshot.data!;
return ListView.builder(
itemCount: persons.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(persons[index].name),
subtitle: Text('Age: ${persons[index].age}'),
);
},
);
},
);
}
}
🔹 Relations
Define relationships between entities:
// Create person and task with relation
Person person = Person(name: 'Bob', age: 30);
int personId = objectBox.personBox.put(person);
Task task = Task(title: 'Buy groceries');
task.person.target = person; // Set relation
objectBox.taskBox.put(task);
// Query tasks with person
Task? retrievedTask = objectBox.taskBox.get(task.id);
Person? taskOwner = retrievedTask?.person.target;
print('Task owner: ${taskOwner?.name}');
// Backlinks - Get all tasks for a person
@Entity()
class Person {
// ... existing fields ...
@Backlink('person')
final tasks = ToMany<Task>();
}
// Access person's tasks
Person? person = objectBox.personBox.get(personId);
List<Task> personTasks = person?.tasks ?? [];
print('${person?.name} has ${personTasks.length} tasks');
🔹 Complete Example
A simple note-taking app:
@Entity()
class Note {
@Id()
int id = 0;
String title;
String content;
@Property(type: PropertyType.date)
DateTime createdAt;
Note({
required this.title,
required this.content,
DateTime? createdAt,
}) : createdAt = createdAt ?? DateTime.now();
}
class NotesPage extends StatefulWidget {
@override
_NotesPageState createState() => _NotesPageState();
}
class _NotesPageState extends State<NotesPage> {
late Stream<List<Note>> notesStream;
@override
void initState() {
super.initState();
final query = objectBox.noteBox.query().order(Note_.createdAt).build();
notesStream = query.watch(triggerImmediately: true).map((q) => q.find());
}
void addNote(String title, String content) {
final note = Note(title: title, content: content);
objectBox.noteBox.put(note);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('My Notes')),
body: StreamBuilder<List<Note>>(
stream: notesStream,
builder: (context, snapshot) {
if (!snapshot.hasData) return CircularProgressIndicator();
final notes = snapshot.data!;
return ListView.builder(
itemCount: notes.length,
itemBuilder: (context, index) {
final note = notes[index];
return Card(
child: ListTile(
title: Text(note.title),
subtitle: Text(note.content),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => objectBox.noteBox.remove(note.id),
),
),
);
},
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => addNote('New Note', 'Content here'),
child: Icon(Icons.add),
),
);
}
}
🔹 Best Practices
- Close Queries: Always close queries after use
- Batch Operations: Use putMany() for multiple inserts
- Indexes: Add @Index() for frequently queried fields
- Transactions: Use store.runInTransaction() for consistency
- Unique Constraints: Use @Unique() annotation when needed