ObjectBox Flutter database reached 2.0 today – with new async APIs.
This release has major improvements when using ObjectBox with asynchronous programming in Dart. Notably, the Box API has added async variants of methods to get and put one or more objects.
Behind the scenes, these use the existing async API provided by Store which runs the operation on a worker isolate. With this release we have made additional improvements that allow it to be used with objects that contain relations (ToOne and ToMany). This made us comfortable to offer it through the new async convenience methods on Box and Query mentioned above.
Sometimes, it is favorable to bundle multiple database operations and execute them “at once”. This is what runInTransactionAsync() is for. It allows to use the synchronous APIs and wrap them in a single, asynchronous transaction. For example, let’s look at transferring funds between two bank accounts. The fund can safely be transferred from one account to another by reading the current balances, deducting the amount from one account and adding the amount to another account:
1
2
3
4
5
6
7
8
9
10
11
12
void_transferFunds(Store store,List<int>ids){
finalbox=store.box<account>();
finalfrom=box.get(ids[0])!;
finalto=box.get(ids[1])!;
from.balance-=100;
to.balance+=100;
box.putMany([from,to]);
}
await store.runInTransactionAsync(TxMode.write,
_transferFunds,[accountIdFrom,accountIdTo]);
</account></int>
This is more efficient than calling multiple async operations and further offers transactional safety as ObjectBox offers ACID semantics.
Ready to go? To upgrade to this major release run flutter pub upgrade objectbox --major-versions (or for Dart Native apps dart pub upgrade objectbox --major-versions ).
To add ObjectBox database to your Flutter project read our Getting Started guide.
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.
Please note: We only consider applications that have a cover letter specific to this job offer.
The funded and growing team at ObjectBox is seeking a developer relations talent.
ObjectBox (Techstars ’17) is a company on a mission to bring joy and delight to app developers. The core of our solution is a superfast lightweight, embedded database with easy native language APIs for Java, Kotlin, Swift, Dart (Flutter), Go, and C / C++. On top of the database, we’re building a data synchronisation solution (currently in Early Access).
There’s quite some flexibility in defining the role and working conditions as well as lots of room to advance within the company depending on your skillset, dreams, and needs. At this moment, we are looking for someone that has a good developer background and a strong passion for developer relations and enjoys making developer products shine. This is a remote part-time position (5-10 hours/week to start with, up to 20 depending on skill set, flexible), ideal for a Computer Science Master student or a freshly graduated Computer Science freelancer.
This is a great position for someone who is looking to gain working experience within the developer market at the intersection of development, community management, and sales.
Main responsibilities
Interact with our users and customers, helping them to use the ObjectBox database and Sync. It entails supporting new customers along the way and providing them with more information when necessary. The associated tasks include:
Respond to new inbound leads, helping them to set things up
Reply to specific dev-related questions about our products
Help maintain our community management channels on GitHub and Stack Overflow
Improve our documentation based on user experience
We are looking for someone that is driven and ready to help us take our company to the next level. All of the following tasks are optional, depending on your skill set and ambitions:
Developer content creation: tutorials, tech blog articles, videos, etc. – from idea to publishing
Developer Marketing: Social Listening Social Media postings (managing the channels), SEO measures, website maintenance (update texts, new content, design)
Pitch decks, (web) design, brand positioning
Represent ObjectBox at meetups, tech conferences etc (you are expected to submit papers yourself; though we do help of course)
Requirements
You are currently pursuing a Masters degree in Computer Science, Information Technology or similar; or have just recently graduated
You have an affinity for mobile technologies, IoT, open source, or the digital industry in general
You are proficient in at least one programming language (ideally C/C++, Java or Dart) with ideally some application development experience
You have a hands-on mentality with strong analytical and communication skills
You have very strong English skills
You pay attention to detail and enjoy working diligently
You are a team-player and fast learner who likes to take over responsibility and appreciates working responsibly
Nice to haves
Experience with working in a startup
Experience with speaking at tech events (meetups, conferences, etc.)
Experience with tech writing
Experience with GitHub / open source
Experience with Social Media
What’s in it for you
The chance to work in one of Europe’s hottest startups (check out our slush pitch)
An easy-going and super-friendly working environment in a start-up that is growing, where you will be shaping the company together with us
Exciting tasks with the option to learn and grow and take over much more responsibilities as fits your skill set and personal goals
Flexibility in every respect: No matter if you just finished your degree or already have a family. We are flexible and looking forward to design the job conditions and contract together with you to match your needs
A job in one of the most creative and exciting growing industries: Mobile and IoT
This position is remote, however we’re happy to meet up (occasionally or regularly) if you live in Berlin or Munich.
If this appeals to you, we are looking forward to receiving your application including a possible starting date and salary requirements.
While – strictly speaking – “database” refers to a (systematic) collection of data, “Database Management System” (or DBMS) refers to the piece of software for storing and managing that data. However, often the term “database” is also used loosely to refer to a DBMS, and you will find most DBMS only use the term database in their name and communication.
What does embedded mean in the realm of databases?
The term “embedded” can be used with two different meanings in the database context. A lot of confusion arises from these terms being used interchangeably. So, let’s first bring clarity into the terminology.
💡 The term “embedded” in databases
“Embedded database”, meaning a database that is deeply integrated, built into the software instead of coming as a standalone app. The embedded database sits on the application layer and needs no extra server. Also referred to as an “embeddable database”, “embedded database management system” or “embedded DBMS (Database Management System)”.
“Database for embedded systems” is a database specifically designed to be used in embedded systems. Embedded systems consist of a hardware / software stack that is deeply integrated, e.g. microcontrollers or mobile devices. A database for such systems must be small and optimized to run on highly restricted hardware (small footprint, efficiency). This can be also called an “embedded system database”. For clarity, we will only use the first term in this article.
Embedded Database vs Embedded System
What is an embedded system / embedded device?
Embedded systems / embedded devices are everywhere nowadays. They are used in most industries, ranging from manufacturing and automotive, to healthcare and consumer electronics. Essentially, an embedded system is a small piece of hardware that has software integrated in it. These are typically highly restricted (CPU, power, memory, …) and connected (Wi-Fi, Bluetooth, ZigBee, …) devices. Embedded Systems very often form a part of a larger system. Each individual embedded system serves a small number of specific functions within the larger system. As a result, embedded systems often form a complex decentralized system.
Examples of embedded systems: smartphones, controlling units, micro-controllers, cameras, smart watches, home appliances, ATMs, robots, sensors, medical devices, and many more.
Embedded Database vs Database for Embedded Systems
When and why is there a need for a database for embedded devices?
A large number of embedded systems has limited computational power, so the efficiency and footprint of the DBMS is vital. This fact gave rise to the new market of databases specifically made for embedded systems. Because of being lightweight and highly-performant, embedded databases might work well in embedded systems. However, not all embedded databases are suitable for embedded devices. Features like fast and efficient local data storage and efficient synchronisation with the backend play a huge role in determining which databases work best in embedded systems.
A database that is both embedded in the application and works well in embedded systems is called an Edge database. To clarify, Edge Database is an embedded database optimised for resource-efficiency on restricted decentralised devices (this typically means embedded devices) with limited resources. Mobile databases, for example, are a type of Edge databases that support mobile operating systems, like Android and iOS.
New Edge databases solve the challenge of an insanely growing number of embedded devices. This applies to both in the professional / industrial as well as the consumer world. Edge databases hence create value for decentralised devices and data by making the former more useful.
A database for embedded systems / embedded devices can be simultaneously an embedded database. However, more important is its performance with regards to on-device resource use to serve the restricted devices. A database that is embedded and optimized for restricted devices is called “Edge database”.
Why use an embedded database in an embedded system?
First of all, local data storage enabled by embedded databases is a big advantage for embedded systems. Due to the limited connectivity or realtime requirements that these systems often experience, one often cannot rely on it for retrieving data from the cloud. Instead, a smart solution would be to store data locally on the device and sync it with other parts of the system only when needed.
Aside: a word about data sync. Embedded systems often deal with large amounts of data, while also having an unreliable or non-permanent connection. This can be imposed by the limitations of the system or done deliberately to save battery life. Thus, a suitable synchronisation solution should not only sync data every time there is a connection, but also do it efficiently. For example, differential sync works well: by only sending the changes to the server, it will help to avoid unnecessary energy use and also save network costs.
The two most important features of databases in embedded systems are performance and reliability. A database used in embedded systems should perform well on devices with limited CPU and memory. This is why embedded databases might work well in embedded systems – they are largely designed to work in exactly such environments. Some of them are truly tiny, which means they thrive in small applications. While better performance helps to eliminate some of the risks, it does not help with sudden power failures. Therefore, a good data recovery procedure is also important. This is most consisely demonstrated by ACID compliance.
Let’s have a look at the features of embedded databases that make them a great choice for embedded systems.
Advantages of embedded databases
High performance. Truly embedded databases benefit from simpler architecture, as they do not require a separate server module. While the client/server architecture might benefit from the ability to install the server on a more powerful computer, this also means more risk. Getting rid of the client/server communication level reduces complexity and therefore boosts performance.
Reliability. Many embedded devices use battery power, so sudden power failures might happen. Therefore, the data management solution should be built to ensure that data is fully recovered in case of a power failure. This is a popular feature of embedded databases that are built with embedded systems in mind.
Ease of use and low maintenance. Other important benefits of using an embedded database include easy implementation and low maintenance. Designing embedded devices often requires working in tight schedules, so choosing an out-of-the-box data persistence solution is the best choice for many projects. Since embedded databases are embedded directly in the application, they do not need administration and effectively manage themselves.
Small footprint. Embedded databases do not always have a small footprint, but some of them are smaller than 1 MB, which makes them particularly suitable for mobile and IoT devices with limited memory.
Scalability. As the number of embedded devices grows every year, so does the data volume. An efficient solution should not only perform well with large sets of data, but also adapt to new device features and easily change to fit the needs of a new device. This is where rigid database schemas come as a disadvantage.
How to choose an embedded database
When choosing an embedded database, look out for such factors as ACID (atomicity, consistency, isolation, durability) compliance, CRUD performance, footprint, and (depending on the device needs) data sync.
SQLite and SQlite alternatives – a detailed look at the market of embedded databases
When to use an Embedded Database and how to choose one
Firstly, when choosing a database for an embedded system, one has to consider several factors. The most important ones are performance, reliability, maintenance and footprint. On highly restricted devices, even a small difference in one of those parameters might make an impact. While building your own solution with a particular device in mind would certainly work well, tight schedules and additional effort don’t always justify this decision. This is why we recommend choosing one of the ready-made solutions that were built with the specifics of embedded systems in mind.
Secondly, to avoid unnecessary network and battery use, you might want to choose an embedded database. On top, an efficient differential data sync solution will help reduce overhead and reduce the environmental footprint.
Finally, there are several embedded databases that perform well on embedded devices. Each has its own benefits and drawbacks, so it’s up to you to choose the right one for your use case. That being said, we’d like to point out that ObjectBox outperforms all competitors across each CRUD operation. See it for yourself by checking out our open source performance benchmarks.
Cross platform data sync can be simple: In this tutorial we will show you how you can easily sync data across devices.
Built for fast and effortless data access on and across embedded devices from Mobile to IoT, ObjectBox keeps data in sync between devices for you. The Database and Data Snyc works across platforms (iOS, Android, Linux, Rasbian, Windows, MacOS) and supports a variety of languages with easy native APIs (Swift, Java, Kotlin, C / C++, Flutter / Dart, Golang).
For example, you can sync between an Industrial IoT sensor app in Go and a C++ monitoring application – and a mobile Android app written in Kotlin or Java – and of course an iOS app written in Swift – and… you get the drift 😉
ObjectBox is a high-performance embedded database for Edge Computing with integrated Data Sync. The ObjectBox database is quick to set up and free and easy to use. Our powerful and intuitive APIs are a great match for multiplatform development environments.
Syncing data across devices – a task-list app example
In this tutorial, we are going to sync data across three instances of an example task-list app (written in C++, Go and Java).
With the task-list app, users can create simple text-based tasks and mark them as done. It stores tasks together with their creation dates. There is also a parameter to store the date when the task was completed. It acts as a filter for users to only see unfinished tasks.
This app is a standard cross platform ObjectBox example that is available for all language bindings. Here are the repositories of each example app that we will be looking at today:
In this section, we’ll quickly review how the the task-list example app uses ObjectBox Sync. For a more detailed description, check out the Sync docs. If you want to see how each of these steps were incorporated into the example code, go to the next section.
Note: The basic use of the database and its sync features is the same for all programming languages. If you haven’t used the ObjectBox DB yet, please refer to the corresponding documentation: C/C++ Docs, Java/Kotlin/Dart Docs, Go Docs, Swift Docs.
For sync to work in any app, we generally only need four things:
The sync-enabled library — this is not the same as the general ObjectBox library and has to be downloaded separately.
Database objects enabled for sync — for this we need include the sync annotation in the ObjectBox schema file.
ObjectBox Sync Server — please apply for a free Sync Trial here to get your own copy of the Sync Server (available for Linux and Docker). Note that this will only have to be started once and in this tutorial we’ll show you how to run the server on Linux. If you are using Docker, follow the steps outlined here.
Start a Sync Client in the app — as one can see from the Sync Client docs, creating and starting a sync client is just a matter of a couple of lines of code.
Important: When syncing between different apps, please make sure that the UIDs in the model JSON file (e.g. objectbox-default.json) are the same everywhere.
How to run the examples
Here you’ll find requirements and step-by-step guides for running the task-list example app in each of the three languages.
Now configure and build the project via CMake: Configure (Clang), CMake: Build.
2. Sync-enabled objects: note the first line in tasklist.fbs.
3. [if not running a server already] Start the ObjectBox Sync Server on Linux by running ./sync-server --model build/_deps/objectbox-src/examples/cpp-gen/objectbox-model.json --unsecured-no-authentication
where sync-server is the path to your sync server executable. You can find more information about the server in the Sync Server docs.
4. Sync Client: launch [objectbox-c-examples-cpp-gen-sync], and the Sync Client will start automatically. You can see how it was implemented in main.cpp.
As this is just an example, we opted for no authentication to make things simple. This is not what you would use in production. We currently offer two authentication methods: shared secret and Google Sign-In. Here is the relevant Sync docs section on authentication options that explains how to use these.
5. Let’s add a first task, called “task-cpp” (new task-cpp-1), to check if our C++ app syncs correctly. The output should look like this:
6. You can finally open the Admin UI to check if the task appears there. This is most easily done by opening http://127.0.0.1:9980/ in any web browser. For a more detailed description of what this can do, check out the Admin UI docs.
1. First, clone the objectbox-go repository to your VS Code project. Make sure the current directory is objectbox-go.
2. Sync-enabled objects. There are two versions of the task-list example: with and without sync. To run the one with sync, we need to enable our Task object for syncing. To do this, simply put the sync annotation on a new line in examples/tasks/internal/model/task.go:
1
2
3
4
5
6
7
8
9
10
// Put this on a new line to enable sync:
// `objectbox:"sync"`
typeTaskstruct{
Id uint64
Text string
DateCreated time.Time`objectbox:"date"`
// DateFinished is initially set to unix epoch (value 0 in ObjectBox DB) to tag the task as "unfinished"
DateFinished time.Time`objectbox:"date"`
}
Then run the generator: go generate examples/tasks/internal/model/task.go to update the schema.
3. [if not running a server already] Now start the ObjectBox Sync Server: ./sync-server --model=examples/tasks/internal/model/objectbox-model.json --unsecured-no-authentication,
where sync-server is the path to your sync server file. You can find more information about the server in the Sync Server docs.
4. Run go run examples/tasks/main.go. The Sync Client will start within the app; check main.go to see how this was implemented.
As this is just an example, we opted for no authentication to make things simple. This is not what you would use in production. We currently offer two authentication methods: shared secret and Google Sign-In. Here is the relevant Sync docs section on authentication optionsthat explains how to use these.
5. Now we can add our first task (new task-go) – if it synced correctly, you should already see that from the output of the app. In particular, there will be a message from the change listener (“received 1 changes”):
6. Lastly, open the Admin UI to check if the task appears there. This is most easily done by opening http://127.0.0.1:9980/ in any web browser. For a more detailed description of what this can do, check out the Admin UI docs.
where sync-server is the path to your sync server file. You can find more information about the server in the Sync Server docs.
Now you can run “android-app-sync” on a device of your choice. The Sync Client will start in the app.
As this is just an example, we opted for no authentication to make things simple. This is not what you would use in production. We currently offer two authentication methods: shared secret and Google Sign-In (only for Java, Kotlin, Dart, C & Go). Here is the relevant Sync docs section on authentication optionsthat explains how to use these.
5. Add a new task called “task-java”.
6. Finally, open the Admin UI to check if the task appears there. This is most easily done by opening http://127.0.0.1:9980/ in any web browser. For a more detailed description of what this can do, check out the Admin UI docs.
Next Steps
How easy was that? Now that you’ve run your first ObjectBox Sync example, why not build something yourself? Use any combination of the supported languages to build your own cross platform app.
We’re eager to see your use case examples! Don’t hesitate to share your results with us by posting on Social Media and tagging @objectbox_io, or simply sending us an email on contact[at]objectbox.io.