callstack / callstack/react-native-bottom-tabs

[Android] Tab screens stay clipped at a transient startup height for the whole session (onNativeLayout guard/report size mismatch)

Offen
#555 0 Kommentare 1 Reaktion 0 zugewiesene Personen Auf GitHub ansehen
Vorherrschende Sprache
TypeScript
Sterne
1.5k
Forks
109
Ø Merge
11 Std. 44 Min.
Gemergte PRs (30 T.)
8

Beschreibung

## Description

On Android, tab screens can get stuck clipped to a fraction of the window for the entire session: content renders in the top ~half of the screen with dead space below, while the tab bar, absolutely-positioned siblings, and pushed stack screens render normally. A force-close sometimes clears it; the race is per cold start. We observed this persistently in production on a Pixel 6a / Android 16.

Tab screen content latched at ~40% of the window with dead space below; tab bar and floating elements unaffected

## Root cause

In `RCTTabView.kt`, the layout-change listener guards re-reporting on the **outer** view's size but reports the **inner** `layoutHolder`'s size to JS:

```kotlin
val newWidth = right - left // outer view (guard)
val newHeight = bottom - top
...
val dpHeight = Utils.convertPixelsToDp(context, layoutHolder.height) // inner view (report)
...
lastReportedSize = Size(newWidth, newHeight) // stores outer size
```

During startup the outer frame can settle at its final size while `layoutHolder` is still mid-layout at a transient height. If the listener fires in that window, the transient height is reported to JS and the **outer** size is latched into `lastReportedSize`. When `layoutHolder` finishes laying out, the outer size hasn't changed, so the guard never allows a re-report — the transient height is styled onto every tab screen for the rest of the session.

Regression from #283, which switched the *reported* size to `layoutHolder` without switching the *guard*.

## Reproduction

Timing-dependent in the wild, but deterministic if you simulate the transient: force `layoutHolder` to a non-final height when the listener first fires, then restore it (snippet in the linked PR). On current `main` this latches the transient height with no further report; screens stay clipped. Logcat traces for both before and after the fix are in the PR.

## Fix

Key the guard on `layoutHolder`'s own dimensions — the same view whose size is reported — so any later correct layout is detected and re-reported (self-heals in a single frame; 5 ms measured in our repro). PR incoming.

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

Beitragsleitfaden

Beitragsleitfaden öffnen

Rechercherichtung

Start in RCTTabView.kt by tracing the layout-change listener, its size guard, and the layoutHolder dimensions reported to JS. Reproduce the transient-height scenario described in the issue and inspect the Logcat traces or linked PR reproduction. Done means a later layoutHolder size change is reported and tab screens no longer remain clipped for the session.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
android, kotlin, react-native
Bereich
mobile
Issue-Typ
Bug
Schwierigkeit
3/5
Geschätzter Aufwand
1-2 Tage
Aktivitätsstatus
Ruhig
Klarheit
Klar beschrieben
Anfängerfreundlichkeit
74/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.