Type-safe, FP-style abstraction layers for Hive's boxes. Provides Managers for almost all conceivable use-cases and scenarios.
The fundamental distinction in Hive (and this library) is between Box and LazyBox. Understanding when to use each is crucial for optimal performance.
What it does: Loads all data into memory when opened.
β Pros:
- β‘ Instant reads - Data is already in memory (synchronous access)
- π― Simple API - No async/await needed for reads
- π Best for frequent access - Perfect when you need to read data repeatedly
- πͺ Predictable performance - No I/O delays during reads
β Cons:
- πΎ High memory usage - Entire box contents loaded into RAM
- π Slower startup - Takes time to load all data when opening
β οΈ Not suitable for large datasets - Can cause memory issues with >10MB of data- π± Mobile unfriendly - Limited RAM on mobile devices
When to use:
- Small datasets (< 1MB)
- Frequently accessed data (e.g., user preferences, app settings)
- Data that needs to be read synchronously
- When memory is not a constraint
What it does: Loads data on-demand from disk only when requested.
β Pros:
- πͺΆ Minimal memory footprint - Only loads what you need
- β‘ Fast startup - Opens instantly regardless of data size
- π Scales well - Can handle large datasets (100MB+)
- π± Mobile friendly - Conserves precious device memory
β Cons:
- β±οΈ Async reads - Every read requires disk I/O (slower)
- π More complex API - Must use async/await for all operations
- π Slower for frequent access - Repeated reads hit disk each time
- π» I/O overhead - Each access has file system overhead
When to use:
- Large datasets (> 1MB)
- Infrequently accessed data (e.g., cached images, historical logs)
- Memory-constrained environments (mobile apps)
- When you only need specific items, not the whole dataset
Purpose: Multi-item storage with custom indices (int or String keys).
Use cases:
- Storing multiple users, products, or entities
- Key-value storage where you control the keys
- Collections that need individual item access
Example:
// Eager loading - all users in memory
final userBox = BoxManager<User, int>(
boxKey: 'users',
defaultValue: User.empty(),
);
// Lazy loading - users loaded on demand
final userLazyBox = LazyBoxManager<User, String>(
boxKey: 'users_lazy',
defaultValue: User.empty(),
);
// Usage
await userBox.init();
await userBox.put(index: 1, value: user1).run(); // Synchronous read later
final user = userBox.get(1); // Instant!
await userLazyBox.init();
await userLazyBox.put(index: 'user_123', value: user2).run();
final user2 = await userLazyBox.get('user_123').run(); // Async readTrade-offs:
| Aspect | BoxManager | LazyBoxManager |
|---|---|---|
| Read Speed | β‘ Instant | π Disk I/O |
| Memory | πΎ High | πͺΆ Low |
| Startup | π Slow | β‘ Fast |
| Best for | Small, frequent | Large, occasional |
Purpose: Store one value per box (like a single setting or configuration).
Why needed: Simplified API when you only need to store a single value without managing indices.
Variants:
SingleIndexBoxManager<T>- Eager loadingSingleIndexLazyBoxManager<T>- Lazy loading
Use cases:
- App theme preference
- User authentication token
- Last sync timestamp
- App configuration object
Example:
final themeBox = SingleIndexBoxManager<AppTheme>(
boxKey: 'app_theme',
defaultValue: AppTheme.light,
);
await themeBox.init();
await themeBox.put(value: AppTheme.dark).run();
final theme = themeBox.get(); // No index needed!Trade-offs:
- β Simpler API - No index management
- β Clear intent - Obvious it's a single value
- β Limited to one value - Can't store multiple items
- π‘ Choose Lazy variant if the value is large (e.g., cached JSON)
Purpose: Store collections (Lists, Sets) of custom types as values.
Why needed: Hive has limitations reading Iterable<CustomType> directly (issue #150). This manager works around that limitation.
Variant:
CollectionLazyBoxManager<T, I>- StoresIterable<T>at each index
Use cases:
- Storing lists of items per category
- Multiple tags per entity
- Historical records grouped by date
- Batch data storage
Example:
final tagsBox = CollectionLazyBoxManager<String, int>(
boxKey: 'post_tags',
defaultValue: <String>[],
);
await tagsBox.init();
await tagsBox.put(
index: 1, // Post ID
value: ['flutter', 'dart', 'mobile'],
).run();
final tags = await tagsBox.get(1).run(); // Returns Iterable<String>Trade-offs:
- β Solves Hive limitation - Enables storing custom type collections
- β Type-safe - Proper generic typing
β οΈ Lazy only - No eager variant (memory concerns with collections)- π‘ Use for grouped data - Perfect for one-to-many relationships
Purpose: Store data with two indices (composite keys) - like a 2D grid or matrix.
Why needed: When your data naturally has two dimensions (e.g., user + date, row + column).
Variants:
Store and retrieve by two indices simultaneously.
DualIntIndexBoxManager<T>- Eager, int + int indicesDualIntIndexLazyBoxManager<T>- Lazy, int + int indices
Encoding Strategy: Bit-Shift
β Pros:
- β‘ Maximum performance - Bit operations are the fastest CPU operations
- π― Perfect distribution - Uses all 32 bits efficiently (16 bits each)
- πΎ No wasted space - Can represent 0 to 65,536 for both indices
- π Collision-free - Mathematically proven unique encoding
β Cons:
- π Fixed range - Limited to 16-bit integers (0-65,536)
- β No negative numbers - Without additional handling
- π³οΈ Not great for sparse data - Wastes encoding space if data is sparse
β οΈ Platform dependency - Potential issues if Dart's integer behavior changes
Use cases:
- Grid-based games (x, y coordinates)
- Time-series data (user ID + day index)
- Matrix storage (row + column)
- Relational data with two keys
Example:
final gameBoard = DualIntIndexLazyBoxManager<Tile>.bitShift(
boxKey: 'game_tiles',
defaultValue: Tile.empty(),
);
await gameBoard.init();
await gameBoard.put(
primaryIndex: 5, // X coordinate
secondaryIndex: 10, // Y coordinate
value: tile,
).run();
final tile = await gameBoard.get(
primaryIndex: 5,
secondaryIndex: 10,
).run();Purpose: Retrieve data by either index independently (reverse lookups).
QueryDualIntIndexBoxManager<T>- Eager variantQueryDualIntIndexLazyBoxManager<T>- Lazy variant
Why needed: Sometimes you need to find all items matching one index:
- "Give me all tiles at X=5" (any Y)
- "Give me all events on day 10" (any user)
β Pros:
- β Perfect accuracy - Always reflects current box state
- πΎ Zero storage overhead - No additional boxes or memory structures
- πͺΆ Memory efficient - Only stores seen indices during iteration
- π‘οΈ Simple & robust - Less code, fewer failure points
- β‘ Leverages Hive optimizations - Uses Hive's built-in key indexing
β Cons:
- π O(K) per query - Performance degrades with total records
- π No pre-computation - Each query scans all keys
β οΈ Not optimal for very large datasets - >100K records may cause UI jank- π No indexing benefits - Each decomposition is a full scan
Use cases:
- Finding all events for a specific user
- Getting all data points for a specific date
- Querying one dimension of a 2D dataset
Example:
final userEvents = QueryDualIntIndexLazyBoxManager<Event>.bitShift(
boxKey: 'user_events',
defaultValue: Event.empty(),
);
await userEvents.init();
// Store events
await userEvents.put(
primaryIndex: userId,
secondaryIndex: dayIndex,
value: event,
).run();
// Query all events for a user (any day)
final allUserEvents = await userEvents
.getAllByPrimaryIndex(userId)
.run();
// Query all events on a specific day (any user)
final dailyEvents = await userEvents
.getAllBySecondaryIndex(dayIndex)
.run();Trade-offs:
| Aspect | Standard Dual | Query Dual |
|---|---|---|
| Storage | Single box | Single box |
| Retrieval | By both indices | By either index |
| Performance | O(1) lookup | O(K) scan |
| Use case | Exact lookups | Partial queries |
| Complexity | Simple | Moderate |
Need to store...
ββ Single value?
β ββ Small (< 100KB)? β SingleIndexBoxManager
β ββ Large (> 100KB)? β SingleIndexLazyBoxManager
β
ββ Multiple items with one key?
β ββ Small dataset (< 1MB), frequent access? β BoxManager
β ββ Large dataset (> 1MB), occasional access? β LazyBoxManager
β ββ Collections of custom types? β CollectionLazyBoxManager
β
ββ Multiple items with two keys?
ββ Only need exact lookups (both keys)? β DualIntIndexBoxManager/LazyBoxManager
ββ Need to query by either key? β QueryDualIntIndexBoxManager/LazyBoxManager
- Hive Documentation
- fpdart Documentation - For understanding
Taskand functional patterns
See LICENSE file for details.