Files
buzz/mobile/test
Tom Brow 5f1cd67985 perf(mobile): stop rebuilding Home's channel list when a route covers it (#9459)
Opening any route or sheet above Home (search, a message's action menu,
a profile sheet, Activity, Settings) rebuilt Home's whole channel list
twice: once when the route started covering Home and once when it
finished. On a phone the open channel sits inside Home's conversation
pane, so a sheet over a channel (long-press menu, profile) did the same.
`HomePage` and `ChannelsPage` read `ModalRoute.of(context)` in their own
builds, which depends on every part of the route's state (whether it's
current, whether it's offstage, and so on). `ChannelsPage` also depended
on the whole `DuoToolbarScope`, whose `active` flag flips whenever a
route covers the pane, through `frostedAppBarHeight`. Every channel row
and the Solo section depended on `ConversationPaneScope` for the
selected channel, and that scope notified all of them when its
route-dependent `active` flag flipped. Each of those rebuilt a fresh
channel inventory, including Solo rows that had scrolled away, which
resubscribed their typing feeds for 30 seconds.

Now:

- Home's native controls (Activity bell, tab bar, Duo rail) read the
route in a small `_NativeControlsGate` leaf, so only they rebuild when
Home is covered.
- `ChannelsPage` mirrors route currency into its header suppression
notifier from a `_RouteCurrencySync` leaf above the header, so the
header still switches in the same frame. The two "wait until this route
is current again" loops read the same notifier.
- `DuoToolbarScope` is an `InheritedModel` with a strip-only lookup,
`maybeStripOf`. The frosted header's height and `ChannelsPage` use it,
so `active` changes no longer reach them. Every other reader keeps
`maybeOf` and its current behavior.
- `ConversationPaneScope`'s `active` flag lives in its own private
inherited widget, read only by `isActiveOf`. Rows that only need the
controller no longer rebuild when it flips.
- Opening a channel looks up the conversation pane instead of depending
on it.

### Validation

New test: opening and closing a sheet above Home rebuilds neither
`HomePage`, `ChannelsPage` nor any channel row. It fails without this
change.

Full mobile test suite, `flutter analyze` and the file-size check pass.

On the iPhone 12 mini (profile build, Builderlab):

Same scripted run on `main` and on this branch (search opened and closed
3 times, then a message's long-press menu and a profile sheet over an
open channel):

| | main | this branch |
| --- | --- | --- |
| Widget builds per search open | 126 to 350 | 56 to 60 |
| Build time per search open | 79 to 90 ms | 29 to 41 ms |
| Build time, long-press menu open | 64 ms | 17 ms |
| Typing subscriptions reopened by the sheets | 9 (about 80 KB of
messages downloaded) | 0 |

The menu, profile sheet, channel header and Home's tab bar and Activity
button behaved the same in both runs.

### Regressions

None expected. The native controls and header switch from the same route
state as before, in the same frame. A `DuoToolbarScope` strip change
still reaches the header and the channel list.

---------

Signed-off-by: Tom Brow <tomb@squareup.com>
Co-authored-by: Tom Brow <tomb@squareup.com>
Co-authored-by: studio <dc2fc481343672df31a689840c09299972e704dac592e0da6f18486646a7ee3e@buzz.block.builderlab.xyz>
Co-authored-by: studio <dc2fc481343672df31a689840c09299972e704dac592e0da6f18486646a7ee3e@localhost>
2026-11-13 14:40:22 -08:00
..