Files
buzz/mobile/test/shared/widgets
Tom Brow 920b1e64bd perf(mobile): keep covered pages still while the keyboard moves (#8941)
## Summary

A keyboard raised by a route above (the Add members sheet, search, a
native picker) changes the app's `MediaQuery` on every animation frame.
Covered pages that read `MediaQuery` directly or through a `Scaffold`
rebuilt and relaid out all their content for keyboard motion they cannot
show. On #4things, typing in Add members rebuilt every visible message
of the channel page underneath for each keyboard frame.

`CoveredRouteKeyboardInsets` holds a page's `viewInsets` and `padding`
at their last values from when its route was current, while the route is
covered. Every other `MediaQueryData` field passes through. A covered
page still follows the keyboard down, so when a covering route takes
focus and the page's own keyboard closes, the page lowers behind the
scrim. A size change (rotation) drops the held values so safe-area
padding stays right. When the route becomes current again with the
keyboard still above the held value, the page keeps the held values
rather than jumping up and riding the keyboard back down. It switches to
live values once the keyboard reaches the held value, or as soon as
focus is inside the route (the page raised its own keyboard). Otherwise
the live values return. It always returns the same widget shape, so
covering a page never remounts it. Home, search, the channel page and
channel details use it.

## Measurements

iPhone 12 mini, Profile build, Builderlab #4things (1,361 members).
Flow: open the channel, open its details, tap Add members, tap the
search field, then type "chris" one character per second. The window is
the 10 s of typing, keyboard included. Both builds also contain the
fixes from [#8928](https://github.com/block/buzz/pull/8928),
[#8929](https://github.com/block/buzz/pull/8929) and
[#8931](https://github.com/block/buzz/pull/8931).

| | Before | After |
| --- | --- | --- |
| UI frame time, total | 3,344 ms | 383 ms |
| Frames over 16.7 ms | 101 of 232 | 2 of 297 |
| Median / p90 frame | 14.1 / 26.1 ms | 0.6 / 2.0 ms |
| UI isolate CPU samples (1 ms) | 2,793 | 868 |
| Samples in `MessageContent.build` | 621 | 0 |

Single runs per side, not repeated.

## Testing

- New widget tests: a covered page doesn't rebuild for keyboard frames
and gets the live insets back when it's current again. A current page
follows the keyboard. Covering a page keeps its state. A page uncovered
with the keyboard still up does not jump, and follows the keyboard once
it reaches the held value. A page whose own field takes focus after
being uncovered follows the keyboard immediately. A covered page follows
its own keyboard closing and takes new padding when rotated. The last
test fails (3 mounts instead of 1) against a version that returned a
different widget tree when covered.
- `just mobile-check mobile-test` passes.
- On the phone: Add members search, typing and results behave as before.
The channel page and search still move above the keyboard when they're
current.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

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: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: studio <dc2fc481343672df31a689840c09299972e704dac592e0da6f18486646a7ee3e@localhost>
2026-11-04 20:27:59 -08:00
..