How to Store the data locally with hive in Flutter.

Shubham Pandey
FlutterFly
Published in
7 min readMay 29, 2023

Firstly we learn why we need the hive? what is necessity of the hive?

Let’s Start…

One of the most important parts of app development is data storage and manipulation, and the same is true for flutter apps. There are many ways to store local data in flutter apps like as Shared Preferences, Secure Storage, SQLite, Hive etc, but in this article, we will learn about just one — Hive.

Hive is a fast, lightweight, NoSQL database, for flutter and dart apps. Hive is really useful if you want a simple key-value database, without many relations, and really easy to use.

Main Page

Hive is a lightweight key-value database that can be used to store data locally in mobile, desktop, and web applications. It is written in pure Dart and works very well with Flutter. CRUD means create, read, update, and delete, the four essential operations of persistent storage.

In this article, we’re going to build a full-featured and quite useful app with Hive and Flutter.

Table Of Contents

  1. Overview

1.1. The Basics

1.2. Storing a list of items with auto-increment keys

2. The Complete Example

2.1. App Preview

2.2. The Code

3. Conclusion.

Overview

The basics

Some of the great advantages of Hive:

  • Easy to use, just like Dart maps. You don’t have to write long and complicated queries to query data.
  • High flexibility. You can change your data structure easily because Hive doesn’t use tables like SQLite.
  • No native dependencies
  • Fast and can handle a large amount of data.
  • Supports primitives (string, number, list, map, etc.) and Dart objects (with the help of adapters)

In order to use Hive in Flutter, you need to install the hive plugin and the hive_flutter plugin.

You can initialise Hive in the main() function of your app:

void main() async{

// Initialized the hive
await Hive.initFlutter();

runApp(const MyApp());
}

All data stored in Hive is organized in boxes. A box can be compared to a table in SQL, but it does not have a structure and can contain anything. You can open a box like this:

await Hive.openBox('database_box'); //the database name is totally up to you

Once a box is open, all its data from the local storage is loaded into memory for immediate access. You can retrieve data synchronously without using async/await:

final _databaseBox = Hive.box('database_box');
final something = _databaseBox.get('my_key');

Adding a new item to the box:

await _databaseBox.put('some_key', someValue);

Updating an existing item in the box:

await _databaseBox.put('some_key', someValue);

Deleting an item in the box:

await _databaseBox.delete('some_key');

Storing a list of items with auto-increment keys

You can save a list of items with Hive easily by using the add() method. In this case, the keys are auto-generated and auto-increment, starting from 0, then 1, 2, 3, and so on. For example:

final listBox = Hive.box('my_list');
int newKey = await listBox.add(someData);

You can retrieve all keys and values from the box like this:

final keys = listBox.keys; 
final values = listBox.values;

You can use loops with keys and values as needed. In cases where you prefer to create and set your own keys (by using the put() method as mentioned earlier), using DateTime.now().toString() is a good idea. You can also find some other useful techniques in this article: Flutter & Dart: 3 Ways to Generate Random Strings.

The Complete Example

App Preview

Imagine you are an data entry administrator, you had to add all visitor list who come to visit your organisation. It’s hard to memorize those peoples, so you seek a pen and paper to scribble them down. However, you cannot find what you need. Here the rescuer comes in. It’s a data entry list app that can run totally offline without an internet connection.

The Data Entry list app we are talking about has a floating button. When this button is pressed, a bottom sheet with text fields will appear so that you add a new person (including its name, email, phone, qualification, designation and experience) and store it in the Hive database.

All saved items are displayed in a list view. Associating with each person data is an edit button and a delete button, used for updating and deleting the person data, respectively.

App Preview

The Code

Installing hive and hive_flutter by running:

dependencies:
flutter:
sdk: flutter


hive: ^2.2.3
hive_flutter: ^1.1.0

The full source code in main.dart with detailed explanations:

import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';

void main() async{

// Initialized the hive
await Hive.initFlutter();

//
await Hive.openBox('database_box');
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const FirstPage(),
);
}
}

class FirstPage extends StatefulWidget {
const FirstPage({Key? key}) : super(key: key);

@override
State<FirstPage> createState() => _FirstPageState();
}

class _FirstPageState extends State<FirstPage> {
List<Map<String, dynamic>> _items = [];
final _databaseBox = Hive.box('database_box');

@override
void initState() {
super.initState();
_refreshItems(); // Load data when app starts
}

// Get all items from the database
void _refreshItems() {
final data = _databaseBox.keys.map((key) {
final value = _databaseBox.get(key);
return {"key": key,
"name": value["name"],
"email": value['email'],
"phone": value['phone'],
"qualification": value['qualification'],
"designation": value['designation'],
"experience": value['experience']};
}).toList();
setState(() {
_items = data.reversed.toList();
});
}

// Create new item
Future<void> _createItem(Map<String, dynamic> newItem) async {
await _databaseBox.add(newItem);
_refreshItems(); // update the UI
}


Map<String, dynamic> _readItem(int key) {
final item = _databaseBox.get(key);
return item;
}

// Update a single item
Future<void> _updateItem(int itemKey, Map<String, dynamic> item) async {
await _databaseBox.put(itemKey, item);
_refreshItems(); // Update the UI
}

// Delete a single item
Future<void> _deleteItem(int itemKey) async {
await _databaseBox.delete(itemKey);
_refreshItems(); // update the UI

// Display a snackbar
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('An item has been deleted')));
}

// TextFields' controllers
final TextEditingController _nameController = TextEditingController();
final TextEditingController _quantityController = TextEditingController();
final TextEditingController _designationController = TextEditingController();
final TextEditingController _experienceController = TextEditingController();
final TextEditingController _qualificationController = TextEditingController();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _phoneController = TextEditingController();


void _showForm(BuildContext ctx, int? itemKey) async {
if (itemKey != null) {
final existingItem =
_items.firstWhere((element) => element['key'] == itemKey);
_nameController.text = existingItem['name'];
_designationController.text = existingItem['designation'];
_experienceController.text = existingItem['experience'];
_qualificationController.text = existingItem['qualification'];
_emailController.text = existingItem['email'];
_phoneController.text = existingItem['phone'];
}

showModalBottomSheet(
context: ctx,
elevation: 5,
isScrollControlled: true,
builder: (_) => Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(ctx).viewInsets.bottom ,
top: 15,
left: 15,
right: 15),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text("Fill the Details:",style: TextStyle(fontWeight: FontWeight.bold,
fontSize: 18),),
IconButton(onPressed: (){
_nameController.text = '';
_emailController.text = '';
_phoneController.text = '';
_experienceController.text = '';
_qualificationController.text = '';
_designationController.text = '';
Navigator.pop(context);
},
icon: const Icon(Icons.cancel_outlined)
)
],
),

// Divider(color: Colors.black45,thickness: 1.5,),
TextField(
controller: _nameController,
decoration: const InputDecoration(hintText: 'Full Name'),
),
const SizedBox(
height: 10,
),
TextField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
decoration: const InputDecoration(hintText: 'Email Address'),
),
const SizedBox(
height: 10,
),
TextField(
controller: _phoneController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(hintText: 'Phone Number'),
),
const SizedBox(
height: 10,
),
TextField(
controller: _qualificationController,
keyboardType: TextInputType.text,
decoration: const InputDecoration(hintText: 'Qualification'),
),
const SizedBox(
height: 10,
),
TextField(
controller: _designationController,
keyboardType: TextInputType.text,
decoration: const InputDecoration(hintText: 'Designation'),
),
const SizedBox(
height: 10,
),
TextField(
controller: _experienceController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(hintText: 'Experience'),
),
const SizedBox(
height: 20,
),
ElevatedButton(
onPressed: () async {
// Save new item
if (itemKey == null) {
_createItem({
"name": _nameController.text,
"email": _emailController.text,
"phone": _phoneController.text,
"qualification": _qualificationController.text,
"designation": _designationController.text,
"experience": _experienceController.text,
});
}

if (itemKey != null) {
_updateItem(itemKey, {
"name": _nameController.text.trim(),
"email": _emailController.text.trim(),
"phone": _phoneController.text.trim(),
"qualification": _qualificationController.text.trim(),
"designation": _designationController.text.trim(),
"experience": _experienceController.text.trim(),
});
}
// Clear the text fields
_nameController.text = '';
_emailController.text = '';
_phoneController.text = '';
_experienceController.text = '';
_qualificationController.text = '';
_designationController.text = '';
Navigator.of(context).pop(); // Close the bottom sheet
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(itemKey == null ? 'Create New' : 'Update')
],
),
),
const SizedBox(
height: 15,
)
],
),
),
));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Employee List"),
),
body: _items.isEmpty
? const Center(
child: Text(
'No Data',
style: TextStyle(fontSize: 30),
),
)
: ListView.builder(
// the list of items
itemCount: _items.length,
itemBuilder: (_, index) {
final currentItem = _items[index];
return Card(
color: Colors.white,
margin: const EdgeInsets.all(10),
elevation: 3,
child: Padding(
padding: const EdgeInsets.only(top:10,right: 15,left: 15,bottom: 10),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(child: Text(currentItem['name'],style: const TextStyle(
fontSize: 20,fontWeight: FontWeight.bold,
color: Colors.black
),),),
Row(
mainAxisSize: MainAxisSize.min,
children: [
// Edit button
InkWell(
child: const Icon(Icons.edit,color: Colors.blue,),
onTap: () =>
_showForm(context, currentItem['key'])),
const SizedBox(width: 10,),
// Delete button
InkWell(
child: const Icon(Icons.delete, color: Colors.red,),
onTap: () => _deleteItem(currentItem['key']),
),
],
)
],
),
const SizedBox(height: 5,),
Row(
children: [
const Text("Email: ",style: TextStyle(
fontSize: 14,fontWeight: FontWeight.bold,
color: Colors.grey
),),
Text(currentItem['email'],
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600
)),
],
),
const SizedBox(height: 5,),
Row(
children: [
const Text("Phone: ",style: TextStyle(
fontSize: 14,fontWeight: FontWeight.bold,
color: Colors.grey
),),
Text(currentItem['phone'],style: TextStyle(
fontSize: 14, fontWeight: FontWeight.w600
)),
],
),
const SizedBox(height: 5,),
Row(
children: [
const Text("Qualification: ",style: TextStyle(
fontSize: 14,fontWeight: FontWeight.bold,
color: Colors.grey
),),
Text(currentItem['qualification'],style: TextStyle(
fontSize: 14, fontWeight: FontWeight.w600
)),
],
),
const SizedBox(height: 5,),
Row(
children: [
const Text("Designation: ",style: TextStyle(
fontSize: 14,fontWeight: FontWeight.bold,
color: Colors.grey
),),
Text(currentItem['designation'],style: TextStyle(
fontSize: 14, fontWeight: FontWeight.w600
)),
],
),
const SizedBox(height: 5,),
Row(
children: [
const Text("Experience: ",style: TextStyle(
fontSize: 14,fontWeight: FontWeight.bold,
color: Colors.grey
),),
Text(currentItem['experience'].toString(),style: const TextStyle(
fontSize: 14, fontWeight: FontWeight.w600
)),
],
),
],
),
)
);
}),
// Add new item button
floatingActionButton: FloatingActionButton(
onPressed: () => _showForm(context, null),
child: const Icon(Icons.add),
),
);
}
}

Conclusion

You’ve learned the fundamentals of the Hive database in Flutter. You’ve also examined a small but full-feature app that uses Hive to store data offline.

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.

Thank You for your Reading!!

--

--