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)

Open
#555 0 comments 1 reaction 0 assignees View on GitHub
Dominant language
TypeScript
Stars
1.5k
Forks
109
Avg merge
11h 44m
Merged PRs (30d)
8

Description

## 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)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.