Build a Simple To-Do Flutter App Using Hive Database | by Radhika patel | Mar, 2022

Photo by OpenClipart-Vectors on Pixabay

I wanted to add a local database to my app. So I started searching for an excellent library to implement in my app. I have also come across the localstorage Flutter library, which uses JSON implementation for storing data. But it is not as effortless as Hive. So I started implementing Hive into my app.

Before we start, I just wanted to tell you, by no means, I am an expert in a Flutter. I am learning Flutter like you all. It is just my humble try to make it a little bit easier for a beginner to learn Flutter and Hive database. So if you have any suggestions, feel free to write in the comments section.

Hive is a lightweight and blazing fast key-value database written in pure Dart, inspired by Bitcask.

More information can be found on Hive’s official documentation.

It has the following features:

  • Cross-platform: mobile, desktop, browser
  • High performance (see benchmark)
  • Simple, powerful, and intuitive API
  • Strong encryption built-in
  • NO native dependencies
  • Batteries included
Image from Hive official documentation

I fell in love with the Hive database because it is simple, fast, and efficient. It is also a local storage database. It is super fast to learn because of its simplicity.

I will go over the following steps in this blog while building a real app.

  • Overview of some useful instructions to read and write data in the Hive database
  • Project setup
  • Create a type adapter
  • Write and read data
  • Delete data

Before we jump to the example, let’s get a quick overview of some useful instructions provided by Hive.

You should have basic knowledge of Flutter, such as how to create a Flutter project, what the widgets are, and so on.

Hive stores all the data in boxes. In simpler words, a hive box is just like a table in SQL without any structure, and it can contain anything. We can create multiple boxes to organize the data and also store encrypted data to secure sensitive data. It is advantageous in large apps.

Before we start writing into a box, we have to open a box.

var box = await Hive.openBox<E>(‘boxName’);

Where E is an optional type parameter. It provides the type of values ​​in the box.

There is also something called Hive.openLazyBox(). It is used for an extensive database because it will not load all the data into memory. But our example is simple, so we will use a regular box. But you can check out more information about the lazy box in the hive documentation.

There are various ways one can use to write data into a database.

As you would have noticed, keys must be either String or int.

In our example, we will use box.add() instruction. It would be easier to let Hive take care of the index, and we can take care of input data. It makes it easier to iterate over all the data via a listview builder.

Read data from the database is straightforward, as shown below:

var box = Hive.box('myBox');// .get instruction return stored data at given key.// return default value incase of key does not existString name = box.get(‘key’, defaultValue: -1);// use for an existing indexString name = box.getAt(int index);

Now you would have understood why I wanted to use box.add() instruction because it will automatically create an int index every time it adds data. We can read that data directly by just providing a particular index into box.getAt(). It will make more sense in the example when we use this instruction to add and read data.

Deleting data is also super simple.

var box = Hive.box('myBox');// deletes the given key and databox.delete(‘key’);// deletes the nth key and databox.deleteAt(int index);

In the example, we are going to use box.deleteAt() to delete data more efficiencies with the listview builder.

There are also more instructions to read, write and delete data. But I have just mentioned a few of them to keep this blog short. If you are curious about other instructions, then you check out the Hive documentation.

The best way to learn new things is by doing. Therefore we are going to build a simple or bare minimum TODO app using the Hive database. But feel free to add more features as you like. Please keep in mind that my focus is more on showing how to use the Hive database instead of How to build a TODO app.

To make my explanation a little more manageable, I will also write some descriptions as a comment in the code.

So without further due, let’s jump into the example.

I am going to assume that you already have a little knowledge about Flutter such as how to create a new Flutter project, how to add packages into the pubspec.yamlfile of the project, and so on.

Add the following lines to your project’s pubspec.yaml file:

In Flutter version 1.20, if we run the async function inside the main function then it will throw “FlutterError (ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized“. To avoid this error, you have to add WidgetsFlutterBinding.ensureInitialized() as the first line of the main function.

To initialize Hive, we need to give the path of the directory where it stores the data. This can be done by following two lines:

final applicatonDocumentDir =await path_provider.getApplicationDocumentsDirectory();Hive.init(applicatonDocumentDir.path);

Now our main function would look like this:

As shown below, I have not made any significant changes to MyApp class from the pre-generated class except changing the title to “Simple TODO App Using Hive” from “Flutter Demo.”

To keep the example simple, I am going to create an introductory Task class with only one attribute called a task in a different file called task_model.dart.

class Task{String task;Task({this.task});}

Hive supports only the following data types:

The Task class has only one attribute, so for sure we can store direct tasks into the database, as I said String is a supported data type. But I am assuming Task is a more significant class. You might add more attributes in the future.

We can not store our task directly into the database using Hive.add(Task(task:’ goto gym’)). We have to create a TypeAdapter for the class Task. There is also another way to convert the object to JSON string, but I think using TypeAdapter is a better way to do it.

Let’s create a TypeAdapter first. The best way to make TypeAdapter is to auto-generate the TypeAdapter code instead of writing manually. This way, we can avoid many typos and save lots of time (here it is just a simple class, but in a complex class, auto-generation comes in pretty handy).

To auto-generate TypeAdapterwe need to take help from the Hive_generator package, which we already added into pubspec.yaml. Now we have to add the following lines to our Task class:

The only thing to keep in mind is that we have to write exactly the same name of the model file with the part keyword and then add a “.g.dart” extension to it. I have created a separate file called task_model.dart. Therefore I have written task_model.g.dart here.

That’s all you have to do in your Task class. Now it is magic time. Run the following command in the terminal inside the project directory:

flutter packages pub run build_runner build

This command will automatically generate a new file with the given file name with the ‘part’ keyword. In our case, it’s called task_model.g.dartwhich looks like the following:

Just generating TypeAdapter is not enough, we also have to register it by adding Hive.registerAdapter(TaskAdapter()) into the main function after initializing hive and I have also created/open a “TODOs” box to add todo tasks. Now the main function will look like this:

Now let’s design and create a home page. The homepage will look like the following:

simple to-do list with three to-dos.
Image by author

As shown in the above picture, I have created a stateful widget with the name of my home page, and it returns a scaffold widget. In the scaffold widget, I have added an appbar with the title ‘Simple TODO app using Hive.’ In the body, I have added a ValueListenableBuilder widget

If we use only use Listview.Builder, then every time we add a new entry into the database, we have to rebuild the UI manually, which is not a good idea. That’s why I have used the ValueListenableBuilder widget

I have noticed in other Hive tutorials which are not up to date, people used the WatchBoxBuilder widget It is already depreciated in the latest version of the Hive (hive 1.4.3). Therefore, I have used the ValueListenableBuilder widget

I think it is good practice to use the ValueListenableBuilder widget It is more performance efficient because it will only rebuild the UI when the variable on valueListenable will be updated. In our case, every time we add a new to-do entry into the database.

If you are getting an error at Hive.box<Task>(‘TODOs’).listenable()then you might forget to import package:hive_flutter/hive_flutter.dart in your myHomePage file. The code will look like the following:

You might have noticed, by long pressing on ListTilewe can delete the todo task (using todos.deleteAt()), and I have also added the FloatingActionButton widget It will open a simple dialog box to write to-do tasks, and it will look something like this:

new todo list task with two buttons: add and cancel
Image by Author

I have got a little carried away designing the simple dialog. The code of simpleDialog is straightforward. I have added a SimpleDialog widget, and inside the SimpleDialog widget, I have added one TextField widget and two FlatButton widgets

When the “Add” button is pressed, it will call the _addTodo function to add a new to-do task. By pressing the “Cancel” button, we can go back to the home screen. The code for _simpleDialog and the _addTodo function (used todosBox.add() to add a task into the ‘TODOs’ box) is the following:

Now we coded most of the app. So, now it’s clean-up time: the Hive is an append-only data store. It writes at the end of the box file, which leads to a growing box. To overcome this, we can either use the .compact() method manually, or Hive can handle it automatically.

I have overridden the dispose method to close the Openbox. Here I have used todosBox.close() to specify a specific box. But we can also use Hive.close()which can close all the open boxes before closing the page.

@override
void dispose() {
// remove the deleted index holes/slots from database
// to free up the space
todosBox.compact();
todosBox.close();
super.dispose();
}

So, now you add any tasks and then restart the app. It will automatically open all the tasks on the home screen.

All the code can be found here:

The Hive is an excellent, easy-to-use, fast, and efficient database. It also has custom TypeAdapters which we have already used. I will use the Hive in my future projects. I would especially like to thank Simon Leier, the author of this amazing package.

Let me know if you have any feedback or you find it difficult to understand in the comments. After all, we are all here to learn something.

Leave a Comment