Perform CRUD operations in Flutter with Hive
Tags: flutter, hive, mobile
Published: 2023-02-28 00:52:08


by [Emmanuel Uchechukwu](https://blog.openreplay.com/authors/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](https://docs.hivedb.dev/#/).

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.



<h2>Session Replay for Developers</h2> 
<p><em>
Uncover frustrations, understand bugs and fix slowdowns like never before with <strong><a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a></strong> — an open-source session replay suite for developers. It can be <strong>self-hosted</strong> in minutes, giving you complete control over your customer data
</em></p>
<img alt="OpenReplay" width="768" height="400" src="https://blog.openreplay.com/assets/overview_Z17Qdyg.png" class="astro-UXNKDZ4E" loading="lazy" decoding="async">
<em>Happy debugging! <a href="https://openreplay.com" target="_blank">Try using OpenReplay today.</a></em>




## Setting Up Flutter Project
To start building our app, click [here to clone the starter files](https://github.com/UcFlutter/CRUD-APP-WITH-FLUTTER-AND-HIVE). 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 .
```
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.


![-](https://blog.openreplay.com/images/perform-crud-operations-in-flutter-with-hive/images/tEOm1I1.png)



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
```
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.

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


void main() async {
  WidgetsFlutterBinding.ensureInitialized()
  await Hive.initFlutter();
  await Hive.openBox('data_box');
  
  runApp(const MyApp());
}
```
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:
```dart
class Data {
  String title;
  String description;

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

```


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:

```dart
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');
  }
```
Next, we write and implement our create method.

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

    dataBox.add(newData);
  }
```
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:

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

### 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`
```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),
                  ),
                );
              },
            );
          }
        },
      ),
    );
  }
}

```

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:
```dart
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,
  });
}
```
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
```



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:
```dart
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());
}
```

`Note`: If the file does not generate, on your terminal run the command below:
```
>>> flutter pub add hive_generator
```
Then try running the previous command.

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

![-](https://blog.openreplay.com/images/perform-crud-operations-in-flutter-with-hive/images/90Q8VD7.gif)

### 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`.

```dart
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();
}

```

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:

```dart

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),
),
```
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.

```dart
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);
  }
```

Next, we create our update function as such:
```dart
_updateData() {
    Data newData = Data(
      title: titleController.text,
      description: descriptionController.text,
    );
    dataBox.putAt(widget.index!, newData);
  }
```

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

```dart
ElevatedButton(
    onPressed: () {
        _updateData();
        Navigator.push(
            context,
            MaterialPageRoute(
                builder: (context) => const ReadScreen(),
            ),
        );
    },
    child: const Text('UPDATE DATA'),
)
```
The Update Screen should look like such:
![-](https://blog.openreplay.com/images/perform-crud-operations-in-flutter-with-hive/images/owO8ePg.gif)


### 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.

```dart
  _deleteData(int index) {
    dataBox.deleteAt(index);
  }
```

Lastly, we call the method on our delete icon:

```dart
trailing: IconButton(
    onPressed: () {
        _deleteData(index);
    },
    icon: const Icon(Icons.delete),
),
```

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

![-](https://blog.openreplay.com/images/perform-crud-operations-in-flutter-with-hive/images/DXnpfy5.gif)


< 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:
```dart
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()),
    );
  }
}
```

## Testing the App
With all that completed, we can now test our app. 

On your terminal, run the command below:
```
>>> flutter run
```
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](https://github.com/UcFlutter/CRUD-APP-WITH-FLUTTER-AND-HIVE-COMPLETE), and here's a video showing [how the app works](https://blog.openreplay.com/images/perform-crud-operations-in-flutter-with-hive/images/video.mp4).





[![newsletter](https://blog.openreplay.com/newsletter_Z1P0UIP.png)](https://newsletter.openreplay.com/)