Hive
A lightweight, blazing fast key-value database written in pure Dart — perfect for storing complex objects with ease.
Hive is a lightweight, blazing fast key-value database written in pure Dart. It's inspired by Bitcask and designed to be simple, powerful, and intuitive .
✅ When to Use Hive
- Storing complex objects (custom classes)
- Large datasets that need to be queried and cached
- Offline-first applications
- When you need encrypted storage
- When you need real-time UI updates with ValueListenableBuilder
SharedPreferences
- Simple key-value pairs
- Primitive types only
- Small data (under 1MB)
- No encryption
- Platform-specific
- Slower for large data
Hive
- Key-value with complex objects
- Any Dart object (with type adapters)
- Large datasets
- AES-256 encryption
- Pure Dart (cross-platform)
- Blazing fast
💡 Which One to Choose?
- Use SharedPreferences – for simple app settings, user preferences, and small data
- Use Hive – for complex data, large datasets, offline-first apps, and encrypted storage
To use Hive, you need to add the dependency and initialize it.
dependencies:
hive: ^2.2.3
hive_flutter: ^1.1.0
dev_dependencies:
hive_generator: ^2.0.1
build_runner: ^2.4.0
import
'package:flutter/material.dart'
;
import
'package:hive_flutter/hive_flutter.dart'
;
void
main
() async {
// Initialize Hive
await
Hive.initFlutter();
// Register adapters (for custom objects)
Hive.registerAdapter(PersonAdapter());
// Open a box
await
Hive.openBox<Person>(
'peopleBox'
);
runApp(
MyApp
());
}
Hive works like a map. Here's how to use it for basic operations.
import
'package:hive_flutter/hive_flutter.dart'
;
class
HiveService
{
// Get a box reference
static
Future
<Box>
getBox
() async {
return
await Hive.openBox(
'myBox'
);
}
// ----- WRITE DATA -----
static
Future
<
void
>
putData
(String key, dynamic value) async {
final
box = await getBox();
await
box.put(key, value);
}
static
Future
<
void
>
putMultiple
(Map<String, dynamic> data) async {
final
box = await getBox();
await
box.putAll(data);
}
static
Future
<
void
>
addToList
(dynamic value) async {
final
box = await getBox();
await
box.add(value);
}
// ----- READ DATA -----
static
Future
<dynamic>
getData
(String key) async {
final
box = await getBox();
return
box.get(key);
}
static
Future
<List<dynamic>>
getAllValues
() async {
final
box = await getBox();
return
box.values.toList();
}
static
Future
<Map<dynamic, dynamic>>
getAll
() async {
final
box = await getBox();
return
box.toMap();
}
// ----- DELETE DATA -----
static
Future
<
void
>
deleteData
(String key) async {
final
box = await getBox();
await
box.delete(key);
}
static
Future
<
void
>
clearBox
() async {
final
box = await getBox();
await
box.clear();
}
// ----- CHECK DATA -----
static
Future
<
bool
>
containsKey
(String key) async {
final
box = await getBox();
return
box.containsKey(key);
}
static
Future
<
int
>
getBoxSize
() async {
final
box = await getBox();
return
box.length;
}
}
Hive can store any Dart object using type adapters . You need to define a model class with annotations and generate the adapter.
import
'package:hive/hive.dart'
;
@HiveType
(typeId:
0
)
// 👈 Unique ID for this type
class
Person
extends
HiveObject {
@HiveField
(
0
)
final
String id;
@HiveField
(
1
)
final
String name;
@HiveField
(
2
)
final
int
age;
@HiveField
(
3
)
final
String? email;
Person
({
required
this
.id,
required
this
.name,
required
this
.age,
this
.email,
});
}
# Run this command to generate the adapter
flutter packages pub run build_runner build
# Or watch for changes
flutter packages pub run build_runner watch
import
'package:hive_flutter/hive_flutter.dart'
;
import
'models/person.dart'
;
import
'models/person.g.dart'
;
// Generated file
void
main
() async {
await
Hive.initFlutter();
// Register the adapter
Hive.registerAdapter(PersonAdapter());
// Open a box with type
await
Hive.openBox<Person>(
'peopleBox'
);
runApp(
MyApp
());
}
import
'package:hive_flutter/hive_flutter.dart'
;
import
'models/person.dart'
;
class
PersonService
{
static
Future
<Box<Person>>
getBox
() async {
return
await Hive.openBox<Person>(
'peopleBox'
);
}
// Add a person
static
Future
<
void
>
addPerson
(Person person) async {
final
box = await getBox();
await
box.put(person.id, person);
}
// Get a person
static
Future
<Person?>
getPerson
(String id) async {
final
box = await getBox();
return
box.get(id);
}
// Get all people
static
Future
<List<Person>>
getAllPeople
() async {
final
box = await getBox();
return
box.values.toList();
}
// Delete a person
static
Future
<
void
>
deletePerson
(String id) async {
final
box = await getBox();
await
box.delete(id);
}
// Update a person
static
Future
<
void
>
updatePerson
(Person person) async {
final
box = await getBox();
await
box.put(person.id, person);
}
}
Hive integrates beautifully with Flutter through
ValueListenableBuilder
,
which rebuilds the UI when the data changes.
class
PeopleListScreen
extends
StatefulWidget {
@override
State<PeopleListScreen>
createState
() => _PeopleListScreenState();
}
class
_PeopleListScreenState
extends
State<PeopleListScreen> {
late
Box<Person> _box;
final
TextEditingController _nameController = TextEditingController();
final
TextEditingController _ageController = TextEditingController();
@override
void
initState
() {
super
.initState();
_openBox();
}
Future
<
void
>
_openBox
() async {
_box = await Hive.openBox<Person>(
'peopleBox'
);
setState(() {});
}
void
_addPerson
() {
final
name = _nameController.text.trim();
final
age = int.tryParse(_ageController.text.trim());
if
(name.isNotEmpty && age !=
null
) {
final
person = Person(
id: DateTime.now().millisecondsSinceEpoch.toString(),
name: name,
age: age,
);
_box.put(person.id, person);
_nameController.clear();
_ageController.clear();
}
}
void
_deletePerson
(String id) {
_box.delete(id);
}
@override
Widget
build
(BuildContext context) {
return
Scaffold(
appBar: AppBar(
title: Text(
'👥 People'
),
centerTitle:
true
,
),
body: Column(
children: [
// Input fields
Padding(
padding: EdgeInsets.all(
16
),
child: Row(
children: [
Expanded(
flex:
3
,
child: TextField(
controller: _nameController,
decoration: InputDecoration(
hintText:
'Name'
,
border: OutlineInputBorder(),
),
),
),
SizedBox(width:
8
),
Expanded(
flex:
2
,
child: TextField(
controller: _ageController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText:
'Age'
,
border: OutlineInputBorder(),
),
),
),
SizedBox(width:
8
),
Expanded(
child: ElevatedButton(
onPressed: _addPerson,
child: Icon(Icons.add),
),
),
],
),
),
// List with real-time updates
Expanded(
child: _box ==
null
? Center(child: CircularProgressIndicator())
: ValueListenableBuilder(
valueListenable: _box.listenable(),
builder: (context, Box<Person> box, _) {
final
people = box.values.toList();
if
(people.isEmpty) {
return
Center(
child: Text(
'No people yet. Add one!'
),
);
}
return
ListView.builder(
itemCount: people.length,
itemBuilder: (context, index) {
final
person = people[index];
return
ListTile(
leading: CircleAvatar(
child: Text(person.name[
0
].toUpperCase()),
),
title: Text(person.name),
subtitle: Text(
'Age: ${person.age}'
),
trailing: IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () => _deletePerson(person.id),
),
);
},
);
},
),
),
],
),
);
}
}
Here's a complete to-do app using Hive for persistent storage with real-time UI updates.
import
'package:hive/hive.dart'
;
@HiveType
(typeId:
1
)
class
Todo
extends
HiveObject {
@HiveField
(
0
)
final
String id;
@HiveField
(
1
)
final
String title;
@HiveField
(
2
)
final
String? description;
@HiveField
(
3
)
final
bool
isCompleted;
@HiveField
(
4
)
final
DateTime createdAt;
Todo
({
required
this
.id,
required
this
.title,
this
.description,
this
.isCompleted =
false
,
DateTime
? createdAt,
}) :
this
.createdAt = createdAt ?? DateTime.now();
Todo
copyWith
({
String? id,
String? title,
String? description,
bool? isCompleted,
DateTime? createdAt,
}) {
return
Todo(
id: id ??
this
.id,
title: title ??
this
.title,
description: description ??
this
.description,
isCompleted: isCompleted ??
this
.isCompleted,
createdAt: createdAt ??
this
.createdAt,
);
}
}
class
TodoScreen
extends
StatefulWidget {
@override
State<TodoScreen>
createState
() => _TodoScreenState();
}
class
_TodoScreenState
extends
State<TodoScreen> {
late
Box<Todo> _todoBox;
final
TextEditingController _titleController = TextEditingController();
final
TextEditingController _descController = TextEditingController();
@override
void
initState
() {
super
.initState();
_openBox();
}
Future
<
void
>
_openBox
() async {
_todoBox = await Hive.openBox<Todo>(
'todoBox'
);
setState(() {});
}
void
_addTodo
() {
final
title = _titleController.text.trim();
if
(title.isEmpty)
return
;
final
todo = Todo(
id: DateTime.now().millisecondsSinceEpoch.toString(),
title: title,
description: _descController.text.trim(),
);
_todoBox.put(todo.id, todo);
_titleController.clear();
_descController.clear();
}
void
_toggleTodo
(String id) {
final
todo = _todoBox.get(id);
if
(todo !=
null
) {
final
updated = todo.copyWith(isCompleted: !todo.isCompleted);
_todoBox.put(id, updated);
}
}
void
_deleteTodo
(String id) {
_todoBox.delete(id);
}
void
_showAddDialog
() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(
'Add Todo'
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: _titleController,
decoration: InputDecoration(
hintText:
'Title...'
,
border: OutlineInputBorder(),
),
),
SizedBox(height:
8
),
TextField(
controller: _descController,
decoration: InputDecoration(
hintText:
'Description (optional)'
,
border: OutlineInputBorder(),
),
maxLines:
3
,
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(
'Cancel'
),
),
ElevatedButton(
onPressed: () {
_addTodo();
Navigator.pop(context);
},
child: Text(
'Add'
),
),
],
),
);
}
@override
Widget
build
(BuildContext context) {
return
Scaffold(
appBar: AppBar(
title: Text(
'✅ Todo List'
),
centerTitle:
true
,
),
body: _todoBox ==
null
? Center(child: CircularProgressIndicator())
: ValueListenableBuilder(
valueListenable: _todoBox.listenable(),
builder: (context, Box<Todo> box, _) {
final
todos = box.values.toList();
if
(todos.isEmpty) {
return
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.checklist, size:
80
, color: Colors.grey.shade400),
SizedBox(height:
16
),
Text(
'No todos yet!'
,
style: TextStyle(fontSize:
20
, fontWeight: FontWeight.w600),
),
Text(
'Tap the + button to add one'
,
style: TextStyle(color: Colors.grey.shade400),
),
],
),
);
}
return
ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
final
todo = todos[index];
return
Card(
margin: EdgeInsets.symmetric(horizontal:
16
, vertical:
4
),
child: ListTile(
leading: Checkbox(
value: todo.isCompleted,
onChanged: (_) => _toggleTodo(todo.id),
),
title: Text(
todo.title,
style: TextStyle(
decoration: todo.isCompleted
? TextDecoration.lineThrough
:
null
,
color: todo.isCompleted
? Colors.grey.shade600
:
null
,
),
),
subtitle: todo.description !=
null
? Text(
todo.description!,
style: TextStyle(
decoration: todo.isCompleted
? TextDecoration.lineThrough
:
null
,
color: todo.isCompleted
? Colors.grey.shade500
:
null
,
),
)
:
null
,
trailing: IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () => _deleteTodo(todo.id),
),
),
);
},
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: _showAddDialog,
child: Icon(Icons.add),
),
);
}
}
import
'package:flutter/material.dart'
;
import
'package:hive_flutter/hive_flutter.dart'
;
import
'models/todo.dart'
;
import
'models/todo.g.dart'
;
import
'screens/todo_screen.dart'
;
void
main
() async {
await
Hive.initFlutter();
// Register adapters
Hive.registerAdapter(TodoAdapter());
// Open box
await
Hive.openBox<Todo>(
'todoBox'
);
runApp(
MyApp
());
}
class
MyApp
extends
StatelessWidget {
@override
Widget
build
(BuildContext context) {
return
MaterialApp(
title:
'Hive Todo'
,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3:
true
,
),
home: TodoScreen(),
);
}
}
hive
,
hive_flutter
, and dev dependencies for code generation.
@HiveType
and
@HiveField
annotations.
build_runner
to generate the
.g.dart
file.
Hive.initFlutter()
and register the adapter.
Hive.openBox<T>('boxName')
to open a typed box.
ValueListenableBuilder
for real-time UI updates when data changes.
✅ Do's
- Use type adapters for all custom objects
- Use ValueListenableBuilder for real-time UI updates
- Use HiveObject for objects that need save/delete methods
- Group related data in different boxes for better organization
- Use encryption for sensitive data
-
Call
box.compact()periodically to reduce file size
❌ Don'ts
- Don't forget to register adapters before opening boxes
-
Don't change
typeIdafter data is stored - Don't store excessively large objects (consider SQLite)
- Don't use Hive for complex queries (use SQLite)
If you don't register the adapter, Hive won't know how to serialize/deserialize your custom objects.
Hive.registerAdapter(MyObjectAdapter());
If you change a
typeId
, Hive won't be able to read previously stored data.
Once you assign a
typeId
, never change it. If you need to add new fields, just add them.
Without
ValueListenableBuilder
, the UI won't update when the data changes.
ValueListenableBuilder(valueListenable: box.listenable(), builder: ...)
🎯 Key Takeaway
Hive is a powerful, fast, and easy-to-use NoSQL database for Flutter. It's perfect for storing complex objects with real-time UI updates. The type adapter system makes it type-safe, and ValueListenableBuilder provides automatic UI updates. Use Hive for offline-first apps and large datasets.