Flutter:Hive With Api
Tags: flutter, dart, offline, hive
Published: 2024-05-17 06:11:27

## Introduction

- Hive is a lightweight and fast **NoSQL** database that can be used to store data in Flutter applications. 
- In this blog, we will explore how to fetch data from APi, store and use it in Hive in Flutter. 
- This Blog includes defining Hive type adapters, storing and reading from Hive. 
- By using Hive in our Flutter applications, we can easily store and retrieve data without the overhead of traditional SQL databases.
- It is mostly used for **offline** support for the application.

### Boxes
Hive uses Boxes to store data. All data in Hive is organized into boxes. A box is similar to a table in SQL, but it lacks structure and can contain any data.We can store an integer, string, list of strings, Boolean, double, etc. in Hive.

To manipulate the Box, we have to open it first.

#### OpenBox
Open a Hive box in your app to store your data:

```dart
var box = await Hive.openBox<Data>('dataBox');
```

The **<Data>** depicts that the box belongs to a specific type.
#### Read Box
To read an already opened box:
```dart
var box = Hive.box('dataBox');
```
#### Close Box
If you don't need a box again, close it. All the data in the box will be removed from memory, and the box file will be closed once all active operations have completed.

```dart
var box = await Hive.openBox('dataBox');
await box.put('hello 'world');
await box.close();
```


#### Operations
The Box has the below operations which is used mostly:
##### Read
```dart
var box = Hive.box('dataBox');
String name = box.get('name');
```
##### Write
```dart
var box = Hive.box('dataBox');
box.put('name', 'Paul');
```
##### Delete
```dart
var box = await Hive.openBox('dataBox');
await box.put('key1', 'value1');
await box.delete('key1');
```
##### Clear
```dart
var box = await Hive.openBox('dataBox');
await box.clear();
```

![Hive box](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t0u1pmbzniq2gcphsl2m.jpg)

## TypeAdapters:
If you want to store objects, you must register a **TypeAdapter** that converts them both to and from binary form.

We can either write or generate a TypeAdapter. Most of the time, the generated adapter works exceptionally well. A manually written adapter can sometimes be used to improve minor details.

### Generate Adapter
The hive_generator package can generate TypeAdapters for nearly any class.

- To generate a type adapter for a class, give it with @HiveType and provide a typeId (between 0 and 223)
- All fields that should be saved with @HiveField.

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

part 'data.g.dart';

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

  @HiveField(1)
  String id;

  Data({required this.name, required this.id});
}
```

- After this, open terminal and run the command `dart run build_runner build`
- To use this type adapter, first generate the adapter.

Let’s start the application.

## Simple Data List
Let’s first add the dependencies:
**_`pubspec.yaml`_**

```dart
dependencies:
  flutter:
    sdk: flutter
  hive: ^2.2.3
  hive_flutter: ^1.1.0
  http: ^0.13.6

dev_dependencies:
  flutter_test:
    sdk: flutter
  hive_generator: ^2.0.1
  build_runner: ^2.4.9

```
Once the dependencies are added, run **_`flutter pub.get`_**
Then add the **import** statements.

```dart
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
```
**_`data.dart`_**

- Generate the needed adapter.
- Note that the name used for the file should be used in statement which will generate the adapter

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

part 'data.g.dart';  // Statement which will generate the adapter

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

  @HiveField(1)
  String id;

  Data({required this.name, required this.id});
}

```

- Now run the command `dart run build_runner build`.
- This will create the required TypeAdapter.
As we generate the TypeAdapter, let’s register the adapter in the **_`main.dart`_**.

```dart
import 'data.dart';

void main() async {
  await Hive.initFlutter();
  Hive.registerAdapter(DataAdapter());
  runApp(MyApp());
}

```
**_`api.dart`_**
Once the adapter has been registered, let’s create a simple get request from the mock Api endpoint.

If you want to create a mockApi, please visit {% embed https://mockapi.io/ %}

```dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:hive/hive.dart';

import 'data.dart';

class ApiService {
  static const String _baseUrl = 'https://66443e296c6a656587099b9d.mockapi.io/person/data1'; // change it with your GET api

  static Future<void> fetchDataAndStoreInHive() async {
    final response = await http.get(Uri.parse('$_baseUrl'));
    if (response.statusCode == 200) {
      final List<dynamic> responseData = jsonDecode(response.body);
      print(responseData);
      final box = await Hive.openBox('myData');
      await box.clear();
      responseData.forEach((data) {
        box.add(Data(id: data['id'], name: data['name']));
      });
    } else {
      throw Exception('Failed to load data');
    }
  }
}

```

- Once the response has been parsed using jsonDecode, it will be stored as responseData.
- Then we open the Hive box and clear it for if  any existing data is available.
- Then we add the data from response data into the box with keys as “id” and “name”.

**_`main.dart`_**
Once the data has been stored in the box, we use it in the main.dart to list the data in the box.

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

import 'api.dart';
import 'data.dart';

void main() async {
  await Hive.initFlutter();
  Hive.registerAdapter(DataAdapter());
  runApp(MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Data app',
      theme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSwatch(accentColor: Colors.blue),
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Hive with API”),
      ),
      body: FutureBuilder(
        future: Hive.openBox('myData'),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            if (snapshot.hasError) {
              return Center(
                child: Text('Error'),
              );
            } else {
              final myDataBox = Hive.box('myData');
              return ListView.builder(
                itemCount: myDataBox.length,
                itemBuilder: (context, index) {
                  final myData = myDataBox.getAt(index) as Data;
                  return Card(
                    child: ListTile(
                      title: Text(myData.name),
                      subtitle: Text('ID: ${myData.id}'),
                    ),
                  );
                },
              );
            }
          } else {
            return Center(
              child: CircularProgressIndicator(),
            );
          }
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          await ApiService.fetchDataAndStoreInHive();
        },
        child: Icon(Icons.refresh),
      ),
    );
  }
}
```


- Here we open the box and assign it to the future of the futureBuilder.
- Then we get the data along with its index and display the name and id of each individual object stored.
- For additional measures, if the data changes overtime we can use the floating action button to get the data from the api and overwrite the existing data.
- Now even if you stop the server or offline the data will still be available as the data is still stored in the local storage


**_Hope this has helped!_**