Hive
Hive is Powerful lightweight Database (storage) to develop your app with and It runs much fast on a device and You also can run it on the Web.
In this article, We will build a simple Todo Application. We also will give you a look at UI (user interface) logic and will implement CRUD (create, read, update and delete) operations for this.
Steps To Cover
- Setup Project
- Initialize Hive
- Type Adapters
- Adding or Storing Data
- Reading Data
- Update Data
- Delete Data
Setup Project
Our application UI looks like this :
——————————————–
Add Dependencies
After creating your project there are some dependencies required for working with Hive that need to be added in the file named pubspec.yaml in your project.
dependencies:
flutter:
sdk: flutter
hive: ^2.0.4
path_provider: ^2.0.7
hive_flutter: ^1.1.0
Also add two dev_dependencies
dev_dependencies:
flutter_test:
sdk: flutter
build_runner:
hive_generator: ^1.1.1
- The package hive_generator will generate Type Adapters for us; these will be explained later in the article.
Hive Initialize
Before starting work with Hive we should Initialize Hive in our main() file. And remember these initialization will come before runApp().
import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart' as path_provider;
Future main() async {
final appDocumentDir = await path_provider.getApplicationDocumentsDirectory();
Hive.init(appDocumentDir.path);
runApp(MyApp());
}
- To get the Document Directory we first need to import the Path Provider as path_provider.
- And then using the path_provider get the ApplicationDocumentDirectory.
- And Lastly Initialize Hive by passing the appDocumentDir.path in Hive paranthesis.
Boxes
Hive works with boxes, to read the data from the box we first need to open the box. A box can be opened like
await Hive.openBox('name');
and to take the instance of the opened box or use a box
Hive.box('name');
Here the name is the box name we can say the database name.
Tip : Once you open the Box you are available to use it anywhere in your app, You also can open it again in some other page it doesn’t affect the already open Box.
- Question: Where and how will we open the box ?
Right down the main() in the StatefulWidget named MyApp you should do something like this
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: "Todo App",
home: FutureBuilder(
// As openBox method is a future
future: Hive.openBox('todos'),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If some errors occurs
if(snapshot.hasError) {
return Text(snapshot.error.toString());
} else {
return TodoPage();
}
// openBox is future method
// It will take a little time
// it's good to return something in else condition
} else {
return Scaffold();
}
},
)
);
}
}
Type Adapters
In Hive we can store data in most standard types such as List, Map, int, and Strings and many times we can have model classes for our data to store them in DB this thing makes our development easier and faster. And to use these model classes first you need to register the Type Adapters in the main() which encode/decode objects in Binary Format in our desk.
There’s also the way to write them manually but in our case we will generate the hive_generator and build_runner we have added in the dev_dependencies they will provide us a generated file which contains the Type Adapters.
In the lib Directory create a new file mine is todo.dart in this file there will be a class and will contain two fields then put annotations in order to generate the Type Adapters.
import 'package:hive/hive.dart';
part 'todo.g.dart';
@HiveType(typeId: 0)
class Todo {
@HiveField(0)
final String? name;
@HiveField(1)
final String? description;
Todo({required this.name, required this.description});
}
- Import the Hive
- Part the class like part ‘todo.g.dart’
- Annotate the class with @HiveType(typeId: 0)
- Annotate the fields with @HiveField(index)
Run the command in your Terminal
- flutter pub run build_runner build –delete-conflicting-outputs
With this done it will generate a file todo.g.dart in your lib Directory It’s class name will be TodoAdapter.
Lastly move to the main.dart and in the main() above the runApp() register the adapter like :
Future main() async {
Hive.registerAdapter(TodoAdapter());
runApp(MyApp());
}
Adding Data
To add the data, create a method in your file.
addTodo(Todo todo) {
final todoBox = Hive.box('todos');
todoBox.add(todo);
}
We already have an open box so we just use that already open box and store it in a variable todoBox. Using the add() method append the data of Todo model in paranthesis. There’s also another method put() but in this you need to give a key for each value like
todoBox.put('key', todo);
but in the case of add() it gives us an auto-increment key for each value. Two other methods for adding data.
- todoBox.putAll({‘key’:value}) accepts a Map.
- todoBoxputAll() it accepts a List. These methods are useful while adding multiple values in a single time.
This is TodoFormPage from here you can add the todo item :
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:todo_app/models/todo.dart';
class TodoFormPage extends StatefulWidget {
const TodoFormPage({Key? key}) : super(key: key);
@override
State<TodoFormPage> createState() => _TodoFormPageState();
}
class _TodoFormPageState extends State<TodoFormPage> {
var _name;
var _description;
final _formKey = GlobalKey<FormState>();
addTodo(Todo todo) {
final todoBox = Hive.box('todos');
todoBox.add(todo);
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Container(
margin: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
child: Column(
children: [
Row(
children: [
Expanded(
child: TextFormField(
decoration: InputDecoration(
hintText: "name",
border: OutlineInputBorder()
),
onSaved: (value) {
_name = value;
},
validator: (value) {
if(value == null || value.isEmpty) {
return "field cannot be empty*";
} else {
return null;
}
},
),
),
SizedBox(height: 8),
Expanded(
child: TextFormField(
decoration: InputDecoration(
hintText: "description",
border: OutlineInputBorder()
),
onSaved: (value) {
_description = value;
},
validator: (value) {
if(value == null || value.isEmpty) {
return "field cannot be empty*";
} else {
return null;
}
},
),
),
],
),
ElevatedButton.icon(onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState?.save();
final newTodo = Todo(name: _name, description: _description);
addTodo(newTodo);
} else {
return null;
}
}, icon: Icon(Icons.save), label: Text("Save"))
],
),
),
);
}
}
Reading Data
There are several ways to read the data from the boxes.
- todoBox.get(key) – get the value from the key you give.
- todoBox.getAt(index) – get the value from the specific key given by add() – (auto-increment key)
- todoBox.getAll() – This returns all the items iterable of a box.
- todoBox.watch() – To watch the added data live.
This is the List View method where we are reading the added data. We use StreamBuilder to watch the upcoming added data live.
Widget _listView() {
final todoBox = Hive.box('todos');
return StreamBuilder(
stream: todoBox.watch(),
builder: (context, snapshot) {
return ListView.builder(
itemCount: todoBox.length,
itemBuilder: (context, index) {
final todo = todoBox.getAt(index) as Todo;
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
InkWell(
onTap: () {
_updateDialogBox(context, todoBox, index);
},
onLongPress: () {
_deleteDialogBox(context, todoBox, index);
},
child: Container(
child: Card(
child: ListTile(
title: Text(todo.name!),
subtitle: Text(todo.description!),
),
),
),
),
],
),
);
},
);
}
);
}
The Item Card is wrapped with a widget InkWell it has two widgets onTap() and onLongPress() Their return type is Function.
- onTap – returns the Update Dialogue Box
- onLongPress – returns the Delete Dialogue Box
Update Data
In Hive we don’t have a specific method to update the data but there are some methods which put the data on the already added data.
- put(‘key’, new_value) – put the new value on the key you give.
- putAt(index) – put the new value on the specific key given by add() which has an auto increment key – similar to getAt(index).
We have a dialogue box to update the values. I’m not sharing the whole dialogue box code because It’s gonna be more huge. It has a Form() widget similar to TodoFormPage() above . Containing the same variables, onSaved() methods. Button for updating the values and method’s parameters look like this :
_updateDialogBox(BuildContext context, Box todoBox, int index) {
return showDialog(
context: context,
builder: (context) { . . . . .
ElevatedButton(
onPressed: () {
_formKey.currentState?.save();
final newUpdatedTodo = Todo(name: _name, description: _description);
todoBox.putAt(index, newUpdatedTodo);
Navigator.pop(context);},
child: Text("Update")),
},
);
}
Delete Data
In every Database, the Delete method always goes pretty much simple. The same goes for the Hive. There are also some ways to delete the data from the Box (Database).
- todoBox.delete(key) – delete the value from the key you give.
- todoBox.deleteAt(index) – delete the value from the specific key given by add().
The Delete Dialogue Box Delete button and the parameters will look like this :
_deleteDialogBox(BuildContext context, Box todoBox, int index) {
return showDialog(
context: context,
builder: (context) {
ElevatedButton(
onPressed: () {
todoBox.deleteAt(index);
Navigator.pop(context);
},
child: Text("Delete"),
),
},
);
}

Conclusion
Hive is very cool, easy to use and a fast local database with custom Type Adapters. It has very less or no relations in your data. That was all I hope this article helps you to know about this Database. Have any questions to ask ? Find me on instagram.
Instagram : https://www.instagram.com/ft.adnankhan/
For more informession visit eTechviral.com