← All Projects
Featured Open Source 2026

super_cache

Production LRU and TTL cache for Flutter and Dart, with layered storage and stampede protection.

A caching library for Flutter and Dart that stops duplicate API calls, evicts in O(1), and reports live hit rates. Zero external dependencies in the core.

#dart#flutter#caching#lru#open-source#pub-dev

super_cache is a caching library for Flutter and Dart, published on pub.dev under MIT. The core has no external dependencies and runs on Android, iOS, Web, macOS, Windows and Linux.

It exists because of one specific failure: three widgets asking for the same user at the same moment fire three identical network calls. super_cache pools those into one. The same key in flight once means one request, and every caller waits on the same future.

Around that sit an O(1) LRU with TTL, three cache layers that promote entries upward on a hit, and metrics you can actually read while the app runs.

Key Features

O(1) LRU with TTL

A hash map paired with a doubly linked list keeps get, put and evict at constant time regardless of size. TTL works in absolute or sliding mode per entry, so a session token and a product listing can expire on different rules in the same cache.

Stampede protection

Concurrent reads for a key already in flight share one future instead of each firing its own request. Three widgets mounting at once produce one network call.

Three layers that promote

Memory, an AES-256-GCM encrypted buffer, and file-per-entry disk. A hit on disk promotes the entry up through the layers, so the second read is a memory read.

Policies per call site

CacheAside reads lazily, WriteThrough always fetches fresh, and RefreshAhead serves the stale value while refreshing behind it. The policy is an argument, not a global setting.

Metrics you can watch

Hit rate, eviction count and entry count are available as a snapshot or a broadcast stream, so cache behaviour is observable at runtime rather than inferred from network logs.

Deterministic tests

A companion package ships FakeCache and ManualClock, so TTL expiry and eviction can be tested without sleeping in the test suite.

Tech Stack

DartFlutterLRUTTLAES-256-GCM

Architecture

Three layers sit behind a single orchestrator. L1 is an in-memory LRU, synchronous on a hit. L2 is an AES-256-GCM encrypted in-memory buffer. L3 is file-per-entry disk storage with integrity validation. A read falls through L1 to L2 to L3 and promotes whatever it finds back up, so repeated reads settle into memory.

The API returns FutureOr<V?> rather than always returning a Future. An L1 hit resolves synchronously and never yields to the event loop, which is what keeps a memory read at roughly 0.04 microseconds instead of paying an async hop for a value already in hand.

Eviction can be driven by entry count or, with a sizeEstimator, by bytes. There is also an evictFraction() hook so the app can shed part of the cache when the platform signals memory pressure instead of waiting to be killed.

The core package depends on nothing outside the SDK. Encryption, disk persistence and test doubles live in separate companion packages, so an app that only needs an in-memory LRU does not pull in flutter_secure_storage.

Challenges & Solutions

The hard problems, and how I solved them.

01

Keeping a memory hit out of the event loop

Making every read a Future is the obvious design, and it costs an event loop hop on values already sitting in memory. Returning FutureOr instead means an L1 hit resolves synchronously, which measured 0.04 microseconds per get against 0.22 once the orchestrator adds an async layer. The tradeoff is a slightly more awkward type at call sites, which the repository mixin hides.
02

Being honest about what the encrypted layer protects

SecureCache encrypts values at rest in memory, but decrypted values still live in the process. Calling that secure storage would be a false claim, so the docs say plainly that it is not hardware enclave protection. The README also lists cases where the package is the wrong tool: fewer than five values, multi-isolate access, or blobs above 50 MB, where a local database is the better answer.

Interested in working together?

I'm always open to discussing new projects and opportunities.