Skip to content

Conversation

@kligarski
Copy link
Contributor

@kligarski kligarski commented Oct 28, 2025

Description

Allows preventing tab change on Android. Previously, bottom navigation bar would be desynchronized with shown tab screen (menu item would change even if tab screen would not).

before after
Screen_recording_20251028_110806.mp4
Screen_recording_20251028_110431.mp4

Important

Due to changes in this PR, tab change behaves differently when tab screens are heavy.

before after
Screen_recording_20251028_111634.mp4
Screen_recording_20251028_111759.mp4

This matches behavior on iOS but I'm not sure if this is something we want.

Screen.Recording.2025-10-28.at.11.24.49.mov

Closes https://github.com/software-mansion/react-native-screens-labs/issues/561.

Changes

  • add menuItemSelectedViaContainerUpdate to differentiate between user menu item taps and updates from JS
  • allow item selection only if menuItemSelectedViaContainerUpdate, otherwise just send event to JS

Test code and steps to reproduce

Run TestBottomTabs. Apply this patch to BottomTabsContainer to simulate preventing tab change to Tab4:

Patch
diff --git a/apps/src/shared/gamma/containers/bottom-tabs/BottomTabsContainer.tsx b/apps/src/shared/gamma/containers/bottom-tabs/BottomTabsContainer.tsx
index 7bba66b48..5f535c945 100644
--- a/apps/src/shared/gamma/containers/bottom-tabs/BottomTabsContainer.tsx
+++ b/apps/src/shared/gamma/containers/bottom-tabs/BottomTabsContainer.tsx
@@ -51,6 +51,10 @@ export function BottomTabsContainer(props: BottomTabsContainerProps) {
     (event: NativeSyntheticEvent<NativeFocusChangeEvent>) => {
       const tabKey = event.nativeEvent.tabKey;
 
+      if (tabKey === 'Tab4') {
+        return;
+      }
+
       // Use `startTransition` only if the state is controlled in JS
       // const transitionFn = !configWrapper.config.controlledBottomTabs
       //   ? startTransition

Try to select tab4.

Checklist

  • Included code example that can be used to test this change
  • Ensured that CI passes


appearanceCoordinator.updateTabAppearance(this)

menuItemSelectedViaContainerUpdate = true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we call it in updateSelectedTab instead of updateBottomNavigationViewAppearance? It would be a bit more logical there, or these 2 are not always called together ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it as well but I think on Android managing BottomNavigationView and shown screens/fragments is separated and those methods reflect that. Maybe we should think about renaming those methods though.


// We need to differentiate between user tapping the menu item
// and update requested from JS.
private var menuItemSelectedViaContainerUpdate = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I missing something, or is this supposed to not be triggered when controlledBottomTabs=false, but it is? Also IMO the name is a bit unclear.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Android, only controlled bottom tabs are currently supported.

I did use viaContainerUpdate name because I guess that there is a possibility to call container update from native code, not only as a result of JS focused tab change. I can maybe reverse the logic and use menuItemSelectedByUser or something like this but I'm not sure.

@kligarski kligarski marked this pull request as draft November 17, 2025 13:10
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.

4 participants