Hive
Hive is "a lightweight and blazing fast key-value database for Dart" developed as a NoSQL database solution for local data storage in Flutter applications. Written in pure Dart, it works on Flutter, Dart VM, and Dart2JS with no external dependencies. Compared to traditional SQLite or CoreData, it offers significantly faster performance while maintaining type safety and simple API design. With adapter functionality for persisting arbitrary Dart objects and standard features including encryption, compression, and backup capabilities, it serves as a primary local storage solution for modern Flutter application development.
GitHub Overview
isar/hive
Lightweight and blazing fast key-value database written in pure Dart.
Topics
Star History
Library
Hive
Overview
Hive is "a lightweight and blazing fast key-value database for Dart" developed as a NoSQL database solution for local data storage in Flutter applications. Written in pure Dart, it works on Flutter, Dart VM, and Dart2JS with no external dependencies. Compared to traditional SQLite or CoreData, it offers significantly faster performance while maintaining type safety and simple API design. With adapter functionality for persisting arbitrary Dart objects and standard features including encryption, compression, and backup capabilities, it serves as a primary local storage solution for modern Flutter application development.
Details
Hive 2025 edition fully supports Dart 3.0 and Flutter 3.16+, maximizing the benefits of null safety as a high-performance database engine. Through lazy loading, reactive state management via watch functionality, and smart caching, it provides performance optimized for mobile applications. JSON format data storage ensures human readability, and custom adapters enable efficient storage of complex object structures. With AES encryption support, compact disk usage, and high-speed read/write performance, it has become the standard choice for offline-first Flutter application development.
Key Features
- High Performance: Up to 10x faster read/write performance than SQLite
- Pure Dart Implementation: No external dependencies, full cross-platform support
- Type Safety: Strong type system integration through TypeAdapters
- Reactive: Stream-based data change monitoring functionality
- Encryption Support: Secure data storage with AES-256 encryption
- Lightweight Design: Minimal memory usage and disk footprint
Pros and Cons
Pros
- Significantly faster than SQLite with excellent memory efficiency
- Very simple setup with low learning curve
- Perfect integration with Flutter/Dart ecosystem
- High affinity with reactive programming
- Standard encryption and security features
- Optimal for offline support and local-first architecture
Cons
- Not suitable for complex relationships like relational databases
- Limitations with large datasets and complex queries
- Basic transaction functionality with limited ACID properties
- Difficult to share data between multiple applications
- Backup/restore functionality requires manual implementation
- No SQL-like query language provided
Reference Pages
Code Examples
Setup
# pubspec.yaml
dependencies:
flutter:
sdk: flutter
hive: ^2.2.3
hive_flutter: ^1.1.0
dev_dependencies:
hive_generator: ^2.0.1
build_runner: ^2.4.9
// main.dart - Initialization
import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
void main() async {
// Initialize Hive
await Hive.initFlutter();
// Register custom adapters
Hive.registerAdapter(PersonAdapter());
Hive.registerAdapter(AddressAdapter());
runApp(MyApp());
}
Basic Usage
// Model class definition
import 'package:hive/hive.dart';
part 'person.g.dart'; // Generated file
@HiveType(typeId: 0)
class Person extends HiveObject {
@HiveField(0)
String name;
@HiveField(1)
int age;
@HiveField(2)
String email;
@HiveField(3)
List<String> hobbies;
@HiveField(4)
Address? address;
Person({
required this.name,
required this.age,
required this.email,
this.hobbies = const [],
this.address,
});
@override
String toString() {
return 'Person(name: $name, age: $age, email: $email)';
}
}
@HiveType(typeId: 1)
class Address {
@HiveField(0)
String street;
@HiveField(1)
String city;
@HiveField(2)
String postalCode;
@HiveField(3)
String country;
Address({
required this.street,
required this.city,
required this.postalCode,
required this.country,
});
@override
String toString() {
return 'Address(street: $street, city: $city, postalCode: $postalCode, country: $country)';
}
}
// Adapter generation command:
// dart run build_runner build
Data Operations
// Database operations class
class PersonDatabase {
static const String _boxName = 'persons';
// Open box
static Future<Box<Person>> _getBox() async {
if (!Hive.isBoxOpen(_boxName)) {
return await Hive.openBox<Person>(_boxName);
}
return Hive.box<Person>(_boxName);
}
// Add person
static Future<void> addPerson(Person person) async {
final box = await _getBox();
await box.add(person);
}
// Update person (by key)
static Future<void> updatePerson(int key, Person person) async {
final box = await _getBox();
await box.put(key, person);
}
// Delete person
static Future<void> deletePerson(int key) async {
final box = await _getBox();
await box.delete(key);
}
// Get all persons
static Future<List<Person>> getAllPersons() async {
final box = await _getBox();
return box.values.toList();
}
// Get specific person
static Future<Person?> getPerson(int key) async {
final box = await _getBox();
return box.get(key);
}
// Search functionality
static Future<List<Person>> searchPersons(String query) async {
final box = await _getBox();
return box.values.where((person) {
return person.name.toLowerCase().contains(query.toLowerCase()) ||
person.email.toLowerCase().contains(query.toLowerCase());
}).toList();
}
// Filter by age
static Future<List<Person>> getPersonsByAgeRange(int minAge, int maxAge) async {
final box = await _getBox();
return box.values.where((person) {
return person.age >= minAge && person.age <= maxAge;
}).toList();
}
// Clear box
static Future<void> clearAll() async {
final box = await _getBox();
await box.clear();
}
// Close box
static Future<void> closeBox() async {
final box = await _getBox();
await box.close();
}
}
// Usage example
void demonstrateBasicOperations() async {
// Add data
final person1 = Person(
name: 'John Doe',
age: 30,
email: '[email protected]',
hobbies: ['reading', 'programming'],
address: Address(
street: '123 Main St',
city: 'New York',
postalCode: '10001',
country: 'USA',
),
);
await PersonDatabase.addPerson(person1);
// Add multiple data
final persons = [
Person(name: 'Jane Smith', age: 25, email: '[email protected]'),
Person(name: 'Bob Johnson', age: 35, email: '[email protected]'),
Person(name: 'Alice Brown', age: 28, email: '[email protected]'),
];
for (final person in persons) {
await PersonDatabase.addPerson(person);
}
// Retrieve data
final allPersons = await PersonDatabase.getAllPersons();
print('Total persons: ${allPersons.length}');
// Search
final searchResults = await PersonDatabase.searchPersons('john');
print('Search results: $searchResults');
// Age filter
final youngAdults = await PersonDatabase.getPersonsByAgeRange(20, 30);
print('Young adults: $youngAdults');
}
Watch Functionality and Reactive UI
// Reactive UI implementation
class PersonListWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Person List')),
body: ValueListenableBuilder<Box<Person>>(
valueListenable: Hive.box<Person>('persons').listenable(),
builder: (context, box, _) {
final persons = box.values.toList();
if (persons.isEmpty) {
return Center(
child: Text('No persons found'),
);
}
return ListView.builder(
itemCount: persons.length,
itemBuilder: (context, index) {
final person = persons[index];
final key = box.keyAt(index);
return ListTile(
title: Text(person.name),
subtitle: Text('${person.age} years old - ${person.email}'),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.edit),
onPressed: () => _editPerson(context, key, person),
),
IconButton(
icon: Icon(Icons.delete),
onPressed: () => _deletePerson(key),
),
],
),
onTap: () => _showPersonDetails(context, person),
);
},
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => _addPerson(context),
child: Icon(Icons.add),
),
);
}
void _addPerson(BuildContext context) {
showDialog(
context: context,
builder: (context) => PersonFormDialog(),
);
}
void _editPerson(BuildContext context, dynamic key, Person person) {
showDialog(
context: context,
builder: (context) => PersonFormDialog(key: key, person: person),
);
}
void _deletePerson(dynamic key) async {
await PersonDatabase.deletePerson(key);
}
void _showPersonDetails(BuildContext context, Person person) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(person.name),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Age: ${person.age}'),
Text('Email: ${person.email}'),
if (person.hobbies.isNotEmpty)
Text('Hobbies: ${person.hobbies.join(', ')}'),
if (person.address != null)
Text('Address: ${person.address.toString()}'),
],
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('Close'),
),
],
),
);
}
}
// Form dialog
class PersonFormDialog extends StatefulWidget {
final dynamic key;
final Person? person;
PersonFormDialog({this.key, this.person});
@override
_PersonFormDialogState createState() => _PersonFormDialogState();
}
class _PersonFormDialogState extends State<PersonFormDialog> {
final _formKey = GlobalKey<FormState>();
late TextEditingController _nameController;
late TextEditingController _ageController;
late TextEditingController _emailController;
@override
void initState() {
super.initState();
_nameController = TextEditingController(text: widget.person?.name ?? '');
_ageController = TextEditingController(text: widget.person?.age.toString() ?? '');
_emailController = TextEditingController(text: widget.person?.email ?? '');
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(widget.person == null ? 'Add New Person' : 'Edit Person'),
content: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
controller: _nameController,
decoration: InputDecoration(labelText: 'Name'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a name';
}
return null;
},
),
TextFormField(
controller: _ageController,
decoration: InputDecoration(labelText: 'Age'),
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter an age';
}
if (int.tryParse(value) == null) {
return 'Please enter a valid number';
}
return null;
},
),
TextFormField(
controller: _emailController,
decoration: InputDecoration(labelText: 'Email'),
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter an email';
}
return null;
},
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('Cancel'),
),
ElevatedButton(
onPressed: _savePerson,
child: Text('Save'),
),
],
);
}
void _savePerson() async {
if (_formKey.currentState!.validate()) {
final person = Person(
name: _nameController.text,
age: int.parse(_ageController.text),
email: _emailController.text,
);
if (widget.key == null) {
await PersonDatabase.addPerson(person);
} else {
await PersonDatabase.updatePerson(widget.key, person);
}
Navigator.of(context).pop();
}
}
@override
void dispose() {
_nameController.dispose();
_ageController.dispose();
_emailController.dispose();
super.dispose();
}
}
Encryption and Security
// Encrypted box creation and usage
import 'dart:convert';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
class SecurePersonDatabase {
static const String _boxName = 'secure_persons';
// Generate encryption key
static Uint8List _generateKey(String password) {
final bytes = utf8.encode(password);
final digest = sha256.convert(bytes);
return Uint8List.fromList(digest.bytes);
}
// Open encrypted box
static Future<Box<Person>> _getSecureBox(String password) async {
final key = _generateKey(password);
if (!Hive.isBoxOpen(_boxName)) {
return await Hive.openBox<Person>(
_boxName,
encryptionCipher: HiveAesCipher(key),
);
}
return Hive.box<Person>(_boxName);
}
// Add secure data
static Future<void> addSecurePerson(Person person, String password) async {
final box = await _getSecureBox(password);
await box.add(person);
}
// Get secure data
static Future<List<Person>> getSecurePersons(String password) async {
try {
final box = await _getSecureBox(password);
return box.values.toList();
} catch (e) {
print('Wrong password or corrupted data: $e');
return [];
}
}
// Create backup
static Future<void> createBackup(String password, String backupPath) async {
final box = await _getSecureBox(password);
final persons = box.values.toList();
final backupData = {
'timestamp': DateTime.now().toIso8601String(),
'version': '1.0',
'data': persons.map((p) => {
'name': p.name,
'age': p.age,
'email': p.email,
'hobbies': p.hobbies,
'address': p.address != null ? {
'street': p.address!.street,
'city': p.address!.city,
'postalCode': p.address!.postalCode,
'country': p.address!.country,
} : null,
}).toList(),
};
// Actual file write operation would be performed here
// File(backupPath).writeAsString(jsonEncode(backupData));
print('Backup created: ${jsonEncode(backupData)}');
}
// Restore from backup
static Future<void> restoreFromBackup(String backupPath, String password) async {
// Actual file read operation would be performed here
// final backupContent = await File(backupPath).readAsString();
// final backupData = jsonDecode(backupContent);
// Demo sample data
final backupData = {
'data': [
{
'name': 'Restored User',
'age': 30,
'email': '[email protected]',
'hobbies': ['restore', 'test'],
}
]
};
final box = await _getSecureBox(password);
await box.clear(); // Clear existing data
for (final personData in backupData['data']) {
final person = Person(
name: personData['name'],
age: personData['age'],
email: personData['email'],
hobbies: List<String>.from(personData['hobbies'] ?? []),
);
await box.add(person);
}
}
}
// Security features usage example
void demonstrateSecurityFeatures() async {
const password = 'my_secure_password_123';
// Save encrypted data
final securePerson = Person(
name: 'Secure User',
age: 35,
email: '[email protected]',
hobbies: ['security', 'encryption'],
);
await SecurePersonDatabase.addSecurePerson(securePerson, password);
// Retrieve encrypted data
final securePersons = await SecurePersonDatabase.getSecurePersons(password);
print('Secure persons: $securePersons');
// Create backup
await SecurePersonDatabase.createBackup(password, 'backup.json');
// Restore from backup
await SecurePersonDatabase.restoreFromBackup('backup.json', password);
}
Error Handling
// Comprehensive database manager with error handling
class RobustPersonDatabase {
static const String _boxName = 'robust_persons';
// Safe box retrieval
static Future<Box<Person>?> _getSafeBox() async {
try {
if (!Hive.isBoxOpen(_boxName)) {
return await Hive.openBox<Person>(_boxName);
}
return Hive.box<Person>(_boxName);
} catch (e) {
print('Error opening box: $e');
return null;
}
}
// Safe data addition
static Future<bool> addPersonSafely(Person person) async {
try {
final box = await _getSafeBox();
if (box == null) return false;
await box.add(person);
return true;
} catch (e) {
print('Error adding person: $e');
return false;
}
}
// Safe data update
static Future<bool> updatePersonSafely(int key, Person person) async {
try {
final box = await _getSafeBox();
if (box == null) return false;
if (box.containsKey(key)) {
await box.put(key, person);
return true;
} else {
print('Key $key not found');
return false;
}
} catch (e) {
print('Error updating person: $e');
return false;
}
}
// Safe data deletion
static Future<bool> deletePersonSafely(int key) async {
try {
final box = await _getSafeBox();
if (box == null) return false;
if (box.containsKey(key)) {
await box.delete(key);
return true;
} else {
print('Key $key not found');
return false;
}
} catch (e) {
print('Error deleting person: $e');
return false;
}
}
// Safe data retrieval
static Future<List<Person>> getPersonsSafely() async {
try {
final box = await _getSafeBox();
if (box == null) return [];
return box.values.toList();
} catch (e) {
print('Error getting persons: $e');
return [];
}
}
// Database status check
static Future<Map<String, dynamic>> getDatabaseStatus() async {
try {
final box = await _getSafeBox();
if (box == null) {
return {
'status': 'error',
'message': 'Cannot open database',
'count': 0,
'size': 0,
};
}
return {
'status': 'healthy',
'message': 'Database is operational',
'count': box.length,
'size': box.values.length,
'path': box.path,
'isOpen': box.isOpen,
};
} catch (e) {
return {
'status': 'error',
'message': 'Error checking database: $e',
'count': 0,
'size': 0,
};
}
}
// Data integrity check
static Future<Map<String, dynamic>> performIntegrityCheck() async {
try {
final box = await _getSafeBox();
if (box == null) {
return {'passed': false, 'errors': ['Cannot open database']};
}
final errors = <String>[];
int validCount = 0;
for (int i = 0; i < box.length; i++) {
try {
final person = box.getAt(i);
if (person == null) {
errors.add('Null person at index $i');
continue;
}
// Basic validation
if (person.name.isEmpty) {
errors.add('Empty name at index $i');
}
if (person.age < 0 || person.age > 150) {
errors.add('Invalid age at index $i: ${person.age}');
}
if (!person.email.contains('@')) {
errors.add('Invalid email at index $i: ${person.email}');
}
validCount++;
} catch (e) {
errors.add('Error reading person at index $i: $e');
}
}
return {
'passed': errors.isEmpty,
'errors': errors,
'validCount': validCount,
'totalCount': box.length,
};
} catch (e) {
return {
'passed': false,
'errors': ['Integrity check failed: $e'],
'validCount': 0,
'totalCount': 0,
};
}
}
// Cleanup and maintenance
static Future<void> performMaintenance() async {
try {
final box = await _getSafeBox();
if (box == null) return;
// Remove duplicate data
final seen = <String>{};
final toDelete = <int>[];
for (int i = 0; i < box.length; i++) {
final person = box.getAt(i);
if (person != null) {
final key = '${person.name}_${person.email}';
if (seen.contains(key)) {
toDelete.add(i);
} else {
seen.add(key);
}
}
}
// Delete duplicates
for (final index in toDelete.reversed) {
await box.deleteAt(index);
}
// Compact database
await box.compact();
print('Maintenance completed. Removed ${toDelete.length} duplicates.');
} catch (e) {
print('Maintenance failed: $e');
}
}
}
// Error handling usage example
void demonstrateErrorHandling() async {
// Check database status
final status = await RobustPersonDatabase.getDatabaseStatus();
print('Database status: $status');
// Safe data operations
final person = Person(
name: 'Test User',
age: 25,
email: '[email protected]',
);
final added = await RobustPersonDatabase.addPersonSafely(person);
if (added) {
print('Person added successfully');
} else {
print('Failed to add person');
}
// Integrity check
final integrityResult = await RobustPersonDatabase.performIntegrityCheck();
print('Integrity check: $integrityResult');
// Perform maintenance
await RobustPersonDatabase.performMaintenance();
}