callstack / callstack/react-native-bottom-tabs
[Android] Content doesn't resize when tab bar visibility changes (tabBarHidden)
- 主要言語
- TypeScript
- スター
- 1.5k
- フォーク
- 109
- 平均マージ
- 11時間 44分
- マージ済み PR(30日)
- 8
説明
### Before submitting a new issue
- [x] I tested using the latest version of the library, as the bug might be already fixed.
- [x] I tested using a supported version of react native.
- [x] I checked for possible duplicate issues, with possible answers.
### Bug summary
On Android, when `tabBarHidden` is toggled to `true`, the native bottom navigation view is set to `GONE` and its space is reclaimed at the native level, but the React content does not grow to fill it. The screen keeps its old height, leaving a blank strip at the bottom exactly the height of the tab bar. Bottom-anchored elements (`position: 'absolute', bottom: 0`) end up floating ~80dp above the actual bottom of the screen. Toggling back to visible has the same problem in reverse until something else triggers a relayout.
I tracked it down to the layout listener in `RCTTabView.kt`. The size-change guard compares the bounds of the whole container (`ReactBottomNavigationView`), but when only the tab bar visibility changes, the container keeps the exact same size — only the inner `layoutHolder` grows or shrinks:
```kotlin
post {
addOnLayoutChangeListener { _, left, top, right, bottom, _, _, _, _ ->
val newWidth = right - left // container size — unchanged when tab bar is hidden
val newHeight = bottom - top
onTabBarMeasuredListener?.invoke(...)
if (newWidth != lastReportedSize?.width || newHeight != lastReportedSize?.height) {
// never reached in this case, so onNativeLayoutListener doesn't fire
onNativeLayoutListener?.invoke(dpWidth, dpHeight)
lastReportedSize = Size(newWidth, newHeight)
}
}
}
```
Two problems compound here:
1. The guard compares the container size, while the value actually reported to JS is the `layoutHolder` size. When the tab bar is hidden, the container stays the same but `layoutHolder` changes, so the guard filters the update out.
2. The listener is attached to the container, and `OnLayoutChangeListener` only fires when that view's own bounds change — which they don't in this scenario. So even the callback itself may not run.
As a result `onNativeLayout` never reaches the JS side, `setMeasuredDimensions` keeps the stale height, and the children rendered by `TabView` stay at the old size.
Verified on an emulator with `uiautomator dump`: after hiding the tab bar, the native `layoutHolder` grows to full screen height (2400px on my device), while the React subtree inside it stays at the previous 2126px.
iOS is not affected.
### Library version
1.4.0
### Environment info
```shell
System:
OS: macOS 26.5.2
CPU: (18) arm64 Apple M5 Pro
Binaries:
Node: 24.13.0
Yarn: 3.6.4
npm: 11.6.2
npmPackages:
react: 19.2.3
react-native: 0.86.0
Android:
hermesEnabled: true
newArchEnabled: true
iOS:
hermesEnabled: true
newArchEnabled: true
```
### Steps to reproduce
1. Android app using `createNativeBottomTabNavigator` (New Architecture).
2. Render a screen with a bottom-anchored view, e.g. an action bar with `position: 'absolute', bottom: 0`.
3. Toggle `tabBarHidden` to `true` (typical case: entering a selection/edit mode).
4. The tab bar disappears, but the bottom-anchored view stays where the tab bar's top edge used to be, with an empty strip below it.
Tested on an Android 16 emulator (arm64), RN 0.86, and reproduced consistently.
### Reproducible sample code
```tsx
const [hidden, setHidden] = useState(false);
{/* screen content */}
setHidden(v => !v)} />
```
I have a fix working locally (report the `layoutHolder` size instead, and also listen to `layoutHolder` layout changes so the visibility-toggle case is caught). Will open a PR.
コントリビューションガイド
調査の方向性
まず android/src/main/java/com/th3rdwave/safeareacontext/RCTTabView.kt を読み、tabBarHidden を切り替えて、提供されている Android のサンプルを再現します。タブバーが非表示のときに React コンテンツと下端に固定されたビューが利用可能な高さ全体にリサイズされ、タブバーを表示すると正しく元に戻れば完了です。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- android, kotlin, react-native
- 領域
- mobile
- issue の種類
- バグ
- 難易度
- 3/5
- 見積もり時間
- 1〜2日
- 活発さ
- 静か
- 明瞭さ
- 明確に書かれている
- 初心者へのやさしさ
- 35/100