DEV Community

Cover image for Perform CRUD operations in Flutter with Hive
OpenReplay Tech Blog
OpenReplay Tech Blog

Posted on • Originally published at blog.openreplay.com

Perform CRUD operations in Flutter with Hive

by Emmanuel Uchechukwu

CRUD, an acronym for Create, Read, Update, Delete, represents four basic operations a mobile application should be able to perform. It is an essential paradigm common in both web and mobile applications. In this article, readers will learn how to perform and implement CRUD operations in a Flutter application using Hive.

Hive is a lightweight, key-value database written in pure Dart. It is a NoSQL database that can be used to store data locally, either on mobile applications or Web applications.

You’d want to use Hive as database storage for many reasons. Here is a list of a few reasons:

  • It includes a powerful encryption system.
  • There are no native dependencies
  • It has the best performance of any database.

Session Replay for Developers

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — an open-source session replay suite for developers. It can be self-hosted in minutes, giving you complete control over your customer data

OpenReplay
Happy debugging! Try using OpenReplay today.

Setting Up Flutter Project

To start building our app, click here to clone the starter files. We will build a basic Todo Application to show how to implement CRUD functionalities. On your terminal, run the command below to add the missing dependencies to your project.

>>> flutter create .
Enter fullscreen mode Exit fullscreen mode

This will add all missing dependencies to the project.

Project Overview

In our project, we have three buttons, each of which transitions the screen to its respective screen.

-

We also have a Screens folder where we can create, read, and update. The delete functionality of our CRUD app will be handled on the read screen.dart file.

Adding Hive Dependencies To Our Pubspec.yaml File.

To access the Hive database and its features, we must add them to our pubspec.yaml file. On your terminal, run the command below:

>>> flutter pub add hive

>>> flutter pub add hive_flutter

>>> flutter pub add build_runner
Enter fullscreen mode Exit fullscreen mode

This will add the Hive database and other dependencies to our flutter project.

Initialize Hive

Since we are done adding dependencies to our pubspec.yaml file, let's import and initialize our database.

import 'package:hive_flutter/hive_flutter.dart';


void main() async {
  WidgetsFlutterBinding.ensureInitialized()
  await Hive.initFlutter();
  await Hive.openBox('data_box');

  runApp(const MyApp());
}
Enter fullscreen mode Exit fullscreen mode

We initialize our Hive database using the .initFlutter(); next, we open a box called 'data_box' because Hive stores data in boxes. Boxes can be compared to tables in SQL.

Creating Our Model

Before developing our CRUD techniques, let’s create a model. On your lib folder, create a new folder called Model. In the model folder, create a new file called model.dart and paste the code below:

class Data {
  String title;
  String description;

  Data({
    required this.title,
    required this.description,
  });
}

Enter fullscreen mode Exit fullscreen mode

With that out of the way, let’s begin creating our CRUD methods; we will take each of these methods step by step.

Step 1: Creating / Adding Data to Our Database

First, let’s make a reference to our already opened box and also create our controllers:

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

 late final Box dataBox;
 final TextEditingController _titleController = TextEditingController();
 final TextEditingController _descriptionController = TextEditingController();

  @override
  void initState() {

    super.initState();
    dataBox = Hive.box('data_box');
  }
Enter fullscreen mode Exit fullscreen mode

Next, we write and implement our create method.

  _createData() {
    Data newData = Data(
      title: _titleController.text,
      description: _descriptionController.text,
    );

    dataBox.add(newData);
  }
Enter fullscreen mode Exit fullscreen mode

Here, we called the Data class, passing each controller to its respective parameters. Then we called the add() method, which adds our newData variable to the database. Next, we call the method in our onPressed parameter as such:

ElevatedButton(
    onPressed: () {
        _createData();
        Navigator.push(
            context,
            MaterialPageRoute(
                builder: (context) => const ReadScreen(),
            ),
        );
    },
    child: const Text('ADD DATA'),
)
Enter fullscreen mode Exit fullscreen mode

Step 2: Reading The Data

Our read screen is currently empty, so we’ll build the UI and add the read functionality.

read_screen.dart

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

import '/screens/create_screen.dart';
import '/screens/update_screen.dart';

class ReadScreen extends StatefulWidget {
  const ReadScreen({super.key});

  @override
  State<ReadScreen> createState() => _ReadScreenState();
}

class _ReadScreenState extends State<ReadScreen> {
  late final Box dataBox;

  @override
  void initState() {
    super.initState();
    dataBox = Hive.box('data_box');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Read Screen'),
        centerTitle: true,
        actions: [
          IconButton(
            onPressed: () => Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => const CreateScreen(),
              ),
            ),
            icon: const Icon(Icons.add),
          ),
        ],
      ),
      body: ValueListenableBuilder(
        valueListenable: dataBox.listenable(),
        builder: (context, value, child) {
          if (value.isEmpty) {
            return const Center(
              child: Text('Database Is Empty'),
            );
          } else {
            return ListView.builder(
              itemCount: dataBox.length,
              itemBuilder: (context, index) {
                var box = value;
                var getData = box.getAt(index);

                return ListTile(
                  leading: IconButton(
                    onPressed: () {},
                    icon: const Icon(Icons.edit),
                  ),
                  title: Text(getData.title),
                  subtitle: Text(getData.description),
                  trailing: IconButton(
                    onPressed: () {},
                    icon: const Icon(Icons.delete),
                  ),
                );
              },
            );
          }
        },
      ),
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

Let’s explain what we have above. First, we made a reference to our already opened box as we did on the Create Screen. For us to be able to read data in our database, we use the ValueListenableBuilder() widget. This widget will be used to read and refresh our widgets based on the data in the database. Lastly, we made an 'if check' to determine whether the database is empty or has data.

Before we read the items in our database, we must register a TypeAdapter. Hive supports all data types (Maps, List, etc.), and if we want to store any of these objects, we need to register a TypeAdapter, which will be used to convert the object from and to binary form.

On your Flutter project, head over to the model.dart file and update the code as seen below:

import 'package:hive/hive.dart';

part "model.g.dart";

@HiveType(typeId: 1)
class Data {
  @HiveField(0)
  String title;
  @HiveField(1)
  String description;

  Data({
    required this.title,
    required this.description,
  });
}
Enter fullscreen mode Exit fullscreen mode

In our code above, we imported the hive package; also, we annotated our Data class with the @HiveType and provide a type id. Our Data class extends the HiveObject. The part "model.g.dart"; will be generated automatically for us

On your terminal, run the command below:

>>> flutter packages pub run build_runner build
Enter fullscreen mode Exit fullscreen mode

This will generate a model.g.dart file in your Model folder.

To register our generated file, go to your main.dart file, and add the code below on your main() function:

import 'model/model.dart';
//....

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

    await Hive.initFlutter();

    Hive.registerAdapter(DataAdapter()); // Add this line

    await Hive.openBox('data_box');

    runApp(const MyApp());
}
Enter fullscreen mode Exit fullscreen mode

Note: If the file does not generate, on your terminal run the command below:

>>> flutter pub add hive_generator
Enter fullscreen mode Exit fullscreen mode

Then try running the previous command.

With all that completed, the read screen should look like this:

-

Step 3: Update / Editing Data

For us to be able to update data in our database, we need certain things:

  • First, a means to pass the data already stored in our database and read in our app.
  • Secondly, we need to get a reference to our already opened box.

First, let’s create constructors to get data passed from the ReadScreen to the UpdateScreen.

class UpdateScreen extends StatefulWidget {
  final int? index;
  final Data? data;
  final titleController;
  final descriptionController;

  const UpdateScreen({
    super.key,
    this.index,
    this.data,
    this.titleController,
    this.descriptionController
  });

  @override
  State<UpdateScreen> createState() => _UpdateScreenState();
}

Enter fullscreen mode Exit fullscreen mode

To pass data we need to make changes to our readscreen.dart file. On your read_screen.dart file, add the code below On the leading parameter of the ListTile widget:


leading: IconButton(
    onPressed: () {
        Navigator.push(
            context,
            MaterialPageRoute(
                builder: (context) => UpdateScreen(
                    index: index,
                    data: getData,
                    titleController: getData.title,
                    descriptionController: getData.description,
                ),
            ),
        );
    },
    icon: const Icon(Icons.edit),
),
Enter fullscreen mode Exit fullscreen mode

Here we passed certain parameters needed by our update screen: The index at which the data is stored, the title text, and the description text.

Back to our update screen, let’s reference our already opened box and add the text controllers needed.

late final Box dataBox;
late final TextEditingController titleController;
late final TextEditingController descriptionController;

@override
void initState() {
    super.initState();

    dataBox = Hive.box('data_box');
    titleController = TextEditingController(text: widget.titleController);
    descriptionController =TextEditingController(text: widget.descriptionController);
  }
Enter fullscreen mode Exit fullscreen mode

Next, we create our update function as such:

_updateData() {
    Data newData = Data(
      title: titleController.text,
      description: descriptionController.text,
    );
    dataBox.putAt(widget.index!, newData);
  }
Enter fullscreen mode Exit fullscreen mode

This will get the data from the read screen and display it on the text fields.

Lastly, we call the _updateData()function in our onPressed parameter

ElevatedButton(
    onPressed: () {
        _updateData();
        Navigator.push(
            context,
            MaterialPageRoute(
                builder: (context) => const ReadScreen(),
            ),
        );
    },
    child: const Text('UPDATE DATA'),
)
Enter fullscreen mode Exit fullscreen mode

The Update Screen should look like such:
-

Step 4: Deleting The Data

As we stated earlier, the delete function will be done on the ReadScreen.

We begin by creating a function to handle the delete functionality of our app.

  _deleteData(int index) {
    dataBox.deleteAt(index);
  }
Enter fullscreen mode Exit fullscreen mode

Lastly, we call the method on our delete icon:

trailing: IconButton(
    onPressed: () {
        _deleteData(index);
    },
    icon: const Icon(Icons.delete),
),
Enter fullscreen mode Exit fullscreen mode

With all that complete we have successfully added and implemented our CRUD operations.

-

< CTA/>

Adding Final Touches To Our Application

Here, we'll make minor changes to our main.dart UI to improve user experience.

On the main.dart file:

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


import 'screens/read_screen.dart';

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

  Hive.registerAdapter(DataAdapter());
  await Hive.openBox('data_box');

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: HomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(child: ReadScreen()),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Testing the App

With all that completed, we can now test our app.

On your terminal, run the command below:

>>> flutter run
Enter fullscreen mode Exit fullscreen mode

This will build and install the apk file on your emulator or device.

Conclusion & Resources

In this tutorial, we learned how to perform CRUD (Create, Read, Update, and Delete) operations using the Hive database.

Here is the link to the GitHub Source Code, and here's a video showing how the app works.

newsletter

Top comments (0)