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

🧠 Test Your Knowledge

What makes ObjectBox different from traditional databases?