NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev
A unified Flutter storage package combining SharedPreferences, Hive, and Flutter Secure Storage with a clean, simple API.
Last release 10 days ago
28 Sep 2026
Ships fairly regularly
a new release about every 2 months
Most releases are documented
notes for 5 of 6 stable releases
Nothing withdrawn
no release was ever pulled
5 months old
6 releases · first in 2026
One column per month.
The unmaintained hive / hive_flutter packages (last released 2022) are replaced by their maintained community edition, hive_ce: ^2.20.1 and hive_ce_fl
hive_ceThe unmaintained hive / hive_flutter packages (last released 2022) are
replaced by their maintained community edition, hive_ce: ^2.20.1 and
hive_ce_flutter: ^2.4.0. The API this package exposes is unchanged, and so is
the on-disk format: boxes written by hive 2.x — plain, encrypted, typed and
lazy — open and read unchanged, and files written afterwards remain readable by
hive 2.x.
Breaking for consumers:
flutter_secure_storage 11 and hive_ce_flutter), and Android minSdk 24.Box, LazyBox, TypeAdapter, HiveAesCipher,
…) now come from hive_ce. An app that imports package:hive/hive.dart
directly must switch to package:hive_ce/hive_ce.dart, and hive_generator
must be replaced with hive_ce_generator. Do not keep both hive and
hive_ce in one app: each has its own Hive singleton and adapter registry.
Check with flutter pub deps | grep hive.flutter_secure_storage 11flutter_secure_storage moves from ^10.0.0 to ^11.2.0. v11 removed the
encryptedSharedPreferences Android option, which v10 already ignored after
migrating its data to AES-GCM storage with a Keystore-wrapped key. The default
Android options are now plain AndroidOptions(), which is that same storage,
so secrets written through 1.x of this package stay readable.
Breaking for consumers:
encryptedSharedPreferences: and sharedPreferencesName: from any
AndroidOptions passed to BaabaStorage.init or SecureStorage.configure;
both no longer compile. Use storageNamespace in place of
sharedPreferencesName.flutter_secure_storage older than v10 directly, and
whose users skip straight to a build on v11, loses those users' secrets:
v11 cannot read the pre-v10 formats. Ship a v10 build first, or call
FlutterSecureStorage().checkUpgradeStatus() to detect the loss.hive_ce throws a HiveError when a box's first frame is complete but cannot
be read — a wrong cipher, or a cipher applied to a cleartext box — instead of
truncating it under crash recovery. So opening an encrypted box without its
cipher, which 1.3.0 documented as silently destroying the file, now fails and
leaves the data intact. Crash recovery still trims an incomplete frame at the
end of the file. The crashRecovery: false default for ciphered opens is kept.
flutter_secure_storage option types that BaabaStorage.init
and SecureStorage.configure take — AndroidOptions, IOSOptions,
MacOsOptions, LinuxOptions, WindowsOptions, WebOptions and
KeychainAccessibility — so customising secure storage no longer needs
flutter_secure_storage in the consuming app's pubspec.hive 2.2.3 also raised the same
HiveError a second time as an unhandled async error, which reached an
app's zone error handler (and crash reporting) even when the caller caught
it; hive_ce does not.minSdk 24 (was 18); the
Linux libsecret-1-dev build requirement; a warning that Android's
resetOnError default erases every secret — including the Hive encryption
key — on a decryption error.A Hive box was previously always a cleartext file in the app's data directory, and the wrapper offered no way to change that: Hive.openBox accepts an
A Hive box was previously always a cleartext file in the app's data directory,
and the wrapper offered no way to change that: Hive.openBox accepts an
encryptionCipher, but none of openBox, openTypedBox or openLazyBox
passed one through. An app storing PII on-device had no route to an encrypted
box except bypassing this package.
All three openers now take an optional cipher:
await BaabaStorage.hive.openBox(
'citizens',
encryptionCipher: await BaabaStorage.hiveCipher(),
);
| API | Purpose |
|---|---|
hive.openBox(name, {encryptionCipher, crashRecovery}) |
Open a regular box, optionally AES-256 encrypted |
hive.openTypedBox<E>(name, {encryptionCipher, crashRecovery}) |
Same, for a box of custom objects |
hive.openLazyBox(name, {encryptionCipher, crashRecovery}) |
Same, for a lazy box |
BaabaStorage.hiveCipher({key}) |
Resolves a stable per-install AES-256 key out of Keystore-backed secure storage, generating one from a CSPRNG on first use. Memoised and single-flight, so concurrent opens cannot race into generating two keys |
BaabaStorage.hiveKeyAlias |
The secure-storage key hiveCipher uses, so consumers need not hardcode it |
hive.boxExistsOnDisk(name) |
Whether a file exists for a box, without opening it — the only way to tell "first run" from "the box is here but its key is gone", which need opposite handling |
BoxEncryptionMismatchException |
Thrown when a box is already open with a different encryption intent than the one requested |
The package now also re-exports HiveCipher and HiveAesCipher, so a consumer
can name those types without adding hive to its own pubspec.
Hive documents that on an already-open box "all provided parameters are being
ignored", and that includes encryptionCipher. Every opener here short-circuits
on an open box, so this returned a plaintext box with no error and no
encryption:
await BaabaStorage.hive.openBox('citizens'); // cleartext
await BaabaStorage.hive.openBox( // same box!
'citizens',
encryptionCipher: await BaabaStorage.hiveCipher(),
);
HiveStorage now records the encryption intent of every box it opens and throws
BoxEncryptionMismatchException when a later open disagrees, in either
direction. A box adopted from a bare Hive.openBox elsewhere in the app counts
as unknown rather than unencrypted: requesting it plaintext behaves exactly as
before, requesting it encrypted throws, because an unverifiable claim of
encryption is not one this package will make.
crashRecovery defaults to false on an encrypted openOnly affects the new ciphered code path; a call without a cipher is unchanged.
Hive computes each frame's checksum over the encryption key, so opening a
cleartext box with a cipher — or an encrypted box with the wrong key — fails the
checksum on the first frame. Hive's crashRecovery default of true reads that
as a corrupt file, truncates it, and returns an empty box without throwing.
For a damaged cleartext cache that is a reasonable trade. For an encrypted box,
where a key that does not match is far more likely than a damaged file, it turns
a recoverable problem into silent, permanent data loss.
A ciphered open therefore defaults to crashRecovery: false, which raises a
HiveError and leaves the file untouched. Pass the flag explicitly to get
Hive's behaviour back.
Note this protection cannot extend across sessions: nothing in a .hive file
records whether it is encrypted, so opening an encrypted box without its
cipher still looks like corruption to Hive and still truncates. Resolve the
cipher once at startup and pass it to every open of that box.
Two things a consumer must handle, both documented in the README:
android:allowBackup must be false. The .hive files travel in an
Android Auto Backup; the Keystore-backed key does not. A restore onto a new
device would produce encrypted boxes with no key to decrypt them.Backward compatible: every new parameter is optional and named, and no existing call site changes behaviour.
Every data operation resolved its box with Hive.box(name), which only accepts an eagerly-opened box whose value type is exactly dynamic. Hive.isBoxOpe
Every data operation resolved its box with Hive.box(name), which only accepts
an eagerly-opened box whose value type is exactly dynamic. Hive.isBoxOpen,
however, answers true for every flavour — so the BoxNotOpenException guard
passed and Hive then threw HiveError: The box "x" is already open and of type LazyBox<dynamic>, an error naming neither this package nor the caller's
mistake.
In practice that meant choosing openLazyBox or openTypedBox<T> disabled
the entire wrapper: put, putAll, get, delete, deleteKeys,
clearBox, getAll, getKeys, containsKey, length, isEmpty, watch,
listenable and closeBox all threw. closeBox was the one most likely to
bite first, in a "log out and clear storage" path.
HiveStorage now tracks the boxes it opens, along with the value type each was
opened with, and routes every operation to the widest Hive type that supports
it. Operations declared on BoxBase — all writes, deletes, and metadata, plus
watch and closeBox — now work on lazy and typed boxes alike.
Boxes opened outside BaabaStorage (a bare Hive.openLazyBox elsewhere in your
app) are adopted on first use, so the wrapper works on them too.
Backward compatible: the change only affects paths that previously threw.
| API | Purpose |
|---|---|
hive.getLazy<E>(box, key, {defaultValue}) |
Async read; works on lazy and regular boxes |
hive.getAllLazy<E>(box) |
Async read of every value; works on both flavours |
hive.isBoxLazy(box) |
true if the box is open and was opened lazily |
BoxIsLazyException |
Thrown by get / getAll / listenable on a lazy box, naming the operation and pointing at the async equivalent |
BoxTypeMismatchException |
Thrown when a box name is already open in another flavour, replacing a raw HiveError |
The package now re-exports the Hive types its own API returns — Box,
LazyBox, BoxBase, BoxEvent, TypeAdapter, BinaryReader,
BinaryWriter, HiveError, HiveObject, HiveObjectMixin — so consuming
apps can write those types in their own signatures without adding hive to
their pubspec. (Adapters generated by hive_generator are the exception: the
generated code emits its own hive import.) hive is now a direct dependency
so the re-exported version is one this package pins.
putAll is now putAll<E>(String boxName, Map<dynamic, E> entries). Hive
checks the map against the box's value type as a whole, so a
Map<dynamic, dynamic> could never satisfy a Box<UserProfile> even when
every value was one. Inference makes this source-compatible.deleteBox now calls the initialisation guard, so using it before
BaabaStorage.init() raises StorageNotInitializedException instead of
failing inside Hive.openBox<UserProfile>, which does
not exist — it is openTypedBox<UserProfile>.Nothing published for this version
PrefsStorage now emits change events so UI widgets can rebuild automatically without manual setState calls, matching the reactive API already availabl
PrefsStorage now emits change events so UI widgets can rebuild automatically
without manual setState calls, matching the reactive API already available on
HiveStorage.
| API | Returns | Use with |
|---|---|---|
prefs.watch<T>('key') |
Stream<T?> |
StreamBuilder |
prefs.listenable('key') |
ValueListenable<dynamic> |
ValueListenableBuilder |
prefs.changes |
Stream<MapEntry<String, dynamic>> |
general listener |
All write methods (setString, setInt, setDouble, setBool,
setStringList, set<T>, remove, clear) now dispatch a change event after
a successful write. remove and clear emit null as the value.
`BaabaStorage` — unified facade that initialises all three backends with a single await BaabaStorage.init() call.
Initial release.
BaabaStorage — unified facade that initialises all three backends with a single await BaabaStorage.init() call.PrefsStorage — singleton wrapper around shared_preferences with typed getters/setters (getString, setInt, …) and a generic get<T> / set<T> API. Supports String, int, double, bool, and List<String>.HiveStorage — singleton wrapper around hive_flutter with box management (openBox, openTypedBox, openLazyBox), bulk operations (putAll, getAll, deleteKeys), TypeAdapter registration, and reactive helpers (watch, listenable).SecureStorage — singleton wrapper around flutter_secure_storage with auth-token shortcuts (saveToken, getToken, hasToken, deleteToken), HTTP-header storage (saveAuthHeaders, getAuthHeaders, deleteAuthHeaders), and configurable platform options.StorageNotInitializedException, BoxNotOpenException, UnsupportedTypeException with descriptive messages.Your coding agent can read these notes before it upgrades. Set up the MCP server →