Digital wallet using Hive- NoSQL Database

Fast, Enjoyable & Secure NoSQL Database

What is hive?

Hive is a high performance and lightweight NoSQL database, where the data is stored within a database locally on your device as key value pairs. By default, if you close your flutter app then the state of your app is not persisted, and therefore we need to have a database storage to also persist our data locally on our device. We can use different database to overcome it, we will understand using one of the database called “hive”. Which is supported on  platform like Android, IOS, Desktop and also Web. Specifically for flutter web. Especially for flutter web, it uses the IndexDB within your browser to store your data.

Steps to use Hive.

1. Setup hive:

Go to pubspec.yaml file and under dependencies add

hive: ^2.0.6

hive_flutter: ^1.1.0

and go to dev_dependencies and add

build_runner: ^2.1.7

hive_generator: ^1.1.2

then go to main.dart and import hive and hive_flutter 

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

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Hive.initFlutter();

  runApp(MyApp());
}

Before calling runApp we must include await hive.initFlutter to initialize hive.

2. Create Hive Model:

Transform our model class that we want to persist locally to a hive model. For each model class that you want to store and load within your hive database storage, you need to follow three simple steps.

<i.> Modify Model Class:

For modifying first of all import in model class

import 'package:hive/hive.dart';
/*format for part is part 'filename.g.dart'; here in my case
I want to transform my transaction model class for digital wallet app
so I put -> part 'transaction.g.dart';
*/
part 'transaction.g.dart';

next you need to annotate model class with hivetype with typeId which should be a unique number between all of your model class, which means if you create model class with typeId: 0, then the new model class must have a different typeId. Also annotate all fields in model class and this is how transformation done in the given pics

#before modification of model(transaction class for digital wallet) class,

class Transaction{
  late String name;
  late DateTime createdDate;
  late bool isExpense = true;
  late double amount;
}

#after modification of model(transaction class for digital wallet)  class,

import 'package:hive/hive.dart';
part 'transaction.g.dart';

@HiveType(typeId: 0)
//optionally, you can extend model class to hiveobject
// then we can have some convenient message that we can use
class Transaction extends HiveObject {
  @HiveField(0)
  late String name;

  @HiveField(1)
  late DateTime createdDate;

  @HiveField(2)
  late bool isExpense = true;

  @HiveField(3)
  late double amount;
}

<ii.> Generate Model class Adapter

To generate model class adapter,  go inside terminal of the root folder of the flutter project and run 

build_runner build 

after some seconds of pressing enter to run that command, you should see a generated model class adapter as shown in the below picture.

<iii.> Register Model Class Adapter within our hive system:

For doing this go to main.dart and register like this

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Hive.initFlutter();

  Hive.registerAdapter(TransactionAdapter());

  runApp(MyApp());
}


3. Use CRUD operations:

Implementing CRUD operation means create, read, update and delete.

Therefore, let us understand about boxes(our data is stored in hive within key value stores). For understanding, you can  relate box with a map. Box doesn't have any schema, so you can place any object as values inside even if you have objects of different types everything can be placed in same box. You can also create other boxes, and then you can also place there key values inside. This works internally by creating a different file for each box locally on your file storage.



Before you can use a box, you need to open it, therefore go to main.dart, and after registering the model class adapter open the box

 Hive.registerAdapter(TransactionAdapter());
  await Hive.openBox<Transaction>('transactions');

and after that define the name of the box ('transactions') for this case. This is the box where we want to store our data(transaction data) inside. Opening the box will load all of our key-value pairs from the local storage into our memory to access imminently.

NOTE: For closing the box,  you can use

@override
  void dispose() {
    Hive.close();
    super.dispose();
  }

which will free up all the opened boxes, this means all the key value pairs loaded in your memory are released.

for closing specific box, you can use

@override
  void dispose() {
    Hive.close();
    super.dispose();
  }

Implementing Create Operation

To implement it, you can create UI where you can see all the stuff going on, let's see it programmatically as well as on emulator to have a clear picture of what's going on. 

Here is the UI with floating action button to add new transaction if you will click on it then a dialog box will pop up to add transaction as shown below


Here you can fill details to add a new transaction, once you click on add button then createTrransaction method(as shown below) will trigger, and now we have to store all the credentials inside our local storage therefore in the given code below there is a transaction object which is created, and all fields are filled by right data according to Transaction model class.
 Future CreateTransaction(String name, double amount, bool isExpense) async {
    final transaction = Transaction()
      ..name = name
      ..createdDate = DateTime.now()
      ..amount = amount
      ..isExpense = isExpense;

    final box = Boxes.getTransactions();
  }

To store a transaction object within our box, first of all we need to get the transaction box as shown above. Where "Boxes" (in the above code)is a class,  and inside ''Boxes'' class we can create getTransactions method which returns a box of transaction type(Expense or Income). Hive box is used to access our box to put the name of our transaction inside, and that should be the same name which we used to open our box('transaction').

import 'package:hive/hive.dart';
import 'package:hive_database_example/model/transaction.dart';

class Boxes {
  static Box<Transaction> getTransactions() =>
      Hive.box<Transaction>('transactions');
}

after this we can call add method on our box as shown below to add our transaction inside so within the add method we can add only value and key is automatically generated.

 Future CreateTransaction(String name, double amount, bool isExpense) async {
    final transaction = Transaction()
      ..name = name
      ..createdDate = DateTime.now()
      ..amount = amount
      ..isExpense = isExpense;

    final box = Boxes.getTransactions();
    box.add(transaction);
  /* NOTE: IF YOU WANT  TO CONTROL KEY AS WELL AS VALUE
    YOU CAN USE THIS METHOD
    box.put('mykey', transaction);
   
    IF YOU WANT TO READ BOX AGAIN
    ACCESING OUT OF THE UI U CAN USE THE CODE DOWN
    final mybox = Boxes.getTransactions();
    final myTransaction = mybox.get('key');

    YOU CAN EVEN ACCES ALL THE VALUE OF BOX USING CODE BELOW
    mybox.values;
     mybox.keys; */

  }

Implementing Read Operation

After creating operation we will see read operation, basically the transaction that we created as well as stored locally that is what we want to see, of course there can be different ways to present on UI. Let us first understand how to read the stored data, then present it on UI.

ValueListenableBuilder<Box<Transaction>>(
          valueListenable: Boxes.getTransactions().listenable(),
          builder: (context, box, _) {
            final transactions = box.values.toList().cast<Transaction>();

            return buildContent(transactions);
          },
        ),

as we can see, code buildcontext  method is wrap with value builder and here inside we are then  listening to the box  by accessing from class ''Boxes'' that we created before. And on box we call listenable to get here every time the changes, if anything in box changes then we get new box in builder. Within builder, we can access our box and values stored in it and transfer in to list, and it is important to cast it to right type because our box here hold only Transaction therefore we also need to convert to Transaction.




So the stored data is now rendered on-screen after we read it. And it is also persisted in our local storage. 

The UI which we used to render data contains buttons to update as well delete, which we will use to understand the remaining operation.

Implementing Update Operation

On clicking edit button, dialog box will appear which we can use to update the existing transaction.

Here you can fill details to update existing transaction, once you click on save button then updateTransaction method(as shown below) will trigger, calling transaction. Save will persist object in the local database, and You can use save method only if you have extended model class to HiveObject while creating it as discussed above.
void updateTransaction(
    Transaction transaction,
    String name,
    double amount,
    bool isExpense,
  ) {
    transaction.name = name;
    transaction.amount = amount;
    transaction.isExpense = isExpense;
    transaction.save();
/*
IF YOU DON'T WANT TO USE SAVE METHOD THEN,
 YOU CAN FOLLOW THE CODE BELOW
     final box = Boxes.getTransactions();
   box.put(transaction.key, transaction);
*/
  }

And hence, update operation is successfully implemented.

Implementing Delete Operation

There is a delete button as shown in the above picture so that button should clear the existing transaction.

 void deleteTransaction(Transaction transaction) {
    transaction.delete();
    /*
IF YOU DON'T WANT TO USE DELETE METHOD THEN,
 YOU CAN FOLLOW THE CODE BELOW
     final box = Boxes.getTransactions();
     box.delete(transaction.key);
*/
   
  }

once you click on delete then deleteTransaction method(as shown above) will trigger, and it will delete the transaction, and You can use delete method only if you have extended model class to HiveObject while creating it as discussed above.

Here all the CRUD operation of hive is completed

Limitation of Hive:

* Hive doesn't have Query Language

NOTE: Be careful on model changes, hiveField number should not change and never reuse hiveField numbers.

Pagination in hive

You can use Iterable<T> box.values to paginate items.

var box = await Hive.openBox<Post>('transactions');
var items = box.values.take(10 /* limit */).skip(20 /* offset */);

Post a Comment

Previous Post Next Post

Subscribe Us


Get tutorials, Flutter news and other exclusive content delivered to your inbox. Join 1000+ growth-oriented Flutter developers subscribed to the newsletter

100% value, 0% spam. Unsubscribe anytime