Skip to content

Scroll-view: honour auto_scroll_to, gap on Android, and chrome collapse - #27

Open
shanerbaner82 wants to merge 1 commit into
mainfrom
feat/scroll-view-auto-scroll-to
Open

Scroll-view: honour auto_scroll_to, gap on Android, and chrome collapse#27
shanerbaner82 wants to merge 1 commit into
mainfrom
feat/scroll-view-auto-scroll-to

Conversation

@shanerbaner82

Copy link
Copy Markdown
Contributor

Three scroll-view renderer gaps, all surfaced while building a chat screen.

Important

Depends on NativePHP/mobile-air#241 — this consumes ChromeScrollController from core and will not compile without it. Merge and release that first.

1. auto_scroll_to was never implemented

ScrollView::autoScrollTo() sets the prop, but no renderer on either platform read it — it was written into the wire payload and dropped on the floor. BenchmarkComponent.php:992 relies on it to scroll a long list, so that benchmark scenario has been measuring a static list.

  • iOS — reuses the ScrollViewReader already on the vertical branch, scrolling to the target child's id; the horizontal branch gains a reader so it behaves the same.
  • Android — drives LazyColumn's existing listState by index; LazyRow gains a state to drive.

Both jump without animation on first appear (opening at a target shouldn't look like a scroll) and animate later moves, matching the existing scroll-anchor path. An explicit index wins over scroll-anchor="bottom". Negative and out-of-range indices are no-ops.

2. gap-* was ignored on Android

The flex engine lays out ordinary containers, but a lazy list arranges its own items — so gap was read on iOS (into LazyVStack/LazyHStack spacing) and silently dropped on Android.

That gap matters more than it looks. Without it the only way to space a list is wrapping it in a <column>, which collapses the entire list into a single lazy item — killing virtualization. Measured on a real chat log: directChildren=1, canScrollForward=false, and firstVisibleItemScrollOffset climbing past 1000px inside one item.

3. Programmatic scrolls left a collapsing top bar expanded

Both scroll paths now drive core's ChromeScrollController after moving the list. Skipped on the initial jump — opening a screen shouldn't animate the chrome shut.

Compatibility — please read

Two user-visible behaviour changes for existing apps:

  • gap on a scroll-view now spaces on Android. Any app relying on the current no-op will see layouts shift. Arguably the fix, but it is a change.
  • autoScrollTo() now scrolls. Anyone who called it and got nothing will now get scrolling.

Also auto_scroll_to taking precedence over scroll_anchor — previously moot, since auto_scroll_to did nothing.

Testing status — partial, please review accordingly

  • Chrome collapse: verified on a physical Pixel 9. Bar travels 0 → -231, then short-circuits on repeat messages.
  • auto_scroll_to (both platforms) and the Android gap fix: NOT yet exercised on device. The test app uses scroll-anchor="bottom" with a wrapped log, so neither path was hit. Reviewed against surrounding code, imports verified, but they want a real build before merge.

🤖 Generated with Claude Code

Three related scroll-view renderer gaps, all surfaced while building a
chat screen:

auto_scroll_to had a PHP setter (ScrollView::autoScrollTo()) but no
consumer on either platform — it was written into the wire payload and
dropped. BenchmarkComponent relies on it to scroll a long list, so that
scenario has been measuring a static list. iOS reuses the ScrollViewReader
already on the vertical branch and scrolls to the target child's id;
Android drives LazyColumn's listState by index. Both jump without
animation on first appear and animate later moves, matching the existing
scroll-anchor path. An explicit index wins over `scroll-anchor="bottom"`.
Out-of-range and negative indices are no-ops.

The `gap-*` class was read on iOS (into LazyVStack/LazyHStack spacing)
and silently dropped on Android, because a lazy list arranges its own
items rather than going through the flex engine. That gap matters more
than it looks: the workaround is wrapping content in a <column> to get
spacing, which collapses the whole list into a single lazy item and
defeats virtualization entirely.

Programmatic scrolls emit no nested-scroll deltas, so a collapsing top
bar never learned the content moved. Both scroll paths now drive the new
ChromeScrollController from nativephp/mobile. Skipped on the initial
jump — opening a screen should not animate the chrome shut.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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