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.

NoSQLFlutterDartDatabaseKey-ValueLocal

GitHub Overview

isar/hive

Lightweight and blazing fast key-value database written in pure Dart.

Stars4,280
Watchers63
Forks431
Created:July 8, 2019
Language:Dart
License:Apache License 2.0

Topics

dartdatabaseencryptionflutterhivekey-valuenosql

Star History

isar/hive Star History
Data as of: 7/17/2025, 06:57 AM

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();
}