Skip to content

fix(chat): share one WebView asset loader instead of one per mount - #391

Merged
hydall merged 1 commit into
nightlyfrom
fix/webview-asset-loader-singleton
Sep 5, 2026
Merged

fix(chat): share one WebView asset loader instead of one per mount#391
hydall merged 1 commit into
nightlyfrom
fix/webview-asset-loader-singleton

Conversation

@hydall

@hydall hydall commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Follow-up to #390, which cut the per-rebuild allocation but left one leaked AssetsPathHandler per chat surface mount.

chatWebViewAssetLoader() built a fresh WebViewAssetLoader on every call. Each AssetsPathHandler registers a MethodChannel under a unique name in its _init and installs a method call handler on the binary messenger; nothing disposes it, and the binding's handler map holds it for the process lifetime.

On Android the chat WebView is kept alive and reused. FlutterWebViewFactory.create returns the cached FlutterWebView for a known keepAliveId and only constructs a new one — the only place initialSettings is parsed — when there is no cached instance. Every mount after the first therefore handed the native side settings it never reads, while the handler they carried stayed registered on the Dart side.

Disposing per mount would be the wrong shape: whichever mount created the native WebView owns the live handler, and in practice that is the preloader, not the surface. Sharing one loader sidesteps the ownership question entirely — the native side only ever uses one, and handle() is stateless.

Changes

  • chat_webview_settings.dart: chatWebViewAssetLoader() returns a lazily created shared WebViewAssetLoader instead of a new one per call.

Verification

Profile build on a Pixel 8 Pro emulator (Android 16, x86_64) with a seeded 600-message chat, driven over adb; heap sampled through the Dart VM service with getAllocationProfile(gc: true).

  • AssetsPathHandler across 30 chat open/close cycles: 1 → 1 → 1 → 1 (+0, +0, +0). MethodChannel flat at 23. For comparison: +190 per 10 cycles before fix(chat): build the WebView settings once per surface, not per rebuild #390, +10 after it.
  • Dart heap over the same 30 cycles: 88,342,144 → 88,342,272 → 88,341,920 bytes — flat.
  • Cold start into the chat renders correctly: styles, italics, quote colouring, avatars, Context card. No asset-loading or console errors in logcat, so the shared handler serves appassets.androidplatform.net as before.
  • flutter analyze — 9 issues, all info/warning, identical to the nightly baseline, 0 errors.
  • flutter test — 3812 pass, including chat_webview_keep_alive_test.dart and chat_webview_security_test.dart, which cover these settings helpers. The same 4 failures as on unpatched nightly remain and are local-only (the contract test needs CI's easy_localization:generate step; the clipboard cases pass in CI on ubuntu).

🤖 Generated with Claude Code

`chatWebViewAssetLoader()` built a fresh `WebViewAssetLoader` — and with
it an `AssetsPathHandler` — on every call. Each handler's `_init`
registers a `MethodChannel` under a unique name and installs a method
call handler on the binary messenger, and nothing ever disposes it.

On Android the chat WebView is kept alive and reused
(`chatWebViewKeepAliveForPlatform`). `FlutterWebViewFactory.create`
returns the cached `FlutterWebView` for a known `keepAliveId` and only
constructs a new one — the sole place `initialSettings` is parsed — when
there is no cached instance. So every mount after the first handed the
native side settings it never reads, while the handler they carried
stayed registered on the Dart side forever.

Disposing per mount would be wrong: whichever mount created the native
WebView owns the live handler, and that is the preloader in practice.
One shared loader sidesteps the ownership question — the native side
only ever uses a single one, and `handle()` is stateless.

Verified on a Pixel 8 Pro emulator (profile build, 600-message chat):
`AssetsPathHandler` and `MethodChannel` now stay flat at 1 and 23 across
30 chat open/close cycles (+0, +0, +0), down from +10 per 10 cycles
after #390 and +190 before it. Dart heap is flat too. The chat renders
with styles, avatars and the Context card intact, and the log carries no
asset-loading errors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@hydall
hydall merged commit 1f83ac5 into nightly Sep 5, 2026
3 checks passed
@hydall
hydall deleted the fix/webview-asset-loader-singleton branch September 6, 2026 16:19
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