Hive CE Database Setup
This directory contains the Hive CE (Community Edition) database configuration and services for the Worker Flutter app.
Overview
The app uses Hive CE for offline-first local data persistence. Hive is a lightweight, fast NoSQL database written in pure Dart, perfect for Flutter applications.
Key Features
- Offline-First: All data is stored locally and synced with the backend
- Fast Performance: Hive is optimized for speed with minimal overhead
- Type-Safe: Uses type adapters for strong typing
- Encryption Support: Optional AES encryption for sensitive data
- Auto-Compaction: Automatic database maintenance and cleanup
- Migration Support: Built-in schema versioning and migrations
File Structure
lib/core/database/
├── README.md # This file
├── hive_service.dart # Main Hive initialization and lifecycle management
├── database_manager.dart # High-level database operations
└── models/
├── cached_data.dart # Generic cache wrapper model
└── enums.dart # All enum type adapters
Setup Instructions
1. Install Dependencies
The required packages are already in pubspec.yaml:
dependencies:
hive_ce: ^2.6.0
hive_ce_flutter: ^2.1.0
dev_dependencies:
hive_ce_generator: ^1.6.0
build_runner: ^2.4.11
Run:
flutter pub get
2. Generate Type Adapters
After creating Hive models with @HiveType annotations, run:
dart run build_runner build --delete-conflicting-outputs
Or for continuous watching during development:
dart run build_runner watch --delete-conflicting-outputs
3. Initialize Hive in main.dart
import 'package:flutter/material.dart';
import 'core/database/hive_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Hive
final hiveService = HiveService();
await hiveService.initialize();
runApp(const MyApp());
}
Creating Hive Models
Basic Model Example
import 'package:hive_ce/hive.dart';
import '../../constants/storage_constants.dart';
part 'user_model.g.dart'; // Generated file
@HiveType(typeId: HiveTypeIds.user)
class UserModel extends HiveObject {
@HiveField(0)
final String id;
@HiveField(1)
final String name;
@HiveField(2)
final String email;
UserModel({
required this.id,
required this.name,
required this.email,
});
}
Enum Example
@HiveType(typeId: HiveTypeIds.memberTier)
enum MemberTier {
@HiveField(0)
gold,
@HiveField(1)
platinum,
@HiveField(2)
diamond,
}
Important Rules
- Type IDs must be unique across the entire app (0-223 for user types)
- Never change field numbers once assigned - it will break existing data
- Use
partdirective to include generated adapter file - Extend HiveObject for model classes (optional but recommended for auto-save)
- Register adapters before opening boxes (handled by HiveService)
Box Management
Available Boxes
The app uses these pre-configured boxes (see storage_constants.dart):
user_box- User profile and auth data (encrypted)product_box- Product catalog cachecart_box- Shopping cart items (encrypted)order_box- Order history (encrypted)project_box- Construction projects (encrypted)loyalty_box- Loyalty transactions (encrypted)rewards_box- Rewards catalogsettings_box- App settingscache_box- Generic API cachesync_state_box- Sync timestampsnotification_box- Notificationsaddress_box- Delivery addresses (encrypted)offline_queue_box- Failed API requests queue (encrypted)
Using Boxes
// Using DatabaseManager (recommended)
final dbManager = DatabaseManager();
// Save data
await dbManager.save(
boxName: HiveBoxNames.productBox,
key: 'product_123',
value: product,
);
// Get data
final product = dbManager.get(
boxName: HiveBoxNames.productBox,
key: 'product_123',
);
// Get all
final products = dbManager.getAll(boxName: HiveBoxNames.productBox);
Caching Strategy
Save to Cache
final dbManager = DatabaseManager();
await dbManager.saveToCache(
key: HiveKeys.productsCacheKey,
data: products,
);
Get from Cache
final products = dbManager.getFromCache<List<Product>>(
key: HiveKeys.productsCacheKey,
maxAge: CacheDuration.products, // 6 hours
);
if (products == null) {
// Cache miss or expired - fetch from API
final freshProducts = await api.getProducts();
await dbManager.saveToCache(
key: HiveKeys.productsCacheKey,
data: freshProducts,
);
}
Check Cache Validity
final isValid = dbManager.isCacheValid(
key: HiveKeys.productsCacheKey,
maxAge: CacheDuration.products,
);
if (!isValid) {
// Refresh cache
}
Offline Queue
Handle failed API requests when offline:
// Add to queue when API call fails
await dbManager.addToOfflineQueue({
'endpoint': '/api/orders',
'method': 'POST',
'body': orderData,
});
// Process queue when back online
final queue = dbManager.getOfflineQueue();
for (var i = 0; i < queue.length; i++) {
try {
await api.request(queue[i]);
await dbManager.removeFromOfflineQueue(i);
} catch (e) {
// Keep in queue for next retry
}
}
Data Synchronization
Track sync state for different data types:
// Update sync timestamp
await dbManager.updateSyncTime(HiveKeys.productsSyncTime);
// Get last sync time
final lastSync = dbManager.getLastSyncTime(HiveKeys.productsSyncTime);
// Check if needs sync
final needsSync = dbManager.needsSync(
dataType: HiveKeys.productsSyncTime,
syncInterval: Duration(hours: 6),
);
Encryption
Enable encryption for sensitive data in storage_constants.dart:
class HiveDatabaseConfig {
static const bool enableEncryption = true;
}
Generate and store encryption key securely:
// Generate key
final encryptionKey = HiveService.generateEncryptionKey();
// Store securely using flutter_secure_storage
final secureStorage = FlutterSecureStorage();
await secureStorage.write(
key: 'hive_encryption_key',
value: base64Encode(encryptionKey),
);
// Initialize with key
final storedKey = await secureStorage.read(key: 'hive_encryption_key');
await hiveService.initialize(
encryptionKey: base64Decode(storedKey!),
);
Migrations
Handle schema changes:
// In hive_service.dart, add migration logic:
Future<void> _migrateToVersion(int version) async {
switch (version) {
case 2:
await _migrateV1ToV2();
break;
}
}
Future<void> _migrateV1ToV2() async {
// Example: Add new field to existing data
final userBox = Hive.box(HiveBoxNames.userBox);
for (var key in userBox.keys) {
final user = userBox.get(key);
// Update user data structure
await userBox.put(key, updatedUser);
}
}
Database Maintenance
Clear Expired Cache
await dbManager.clearExpiredCache();
Compact Boxes
final hiveService = HiveService();
// Compaction happens automatically during initialization
Clear User Data (Logout)
await hiveService.clearUserData();
Clear All Data
await hiveService.clearAllData();
Get Statistics
final stats = dbManager.getStatistics();
dbManager.printStatistics();
Best Practices
- Always initialize Hive before using any boxes
- Use DatabaseManager for common operations
- Cache frequently accessed data
- Set appropriate cache expiration times
- Handle errors gracefully - Hive operations can fail
- Use transactions for multiple related updates
- Compact boxes periodically for optimal performance
- Never store large files in Hive - use file system instead
- Test migrations thoroughly before release
- Monitor database size in production
Debugging
Print Box Contents
final box = Hive.box(HiveBoxNames.productBox);
print('Box length: ${box.length}');
print('Keys: ${box.keys}');
print('Values: ${box.values}');
Check Box Location
print('Hive path: ${Hive.box(HiveBoxNames.settingsBox).path}');
View Statistics
DatabaseManager().printStatistics();
Troubleshooting
"Box not found" Error
- Ensure Hive is initialized before accessing boxes
- Check that box name is correct
"TypeAdapter not registered" Error
- Run
build_runnerto generate adapters - Ensure adapter is registered in
HiveService._registerTypeAdapters()
"Cannot write null values" Error
- Make fields nullable with
?or provide default values - Check that HiveField annotations are correct
Data Corruption
- Enable backup/restore functionality
- Implement data validation before saving
- Use try-catch blocks around Hive operations
Testing
import 'package:flutter_test/flutter_test.dart';
import 'package:hive_ce/hive.dart';
import 'package:hive_ce_flutter/hive_flutter.dart';
void main() {
setUp(() async {
await Hive.initFlutter();
// Register test adapters
});
tearDown(() async {
await Hive.deleteFromDisk();
});
test('Save and retrieve user', () async {
final box = await Hive.openBox('test_box');
await box.put('user', UserModel(id: '1', name: 'Test'));
final user = box.get('user');
expect(user.name, 'Test');
});
}
Resources
Type Adapter Registry
Registered Type IDs (0-223)
| Type ID | Model | Status |
|---|---|---|
| 0 | UserModel | TODO |
| 1 | ProductModel | TODO |
| 2 | CartItemModel | TODO |
| 3 | OrderModel | TODO |
| 4 | ProjectModel | TODO |
| 5 | LoyaltyTransactionModel | TODO |
| 10 | OrderItemModel | TODO |
| 11 | AddressModel | TODO |
| 12 | CategoryModel | TODO |
| 13 | RewardModel | TODO |
| 14 | GiftModel | TODO |
| 15 | NotificationModel | TODO |
| 16 | QuoteModel | TODO |
| 17 | PaymentModel | TODO |
| 18 | PromotionModel | TODO |
| 19 | ReferralModel | TODO |
| 20 | MemberTier (enum) | Created |
| 21 | UserType (enum) | Created |
| 22 | OrderStatus (enum) | Created |
| 23 | ProjectStatus (enum) | Created |
| 24 | ProjectType (enum) | Created |
| 25 | TransactionType (enum) | Created |
| 26 | GiftStatus (enum) | Created |
| 27 | PaymentStatus (enum) | Created |
| 28 | NotificationType (enum) | Created |
| 29 | PaymentMethod (enum) | Created |
| 30 | CachedData | Created |
| 31 | SyncState | TODO |
| 32 | OfflineRequest | TODO |
IMPORTANT: Never reuse or change these type IDs once assigned!