Hive Flutter Database-img

Hive Flutter Database

This guide will explain how to create a basic todo app using Hive database. It consists of an intro to hive and will go through all CRUD operations
There are multiple ways to store data locally in a Flutter app and every solution has its pros and cons. You generally want to have data stored locally for offline use or to store some user details. Hive is a powerful, fast, and lightweight database that is easy to add to any project. It also supports all platforms (Mobile, macOS, Windows, Linux, and the Web).

In this guide, we will create a basic todo app that stores todos locally using Hive. In order to make this guide short and on topic, we will not focus on UI elements. The UI will be there to support Hive functionality.

Getting Started

Let's start by adding the package to our pubspec.yaml file
dependencies:
  hive: ^2.0.0
  hive_flutter: ^1.0.0

dev_dependencies:
  hive_generator: ^1.0.0
  build_runner: ^1.11.5
We also need to initialize Hive in order to create a file in our app's file directory. We can do that in our main function.
// import both packages
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';

void main() async {
  await Hive.initFlutter();
  runApp(MyApp());
}

Intro to Hive

Hive revolves around a concept of a box. You can think of it as a table in a relational database but can take any type of data. We can create boxes that accept a certain type.

Data stored can only be accessed from an open box. This means that all the data is loaded from the box into memory for quick access. Let's create a todos box
await Hive.openBox<Todo>('todos');
Hive supports primitive data types but anything custom like our todo class that we will define in a bit needs to have a TypeAdapter . This adapter converts our object to binary

Let's create our class that can generate an adapter
import 'package:hive/hive.dart';

part 'todo.g.dart';

@HiveType(typeId: 0) // 1
class Todo {
  @HiveField(0) // 2
  String title;

  @HiveField(1) // 2
  bool completed;

  Todo({
    this.title,
    this.completed,
  });
}
  1. We need to do this because it is a way for dart to split the code up (this file will be auto-generated by a command later)
  2. In order to generate a custom type, we need to annotate our class with HiveType and give it an id. This id should increase when we add more classes and should never be changed
  3. Same as HiveType, We use HiveField to annotate the fields of our class. The id 0-255 should be unique in a class and should not be changed
Now that we have the class we're going to have to generate the adapter. Run this in your app directory
flutter packages pub run build_runner build
This will generate the todo.g.dart file. Now that we have the adapter we need to register it before we open the box.
Hive.registerAdapter<Todo>(TodoAdapter());
await Hive.openBox<Todo>('todos');
We have now created a box with a custom type.

Adding a Todo

In order not to make this guide longer than it should be. Let's create a button that adds a todo without us having to create a form. This guide is to show the functionality of hive.

Let's create a Stateful widget to house all of this functionality.
class TodoListScreen extends StatefulWidget {
  const TodoListScreen({Key key}) : super(key: key);

  @override
  _TodoListScreenState createState() => _TodoListScreenState();
}

class _TodoListScreenState extends State<TodoListScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Todos'),
      ),
      body: Container(),
    );
  }
}
In order to add to hive we need to get access to the box that we opened when the app started. (You can open boxes whenever is best it does not have to be at the beginning).

In our stateful widget let's create a reference to the box. This will throw an error if the box has not been opened yet.
class _TodoListScreenState extends State<TodoListScreen> {
	Box<Todo> todosBox = Hive.box<Todo>("todos");
	//rest of code
Now that we have this we can add the functionality to add a new todo.
addTodo() {
  todosBox.add(new Todo(title: "New Todo", completed: false));
}
Adding an entry to the DB like this auto increments the index every time we add a new todo. A key can also be used to store values in the box. It would look something like this
addTodo() {
  todosBox.put("someUniqueId", new Todo(title: "New Todo", completed: false));
}
For simplicity, we will use the first approach.

Let's create a button that uses this function. Our app bar should look like this.
appBar: AppBar(
  title: Text('Todos'),
  actions: [IconButton(icon: Icon(Icons.add), onPressed: () => addTodo())],
),

Reading a Todo

Now that we can add todos let's read them from the DB and show them in a list. There are multiple ways we can fetch data from the DB.

  1. If we used the index approach (no key) ⇒todoBox.get(index) or todoBox.getAt(index)
  2. If we used the key approach ⇒ todoBox.get(key)
We want to listen to changes on the DB to update the list automatically. In order to do this, we need to use ValueListenableBuilder and read the data inside it.

Let's replace the Container in the body of the Scaffold with
body: ValueListenableBuilder( // 1
  valueListenable: todosBox.listenable(), // 2
  builder: (context, box, widget) { // 3
    return ListView.builder(
      itemCount: box.length, // 4
			builder: (BuildContext context, Box<Todo> box, Widget widget) {
				Todo todo = todosBox.get(index); // 5

        return ListTile(
          title: Text(todo.title),
          subtitle: Text(todo.completed.toString()),
        );
      },
    );
  },
),
  1. We use the ValueListenableBuilder widget provided by Hive. This listens to our box and rebuilds the builder whenever a change is detected.
  2. providing the box as a listenable using .listenable()
  3. A builder that provides the data to be used
  4. we use get(index) to get the individual todo. We can also use getAt(index)
Now if we tap the + button we will see a new todo add automatically

Updating a Todo

Same as adding we can use put(key,value) or putAt(index,value) to update an entry for a specific key or index.

Let's make it that when we tap the ListTile the completed property value changes. We can do this easily by adding an onTap
onTap: () {
  todo.completed = !todo.completed;
  box.putAt(index, todo);
},
Now when we tap any tile we should see the text below change between true and false.

Deleting a Todo

Deleting items is similar to adding and updating. We can use delete(key) or deleteAt(index) to delete an entry for a specific key or index.

Let's add a trailing icon button to delete a todo to our ListTile.
trailing: IconButton(
  icon: Icon(Icons.delete),
  onPressed: () {
    box.deleteAt(index);
  },
),

Conclusion

This guide went through explain the core concepts of Hive and building a basic CRUD application to demonstrate offline storage functionality. Hive is a powerful platform-independent package that I recommend when a relational DB is not needed.

Like the post?

feel free to share it anywhere

Related Guides

Backup, Restore & Share JSON file Flutter-img

Backup, Restore & Share JSON file Flutter

Read, Write data to file and share it