Modtion logo
Modtion
flutter

Flutter : Store your Local Data with Hive

Flutter : Store your Local Data with Hive

Flutter Hive

Local Storage is vital for mobile app development. Nowadays, many super apps use local storage to implement Offline-first App. So user still can open the app, watch and check their latest data even when their device on offline condition. Local storage have many benefits:

Hive is one of many Library and Package to store local data for flutter that lightweight and fast NoSQL database. Hive works based on key-value pairs, similar to dictionaries. You define a unique key for each piece of data, and Hive associates it with the actual value. Hive is known for its speedy performance and uses binary storage, making it ideal for frequently accessed data and highly efficient.

Hive Dependency

pubspec.yaml

dependencies:
  hive: ^2.2.3
  hive_flutter: ^1.1.0

dev_dependencies:
  build_runner: ^2.4.14
  hive_generator: ^2.0.1

However, since the Hive package has not seen updates in over two years, you can also use the community edition (Hive CE).

dependencies:
  hive_ce: ^2.9.0
  hive_ce_flutter: ^2.2.0

dev_dependencies:
  build_runner: ^2.4.14
  hive_ce_generator: ^1.8.2

Initialize

lib/main.dart

import 'package:hive_flutter/hive_flutter.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // initialize Hive for flutter
  await Hive.initFlutter();
  runApp(const MyApp());
}

Entity

Let's create some class for entity or model.

lib/entity/user.dart

import 'package:hive/hive.dart';

part 'user.g.dart';

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

  @HiveField(1)
  final int age;

  @HiveField(2)
  final DateTime createdAt;

  User({required this.name, required this.age, required this.createdAt});
}

Type Adapter

If your data model includes a non-supported type like a custom class, you need to define a Type Adapter. use this code below in terminal to run build generator and generate TypeAdapter for the entity.

dart run build_runner build --delete-conflicting-outputs

Register the builded Type adapter to lib/main.dart.

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

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

  // register the type adapter
  Hive.registerAdapter(UserAdapter());

  runApp(const MyApp());
}

Hive Box

Create Box of Hive for each of Entity that you created. You can set the Box inside lib/main.dart, Screen Class or your Data package if you use Clean Architecture. for Example in lib/main.dart

void main() async {
  ...
  var boxUser = await Hive.openBox<User>('userBox');
  runApp(const MyApp());
}

Closing Hive Box When your app is closing or no longer needs the Hive box.

await boxUser.close();

CRUD Operation

boxUser.add(User(name: "Modtion", age: 28, createdAt: DateTime.now()));

boxUser.addAll(
    [
    User(name: "Modtion", age: 28, createdAt: DateTime.now()),
    User(name: "Modtion", age: 28, createdAt: DateTime.now()),
    ]);
var data = boxUser.getAt(0); // index
var list = boxUser.values.toList() as List<User>;
boxUser.putAt(0, User(name: "Modtion", age: 28, createdAt: DateTime.now())); // index
boxUser.deleteAt(0); // index
boxUser.deleteAll();
boxUser.clear();

Example

I'll show you some simple code to use basic of Hive in flutter.

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

import 'boxes.dart';
import 'entity/user.dart';

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

  boxUser = await Hive.openBox<User>('userBox');
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  void addUser() {
    boxUser.add(User(name: "Modtion", age: 28, createdAt: DateTime.now()));
    setState(() {});
  }

  void deleteUser(int index) {
    boxUser.deleteAt(index);
    setState(() {});
  }

  void clearUser() {
    boxUser.clear();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView.builder(
        itemCount: boxUser.length,
        itemBuilder: (context, index) {
          User task = boxUser.getAt(index);
          return ListTile(
            title: Text(task.name),
            subtitle: Text(task.createdAt.toString()),
            trailing: Text("age: ${task.age}"),
            leading: IconButton(
                onPressed: () {
                  deleteUser(index);
                },
                icon: const Icon(Icons.remove)),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: addUser,
        child: const Icon(Icons.add),
      ),
    );
  }
}

Example 2

If you want to setup all hive method in a class file like HiveService, you can do like this.

import 'package:hive_flutter/hive_flutter.dart';

import 'entity/user.dart';

class HiveService {
  final String _boxName = 'userBox';

  Future<Box> get _box async => await Hive.openBox<User>(_boxName);

  Future<void> create(User user) async {
    var box = await _box;
    box.add(user);
  }

  Future<List<User>> read() async {
    var box = await _box;
    return box.values.toList() as List<User>;
  }

  Future<void> update(int index, User user) async {
    var box = await _box;
    box.put(index, user);
  }

  Future<void> delete(int index) async {
    var box = await _box;
    box.deleteAt(index);
  }

  Future<void> clear() async {
    var box = await _box;
    box.clear();
  }
}