img

Hive, but faster, simpler, and safer. Ready for production.

Meet Hivez — a fast, easy, and type-safe database for Dart and Flutter. With a unified API, automatic initialization, and built-in utilities for search, backups, isolation, and syncing, Hivez makes local data handling effortless, concurrency-safe, and production-ready — all while remaining fully compatible with Hive (via the hive_ce engine).

Migration-free upgrade: Switching from Hive or Hive CE to Hivez needs no migrations or data changes — just set up your adapters correctly and keep the same box names and types.

Table of Contents

✅ Features

  • Zero setup – no manual openBox, auto-init on first use
  • Type-safe – no dynamic, compile-time guarantees
  • Unified API – one interface for Box, Lazy, Isolated
  • Concurrency-safe – atomic writes, safe reads
  • Clean architecture – decoupled, testable design
  • Production-ready – encryption, crash recovery, compaction
  • Utility-rich – backup/restore, search, iteration, box tools
  • Future-proof – swap box types with one line
  • Hive-compatible – 100% features, zero loss

Type-safe – no dynamic, no surprises

final users = Box<int, User>('users');
await users.put(1, User('Alice'));
final u = await users.get(1); // User('Alice')

Zero setup – no openBox, auto-init on first use

final settings = Box<String, bool>('settings');
await settings.put('darkMode', true);
final dark = await settings.get('darkMode'); // true

📦 How to Use Hivez

Hivez act as complete, self-initializing services for storing and managing data. Unlike raw Hive, you don’t need to worry about opening/closing boxes — the API is unified and stays identical across box types.

Examples

Before diving in — make sure you’ve set up Hive correctly with adapters.
The setup takes less than 1 minute and is explained here: Setup Guide.
Once Hive is set up, you can use Hivez right away:

💡 Tip: For datasets needing fast search, use IndexedBox for blazing-fast search — same API, 100×-1000× faster.

➕ Put & Get

final box = Box<int, String>('notes');
await box.put(1, 'Hello');
final note = await box.get(1); // "Hello"

📥 Add & Retrieve by Index

final id = await box.add('World');   // auto index (int)
final val = await box.getAt(id);     // "World"

✏️ Update & Move Keys

await box.put(1, 'Updated');
await box.moveKey(1, 2); // value moved from key 1 → key 2

❌ Delete & Clear

await box.delete(2);
await box.clear(); // remove all

🔑 Keys & Values

final keys = await box.getAllKeys();     // Iterable<int>
final vals = await box.getAllValues();  // Iterable<String>

🔍 Queries

final match = await box.firstWhereOrNull((v) => v.contains('Hello'));
final contains = await box.containsKey(1); // true / false

🔄 Iteration Helpers

await box.foreachKey((k) async => print(k));
await box.foreachValue((k, v) async => print('$k:$v'));

📊 Box Info

final count = await box.length;
final empty = await box.isEmpty;

⚡ Utilities

await box.flushBox();    // write to disk
await box.compactBox();  // shrink file
await box.deleteFromDisk(); // remove permanently

👀 Watch for Changes

box.watch(1).listen((event) {
  print('Key changed: ${event.key}');
});

If you’re doing a lot of searches, you don’t have to scan values manually — use IndexedBox instead. It’s a drop-in replacement for Box that automatically maintains a tiny on-disk index, giving you instant text queries:

final box = IndexedBox<String, Article>(
  'articles',
  searchableText: (a) => '${a.title} ${a.content}',
);

final results = await box.search('flutter dart');
print(results); // [Article(...)]

✅ Same API as regular boxes
⚡ 100×–1000× faster for text lookups
🧠 Smart analyzers (basic / prefix / n-gram)
🪶 Zero setup — data stays compatible with Hive

📘 Learn more in the IndexedBox Section

🔧 Available Methods

⤴️ Back → Table of Contents

All Box types share the same complete API:

  • Write operations

    • put(key, value) — Insert or update a value by key
    • putAll(entries) — Insert/update multiple entries at once
    • putAt(index, value) — Update value at a specific index
    • add(value) — Auto-increment key insert
    • addAll(values) — Insert multiple values sequentially
    • moveKey(oldKey, newKey) — Move value from one key to another
  • Delete operations

    • delete(key) — Remove a value by key
    • deleteAt(index) — Remove value at index
    • deleteAll(keys) — Remove multiple keys
    • clear() — Delete all data in the box
    • replaceAll(entries) — Clear and replace all values in the box
  • Read operations

    • get(key) — Retrieve value by key (with optional defaultValue)
    • getMany(keys) — Retrieve multiple values by keys
    • getAt(index) — Retrieve value by index
    • valueAt(index) — Alias for getAt
    • getAllKeys() — Returns all keys
    • getAllValues() — Returns all values
    • keyAt(index) — Returns key at given index
    • containsKey(key) — Check if key exists
    • length — Number of items in box
    • isEmpty / isNotEmpty — Quick state checks
    • watch(key) — Listen to changes for a specific key
  • Query helpers

    • getValuesWhere(condition) — Filter values by predicate
    • getKeysWhere(condition) — Filter keys by predicate
    • firstWhereOrNull(condition) — Returns first matching value or null
    • firstKeyWhere(condition) — Returns first matching key or null
    • firstWhereContains(query, searchableText) — Search string fields
    • foreachKey(action) — Iterate keys asynchronously
    • foreachValue(action) — Iterate values asynchronously
    • searchKeyOf(value) — Find key for a given value
  • Box management

    • ensureInitialized() — Safely open box if not already open
    • deleteFromDisk() — Permanently delete box data
    • closeBox() — Close box in memory
    • flushBox() — Write pending changes to disk
    • compactBox() — Compact file to save space
  • Extras

    • generateBackupJson() — Export all data as JSON
    • restoreBackupJson() — Import all data from JSON
    • generateBackupCompressed() — Export all data as compressed binary
    • restoreBackupCompressed() — Import all data from compressed binary
    • toMap() — Convert full box to Map<K, T>
    • estimateSizeBytes() — Approximate in-memory size of all keys and values (bytes)
    • search(query, searchableText) — (Slow search, use IndexedBox instead)

⚙️ Constructor & Properties

All Box types share the same constructor parameters and configuration pattern.
These let you control how your box behaves, where it stores data, and how it handles safety and encryption.

  • Parameters

    • name — The unique name of the box. Used as the on-disk file name.
    • type — The box type: regular, lazy, isolated, or isolatedLazy.
    • encryptionCipher — Optional HiveCipher for transparent AES encryption/decryption.
    • crashRecovery — Enables Hive’s built-in crash recovery mechanism. Default: true.
    • path — Custom file system path for where this box is stored.
    • collection — Logical grouping of boxes (optional). Useful for namespacing.
    • logger — Optional log handler for diagnostics, warnings, or crash reports.

💡 Tip: For datasets needing fast search, use IndexedBox for blazing-fast search — same API, 100×-1000× faster.

Which Box Should I Use?

⤴️ Back → Table of Contents

  • BoxType.regular → Default choice. Fast, synchronous reads with async writes.
  • BoxType.lazy → Use when working with large datasets where values are only loaded on demand.
  • BoxType.isolated → Use when you need isolate safety (background isolates or heavy concurrency).
  • BoxType.isolatedLazy → Combine lazy loading + isolate safety for maximum scalability.

💡 Switching between them is a single-line change.
Your app logic and API calls stay exactly the same — while in raw Hive, this would break your code.
⚠️ Note on isolates: The API is identical across all box types, but using Isolated boxes requires you to properly set up Hive with isolates. If you’re not familiar with isolate management in Dart/Flutter, it’s safer to stick with regular or lazy boxes.

🧠 BoxType Helpers

You can also use fluent helpers for quick box creation:

final isoBox = BoxType.isolated.box<String, MyModel>('myIsoBox');
final lazyConfig = BoxType.lazy.boxConfig('lazyBox');

🔒 Example with Encryption and Logging

final cipher = HiveAesCipher(my32ByteKey);
final box = Box<int, String>(
  'secureNotes',
  encryptionCipher: cipher,
  logger: (msg) => print('[HiveLog] $msg'),
);

🔄 Swap Box Types Instantly

You can switch between any box type (regular, lazy, isolated, isolatedLazy, indexed)
without changing your logic or data — all share the same unified API.

// Regular box (default)
final box = Box<int, String>('users');
final box = Box<int, String>.lazy('users'); // lazy box
final box = Box<int, String>.isolated('users'); // isolated box
final box = Box<int, String>.isolatedLazy('users'); // isolated lazy box

or

final box = Box<int, String>('users');
final box = Box<int, String>('users', type: BoxType.lazy);
final box = Box<int, String>('users', type: BoxType.isolated);
final box = Box<int, String>('users', type: BoxType.isolatedLazy);

Or in IndexedBox for ultra-fast search

final indexed = IndexedBox<int, String>(
  'users',
  searchableText: (v) => v, // define what text to index
  type: BoxType.lazy, // or BoxType.isolated, BoxType.isolatedLazy, BoxType.regular
);

No migrations, same data and file names, drop-in swap between all box types

⚠️ Note on isolates: The API is identical across all box types, but using Isolated boxes requires you to properly set up Hive with isolates. If you’re not familiar with isolate management in Dart/Flutter, it’s safer to stick with regular or lazy boxes.

🧰 Advanced: Box Configuration

You can create or clone configurations using BoxConfig for advanced control.

final config = BoxConfig(
  'myBox',
  type: BoxType.lazy,
  path: '/data/hive',
  crashRecovery: true,
  collection: 'settings',
);

final box = config.box<String, MyModel>();

Or duplicate and modify:

final updated = config.copyWith(
  type: BoxType.isolated,
  path: '/data/hive/isolated',
);

🔗 Setup Guide for hive_ce

⤴️ Back → Table of Contents

To start using Hive in Dart or Flutter, you’ll need the Hive Community Edition (Hive CE) and the Flutter bindings. I made this setup guide for you to make it easier to get started with Hive.

It takes less than 1 minute.

1. Add the packages

One line command to add all packages:

flutter pub add hivez_flutter dev:hive_ce_generator dev:build_runner

or add the following to your pubspec.yaml with the latest versions:

dependencies:
  hivez_flutter: ^1.2.2

dev_dependencies:
  build_runner: ^2.4.7
  hive_ce_generator: ^1.8.2

2. Setting Up Hive Adapters

Hive works out of the box with core Dart types (String, int, double, bool, DateTime, Uint8List, List, Map…), but if you want to store custom classes or enums, you must register a TypeAdapter.

With Hive you can generate multiple adapters at once with the @GenerateAdapters annotation. For all enums and classes you want to store, you need to register an adapter.

Let's say you have the following classes and enums:

class Product {
  final String name;
  final double price;
  final Category category;
}
enum Category {
  electronics,
  clothing,
  books,
  other,
}

To generate the adapters, you need to:

  1. Create a folder named hive somewhere inside your lib folder
  2. Inside this hive folder create a file named hive_adapters.dart
  3. Add the following code to the file:
// hive/hive_adapters.dart
import 'package:hivez_flutter/hivez_flutter.dart';
import '../product.dart';

part 'hive_adapters.g.dart';

@GenerateAdapters([
  AdapterSpec<Product>(),
  AdapterSpec<Category>(),
])
class HiveAdapters {}

Then run this command to generate the adapters:

dart run build_runner build --delete-conflicting-outputs

This creates the following files (do not delete/modify these files):

lib/hive/hive_adapters.g.dart
lib/hive/hive_adapters.g.yaml
lib/hive/hive_registrar.g.dart

3. Registering Adapters

Then in main.dart before running the app, add the following code: Register adapters before running the app:

import 'package:flutter/material.dart';
import 'package:hivez_flutter/hivez_flutter.dart';
import 'hive/hive_registrar.g.dart'; // generated
import 'product.dart';

Future<void> main() async {
  await Hive.initFlutter(); // Initialize Hive for Flutter
  Hive.registerAdapters(); // Register all adapters in one line (Hive CE only)
  runApp(const MyApp());
}

Done! You can now use the Hivez package to store and retrieve custom objects.

⚠️ 4. When Updating/Adding Types

If you add new classes or enums, or change existing ones (like adding fields or updating behavior),
just include them in your hive_adapters.dart file and re-run the build command:

dart run build_runner build --delete-conflicting-outputs

That’s it — Hive will regenerate the adapters automatically.

⚡ Quick Setup hive_ce (no explanations)

⤴️ Back → Table of Contents

For all returning users, you can use the following quick setup to get started quickly.

  1. Add the packages
flutter pub add hivez_flutter dev:hive_ce_generator dev:build_runner
  1. Setting Up Adapters in the file lib/hive/hive_adapters.dart
// lib/hive/hive_adapters.dart
import 'package:hivez_flutter/hivez_flutter.dart';
import '../product.dart';

part 'hive_adapters.g.dart';

@GenerateAdapters([
  AdapterSpec<Product>(),
  AdapterSpec<Category>(),
])
class HiveAdapters {}
  1. Run the build command
dart run build_runner build --delete-conflicting-outputs
  1. Registering Adapters in the file main.dart
// main.dart
import 'package:flutter/material.dart';
import 'package:hivez_flutter/hivez_flutter.dart';
import 'hive/hive_registrar.g.dart';

Future<void> main() async {
  await Hive.initFlutter();
  Hive.registerAdapters();
  runApp(const MyApp());
}

🚀 IndexedBox — Ultra-Fast Full-Text Search for Hive

⤴️ Back → Table of Contents

What it is: a drop-in replacement for Box that adds a tiny on-disk inverted index. You keep the same API, but get instant keyword/prefix/substring search with ~1–3 ms queries on thousands of items.

Why use it:

  • No migrations & no setup needed: your existing data and boxes stay exactly the same.
  • Blazing search: stop scanning; lookups hit the index.
    • 50,000 items: 1109.07 ms → 0.97 ms (~1,143× faster).
    • 500 items: 16.73 ms → 0.20 ms (~84× faster).
  • Zero friction: same Hivez API + search()/searchKeys() helpers.
  • Robust by design: journaled writes, auto-rebuild on mismatch, and an LRU cache for hot tokens.
  • Configurable: choose basic, prefix, or ngram analyzers; toggle AND/OR matching; optional result verification.
final articles = indexedBox.search('flut dart dev'); // Blazing fast search

Benchmarks

🔎 Full-text search (query)

Items in box Box (avg ms) IndexedBox (avg ms) Improvement
100 1.71 0.18 9.5×
1,000 16.73 0.20 84×
5,000 109.26 0.30 364×
10,000 221.11 0.39 567×
50,000 1109.07 0.97 1,143×
1,000,000 28071.89 21.06 1,333×

📥 Bulk inserts (put many)

Items inserted per run Box (avg ms) IndexedBox (avg ms) Cost of indexing
100 0.39 3.67 9.41×
1,000 0.67 9.05 13.51×
5,000 3.84 34.52 8.99×
10,000 8.21 68.02 8.29×
50,000 46.43 323.73 6.97×
1,000,000 2875.04 9740.59 3.39×

Still blazing fast:
Even though writes are heavier due to index maintenance, performance remains outstanding —
you can still write around 50,000 items in just ~0.3 seconds. That’s more than enough for almost any real-world workload, while searches stay instant.

🔄 Instantly Switch from a Normal Box (Even from Hive!)

You don’t need to migrate or rebuild anything — IndexedBox is a drop-in upgrade for your existing Hive or Hivez boxes. It reads all your current data, keeps it fully intact, and automatically creates a search index behind the scenes.

All the same CRUD functions (put, get, delete, foreachValue, etc.) still work exactly the same — you just gain ultra-fast search on top. (See Available Methods for the full API list.)

Example — from Hive 🐝 → IndexedBox ⚡

// Before: plain Hive or Hivez box
final notes = Hive.box<Note>('notes'); //or: HivezBox<int, Note>('notes');

// After: one-line switch to IndexedBox
final notes = IndexedBox<int, Note>('notes', searchableText: (n) => n.content);

That’s it — your data is still there, no re-saving needed.
When the box opens for the first time, the index is built automatically (a one-time process).
After that, all writes and deletes update the index in real time.

Now you can search instantly

final results = await notes.search('meeting notes');
print(results); // [Note(...), Note(...)]

✅ Keeps all your existing data
✅ Works even if the box was created with raw Hive
✅ Same methods and API — just faster, smarter, searchable

💡 You can freely switch back and forth between Box, HivezBox, and IndexedBox.
The data always stays compatible — IndexedBox simply adds its own index boxes under the hood.

Available Methods for IndexedBox:

⤴️ Back → IndexedBox

  • Read & search operations

    • search(query, {limit, offset}) — Retrieve values List<T> matching a search string
    • searchKeys(query, {limit, offset}) — Retrieve keys List<K> matching a search string
    • searchPairs(query, {limit, offset}) — Return key–value Map pairs for matches
    • searchStream(query, {limit, offset}) — Stream live search results Stream<T> (values)
    • searchKeysStream(query) — Stream live search results Stream<K> (keys)
    • firstMatchOrNull(query) — Get the first matching value or null
    • countMatching(query) — Count how many values match the query int
  • Extended search operations

    • searchFiltered(query, {filter, sortBy, limit, offset})
      Retrieve values List<T> matching a search string with optional filtering and sorting
    • searchPaginated(query, {page, pageSize, prePaginate})
      Retrieve values List<T> matching a search string with optional pre-pagination
  • Index management

    • rebuildIndex({bypassInit}) — Fully rebuild index from current data
    • markIndexDirty() — Mark index as dirty to trigger rebuild on next init
    • ensureInitialized() — Initialize box, index, and journal safely
    • resetRuntimeState() — Clear caches and reset journal state

💡 Same API, same power
IndexedBox fully supports all existing methods and properties of regular boxes —
including writes, deletes, backups, queries, and iteration — so you can use it exactly like HivezBox.
See the full Available Methods and Constructor & Properties sections for everything you can do.
The only difference? Every search is now indexed and blazing fast.

IndexedBox - Examples

⤴️ Back → IndexedBox

📦 Create an IndexedBox

This works just like a normal HivezBox, but adds a built-in on-disk index for fast text search.

final box = IndexedBox<String, Article>(
  'articles',
  searchableText: (a) => '${a.title} ${a.content}',
);

That’s it — no adapters, no schema, no rebuilds.

➕ Add some data

You can insert items the same way as a normal Hive box:

await box.putAll({
  '1': Article('Flutter and Dart', 'Cross-platform development made easy'),
  '2': Article('Hive Indexing', 'Instant full-text search with IndexedBox'),
  '3': Article('State Management', 'Cubit, Bloc, and Provider compared'),
});

🔍 Search instantly

Now you can query by any keyword, prefix, or even multiple terms:

final results = await box.search('flut dev');
print(results); // [Article('Flutter and Dart', ...)]

It’s case-insensitive, prefix-aware, and super fast — usually 1–3 ms per query.


🔑 Or just get the matching keys

final keys = await box.searchKeys('hive');
print(keys); // ['2']

Perfect if you want to fetch or lazy-load values later.


⚙️ Tune it your way

You can control how matching works:

// Match ANY term instead of all
final relaxed = IndexedBox<String, Article>(
  'articles_any',
  searchableText: (a) => a.title,
  matchAllTokens: false,
);

Or pick a different text analyzer for substring or prefix matching:

analyzer: Analyzer.ngram, // "hel" matches "Hello"

Done. You now have a self-maintaining, crash-safe, indexed Hive box that supports blazing-fast search — without changing how you use Hive.

🔧 Settings & Options

⤴️ Back → IndexedBox

IndexedBox is designed to be flexible — it can act like a fast keyword indexer, a prefix search engine, or even a lightweight substring matcher. The constructor exposes several tunable options that let you decide how results are matched, cached, and verified.

💡 Same API, same power
IndexedBox fully supports all existing methods and properties of regular boxes —
including writes, deletes, backups, queries, and iteration — so you can use it exactly like HivezBox.
See the full Available Methods and Constructor & Properties sections for everything you can do.
The only difference? Every search is now indexed and blazing fast.


matchAllTokens – AND vs OR Logic

What it does: Determines whether all tokens in the query must appear in a value (AND mode) or if any of them is enough (OR mode).

Mode Behavior Example Query Matches
true (default) Match all tokens "flut dart" "Flutter & Dart Tips"
"Dart Packages"
"Flutter UI"
false Match any token "flut dart" "Flutter & Dart Tips"
"Dart Packages"
"Flutter UI"

When to use:

  • true → For precise filtering (e.g. “all words must appear”)
  • false → For broad suggestions or autocomplete
final strict = IndexedBox<String, Article>(
  'articles',
  searchableText: (a) => a.title,
  matchAllTokens: true, // must contain all words
);

final loose = IndexedBox<String, Article>(
  'articles_any',
  searchableText: (a) => a.title,
  matchAllTokens: false, // any word is enough
);

tokenCacheCapacity – LRU Cache Size

What it does: Controls how many token → key sets are cached in memory. Caching avoids reading from disk when the same term is searched repeatedly.

Cache Size Memory Use Speed Benefit
0 No cache (every search hits disk) 🔽 Slowest
512 (default) Moderate RAM (≈ few hundred KB) ⚡ 100× faster repeated queries
5000+ Larger memory footprint 🔥 Ideal for large datasets or autocomplete

When to use:

  • Small cache (≤256) → occasional lookups, low memory
  • Default (512) → balanced for most apps
  • Large (2000–5000) → high-volume search UIs or live autocomplete
final box = IndexedBox<String, Product>(
  'products',
  searchableText: (p) => '${p.name} ${p.brand}',
  tokenCacheCapacity: 1024, // keep up to 1024 tokens in RAM
);

verifyMatches – Guard Against Stale Index

What it does: Re-checks each result against the analyzer before returning it, ensuring that the value still contains the query terms (useful after manual box edits).

Trade-off: adds a small CPU cost per result.

Value Meaning
false (default) Trusts the index (fastest)
true Re-verifies every hit using analyzer

When to use:

  • You manually modify Hive boxes outside the IndexedBox (e.g. raw Hive.box().put()).
  • You suspect rare mismatches after crashes or restores.
  • You need absolute correctness over speed.
final safe = IndexedBox<String, Note>(
  'notes',
  searchableText: (n) => n.content,
  verifyMatches: true, // double-check each match
);

keyComparator – Custom Result Ordering

What it does: Lets you define a comparator for sorting matched keys before pagination. By default, IndexedBox sorts by Comparable key or string order.

final ordered = IndexedBox<int, User>(
  'users',
  searchableText: (u) => u.name,
  keyComparator: (a, b) => b.compareTo(a), // reverse order
);

Useful for:

  • Sorting newest IDs first
  • Alphabetical vs numerical order
  • Deterministic result ordering when keys aren’t Comparable

analyzer – How Text Is Broken into Tokens

What it does: Defines how each value is tokenized and indexed.
Three analyzers are built in — pick one based on your search style:

Analyzer Example Matches
TextAnalyzer.basic "flutter dart" Matches whole words only
TextAnalyzer.prefix "fl" → "flutter" Matches word prefixes (default)
TextAnalyzer.ngram "utt" → "flutter" Matches substrings anywhere

For a detailed explanation, see analyzer - How Text Is Broken into Tokens.


Example: Tuning for Real Apps

final box = IndexedBox<String, City>(
  'cities',
  searchableText: (c) => c.name,
  matchAllTokens: false,
  tokenCacheCapacity: 2000,
);
  • Fast prefix matching (“new yo” → “New York”)
  • Low-latency cached results
  • Allows partial terms (OR logic)
final box = IndexedBox<int, Document>(
  'docs',
  searchableText: (d) => d.content,
  analyzer: Analyzer.basic,
  matchAllTokens: true,
  verifyMatches: true,
);
  • Each word must appear
  • Uses basic analyzer (lightweight)
  • Re-verifies for guaranteed correctness

Summary Table

Setting Type Default Purpose
matchAllTokens bool true Require all vs any words to match
tokenCacheCapacity int 512 Speed up repeated searches
verifyMatches bool false Re-check results for stale index
keyComparator Function? null Custom sort for results
analyzer Analyzer Analyzer.prefix How text is tokenized (basic/prefix/ngram)

🧩 analyzer – How Text Is Broken into Tokens

⤴️ Back → IndexedBox

What it does: Defines how your data is split into tokens and stored in the index. Every time you put() a value, the analyzer breaks its searchable text into tokens — which are then mapped to the keys that contain them.

Later, when you search, the query is tokenized the same way, and any key whose tokens overlap is returned.

You can think of it like this:

value -> tokens -> saved in index
query -> tokens -> lookup in index -> matched keys

There are three built-in analyzers, each with different speed/flexibility trade-offs:

Analyzer Behavior Example Match Speed Disk Size Use Case
Analyzer.basic Whole-word search "dart" → “Learn Dart Fast” ⚡ Fast 🟢 Small Exact keyword search
Analyzer.prefix Word prefix search "flu" → “Flutter Basics” ⚡ Fast 🟡 Medium Autocomplete, suggestions
Analyzer.ngram Any substring matching "utt" → “Flutter Rocks” ⚡ Medium 🔴 Large Fuzzy, partial, or typo-tolerant search

🧱 Basic Analyzer – Whole Words Only (smallest index, fastest writes)

analyzer: Analyzer.basic,

How it works: It only stores normalized words (lowercase, alphanumeric only).

Example:

Value Tokens Saved to Index
"Flutter and Dart" ["flutter", "and", "dart"]

So the index looks like:

flutter → [key1]
and     → [key1]
dart    → [key1]

Search results:

Query Matching Values Why
"flutter" "Flutter and Dart" full word match
"flu" prefix not indexed
"utt" substring not indexed

Use this if you want fast, strict searches like tags or exact keywords.


🔠 Prefix Analyzer – Partial Word Prefixes (great for autocomplete)

analyzer: Analyzer.prefix,

How it works: Each word is split into all prefixes between minPrefix and maxPrefix.

Example:

Value Tokens Saved
"Flutter" ["fl", "flu", "flut", "flutt", "flutte", "flutter"]
"Dart" ["da", "dar", "dart"]

Index snapshot:

fl → [key1]
flu → [key1]
flut → [key1]
...
dart → [key1]

Search results:

Query Matching Values Why
"fl" "Flutter" prefix indexed
"flu" "Flutter" prefix indexed
"utt" substring not at start
"dart" "Dart" full word or prefix match

Use this for autocomplete, live search, or starts-with queries.


🔍 N-Gram Analyzer – Substrings Anywhere (maximum flexibility)

analyzer: Analyzer.ngram,

How it works: Creates all possible substrings (“n-grams”) between minN and maxN for every word.

Example:

Value Tokens Saved (simplified)
"Flutter" ["fl", "lu", "ut", "tt", "te", "er", "flu", "lut", "utt", "tte", "ter", "flut", "lutt", "utte", "tter", ...]

(for each length n = 2→6)

Index snapshot (simplified):

fl  → [key1]
lu  → [key1]
utt → [key1]
ter → [key1]
...

Search results:

Query Matching Values Why
"fl" "Flutter" substring indexed
"utt" "Flutter" substring indexed
"tte" "Flutter" substring indexed
"zzz" substring not present

⚠️ Trade-off:

  • Slower writes (≈2–4×)
  • More index data (≈2–6× larger)
  • But can match anywhere in the text — ideal for fuzzy, partial, or typo-tolerant search.

Use this if you want “contains” behavior ("utt""Flutter"), not just prefixes.

⚖️ Choosing the Right Analyzer

If you want... Use Example
Exact keyword search Analyzer.basic Searching “tag” or “category”
Fast autocomplete Analyzer.prefix Typing “fl” → “Flutter”
“Contains” matching Analyzer.ngram Searching “utt” → “Flutter”
Fuzzy/tolerant search Analyzer.ngram (with larger n range) “fluttr” → “Flutter”

🧩 Quick Recap (All Analyzers Side-by-Side)

Value: "Flutter and Dart" Basic Prefix (min=2,max=9) N-Gram (min=2,max=6)
Tokens `flutter`, `and`, `dart` `fl`, `flu`, `flut`, `flutt`, `flutte`, `flutter`, `da`, `dar`, `dart` `fl`, `lu`, `ut`, `tt`, `te`, `er`, `flu`, `lut`, `utt`, `tte`, `ter`,...
Query "flu"
Query "utt"
Query "dart"

Hive vs Hivez

⤴️ Back → Table of Contents

Feature / Concern Native Hive With Hivez
Type Safety dynamic with manual casts Box<int, User> guarantees correct types
Initialization Must call Hive.openBox and check state Auto-initializes on first use, no boilerplate
API Consistency Different APIs for Box types Unified async API, switch with a single line
Concurrency Not concurrency-safe (in original Hive) Built-in locks: atomic writes, safe reads
Architecture Logic tied to raw boxes Abstracted interface, fits Clean Architecture & DI
Utilities Basic CRUD only Backup/restore, search helpers, iteration, box management
Production Needs extra care for scaling & safety Encryption, crash recovery, compaction, isolated boxes included
Migration Switching box types requires rewrites Swap BoxBox.lazy/Box.isolated seamlessly
Dev Experience Verbose boilerplate, error-prone Cleaner, safer, future-proof, less code

Migration-free upgrade:
If you're already using Hive or Hive CE, you can switch to Hivez instantly — no migrations, no data loss, and no breaking changes. Just set up your Hive adapters correctly and reuse the same box names and types. Hivez will open your existing boxes automatically and continue right where you left off.

Clean Architecture with Hivez

⤴️ Back → Table of Contents

A major strength of Hivez is how it fits seamlessly into Clean Architecture. Unlike raw Hive, where each box type (Box, LazyBox, IsolatedBox, etc.) exposes different APIs, all Hivez boxes share the same parent interface:

abstract class BoxInterface<K, T> { ... }

Every HivezBox variant (HivezBox, HivezBoxLazy, HivezBoxIsolated, HivezBoxIsolatedLazy) inherits this interface, which defines 35+ functions and getters, all tested and production-grade.

This makes your persistence layer consistent, testable, and replaceable — essential principles of Clean Architecture.

Why this matters

  • Dependency Inversion: Higher layers depend only on the abstract BoxInterface, not on Hive’s raw implementation.
  • Interchangeable Implementations: Swap HivezBoxHivezBoxLazyHivezBoxIsolated with a one-line change, without breaking your repository or use cases.
  • Consistency: All boxes expose the same async-safe, type-safe API. No branching logic depending on box type.
  • Testability: You can mock or fake BoxInterface in unit tests easily.
  • Future-proof: Scaling from a simple Box to an IsolatedBox in production requires no changes in your business logic.

Example: Clean Architecture Repository

With raw Hive:

class UserRepository {
  final Box _box;

  UserRepository(this._box);

  Future<User?> getUser(int id) async {
    return _box.get(id) as User?;
  }
}

Problems:

  • Box ties your repository to Hive’s low-level API
  • Type safety is weak (dynamic everywhere)
  • Changing to LazyBox breaks this class

With Hivez:

class UserRepository {
  final BoxInterface<int, User> _box;

  UserRepository(this._box);

  Future<User?> getUser(int id) => _box.get(id);
}

Advantages:

  • BoxInterface<int, User> guarantees type safety
  • Repository is decoupled from the persistence detail
  • Can inject any HivezBox variant (regular, lazy, isolated) without changing logic
  • Perfectly aligns with dependency inversion in Clean Architecture

In Practice

  • Define repositories and services against BoxInterface<K, T>
  • Swap implementations (HivezBox, HivezBoxLazy, etc.) depending on environment
  • Unit test with a mock BoxInterface — no Hive needed in tests

In short: Hivez enforces Clean Architecture by design. All boxes inherit from a single, production-ready BoxInterface with 35+ consistent, type-safe methods — so you can build scalable, testable, and maintainable apps without worrying about low-level Hive details.

❓ FAQ / Common Pitfalls

⤴️ Back → Table of Contents

Do I still need to call Hive.openBox?

No. All Box types auto-initialize on first use with ensureInitialized() under the hood.
You don’t need to worry about Hive.isBoxOpen checks or manual setup.

Does Hivez replace Hive?

No. Hivez is a safe wrapper around hive_ce. You still use Hive adapters, types, and storage — Hivez just enforces type safety, clean architecture, concurrency safety, and search capabilities.

What’s the difference between Box, Box.lazy, Box.isolated, and Box.isolatedLazy?

  • Box → Default, fast in-memory reads + async writes
  • Box.lazy → Loads values on-demand, better for large datasets
  • Box.isolated → Safe across isolates, for background workers
  • Box.isolatedLazy → Combines isolate safety + lazy loading
  • IndexedBox → Fast search for text-heavy workloads (under the hood can be any of the above)

All share the same API (BoxInterface with 35+ methods), so you can swap them with a single line.

Do I still need to register adapters?

Yes. Hive always requires TypeAdapters for custom objects and enums.
Hivez does not remove this requirement, but provides a quick setup guide.

Is it concurrency-safe?

Yes. All writes use internal locks, ensuring atomicity. Reads are async-safe.
You can safely call multiple operations in parallel without corrupting data.

Can I use Hivez in unit tests?

Yes. Since every box implements the same BoxInterface<K, T>, you can:

  • inject a real Box
  • or mock/fake the interface for fast, Hive-free tests

When should I use isolated boxes?

  • Heavy background isolates (e.g., parsing, sync engines)
  • Multi-isolate apps where multiple isolates may open the same box
    If you’re not familiar with isolate setup, stick to HivezBox or HivezBoxLazy.

Do lazy boxes support values like normal boxes?

No. Lazy boxes only load values on demand.
Use getAllValues() instead — Hivez implements this for you safely.

Can I migrate between box types later?

Yes. Since all boxes share the same API, changing from:

final box = Box<int, User>('users');

to

final box = Box<int, User>.lazy('users');

or even like this (recommended):

final box = Box<int, User>('users', type: BoxType.lazy);

When you need to switch between box type on an IndexedBox:

final box = IndexedBox<int, User>('users');
final box = IndexedBox<int, User>('users', type: BoxType.lazy);

The type is a single-word change, with no code breakage. Across all box types.

What about IndexedBox?

IndexedBox is a drop-in upgrade that adds instant full-text search. It automatically builds a small on-disk index that makes queries up to 1000× faster — while keeping your data 100% Hive-compatible.

Operation Speed Notes
Search 1–3 ms For 100,000+ items
Write ⚙️ Slightly slower Index updates per write
Data 💾 Stored in same box Index stored in hidden “_idx” boxes

You can still write 10,000 items in ~0.1 s, which is more than enough for real-world apps.

Can I use IndexedBox and regular boxes together?

Yes — they’re fully compatible. You can even open an existing box as IndexedBox the data stays synchronized. The index is just a separate lightweight companion box maintained automatically.

What’s the difference between search helpers and IndexedBox?

Feature Regular Box (search()) IndexedBox
Speed 🐢 Scans values (O(n)) ⚡ Indexed (O(log n))
Storage No index Extra _idx box (small)
Use Case Simple filtering Large data / frequent search
Accuracy Text-based match Token-based (analyzer aware)

Use IndexedBox if your app relies on frequent text queries or user search inputs.

Is the extra index space big?

Not much — even an NGram analyzer with 10 K entries adds only a few MB.
That’s a small tradeoff for millisecond search.

How do I troubleshoot errors when generating adapters?

If build_runner throws an error after adding a new model or enum:

  1. Make sure every type is listed in your hive_adapters.dart file
  2. Delete old generated files (.g.dart, .g.yaml)
  3. Re-run the generator:
dart run build_runner build --delete-conflicting-outputs

This regenerates clean adapters for all your types.

If you encounter a bug or limitation that comes from Hive itself, please note that Hivez is only a wrapper around hive_ce. That means such issues can’t be solved in Hivez. For those cases, head over to the hive_ce repository, it’s actively maintained, very stable, and the right place for core Hive questions or bug reports.

Performance & Safety

⤴️ Back → Table of Contents

One of the core design goals of Hivez is to stay as fast as raw Hive, while adding safety, type guarantees, and architectural consistency.

Practically Zero Overhead

Although all boxes in Hivez share the same BoxInterface, there are no runtime type checks on each operation.
Every method call is compiled down to direct Hive operations — engineered to be as fast, easy, and safe as possible.

  • No overhead on reads/writes — same performance as Hive CE
  • Heavily Tested200+ tests across all 35+ methods and box types ensure production safety
  • Engineered concurrency — built-in locks guarantee atomic writes and safe reads

Enforced Type Safety

Raw Hive exposes dynamic APIs, which can lead to runtime type errors.
Hivez enforces compile-time safety for both keys and values:

// Hivez: compile-time type safety
final users = Box<int, User>('users');
await users.put(1, User('Alice'));   // ✅ Valid
await users.put('wrongKey', 'test'); // ❌ Compile error

This prevents silent data corruption and eliminates the need for manual casting.

Safe Switching Between Box Types

In Hive, switching between Box, LazyBox, or IsolatedBox often breaks your code because each exposes different APIs.

Box<User> box = await Hive.openBox<User>('users');
LazyBox<User> lazy = await Hive.openLazyBox<User>('users');
// ❌ LazyBox doesn't have the same API as Box

With Hivez, all boxes (Box, Box.lazy, Box.isolated, Box.isolatedLazy) share the same API:

Box<int, User> box = Box<int, User>('users');
Box<int, User> box = Box<int, User>.lazy('users');

Your repositories and services remain untouched — a single-line change swaps the underlying storage strategy.

In short: Hivez delivers Hive performance with added guarantees — zero runtime overhead, full type safety, safe concurrency, and seamless box switching — all tested and ready for production.

Why Hivez?

⤴️ Back → Table of Contents

Over the years, while building projects both large and small, I noticed a recurring pattern: every time I reached for Hive, I ended up writing the same wrapper code to make it safer, more predictable, and easier to use.

Hive is fast and lightweight, but out-of-the-box it comes with challenges:

  • Initialization boilerplate – You always need to call openBox and check if the box is open.
  • Type safety gaps – By default, Hive uses dynamic, leaving room for runtime errors.
  • Inconsistent APIsBox, LazyBox, and IsolatedBox all have slightly different behaviors.
  • Concurrency risks – Without locks, concurrent writes can corrupt data.
  • Limited tooling – You only get basic CRUD; features like backup, search, or iteration helpers are missing.

For every new project, I found myself solving the same problems in the same way:

  • Add type parameters to enforce compile-time guarantees.
  • Write synchronized access to prevent corruption.
  • Create utility extensions for backup, restore, and search.
  • Wrap Hive APIs in a cleaner interface to fit Clean Architecture principles.

That’s when I decided to create Hivez: instead of repeating this codebase after codebase, I could create a production-ready wrapper that solves these problems once — not just for me, but for the community.

What makes Hivez different?

Hivez is not just a thin wrapper; it’s a designed architecture layer on top of Hive CE:

  • Unified API across all box types Every box — Box, Box.lazy, Box.isolated, Box.isolatedLazy — inherits from the same parent, BoxInterface. That means 35+ functions and getters are guaranteed, tested, and production-grade.

  • Type safety, enforced No more dynamic or runtime casting:

    final users = Box<int, User>('users');
    await users.put(1, User('Alice'));
    final u = await users.get(1); // returns User, not dynamic
    
  • Zero setup required No more boilerplate openBox. Each Box box automatically initializes on first use:

    final settings = Box<String, bool>('settings');
    await settings.put('darkMode', true);
    
  • Clean Architecture, by design Because every box implements the same interface, your repositories and services depend only on BoxInterface<K, T>, not Hive internals. That makes your code more modular, testable, and future-proof.

  • Utility-rich Out of the box, you get:

    • Backup/restore (JSON or compressed binary)
    • Full-text search with pagination
    • Iteration helpers (foreachKey, foreachValue)
    • Safe compaction and flushing
    • Concurrency locks for atomic operations

Why this matters in real projects

When deadlines are tight and projects grow, you don’t want to debug concurrency issues, write boilerplate initialization code, or figure out how to migrate from a Box to a LazyBox.

With Hivez:

  • Switching between box types is a one-line change.
  • Your persistence layer always has the same reliable API.
  • Your business logic is shielded from Hive’s low-level quirks.
  • You can safely scale from small apps to production-grade systems without rewriting storage code.

Hivez was born out of necessity — the necessity to write less boilerplate, avoid bugs, and follow best practices without fighting the storage layer.

📦 More jozz Packages

⤴️ Back → Table of Contents

I’m Jozz — and my packages share a simple philosophy: developer experience first. I try to avoid boilerplate wherever possible, and most of these packages were born out of real needs in my own projects. Each one comes with clear documentation, minimal setup, and APIs that are easy to pick up without surprises.

They’re built to be lightweight, reliable, and ready for production, always with simplicity in mind. There are more packages in the works, following the same approach. If you find them useful and feel like supporting, you’re welcome to do so (:

☕ Buy me a coffee

  • shrink – Compress Anything in One Line
  • track – Persistent Streaks, Counters & Records
  • prf – SharedPreferences, Without the Pain
  • time_plus – Smarter DateTime & Duration Extensions
  • exui – Supercharge Your Flutter UI
  • limit – Cooldowns & Rate Limits, Simplified
  • jozz_events – Strongly-Typed Events for Clean Architecture

🔽 shrink – Compress Anything in One Line

Because every byte counts. shrink makes data compression effortless with a one-line API and fully lossless results. It auto-detects the best method, often cutting size by 5× to 40× (and up to 1,000×+ for structured data). Perfect for Firestore, local storage, or bandwidth-sensitive apps. Backed by clear docs and real-world benchmarks.

📊 track – Persistent Streaks, Counters & Records

Define once, track forever. track gives you plug-and-play tools for streaks, counters, activity logs, and records — all persisted safely across sessions and isolates. From daily streaks to rolling counters to best-ever records, it handles resets, history, and storage automatically. Clean APIs, zero boilerplate, and deeply detailed documentation.

prf – SharedPreferences, Without the Pain

No strings, no boilerplate, no setup. prf lets you define variables once, then get() and set() them anywhere with a type-safe API. It fully replaces raw SharedPreferences with support for 20+ built-in types (including DateTime, Duration, Uint8List, JSON, and enums). Every variable is cached, test-friendly, and isolate-safe with a .isolated mode. Designed for clarity, scale, and zero friction, with docs that make local persistence finally headache-free.

time_plus – Smarter DateTime & Duration Extensions

Stop wrestling with DateTime and Duration. time_plus adds the missing tools you wish Dart had built in: add and subtract time units, start/end of day/week/month, compare by precision, yesterday/tomorrow, fractional durations, and more. Built with 128+ extensions, 700+ tests, and zero dependencies, it’s faster, more precise, and more reliable than the classic time package — while keeping APIs clear and intuitive. Ideal for scheduling, analytics, or any app where every microsecond counts.

🎨 exui – Supercharge Your Flutter UI

Everything your widgets wish they had. exui is a zero-dependency extension library for Flutter with 200+ chainable utilities for padding, margin, centering, gaps, visibility, constraints, gestures, buttons, text styling, and more — all while keeping your widget tree fully native.

No wrappers. No boilerplate. Just concise, expressive methods that feel built into Flutter itself. Backed by hundreds of unit tests and exceptional documentation, exui makes UI code cleaner, faster, and easier to maintain.

limit – Cooldowns & Rate Limits, Simplified

One line. No boilerplate. No setup. limit gives you persistent cooldowns and token-bucket rate limiting across sessions, isolates, and restarts. Perfect for daily rewards, retry delays, API quotas, or chat limits. Define once, automate forever — the system handles the timing, persistence, and safety behind the scenes. Clear docs and practical examples included.

📢 jozz_events – Strongly-Typed Events for Clean Architecture

A domain-first, framework-agnostic event bus built for scalable apps. jozz_events enables decoupled, strongly-typed communication between features and layers — without the spaghetti. It’s lightweight, dependency-free, lifecycle-aware, and integrates naturally with Clean Architecture. Ideal for Flutter or pure Dart projects where modularity, testability, and clarity matter most.

🔗 License MIT © Jozz

☕ Enjoying this package? You can support it here (:

Libraries

hivez
Hivez