Local Data Storage with Hive in Flutter
Local Data Storage with Hive in Flutter
What You’ll Learn
Master Hive, a lightweight and blazing-fast local database for Flutter. Unlike SQLite, Hive is pure Dart, requires no native dependencies, and is perfect for storing structured data, user preferences, or offline caching.
Why Choose Hive?
Hive vs Other Storage Options:
- vs SharedPreferences: Hive handles complex objects, not just strings
- vs SQLite: No SQL syntax, faster, pure Dart implementation
- vs JSON files: Automatic serialization, type-safe queries
Key Benefits:
- Lightning fast (optimized for mobile)
- Works on all platforms (mobile, web, desktop)
- No code generation required for simple use cases
- Encrypted boxes for sensitive data
- Reactive with Stream support
Add Hive to your pubspec.yaml:
dependencies:
hive: ^2.2.3
hive_flutter: ^1.1.0
dev_dependencies:
hive_generator: ^2.0.1
build_runner: ^2.4.6
Example: Todo App with Hive
Let’s build a simple todo app with persistent storage:
import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
// 1. Define your data model
@HiveType(typeId: 0)
class Todo {
@HiveField(0)
final String title;
@HiveField(1)
final bool isCompleted;
@HiveField(2)
final DateTime createdAt;
Todo({
required this.title,
this.isCompleted = false,
DateTime? createdAt,
}) : createdAt = createdAt ?? DateTime.now();
// Helper method to create a copy with changes
Todo copyWith({String? title, bool? isCompleted}) {
return Todo(
title: title ?? this.title,
isCompleted: isCompleted ?? this.isCompleted,
createdAt: createdAt,
);
}
}
// 2. Initialize Hive in main()
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Hive
await Hive.initFlutter();
// Register adapter (after running build_runner)
// Hive.registerAdapter(TodoAdapter());
// Open a box (like a table in SQL)
await Hive.openBox<Todo>('todos');
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hive Todo',
home: TodoScreen(),
);
}
}
// 3. Use the box in your widget
class TodoScreen extends StatefulWidget {
@override
State<TodoScreen> createState() => _TodoScreenState();
}
class _TodoScreenState extends State<TodoScreen> {
late Box<Todo> _todoBox;
final _textController = TextEditingController();
@override
void initState() {
super.initState();
_todoBox = Hive.box<Todo>('todos');
}
@override
void dispose() {
_textController.dispose();
super.dispose();
}
void _addTodo() {
if (_textController.text.isNotEmpty) {
final todo = Todo(title: _textController.text);
_todoBox.add(todo); // Automatically saves to disk
_textController.clear();
}
}
void _toggleTodo(int index, Todo todo) {
_todoBox.putAt(
index,
todo.copyWith(isCompleted: !todo.isCompleted),
);
}
void _deleteTodo(int index) {
_todoBox.deleteAt(index);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Hive Todo App')),
body: Column(
children: [
// Input field
Padding(
padding: EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: TextField(
controller: _textController,
decoration: InputDecoration(
hintText: 'Enter a todo',
border: OutlineInputBorder(),
),
onSubmitted: (_) => _addTodo(),
),
),
SizedBox(width: 8),
IconButton(
icon: Icon(Icons.add),
onPressed: _addTodo,
),
],
),
),
// Todo list with ValueListenableBuilder for reactive updates
Expanded(
child: ValueListenableBuilder(
valueListenable: _todoBox.listenable(),
builder: (context, Box<Todo> box, _) {
if (box.isEmpty) {
return Center(
child: Text('No todos yet. Add one above!'),
);
}
return ListView.builder(
itemCount: box.length,
itemBuilder: (context, index) {
final todo = box.getAt(index)!;
return ListTile(
leading: Checkbox(
value: todo.isCompleted,
onChanged: (_) => _toggleTodo(index, todo),
),
title: Text(
todo.title,
style: TextStyle(
decoration: todo.isCompleted
? TextDecoration.lineThrough
: null,
),
),
trailing: IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () => _deleteTodo(index),
),
);
},
);
},
),
),
],
),
);
}
}
Hive Operations Cheat Sheet
final box = Hive.box<Todo>('todos');
// CREATE
box.add(todo); // Auto-generated key
box.put('key1', todo); // Custom key
// READ
box.get('key1'); // Get by key
box.getAt(0); // Get by index
box.values; // All values
box.keys; // All keys
box.length; // Count
// UPDATE
box.put('key1', newTodo); // Replace by key
box.putAt(0, newTodo); // Replace by index
// DELETE
box.delete('key1'); // Delete by key
box.deleteAt(0); // Delete by index
box.clear(); // Delete everything
// QUERY
box.values.where((t) => t.isCompleted); // Filter
Without Code Generation (Simple Version)
For quick prototypes, skip the adapter generation:
void main() async {
await Hive.initFlutter();
await Hive.openBox('settings');
runApp(MyApp());
}
// Store simple data
final box = Hive.box('settings');
box.put('username', 'Alice');
box.put('darkMode', true);
box.put('lastLogin', DateTime.now().toIso8601String());
// Retrieve data
String? username = box.get('username');
bool darkMode = box.get('darkMode', defaultValue: false);
This works great for preferences and simple key-value storage!
Try It Yourself
Enhance the todo app with these features:
- Add a category field to each todo (Work, Personal, etc.)
- Implement filtering by category
- Add a due date field and sort todos by date
- Create a “Clear Completed” button
Challenge: Build a notes app with Hive that supports:
- Rich text notes with title and body
- Tags for organization
- Search functionality across all notes
- Export notes to JSON
Tip of the Day
Performance: Hive is incredibly fast, but keep boxes organized. Use separate boxes for different data types (e.g., users, settings, cache). This keeps queries efficient and makes data management cleaner.
Encryption: Protect sensitive data with encrypted boxes:
final key = Hive.generateSecureKey();
await Hive.openBox('secure', encryptionCipher: HiveAesCipher(key));
Store the encryption key securely using flutter_secure_storage package!