Hive Tutorial
How to create Data models and perform basic CRUD operations in Flutter with Hive
Working with local databases can be a lot to take in, but making data models helps make things structured and easier to work with. A data model is essentially a class that defines the structure of your data. A data model can be literally anything, like Dog, Expense, Player, etc.
In Flutter, it all begins with installing these packages in your pubsec.yaml file: hive_flutter, hive in your dependencies, and build_runner, hive_generator in your dev_dependencies.
Now, hive_flutter and hive allow you to work with Hive, which, as you may or may not already know, is a local database that allows you to store directly on your device. while the build_runner allows us to create an essential “part” of the data model with the aid of the hive_generator, so Hive can store custom data.
After saving your YAML file, you’ll most definitely receive a dependency error, so all you have to do is change the version of the build runner to ^2.4.13, like so:
Setting up your code
To make our lives easier, we’re going to structure the architecture of our project like so:
Before we do anything at all, we need to do two very important things first
- Setting up Hive
- Creating the data model
Hive Setup
To setup hive, go to your main.dart file and make sure your main.dart looks something like this:
First of all, we add the async keyword to allow us to attach the await keyword so that the app doesn’t freeze while Hive is being set up, then WidgetsFlutterBinding.ensureInitialized() makes sure that Flutter has all its widgets ready before even setting up Hive.
Secondly, await Hive.initFlutter() actually initializes Flutter, and to top it all off, await Hive.openBox(”animalBox”) creates a new Table or “Box” as Hive calls it, which is where we’ll actually store instances of our data model. You can choose any name for your box, but it’s highly recommended to use a name that tells you what the box stores, so you don’t get confused later on.
Creating and “registering” the Data Model
In this section, we’ll be going through creating the Data model in question, so we’ll create an animal Data model, but before diving into the code, we need to identify some of the features that make an animal a well, umm, animal. Some features are the name, the sound they make, and the number of legs.
Create a new file in the models folder we made earlier called “animal_model.dart”. You can choose any name, but I like this because it’s really descriptive. Your animal_model.dart file should look like this:
Let’s take it from the top. That “part ‘animal_model.g.dart’” allows us to create the other essential “part” of the animal_model, so that Hive can store it, as Hive is usually used to store key-value pairs.
Note: The name of the file before the .g.dart must match the exact name of the model file, which in our case is “animal_model”
We create a basic class, but with some tweaks. The class must extend HiveObject so Hive knows that it isn’t the normal key-value pairs, but allows it to store it. The “@HiveType(typeId:0)” just tells Hive “Hey, this is a custom type of data, and since it’s the first of the custom types we created, we set its typeId to 0.”
In a normal class, we just list out its parameters, but since this is a HiveObject we have to explicitly tell Hive where to store these parameters of the Data model in the box, so we use “@HiveField” followed by the index of each parameter.
After all that’s outta the way, we use the build_runner to generate the animal_model.g.dart file, and don’t panic if you see red squiggly lines under part “animal_model.g.dart”. It’s all part of the process.
Open up your terminal (Heh, here’s a shortcut on windows. Ctrl+Shift+~) then type “dart run build_runner build”. You’ll see the .g.dart file inside the models folder we created. Before we proceed, if you encounter any error, just run “dart run build_runner build --delete-conflicting-outputs” and what this does is prevent the build_runner from generating corrupted code by ensuring it takes care of all redundant data.
Now, for the moment of truth. Go to your main.dart and “register” the model adapter. A TypeAdapter is just a fancy tool that turns our custom object/Data model into something that Hive can work with as if it were a normal value.
Note: It’s recommended to register your adapter before opening your box.
Performing basic CRUD operations.
CRUD or Create ,Read, Update, and Delete operations are the holy trinity of the database world or quadrinity(Definitely sure that’s not a word, but bear with me). To kick things off, we’ll create a new file in our services folder called hive_service.dart and this is where we’ll write out all the code that will allow us to perform CRUD operations on our box.
The createNewAnimal function has parameters name, sound and numberOfLegs which will serve as the arguments for the AnimalModel. We use Hive.box(”animalBox”) to get access to the box which we already opened in our main.dart file. Finally, we use box.add() to literally add a new instance of AnimalModel to our box.
To delete an instance, we use the box.deletAt() method to delete it based on its index in the box.
But hold up, that code above is ok, but not great. We’ll most likely run into issues later because we didn’t specify the data type of the box, or to be more precise, the data type of what it’s storing, which is our AnimalModel. We’ll also have to specify the data type in our main.dart file.
Hooking up Hive to the UI
The moment we’ve all been waiting for is finally here. I’d like to assume that we’ve already created the home_page.dart file I made earlier, but if we haven’t, here’s a structure you could follow:
Our current page is, well, empty, but that will change very soon. We’re going to be using something called a ValueListenableBuilder, and just think of it as a tool that allows you to access our hive box and its values. It also allows us to return widgets for each entry in the box.
….So umm here’s our UI….I know, I know, I have some explaining to do. We first of all create a parent column widget in order to resolve the spacing issues we’d get from our listview builder, but I’ll talk more about that soon.
The ValueListenableBuilder has two key parameters of interest to us: valueListenable, and our builder. The valueListenable allows us to set up a channel or a stream to our hive box by attaching .listenable() to it. This way, we can listen to real-time data from Hive without having to reload the app every time we wanna see a change in our box.
The builder is essentially a function that allows us to return widgets and gives us access to the listenable piece of data, which in our case, is our “animalBox” hive box. it takes three arguments which are, context, value(which is just our hive box, but we have to make sure we specify its data type), and widget.
We return a ListView builder so we can fetch live data into our UI with ease, and set the itemcount parameter to the length of the “animals” list we made. final animals = box.values.toList(); takes all the values of our hive box and returns them as a list in order to make our lives easier. The itembuilder parameter of the ListView builder essentially allows us to track each element of a list.
final AnimalModel animal = animals[index]; refers to each individual AnimalModel in our list, and we top it all off by using a listTile to return the parameters of each AnimalModel in the box.
Oh yeah, I did say that I would explain why we used a Column. You probably noticed I wrapped the ValueListenableBuilder in an “Expanded” Widget, and what this does is it ensures the ListView only takes the space available in the visible screen. In layman's terms, it prevents the ListView from stretching to infinity.
Congratulations, you have a working offline app capable of storing questionable stuff, but I hope you were able to get a grip on everything we’ve worked on so far. Run your main.dart file and see your app in action!!










Top comments (0)