Introduction
The data storage locally is a necessity for virtually all apps. Storage and manipulating data is a vital aspect of developing apps and is the same for Flutter applications. Perhaps you’d like to cache requests from REST APIs, create an application that runs on the internet or store information about customers in a food delivery service.
Several options are available for developers to persist local data in Flutter.shared_preferences: Provides a good way to store small pairs of keys and values .sqlite : It’s a good choice when your database must handle complex relationships between relational data.
But, if you’re searching for a quick and secure local database that is also suitable using Flutter Web(), in this case, to manage offline data storage using Flutter Hive is among the most effective options available.
What is Hive in Flutter?
Hive is a light and lightning fast key-value database created in the pure language of Dart that lets you save and sync your application data offline.
As a key-value storage device created in Dart, Hive supports primitive and intricate data structures while delivering the highest degree of performance. Furthermore, it is secured with AES-256.
To illustrate Here is the graph below that compares Flutter Hive to other similar databases:
Getting Started with Handle Offline Data Storage with Flutter Hive
In this blog post, we’ll examine how to utilize the TypeAdapter in Flutter with the Hive DataBase. Additionally, we’ll create a simple one-page application that displays a user overview, allows you to add new users, update current ones, and remove users.
How to use Flutter Hive to Handle Offline Data Storage?
Step 1: Dependency installation
Two prerequisites are needed before we can utilize Hive.
hive and hive_flutter
You need to add the Hive and hive_flutter packages to pubspec.yaml as follows:
dependencies:
Flutter:
sdk: flutter
hive: ^2.2.3
hive_flutter: ^1.1.0
Add the dev dependencies
dev_dependencies:
flutter_test:
sdk: flutter
hive_generator: ^1.1.3
build_runner: ^2.2.0
Step 2: Initialization Hive Database
The first step is to initialize Hive prior to calling runApp in the Flutter app.
void main() async{
WidgetsFlutterBinding.ensureInitialized();
// Initializes Hive with a valid directory in your app files
await Hive.initFlutter();
runApp(const MyApp());
}
The initFlutter() function is provided by Hive.Basically, it initializes Hive by using the path returned by getApplicationDocumentsDirectory
Do you need help with a fast and secure local database with no native dependencies?
Profit from the benefits of Hivean easy key-value database that stores information locally. You will immediately see the benefits over Sqlite because Hive lets you modify the data on the devices you want to use it. Hire Flutter Developer
Box in Hive
Here’s how you can deal with offline data storage using Flutter Hive.
The data that is stored within Flutter Hive are arranged into boxes. The box is akin to the table that is used in SQL but does not have a structure and is able to contain everything. As I explained in my introduction Hive secures data.Additionally these boxes are able to be used to store sensitive data.
Utilizing key-value sets, Hive keeps its information. The first step is to open the box.
void main() async{
WidgetsFlutterBinding.ensureInitialized();
// Initializes Hive with a valid directory in your app files
await Hive.initFlutter();
// open box
await Hive.openBox("userBox");
runApp(const MyApp());
}
Model class with TypeAdapter
Our example contains several users with information such as name, hobby, and description.
import 'package:hive/hive.dart';
part 'user_model.g.dart';
@HiveType(typeId: 0)
class UserModel extends HiveObject {
@HiveField(0)
final String name;
@HiveField(1)
final String hobby;
@HiveField(2)
final String description;
UserModel({
required this.name,
required this.hobby,
required this.description,
});
}
The first step is to import the hive generator package. In order to generate the type adapter, add a section called user_model.g.dart.TypeAdapter does not need to be constructed manually since we are using the hive generator package.
It automatically builds TypeAdapters for virtually any class by using the hive_generator software pack You can observe that the userModel class has been notated with a variety of fields
@HiveType(): Use @HiveType() to make the model class obvious so the generator knows that this is supposed to be a TypeAdapter.
@HiveField(index): Notifying the fields of the class by a field with the associated index is required.
To construct a TypeAdapter class, run the following command. flutter packages pub run build_runner build
This file’s name is user_model.dart and the data_model.g.dart files will also be included, where the word “g” stands for generated. This means that user_model.g.dart is the new generated file.
It’s time to sign up for UserModelAdapter as it’s been successfully built
To achieve this, we have to create that adapter before running the app’s run function.
void main() async{
WidgetsFlutterBinding.ensureInitialized();
// Initializes Hive with a valid directory in your app files
await Hive.initFlutter();
// Register Hive Adapter
Hive.registerAdapter(UserModelAdapter());
// open box
await Hive.openBox("userBox");
runApp(const MyApp());
}
CRUD operations
Creating Data in Hive
You can use the reference to the Hive box to add data by calling add() function.A key-value pair is accepted by this method.
/// Add new user
Future addUser({required UserModel userModel}) async {
await box.add(userModel);
}
The dialog will appear when we press the floating button. Here, you can type in names, hobbies and descriptions. Following that, we click the add button and then the information will show.
The ValuelistenableBuilder() stream in Flutter Hive can also be used to listen to what is happening inside the box.
Retrieving Data in Hive
Box objects can be read by using the get() method. To retrieve its value, you simply need to provide the key, like this
var userHobby = box.get('hobby');
In case you are using auto-incrementing values, you can use the getAt(index) method of the box object to read using the index,like this
var userData = box.getAt(index);
ValueListenableBuilder(
valueListenable: HiveDataStore.box.listenable(),
builder: (context, Box box, widget) {
return SafeArea(
child: box.length > 0 ? ListView.builder(
shrinkWrap: true,
itemCount: box.length,
itemBuilder: (BuildContext context, int index) {
var userData = box.getAt(index);
return Container(
padding: const EdgeInsets.all(10),
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(color: Colors.grey.withOpacity(0.1),
border: Border.all(color: Colors.blue.shade 900),
borderRadius: const BorderRadius.all(Radius.circular(10))),
child: Row(
children: [
Expanded(
flex: 1,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
IntrinsicHeight(
child: Row(
children: [
Text(userData.name, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w700),
),
VerticalDivider(color: Colors.blue.shade 900,thickness: 2,),
Text(userData.description, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
),
],
),
),
const SizedBox(height: 15),
RichText(text: TextSpan(text: 'Hobby: ', style: const TextStyle(color: Colors.black, fontSize: 16, fontWeight: FontWeight.w700),
children: [
TextSpan(text: userData.hobby, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
],
),
),
],
),
),
Expanded(
flex: 0,
child: Row(
children: [
InkWell(
onTap:(){
isUpdate.value = true;
nameEditingCtr.text = userData.name;
hobbyEditingCtr.text = userData.hobby;
descriptionEditingCtr.text = userData.description;
_showDialog(context,index);
},
child: Icon(Icons.edit, size: 30, color: Colors.blue.shade 900,),
),
const SizedBox(width: 10),
InkWell(
onTap: ()async{
await showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Are you sure you want to delete ${userData.name}?'),
actions: [
TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.blue.shade 900),
elevation: MaterialStateProperty.all(3),
shadowColor: MaterialStateProperty.all(Colors.blue.shade 900), //Defines shadowColor
),
onPressed: () {dataStore.deleteUser(index: index);},
child: const Text('Yes', style: TextStyle(color: Colors.white),
),
),
TextButton(
style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Colors.blue.shade 900),
elevation: MaterialStateProperty.all(3),
shadowColor: MaterialStateProperty.all(Colors.blue.shade 900), //Defines shadowColor
),
onPressed: () {Navigator.of(context, rootNavigator: true).pop(); },
child: const Text('No',
style: TextStyle(color: Colors.white),
),
),
],
),
);
},
child: Icon(Icons.delete,size:30,color: Colors.blue.shade 900,))
],
)),
],
),
);
}):const Center(child: Text("No Data Found"),));
}
)
Updating Data in Hive
The put() method can update the data you originally stored for a key.In this way, the newly provided value will be updated at that key.
/// update user data
Future updateUser({required int index,required UserModel userModel}) async {
await box.putAt(index,userModel);
}
Here we have used the auto-incrementing values, you can use the putAt(index) method of the box object to update using the index.
Deleting Data in Hive
In order to delete data, you can pass the key to the delete() method.
/// delete user
Future deleteUser({required int index}) async {
await box.deleteAt(index);
}
Here we have used the auto-incrementing values, you can use the deleteAt(index) method of the box object to delete using the index.
LazyBox
Each time we design the regular box, the contents are recorded in memory.Performance is higher as a result.
The LazyBox is a fantastic way to quickly access data when you have a lot of data within an archive and don’t wish to put it all in memory.
var lazyBox = await Hive.openLazyBox('myLazyBox');
var value = await lazyBox.get('lazyVal');
Box Compression
We have now completed most of the coding for the app. It’s time to clean up: the Hive is an append-only store.It is possible to manually use the .compact() method or let Hive handle it for us.
As a result, I have overridden the dispose method in order to close the Openbox.
@override
void dispose(){
// to free up space
Hive.box('userBox').compact();
// close all the open boxes before closing the page.
Hive.close();
}