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:

Key Benefits:

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:

  1. Add a category field to each todo (Work, Personal, etc.)
  2. Implement filtering by category
  3. Add a due date field and sort todos by date
  4. Create a “Clear Completed” button

Challenge: Build a notes app with Hive that supports:

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!