NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Synchronous, lightweight key-value storage with crash-safe writes (write-ahead + atomic rename)
Last release 2 months ago
28 Jul 2026
Ships on a steady schedule
a new release about every 2 weeks
Nearly every release is documented
notes for 9 of 9 stable releases
Nothing withdrawn
no release was ever pulled
3 months old
11 releases · first in 2026
One column per month.
First stable 1.x release, consolidating the lifecycle, persistence and Web hardening validated during the beta cycle.
First stable 1.x release, consolidating the lifecycle, persistence and Web
hardening validated during the beta cycle.
. and .. are rejected even in
compatibility mode, preventing path traversal and different logical
containers from silently resolving to the same file..db file through repeated separators or
case aliases on Windows/macOS. Safe nested legacy names remain supported.writeAndSave() waits for the normal OS
write, while writeAndFlush() flushes the temporary file before rename and
provides the strongest durability currently implemented by AllBox. It does
not promise universal power-loss survival because parent-directory metadata
is not explicitly synchronized on every platform/filesystem.window.localStorage remains the default backend.
The migration-backed IndexedDB backend remains experimental and available
only through experimentalIndexedDbBackend: true.3.3.0, real Chrome/IndexedDB, WASM
and the Flutter example were validated for the stable release candidate.1.0.0-beta.2.Documentation and release metadata correction for the beta channel.
Documentation and release metadata correction for the beta channel.
^0.7.0.^1.0.0-beta.2.1.0.0-beta.1;
no unpublished intermediate release is documented.Pre-release for validating the Web IndexedDB backend without changing the stable default.
Pre-release for validating the Web IndexedDB backend without changing the stable default.
AllBox.init(..., experimentalIndexedDbBackend: true) routes Web containers through the
localStorage -> IndexedDB migration backend. The default remains
window.localStorage.experimentalIndexedDbBackend
returns Web initialization to localStorage and does not read existing
IndexedDB data.containers object store exists after opening the
database. Incompatible databases fail with an explicit diagnostic instead
of surfacing later as a generic transaction failure.versionchange, so database
deletion/version upgrades can proceed, and that truly blocked deletion is
reported as an error instead of hanging.localStorage data, removes the
legacy copy only after IndexedDB accepts it, reloads from IndexedDB, and
reports backendDetail: indexedDBMigration through the inspector.AllBox.init() back to localStorage
without reading existing IndexedDB data, and that the migration path keeps
multiple containers isolated.AllBox.init() on Web still uses
window.localStorage by default. The IndexedDB backend and migration
wrapper remain experimental and opt-in in this beta.all_observer README example was added.Reliability and release-infrastructure hardening, with a deliberately small public API surface.
Reliability and release-infrastructure hardening, with a deliberately small public API surface.
AllBox.close() flushes pending data, releases the
storage backend and unregisters the container instance; AllBox.destroy()
closes the box and removes the persisted data for logout/reset scenarios.AllBox.init(..., onPersistenceError: ...) reports async persistence failures from debounced write() calls,
preserving the existing synchronous write ergonomics while making dropped
disk/Web writes observable by logging/telemetry code.validateContainerName: true
rejects unsafe/non-portable container names. It remains opt-in to avoid
breaking existing users that already persist names such as user/cache or
cache:name.<container>.db and
<container>.bak exist but are unreadable/corrupted, debug builds now log
a diagnostic that names both files and explains that the in-memory
container started empty. init() still does not throw on corrupted data.AllBoxInspector now deep-freezes nested
Map/List values in snapshots, so later mutations of stored values cannot
mutate an already-created snapshot.write(), writeAndSave() and
writeAndFlush(). No strictSerialization public API was added.stable and the package's minimum SDK
(3.3.0, via pub downgrade), plus Chrome Web storage tests and a
dart2wasm compile smoke test.tool/web_storage_benchmark.dart) and an optional real-Chrome
window.localStorage benchmark
(test/web/all_box_web_storage_browser_benchmark_test.dart).AllBox.init() yet; the default Web backend remains
window.localStorage.AllBoxBackendKind.web stable for the whole browser-storage family and add
the optional JSON/object field backendDetail for tooling that needs to
distinguish localStorage, indexedDB, and indexedDBMigration. The
mutation event kind/payload remains unchanged.window.localStorage; IndexedDB, Workers/Service Workers and safe multi-tab
writes remain future backend work, not promises of this release.AllBoxStorage now
includes close(). Apps using the built-in IO/Web/memory storage do not
need any migration, but custom AllBoxStorage implementations must add a
Future<void> close() method. A no-op implementation is fine when there
are no resources to release.Adds debug-only push notifications for external tooling, as a follow-up to AllBoxInspector (0.5.0) — closes the "Fase 2" item from all_box_devtool's r
Adds debug-only push notifications for external tooling, as a follow-up
to AllBoxInspector (0.5.0) — closes the "Fase 2" item from
all_box_devtool's roadmap.
write(), writeAndFlush(), writeAndSave(), remove()
(when a key actually existed) and erase() now also post a VM Service
extension event (AllBoxInspector.mutationEventKind,
'all_box:mutation') right after mutating memory, in debug/profile
builds only. Payload is intentionally minimal — {container, op, key?}
— no value and no full snapshot; listeners are expected to re-fetch via
AllBoxInspector.snapshotOfAsJson/snapshotAsJson.Stream,
or anything Dart code (including this package's own) can subscribe to.
developer.postEvent only ever reaches tooling attached over the VM
Service protocol and is a no-op with nothing attached — the 0.4.0
promise ("write()/remove()/erase() never notify anything") was always
about Dart-visible notification, which this does not add. See
AllBox._debugPostMutationEvent's doc comment for the full reasoning.allBoxDebugMode guard as everything
else in this family of changes.AllBoxInspector.snapshot() on a timer — polling remains a fine and
fully supported fallback; this is additive, not a replacement.Adds AllBoxInspector: a read-only, debug/profile-only introspection surface, built to support external tooling (e.g. a DevTools extension) without rei
Adds AllBoxInspector: a read-only, debug/profile-only introspection
surface, built to support external tooling (e.g. a DevTools extension)
without reintroducing the listener/reactive API removed in 0.4.0.
AllBoxInspector.snapshot() / AllBoxInspector.snapshotOf(container)
return AllBoxContainerSnapshots: container name, isInitialized,
storage backend (AllBoxBackendKind: io / web / memory /
unsupported / custom), pendingFlush (whether a debounced write is
still waiting), a read-only copy of entries, and an
approximateSizeBytes estimate.AllBoxContainerSnapshot.toJson(), and its JSON-string
counterparts AllBoxInspector.snapshotAsJson() /
snapshotOfAsJson(container), meant for callers on the far side of a VM
Service eval boundary (e.g. a DevTools extension using
EvalOnDartLibrary) — evaluating a single expression that returns a
String is far simpler than walking a List<AllBoxContainerSnapshot>
field-by-field over the VM Service protocol. Non-JSON-encodable values
inside entries are replaced with a '<non-JSON-encodable: Type>'
placeholder instead of making the whole snapshot fail to serialize.null / '[]' / 'null') in release
builds, mirroring the existing allBoxDebugMode guard used by
allBoxDebugLog.AllBox's existing public surface
(read/getKeys/getValues/hasData/write/...) is unchanged. What
was missing before this release was discovery — knowing which
containers exist at all in the current isolate — and backend/flush
metadata that no existing getter exposed; per-container reads already
covered "I know the name and want its data" and remain regular
(non-debug-only) public API.storage: interface
member, so it cannot break existing custom AllBoxStorage
implementations.Web support, plus a breaking change: the reactive layer is gone.
Web support, plus a breaking change: the reactive layer is gone.
The public core import stays exactly the same
(import 'package:all_box/all_box.dart';) — platform selection happens
internally via Dart's conditional imports (dart.library.io /
dart.library.js_interop), not via anything a consumer imports or
configures.
Breaking: reactivity removed. all_box no longer has any
listener/reactive API. Removed:
AllBox.listenKey/removeListenKey/listenAll (the core no longer
notifies anything on write()/remove()/erase()).AllBoxListenable<T> and AllBoxBuilder<T> (the Flutter reactive
layer), and the package:all_box/all_box_flutter.dart entrypoint
entirely — there is now a single entrypoint,
package:all_box/all_box.dart, for every platform including Flutter..val() extension on String (AllBoxValue/AllBoxValueExtension),
which existed only to read/write through the now-removed reactive layer.test/all_box_flutter_test.dart (it only tested the removed reactive
layer); the erase()/remove()/.val() tests in the remaining suites
were updated to no longer assert on listener notifications.Why: this keeps all_box a plain synchronous key-value store with no
opinion on state management, and makes the package pure Dart — it no
longer depends on the Flutter SDK at all (pubspec.yaml no longer
declares a flutter: dependency; flutter_test was replaced by
package:test in the package's own test suite).
Migration: call write()/writeAndFlush()/erase() as before, then
update your UI the same way you would for any other synchronous,
non-reactive storage — e.g. setState right after the call in a
StatefulWidget, or by wiring all_box into a ChangeNotifier you own,
all_observer, or whatever state-management approach your app already
uses. There is no drop-in replacement inside all_box itself for
AllBoxBuilder/AllBoxListenable/listenKey/listenAll/.val().
path-free: AllBox.init('settings') (no
path) now works on Web, backed by window.localStorage via pure
dart:js_interop static interop — never dart:html (which blocks
dart2wasm compilation) and no new dependency (package:web wasn't
needed). On IO platforms, behavior is unchanged: path is still how you
tell AllBox where <container>.db lives.AllBox.memory(container, {initialData}): promoted, non-deprecated
replacement for initWithMemoryBackendForTesting() (which still works,
now as a thin @Deprecated wrapper around memory()).AllBoxStorage (the storage seam — IO, Web,
in-memory, or your own), AllBoxPersistMode (save/flush durability
tiers), AllBoxStorageException (unsupported platform, missing path on
IO, JSON encoding failures, Web storage quota/availability errors). All
advanced/optional — everyday code never touches them.test/all_box_memory_storage_test.dart,
test/all_box_platform_storage_test.dart,
test/web/all_box_web_storage_test.dart (fake-backed, runs on the VM),
and test/web/all_box_web_storage_browser_test.dart (real
window.localStorage, @TestOn('browser')-gated — run with
dart test -p chrome test/web/all_box_web_storage_browser_test.dart).
Added large-payload/large-volume coverage (5,000-key round-trips, ~200 KB
single values) across IO, memory and Web storage.Breaking changes — and why they're mostly not a problem in practice:
AllBox.init() return type changed from Future<void> to
Future<AllBox> (it now returns the initialized instance, so
final box = await AllBox.init('settings'); works directly, instead of
needing a separate AllBox('settings') call afterwards). This is
source-breaking only for code that explicitly typed the return value as
Future<void> or passed AllBox.init itself as a
Future<void> Function(...) callback/tear-off — a plain
await AllBox.init(...) call site (the documented, common usage) is
unaffected.path changed from a required named parameter to an optional one
(String? path). This is not breaking on its own — relaxing
required → optional never breaks a call site that already passed path.
It does change the failure mode on IO when path is omitted: previously
a compile-time "missing required argument" error, now a runtime
AllBoxStorageException with a clear message. Code that already
compiled before is unaffected either way.>=3.0.0 to >=3.3.0
(required by the dart:js_interop extension types used in the Web
storage backend). This is breaking for any consumer pinned to a Dart
SDK older than 3.3.0 — pub get/flutter pub get will fail to resolve
this version for them.dart:io, dart:js_interop
and the conditional-import platform selection are entirely internal to
the package (lib/src/core/storage/platform/); package:all_box/all_box.dart
is still the only import needed, on every platform.Performance release — additive API only, no new dependencies for the package itself, on-disk format unchanged.
Performance release — additive API only, no new dependencies for the package itself, on-disk format unchanged.
New writeAndSave(): intermediate durability tier. Completes once
the OS write finishes (survives an app crash — the same guarantee class
as Hive.put), without the fsync cost of writeAndFlush() (the only
tier that survives power loss). Keeps the full write-ahead +
atomic-rename pipeline. Durability ladder: write() →
writeAndSave() → writeAndFlush(). When both tiers coalesce into the
same flush, the strongest requirement wins.
Flush path ~2× less I/O: the pre-flush backup (.db → .bak) is now
a metadata-only rename instead of a full byte-for-byte copy.
Crash-safety guarantees are unchanged: at any instant either .db or
.bak holds a complete known-good version, and init() already falls
back to .bak.
Flush coalescing: N concurrent writeAndFlush() calls now collapse
into at most 2 real disk writes (one in-flight + one queued) instead of N
sequential full-file writes. Each caller's Future still only completes
once its value is durably on disk.
write() hot path: the debounce timer is now armed once per burst
instead of being cancelled and recreated on every write() (the timer
churn used to cost more than the in-memory map update itself). Side
benefit: continuous writes can no longer starve the flush — it now fires
at most flushDelay after the first write of a burst.
New test suite test/flush_performance_test.dart (21 tests): db/bak
invariants, simulated crash windows, coalescing contracts, timer
semantics, durability round-trips, and randomized mutation stress.
New on-device benchmark screen in the example app (example/lib/benchmark/)
comparing all_box vs Hive and SharedPreferences with the same loops in
the same session (multiple rounds, median reported) — including honest
per-lib durability-guarantee caveats. Competitor dependencies live only
in the example app. GetStorage is not in the measured comparison: its
write() Future resolves without waiting for any disk write (there is
no API in it that does), so there is nothing comparable to measure; it
remains covered in the qualitative comparison docs.
Updated comparison docs and charts with numbers measured on a real device in profile mode.
benchmark/benchmark_test.dart: the package benchmark now runs via
flutter test benchmark/benchmark_test.dart (dart run never worked —
the package imports flutter/foundation).
Documentation-only release — no code changes to lib/, no breaking changes, no new external dependencies.
Documentation-only release — no code changes to lib/, no breaking
changes, no new external dependencies.
README.md/README.pt-BR.md into the same landing-page
format used by all_observer (table of contents, feature highlights,
compact comparison table, "when to use it" section, "other packages by
us" table, EN ⇄ PT-BR language switch links).documentation/en/comparison.md and
documentation/pt-BR/comparison.md: a detailed, factual comparison
against GetStorage, Hive, Isar, and SharedPreferences, including a
performance benchmark table (1,000 operations: in-memory write, sync
read, durable disk write).doc/comparison_benchmark_en.png / doc/comparison_benchmark_pt-BR.png)
plotting all five solutions on a log scale.test/all_box_test.dart.AllBox.init() gains an initialData parameter, seeding default values on a genuine first run only (checked via the presence of .db/ .bak on disk, not v
AllBox.init() gains an initialData parameter, seeding default values
on a genuine first run only (checked via the presence of
<container>.db/<container>.bak on disk, not via in-memory state, so a
container emptied by a previous erase() is correctly never reseeded).
The seed is persisted immediately, bypassing the debounce window.write()/writeAndFlush() no longer throw ArgumentError for a
non-JSON-encodable value. In debug builds only, they now log a loud
debugPrint warning (wrapped in ANSI red) instead, matching
GetStorage's permissive behavior — the write still goes through in
memory either way.Synchronous in-memory reads (read, readOrDefault, hasData, getKeys, getValues).
Initial release.
read, readOrDefault, hasData,
getKeys, getValues).write) and forced-flush writes
(writeAndFlush); flushNow() to bypass the debounce window on demand
(e.g. AppLifecycleState.paused).dart:convert.<container>.tmp first, then an
atomic rename replaces <container>.db; the previous good file is kept
as <container>.bak.<container>.bak, then to an empty container — init()
never throws on a corrupted file.writeAsString calls on the same file
are never allowed to race.path is a required parameter of AllBox.init() — no internal
path_provider dependency, no plugin-path MissingPluginException risk.listenKey/removeListenKey, listenAll (returns a dispose callback),
erase() notifying every previously-existing key's listeners.AllBoxListenable<T> (ChangeNotifier + ValueListenable<T?>) and
AllBoxBuilder<T> widget — pure-Flutter reactive layer..val() extension on String, DI-free.AllBox.initWithMemoryBackendForTesting() — pure in-memory backend for
apps/packages that consume all_box to unit/widget-test their own code,
with no real disk I/O and no real Timer scheduled on write().write()/writeAndFlush() validated that the value was JSON-encodable
synchronously and threw ArgumentError immediately if not (superseded in
0.2.0 by a debug-only warning — see above).Your coding agent can read these notes before it upgrades. Set up the MCP server →