Hive CE is a fast, lightweight key-value database that runs on the device. Use it for user preferences, cached data, offline storage, or anything that needs to persist between app restarts.

This guide uses hive_ce (Community Edition). If you are coming from the original Hive package, the API is the same.

Setup

yaml
dependencies:
  hive_ce: ^2.6.0
  hive_ce_flutter: ^2.1.0

dev_dependencies:
  hive_ce_generator: ^1.6.0
  build_runner: ^2.4.0

Initialize Hive before your app runs:

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

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

Boxes

A box is like a table. You open it by name, then read and write key-value pairs. Boxes can store strings, ints, doubles, bools, lists, maps, and custom objects.

dart
// Open a box (creates it if it doesn't exist)
final box = await Hive.openBox('settings');

// Open with a specific type
final box = await Hive.openBox<String>('names');

// Access an already-opened box (no await needed)
final box = Hive.box('settings');

CRUD Operations

Write

dart
final box = Hive.box('settings');

// Write a single value
box.put('username', 'Mitch');
box.put('darkMode', true);
box.put('fontSize', 16.0);

// Write multiple values at once
box.putAll({
  'username': 'Mitch',
  'darkMode': true,
});

Read

dart
final box = Hive.box('settings');

// Read a value (returns null if not found)
final username = box.get('username');

// Read with a default value
final darkMode = box.get('darkMode', defaultValue: false);

Delete

dart
final box = Hive.box('settings');

// Delete a single key
box.delete('username');

// Delete multiple keys
box.deleteAll(['username', 'darkMode']);

// Clear everything in the box
box.clear();

Type Adapters

To store custom Dart objects, you need a type adapter. Annotate your class with @HiveType and each field with @HiveField, then run code generation.

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

part 'user.g.dart';

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

  @HiveField(1)
  final int age;

  @HiveField(2)
  final String email;

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

Run the generator:

bash
dart run build_runner build

Register the adapter and open the box:

dart
void main() async {
  await Hive.initFlutter();
  Hive.registerAdapter(UserAdapter());
  await Hive.openBox<User>('users');
  runApp(MyApp());
}

Now you can store and retrieve User objects:

dart
final box = Hive.box<User>('users');

// Store
box.put('user1', User(name: 'Mitch', age: 25, email: 'mitch@example.com'));

// Retrieve
final user = box.get('user1');
print(user?.name); // Mitch

// Get all values
final allUsers = box.values.toList();

The typeId and @HiveField numbers must be unique and should never change once your app ships. You can add new fields with new numbers, but never reuse or change existing ones.

Listening to Changes

Watch a box for changes. Useful for updating UI when background operations modify data.

dart
final box = Hive.box('settings');

// Watch a specific key
box.watch(key: 'darkMode').listen((event) {
  print('darkMode changed to: ${event.value}');
  print('was deleted: ${event.deleted}');
});

// Watch all changes
box.watch().listen((event) {
  print('${event.key} changed');
});

Practical Pattern

A common approach: wrap Hive in a small service class so the rest of your app never touches Hive directly.

dart
class SettingsService {
  static const _boxName = 'settings';

  Box get _box => Hive.box(_boxName);

  bool get darkMode => _box.get('darkMode', defaultValue: false);
  set darkMode(bool value) => _box.put('darkMode', value);

  String get username => _box.get('username', defaultValue: '');
  set username(String value) => _box.put('username', value);

  double get fontSize => _box.get('fontSize', defaultValue: 14.0);
  set fontSize(double value) => _box.put('fontSize', value);

  void clear() => _box.clear();
}