Skip to content

feat(ui): configure StreamNetworkImage cache manager with LRU eviction - #143

Open
VelikovPetar wants to merge 1 commit into
mainfrom
feat/FLU-598_lru_image_cache
Open

feat(ui): configure StreamNetworkImage cache manager with LRU eviction#143
VelikovPetar wants to merge 1 commit into
mainfrom
feat/FLU-598_lru_image_cache

Conversation

@VelikovPetar

@VelikovPetar VelikovPetar commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Linear: FLU-598

Github Issue: #

CLA

  • I have signed the Stream CLA (required).
  • The code changes follow best practices
  • Code changes are tested (add some information if not applicable)

Description of the pull request

Routes every StreamNetworkImage through a single shared cache manager
(StreamImageCache.manager) instead of the library's package-default one, so the
whole SDK caches images in one place that we control.

What changed

  • StreamImageCache — a lazily-created, process-wide BaseCacheManager
    (from cached_network_image_ce) shared by every StreamNetworkImage. Within
    this package that is StreamAvatar and StreamEmoji (both render through
    StreamNetworkImage); every downstream consumer inherits it too — e.g. in the
    chat SDK, message image attachments and the composer header preview.
  • Platform-specific factory (conditional import):
    • IO (Android / iOS / macOS / desktop) — a DefaultCacheManager configured
      with LruCleanupStrategy and a dedicated cacheDirectoryProvider pinned to
      <app-temp>/stream_image_cache/. The library then nests its own
      cached_network_image_ce/ folder under that.
    • Web — reuses the library's shared CachedNetworkImageProvider.defaultCacheManager
      (no filesystem to namespace, so per-instance isolation is impossible).
  • Dependency bumpscached_network_image_ce ^4.6.3 → ^4.9.0, which
    provides the cleanupStrategy and cacheDirectoryProvider parameters on
    DefaultCacheManager that the IO factory relies on (LRU CleanupStrategy was
    introduced in 4.8.0). Plus path and path_provider. Mirrored in the
    workspace melos.yaml.
  • Barrel contractlib/src/cache/internal registered under internal_dirs
    in check_barrels.yaml (implementation-only, excluded from the public barrels).

Benefits

  • Size-limited & self-evicting. On IO the cache honours the library's object
    limit and evicts least-recently-used entries first once full, so image disk
    usage stays bounded — the core ask of FLU-598.
  • Isolated from the host app. A bare DefaultCacheManager() (which many apps
    use directly) stores its files + Hive metadata under <temp>/cached_network_image_ce.
    Two managers sharing that directory keep separate, mutually-unaware metadata
    boxes, so each one's orphan sweep can delete the other's files. Nesting our
    manager under a dedicated stream_image_cache/ sub-directory prevents that.
  • One cache for the whole SDK. Avatars and attachments share a single store
    and a single eviction policy, and evictFromCache (used by the tap-to-retry
    path) targets that same manager.
  • No public API change. StreamNetworkImage's signature is unchanged;
    StreamImageCache is an internal detail.

Shortcomings / limitations (intentional, documented here for review)

  • Web caching is unchanged and is not SDK-managed. CachedNetworkImage
    defaults to ImageRenderMethodForWeb.HtmlImage, which renders via
    ui_web.createImageCodecFromUrl(url) — a direct browser fetch. On web the cache
    manager (and its IndexedDB stores) is never consulted for rendering; caching
    is the browser's HTTP cache. This is pre-existing behaviour, not a regression
    (both the old and new cached_network_image_ce default to HtmlImage, and we
    never set the render method). Consequence: web gets no size limit / LRU / offline
    guarantees from this cache, and httpHeaders are unsupported on web with
    HtmlImage (the library throws). Opting web into SDK-managed caching would mean
    passing ImageRenderMethodForWeb.HttpGet, which has its own trade-offs
    (Dart-side decode, memory, GIF/anim differences) — deliberately left out of scope.
  • Cache size and stale period use the library defaults (~200 objects / 7-day
    stale) and are not yet consumer-configurable. A follow-up could expose a knob.
  • Eviction runs at manager init (≈ app launch), not continuously. The library
    sweeps once when the manager first initialises, so within a single long-lived
    session the on-disk count can exceed the limit until the next launch. This is
    cached_network_image_ce behaviour, surfaced here for awareness.

How this was tested

  • Unit: stream_network_image_test.dart asserts all images route through the
    one shared manager; stream_image_cache_factory_io_test.dart is a signature-drift
    guard that compiles the IO factory against the real IO constructor on the VM.
  • Manual, via the stream_chat_flutter sample app (local path override to this
    branch):
    • Android & iOS — loaded avatars + image attachments + a GIF, disabled
      connectivity, killed and relaunched: images still rendered from disk (proving
      cross-session disk cache + offline). Confirmed on-disk that the files land
      under …/stream_image_cache/cached_network_image_ce/ (isolated dir) with the
      Hive metadata box.
    • macOS — confirmed the IO factory is selected and cached avatar files + the
      Hive box land in the same isolated stream_image_cache/ dir under the app
      sandbox (desktop + sandbox path verification).
    • Web (Chrome) — confirmed the browser-cache behaviour described above (the
      SDK manager is instantiated but not used for rendering).

Screenshots / Videos

Before After
img img

Summary by CodeRabbit

  • New Features

    • Improved network image caching with one shared cache across image components.
    • Added app-scoped cache storage with automatic LRU eviction on supported platforms.
    • Preserved standard caching behavior on web and other platforms.
  • Bug Fixes

    • Reduced duplicate cache managers and improved cache consistency for network images.
  • Tests

    • Added coverage verifying shared cache usage and platform-specific cache manager creation.

…and LRU eviction

Route every StreamNetworkImage through a single shared cache manager
(StreamImageCache.manager). On IO platforms it uses an LruCleanupStrategy and an
isolated `<temp>/stream_image_cache/cached_network_image_ce/` directory, so the
SDK image cache is size-limited (library default), evicts least-recently-used
entries first, and cannot be clobbered by a host app's own DefaultCacheManager
sweeping the shared temp location. On web it reuses the library's shared manager
since filesystem isolation is not possible there.

Bumps cached_network_image_ce to ^4.9.0 and adds path / path_provider.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e5ada168-eebc-42a6-868a-ea64c3ccd1fd

📥 Commits

Reviewing files that changed from the base of the PR and between ab4b1fb and e1f47c0.

📒 Files selected for processing (10)
  • melos.yaml
  • packages/stream_core_flutter/CHANGELOG.md
  • packages/stream_core_flutter/check_barrels.yaml
  • packages/stream_core_flutter/lib/src/cache/internal/stream_image_cache.dart
  • packages/stream_core_flutter/lib/src/cache/internal/stream_image_cache_factory.dart
  • packages/stream_core_flutter/lib/src/cache/internal/stream_image_cache_factory_io.dart
  • packages/stream_core_flutter/lib/src/components/common/stream_network_image.dart
  • packages/stream_core_flutter/pubspec.yaml
  • packages/stream_core_flutter/test/cache/internal/stream_image_cache_factory_io_test.dart
  • packages/stream_core_flutter/test/components/common/stream_network_image_test.dart

📝 Walkthrough

Walkthrough

The change adds a shared BaseCacheManager for StreamNetworkImage. IO platforms use an app-scoped temporary cache with LRU eviction. Other platforms use the library default cache manager. Tests cover manager construction and shared widget usage.

Changes

Stream image caching

Layer / File(s) Summary
Cache dependencies and internal configuration
melos.yaml, packages/stream_core_flutter/pubspec.yaml, packages/stream_core_flutter/check_barrels.yaml
The workspace and package dependencies use cached_network_image_ce, path, and path_provider. The internal cache directory is configured for barrel checks.
Platform-specific cache manager creation
packages/stream_core_flutter/lib/src/cache/internal/*
StreamImageCache lazily creates one shared cache manager. IO platforms use an app-scoped temporary directory and LRU eviction. Other platforms use the cached-network-image default manager.
Image rendering and cache validation
packages/stream_core_flutter/lib/src/components/common/stream_network_image.dart, packages/stream_core_flutter/test/cache/internal/*, packages/stream_core_flutter/test/components/common/*, packages/stream_core_flutter/CHANGELOG.md
StreamNetworkImage uses the shared manager for rendering and eviction. Tests validate manager construction and shared widget usage. The changelog records the caching behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant StreamNetworkImage
  participant StreamImageCache
  participant CacheFactory
  participant BaseCacheManager
  participant PathProvider
  StreamNetworkImage->>StreamImageCache: access shared manager
  StreamImageCache->>CacheFactory: createStreamCacheManager()
  CacheFactory->>BaseCacheManager: create configured manager
  BaseCacheManager->>PathProvider: resolve IO cache directory
  StreamNetworkImage->>BaseCacheManager: render image and evict cache entries
Loading

Suggested reviewers: brazol

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: configuring StreamNetworkImage with LRU cache eviction.
Description check ✅ Passed The description follows the template, documents the implementation and limitations, and provides unit and manual testing details.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/FLU-598_lru_image_cache

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.66667% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 54.72%. Comparing base (ab4b1fb) to head (e1f47c0).

Files with missing lines Patch % Lines
...ter/lib/src/cache/internal/stream_image_cache.dart 50.00% 1 Missing ⚠️
.../cache/internal/stream_image_cache_factory_io.dart 80.00% 1 Missing ⚠️
...ib/src/components/common/stream_network_image.dart 50.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #143      +/-   ##
==========================================
+ Coverage   54.70%   54.72%   +0.02%     
==========================================
  Files         185      187       +2     
  Lines        7575     7583       +8     
==========================================
+ Hits         4144     4150       +6     
- Misses       3431     3433       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@VelikovPetar VelikovPetar changed the title feat(ui): configure StreamNetworkImage cache manager with size limit and LRU eviction feat(ui): configure StreamNetworkImage cache manager with LRU eviction Jul 31, 2026
@VelikovPetar
VelikovPetar force-pushed the feat/FLU-598_lru_image_cache branch from d022f9d to e1f47c0 Compare July 31, 2026 09:23
@VelikovPetar
VelikovPetar marked this pull request as ready for review July 31, 2026 09:24
@VelikovPetar
VelikovPetar requested a review from a team as a code owner July 31, 2026 09:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant