The Flutter / Dart and Python binding of our database now enable “vector types”. In both languages these are more commonly referred to as “lists” and now you are able to efficiently store lists of numeric types, i.e. integers and floating point numbers (aka “vectors / vector embeddings”). Native support for that is crucial for data intensive applications, especially in the field of AI.
What are Vector embeddings? Multi-dimensional vectors are a central building block for AI applications. And accordingly, the ability to store vectors to add long-term memory to your AI applications (e.g. via vector databases) is gaining importance. This is what the ObjectBox database now supports natively.
Dart example code
Let’s assume some shapes that use a palette of RGB colors. This allows the shape to reference colors by their index. An entity for this might look like this:
1
2
3
4
5
6
7
8
@Entity()
classShape{
@Id()
intid=0;
// An array of RGB color values that are used by this shape.
Int32List?palette;
}
Python example code
Python is the number one programming language for AI. So let’s assume having an Image class that contains an URL to point to the content (e.g. JPEG/PNG images) and additionally a vector embedding. The latter are supplied by a ML model and contain a list of 32-bit floating points.
1
2
3
4
5
6
7
8
9
@Entity(id=1,uid=1)
classImageEmbedding:
id=Id(id=1,uid=1001)
# Link to the actual image, e.g. on Cloud storage
url=Property(str,id=2,uid=1002)
# The vector embedding for this image (created via some ML model)
The support for vector types is not the only new feature. E.g. ObjectBox Flutter database comes with several fixes and our Python database binding now also supports date types. For details, please check the changelog for Dart DB vector release or Python DB vector release.
This tutorial will help you get started with the ObjectBox Flutter Database. We will create a simple task-list app using all ObjectBox CRUD operations (Create, Read, Update, Delete). Additionally, we will support adding a tag to each task by setting up a to-one relation between tasks and tags. The pure Dart ObjectBox API is very easy to use, as you will see by going through the steps outlined below.
Users can enter a new task, choose which tag to apply and add the task. All added tasks are displayed as a checklist with the associated tag. Users can mark a task as finished by ticking its checkbox and delete it by swiping it away.
Each task entry also shows the date when it was created or finished, depending on the state of the task.
How to start using the ObjectBox Database in your Flutter app
Add the library
Please refer to the Getting Started page for up-to-date information about adding the ObjectBox dependencies to your project.
Create a model file
ObjectBox is an object-oriented non-relational (NoSQL) database. First, we need to tell the database which entities to store. We can do this by defining a model and then using the build_runner to generate the binding code. The model is defined by writing Dart classes and annotating them.
Create a model file (e.g. model.dart), where we want to define two entities: one for task tags and one for tasks. These are just Dart classes with the @Entity annotation. Each entity must have an ID property of type int, which serves as the unique identifier. When this is called “id”, the property is recognized automatically but if you want to use a different name, annotate it with “@Id()”. In the Tag class, we also create a String property for the tag name. Here is how the model will look. Don’t worry about the objectbox.g.dart import for now – this file will be generated later.
1
2
3
4
5
6
7
8
9
10
11
12
import'package:intl/intl.dart';
import'package:objectbox/objectbox.dart';
import'objectbox.g.dart';
@Entity()
classTag{
intid;
Stringname;
Tag(this.name,{this.id=0});
}
Additional properties of our Task entity include a String for the task’s text and two DateTime properties for the date when a task was created and finished. Then we also define a to-one relation between Tasks and Tags. This is needed so that one can assign a tag to each task created within the app. We’ll come back to how relations work at the end of this tutorial.
1
2
3
4
5
6
7
8
9
10
11
12
@Entity()
classTask{
intid;
Stringtext;
DateTime dateCreated;
DateTime?dateFinished;
Task(this.text,{this.id=0,DateTime?dateCreated})
:dateCreated=dateCreated??DateTime.now();
finaltag=ToOne<Tag>();
}
Generate binding code
Once our model is done, we generate the ObjectBox binding code by running flutter pub run build_runner build. This will create the objectbox.g.dart file from the import above. You will need to do this every time you update the model (e.g. by adding or removing an entity or a property), and ObjectBox will take care of the change. However, in cases like entity renaming, you will need to provide ObjectBox with more information. Read more about data model updates in the ObjectBox docs.
Create a Store
Store represents an ObjectBox database and works together with Boxes to allow getting and putting. A Box instance gives you access to objects of a particular type. One would typically create one Store for their app and a number of Boxes that depends on the number of entities they want to store in the database.
In a separate file, e.g. objectbox.dart, we define the ObjectBox class that will help us create the store. We only need a single database store where we get two Boxes – one for each object type. Let’s call these taskBox and tagBox.
So that our app can always display the current list of tasks without the need to actively refresh it, we create a data stream. Within ObjectBox, we can stream data using queries. We query all tasks by using taskBox.query() and order them by date created in a descending order: order(Task_.dateCreated, flags: Order.descending). Then we use the watch() method to create a stream.
Finally, we define the create() method that will create an instance of ObjectBox to use in our app.
1
2
3
4
5
6
staticFuture<objectbox>create()async{
// Future<store> openStore() {...} is defined in the generated objectbox.g.dart
finalstore=await openStore();
returnObjectBox._create(store);
}
</store></objectbox>
Open the store
Now we can initialise the store in our app’s main() function. Do this by calling the create() method of the ObjectBox class.
1
2
3
4
5
6
7
8
9
10
11
12
13
/// Provides access to the ObjectBox Store throughout the app.
late ObjectBox objectbox;
Future<void>main()async{
// This is required so ObjectBox can get the application directory
// to store the database in.
WidgetsFlutterBinding.ensureInitialized();
objectbox=await ObjectBox.create();
…
}
</void>
CRUD operations
CREATE – Put a new Task or Tag into the Store
In the homepage state subclass of main.dart, we define the methods for adding new tasks and tags. Start by creating a task using the input controller. Then, set a tag we want to relate to this task by calling tag.target(). Now we only need to put the new Task object in its Box.
1
2
3
Task task=Task(_taskInputController.text);
task.tag.target=tagToAdd;
objectbox.taskBox.put(task);
READ – Get all tag names
To get all tag names, we call getAll() on our tagBox. This will return a list with all tags. If you want to read just a single object, call the get(id) method to get only the desired single object back. For a range of objects, use getMany(), passing a list of ids to it.
1
List<Tag>tags=objectbox.tagBox.getAll();
Another way of reading data is by using queries. In the “Create a Store” section above, we created a task stream with the help of a query builder. There we just needed all tasks, so no criteria was specified. But generally, one can specify custom criteria to obtain a list of objects matching the needs. Learn more about how to use queries using the ObjectBox Query Docs.
DELETE – remove tasks by swiping
To remove objects from the database, we add a dismissible Flutter widget. Inside the setState method of the onDismissed property, we simply use the ObjectBox remove() operation with the corresponding task id.
1
2
3
4
5
6
7
8
9
Dismissible(
…
onDismissed:(direction){
// Remove the task from the store.
objectbox.taskBox.remove(tasks[index].id);
setState((){});
…
},
),
UPDATE – Updating the date when Task was finished
Our app prints all tasks as a list with checkboxes. Each task’s finished date is initially set to null. We want the finished date of a task to update when the user marks the task as done. The non-null finished date therefore will act as an indicator that the task is finished. Let’s do this inside the setState() method of the onChanged property of our Checkbox class. Set the dateFinished to DateTime.now() if it was null when the checkbox value was changed, and set back to null otherwise.
1
2
3
4
5
6
7
8
9
10
11
12
Checkbox(
value:notNull(tasks[index].dateFinished),
onChanged:(bool?value){
setState((){
if(!notNull(tasks[index].dateFinished)){
tasks[index].dateFinished=DateTime.now();
}else{
tasks[index].dateFinished=null;
}
});
}
)
Relations
Remember we initialised a ToOne relation in the very first section and planned to come back to this at the end? Now that we have covered all CRUD operations, we can come back to discussing those.
Relations allow us to build references between objects. They have a direction: a source object references a target object. There are three types of relations:
to-one relations have one target object, as used above;
one-to-many relations can have multiple target objects, but each target only has one source, e.g. we could add a backlink (one-to-many) to Tag in our example to find out all tasks with a specific tag;
many-to-many relations involve targets that can have multiple sources, e.g. to improve our example app by supporting multiple tags per task, we could replace the to-one with a many-to-many relation.
When listing all tasks, we might want to include each task’s tag next to the task name. The relations are already initialised (see the “Create a model file” section). Now we need to read the tag of each task we want to list. So, inside the Text widget that displays the task name, we use tasks[index].tag.target?.name to print the name of the corresponding tag.
Build your own Flutter app with the ObjectBox Database
Now you have all the tools needed to build your own version of the task-list app with tags. The setup we described is rather minimal, so that anyone can get started easily. However, this gives you lots of room for improvement. For example, you could replace the existing to-one relation with a to-many, allowing users to add more than one tag per task. Or you can add task-list filtering and/or sorting functionality using different queries. The possibilities are truly endless.
We’d like to learn more about the creative ways to use the ObjectBox Database in Flutter you came up with! Let us know via Twitter or email.
In 2019 we first introduced the ObjectBox database v0.1 for Flutter/Dart. Our team has loved the engagement and feedback we’ve received from the developer community since, and we’re thrilled to announce the first stable version 1.0 for ObjectBox Dart/Flutter today.
With this release we bring you the fast and easy to use ObjectBox database for Dart objects: optimized for high performance on mobile and desktop devices. ObjectBox persists your Dart objects (null safe, of course) and comes with relations, queries, transactions, and Data Sync. For a feature list and more, please also check the pub.dev page.
ObjectBox by Example
For those of you new to ObjectBox, here is how you can use it (or check the docs if you want to dive deep right away). By annotating a class with @Entity you tell ObjectBox that you want to persist its objects, which is done putting the object in a Box:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity()
classPerson{
intid=0;
StringfirstName;
StringlastName;
Person(this.firstName,this.lastName);
}
// Open a Store once and keep it open to get type specific Boxes
finalstore=Store(getObjectBoxModel());
finalbox=store.box<Person>();
// let’s put a new Person in the database
varperson=Person("Marty","McFly");
finalid=box.put(person);
What’s new with the 1.0?
Version 1.0 delivers a stabilized API and adds new essential features like async writes and query streams. We’ve also extended support for Flutter desktop. Let’s look at queries and how they can be used depending on the use case:
1
2
3
4
5
6
7
8
9
10
11
12
// find all people whose name starts with "Mc"; 1. Query variant
There are two new approaches to do async puts for asynchronous database writes: putAsync() returns a Future to check if the call was successful.
1
2
3
4
Future<int>idFuture=box.putAsync(person);
...
finalid=await idFuture;
userBox.get(id);// after the Future completed, the object is stored in the database
Or you can use a background queue if you don’t need individual Futures, the following code inserts 100 objects and only waits once:
1
2
3
4
5
6
for(inti=0;i<100;i++){
box.putQueued(Person(...));
}
store.awaitAsyncSubmitted();
// after `awaitAsync*`: objects are inserted
expect(box.count(),equals(100));
If you are interested in further improvements we made to 1.0, please check out the full changelog.
Dart Flutter Database Benchmarks
ObjectBox Dart v1.0 also comes with considerable optimizations bringing a new level of database performance to Flutter apps. ObjectBox enables data-heavy apps that were not possible on Flutter before. Consider this a first sneak-peek; stay tuned for detailed performance benchmarks to be released including queries (hint: they are really fast) along with updated benchmarking code.
What we tested
We looked at some two popular approaches: sqflite, a SQLite wrapper for Flutter (no Dart Native support), and Hive, a key-value store with Class-adapters which seems still popular although its creator abandoned it for architectural shortcomings (it has memory problems and does not support queries). In the previous benchmark we’ve also had a look at Firestore, but being an online-only database it was thousands of times slower than the rest so we’ve left it to rest this time around. Check our previous benchmark if you’re interested.
To get an overview of the databases, we tested CRUD operations (create, read, update, delete). Each test was run multiple times and executed manually outside of the measured time. Data preparation and evaluation were also done outside of the measured time.
Looking at the results, we can see ObjectBox performing significantly faster than sqflite across the board, with up to 100 time speed-up in case of create & update operations. Compared to Hive, the results are a little closer in some cases (read) though ObjectBox still comes out on top in all the metrics. Considering that Hive keeps all Dart objects in memory (!) while ObjectBox does not, should give you a good impression of how fast object persistence with ObjectBox is.
ObjectBox Database for Flutter/Dart Highlights
For those of you new to ObjectBox, here’s a quick summary of what our super-fast embedded database offers, out of the box:
automatic schema migration: adding new classes or fields just works
type-safe APIs, e.g. no interface{} arguments
embedded edge database – no server needed, store all data directly on the device
no ORM, no SQL
relations: to-one, to-many (eager and lazy fetching)
robust query support, including indexes for scalable lookups
Support for implicit (automatic) and explicit (user defined)
transactions: ACID compliant with superfast bulk/batch operations
low memory usage
runs across operating systems: 64-bit Linux, macOS, Windows, small 32-bit ARM-based Linux devices (e.g. Raspberry Pi)
Data Sync: an efficient and easy way to synchronize data between your app and the cloud
Getting Started with ObjectBox for Flutter/Dart Today
Now it’s your turn: let us know what you love, what you don’t, what do you want to see next? Share your feedback with us, or check out GitHub and up-vote the features you’d like to see next in ObjectBox.
This growth comes from a strong community, with more than 1,400 contributors, 10,000 package publishers, and over 50,000 available packages. As the Flutter community expands, the demand for efficient Flutter databases is also increasing. Developers now have access to a range of Flutter database options that cater to various needs and preferences.
In this article, we’ll focus specifically on local storage solutions, as these are essential for enabling offline functionality, improving performance, ensuring data persistence, enhancing data privacy and security, and supporting edge computing capabilities. Furthermore, local data storage is needed to promote sustainability. Let’s dive into the current local database landscape for Flutter and compare the most popular options.
Flutter databases / Flutter Dart data persistence
While the database market is huge and dynamic, there are only few options to choose from if you are a Flutter / Dart app developer. Before we dive into the Flutter database options, advantages and disadvantages, we’re taking a very quick look at databases to make sure, we share a common ground.
What is a database?
A database is a piece of software that allows the storage and systematic use of digital information, in other words: data persistence. As opposed to mere caching, data is reliably stored and available to work with unless actively deleted. A database typically allows developers to store, access, search, update, query, and otherwise manipulate data in the database via a developer language or API. These types of operations are done within an application, in the background, typically hidden from end users. Many applications need a database as part of their technology stack. The most typical database operations are CRUD: Create, Read, Update, Delete.
What are the major types of databases?
There are many types of databases. For our purpose, the most important differentiations are non-relational (NoSQL) versus relational databases (SQL), cloud databases versus edge databases, and maybe embedded versus in-memory. However, databases can be further distinguished by additional criteria e.g. the data types they support, or the way they scale – and definitions can vary.
What is an ORM?
An Object relational Mapper (ORM) is not a database. We’re bringing this up mainly, because we see it confused often. It is a layer that sits on top of a database and makes it easier to use. This is typically especially relevant when the database is a relational database (SQL) and the programming language used is object-oriented. As noted above, Dart is an object-oriuented programming language.
The Flutter local data persistence landscape
There are several Flutter databases that provide offline support, offering the ability to store and access data locally even without an internet connection. Here are some of the notable options:
Hive is a lightweight key-value database written in Dart for Flutter applications, inspired by Bitcask.
ObjectBox DB is a highly performant lightweight NoSQL database with an integrated Data Sync. It stores objects.
sqflite is a wrapper around SQLite, which is a relational database without direct support for Dart objects.
Drift is a reactive persistence library for Flutter and Dart, built ontop of SQLite.
This of course depends… Make up your own mind with the following comparison matrix as a starting point. Note: With very few options to choose from, the following overview is sometimes a bit like comparing apples 🍎 and pears 🍐.
Data
persistence
Description
Primary
Model
Data Sync
Language
License
Fun Fact
"Headquarter"
Drift
ORM on top of SQLite
relational
❌
SQL
SQLite is public domain, Drift is MIT
Formerly known as Moor
🇩🇪
Floor
ORM on top of SQLite
relational
❌
SQL
SQLite is public domain, floor is Apache 2.0
Developed by a mobile app agency, not an individual author
🇳🇱
Isar
Lightweight NoSQL database
NoSQL
❌
Dart
Apache 2.0
Also the author of Hive - both libs are not maintained anymore
🇩🇪
Hive
Predecessor of Isar
NoSQL
❌
Dart
Apache 2.0
Also the author of Isar - both libs are not maintained anymore
🇩🇪
ObjectBox
Lightweight NoSQL database with integrated Data Sync
NoSQL
✅
Dart
Bindings are Apache 2.0
It is used in BMW cars 😮
🇩🇪
Realm
NoSQL database acquired by Mongo DB in spring 2019, Flutter
binding came in 2023, now deprecated
NoSQL
Deprecated, End of life in Sep 2025; closest substitute is
ObjectBox
Dart
Apache 2.0
Originally Realm was developed in Denmark… MongoDB stopped Realm
support and the Sync is deprecated