Profile traces showed 100-370ms UI-thread frames while scrolling the
library screen, dominated by rebuilding and inflating media cards.
- Gate per-card focus/pointer chrome on input mode: FocusableWrapper
skips the scale/border wrappers and creates its AnimationController
lazily outside keyboard mode, and ClickableCursor plus the card tap
region only build MouseRegion/InkWell machinery on desktop - TV and
touch use a bare GestureDetector. Hub cards also drop their outer
gesture wrapper outside keyboard mode; the card's own tap region
always won the gesture arena anyway.
- Memoize sliver children (SliverChildMemo): browse/collections grids
and hub rows return identical widget instances for unchanged items,
so delegate swaps from pagination, watch-state, and deletion
setStates no longer rebuild every realized card inside layout. The
browse tab prunes the memo in lockstep with focus-node eviction so a
cached card can never resurrect a disposed FocusNode.
- Budget fresh inflation (CardInflationBudget): while a scrollable is
moving in pointer/touch mode at most one new card inflates per frame,
the rest render as SkeletonMediaCard and upgrade on following frames.
Hub rows also stop pre-inflating 250px of off-screen cards on entry.
Device traces: worst frame 373ms -> 103ms, per-card build 3.6ms ->
2.4ms median; remaining row-entry work is spread across frames.
Memoize the per-image SHA-1 disk-cache key (ran on the UI thread on
every image build), fade images in via paint alpha instead of an
AnimatedSwitcher saveLayer per in-flight tile, create SettingsBuilder's
merged listenable once instead of per build, and drop the pointer
chrome (InkWell ink machinery, cursor MouseRegions) from cards on TV
where no pointer exists.