Skip to content

Conversation

Ubax
Copy link
Contributor

@Ubax Ubax commented Jul 28, 2025

Description

When tabs were used inside the Stack, the blank screen was presented. This was because in this case, tabs and stack shared FragmentManager and tabs removed ScreenStackFragment

https://github.com/software-mansion/react-native-screens/blob/main/android/src/main/java/com/swmansion/rnscreens/gamma/tabs/TabsHost.kt#L466-L477

There is an issue when using Tabs in Stack with header:

<ScreenStack style={StyleSheet.absoluteFill}>
      <ScreenStackItem
        // headerConfig={{ hidden: true }}
        screenId="12345"
        activityState={2}
        style={{ flex: 1 }}>
        <BottomTabs

moves tabs below the screen

image

Changes

  1. Make Screen extend FragmentProviding interface
  2. Use FragmentProviding as the base to decide wether there is nested Fragment structure

Test code and steps to reproduce

Create Tabs inside Stack, Stack inside Tabs and Stack inside Stack and test if the app work

Checklist

@Ubax Ubax changed the title fix: Use childFragmentManager if tabs are used inside Stack fix (android, tabs): Use childFragmentManager if tabs are used inside Stack Jul 28, 2025
@Ubax Ubax marked this pull request as ready for review July 30, 2025 06:41
@kkafar
Copy link
Member

kkafar commented Jul 30, 2025

Thank you! Superseded by #3084

@kkafar kkafar closed this Jul 30, 2025
kkafar added a commit that referenced this pull request Jul 30, 2025
## Description

Currently only nesting a stack inside tabs is supported & not the other
way around.

This PR changes that. 

## Changes

`Screen` now implements `FragmentProviding`, which allows the `TabHost`
to use it
as a source for child fragment manager.

## Test code and steps to reproduce

<details>

<summary>Code example</summary>


```javascript
import React from 'react';

import { Screen, ScreenStack, enableFreeze } from 'react-native-screens';
import ConfigWrapperContext, {
  type Configuration,
  DEFAULT_GLOBAL_CONFIGURATION,
} from '../../shared/gamma/containers/bottom-tabs/ConfigWrapperContext';
import {
  BottomTabsContainer,
  type TabConfiguration,
} from '../../shared/gamma/containers/bottom-tabs/BottomTabsContainer';
import { Tab1, Tab2, Tab3, Tab4 } from './tabs';
import Colors from '../../shared/styling/Colors';
import { StyleSheet } from 'react-native';

enableFreeze(true);

const TAB_CONFIGS: TabConfiguration[] = [
  {
    tabScreenProps: {
      tabKey: 'Tab1',
      title: 'Tab1',
      isFocused: true,
      icon: {
        sfSymbolName: 'house',
      },
      selectedIcon: {
        sfSymbolName: 'house.fill',
      },
      iconResourceName: 'sym_call_incoming', // Android specific
    },
    contentViewRenderFn: Tab1,
  },
  {
    tabScreenProps: {
      tabKey: 'Tab2',
      badgeValue: 'NEW',
      tabBarItemBadgeBackgroundColor: Colors.GreenDark100,
      tabBarBackgroundColor: Colors.NavyDark140,
      tabBarItemTitleFontSize: 20,
      tabBarItemTitleFontStyle: 'italic',
      tabBarItemTitleFontColor: Colors.RedDark120,
      tabBarItemTitleFontWeight: 'bold',
      tabBarItemTitleFontFamily: 'Baskerville',
      tabBarItemTitlePositionAdjustment: {
        vertical: 8,
      },
      icon: {
        templateSource: require('../../../assets/variableIcons/icon.png'),
      },
      selectedIcon: {
        templateSource: require('../../../assets/variableIcons/icon_fill.png'),
      },
      tabBarItemIconColor: Colors.RedDark120,
      iconResourceName: 'sym_call_missed', // Android specific
      title: 'Tab2',
    },
    contentViewRenderFn: Tab2,
  },
  {
    tabScreenProps: {
      tabKey: 'Tab3',
      badgeValue: '2137',
      tabBarItemBadgeBackgroundColor: Colors.RedDark40,
      tabBarItemBadgeTextColor: Colors.RedDark120,
      icon: {
        imageSource: require('../../../assets/variableIcons/icon.png'),
      },
      selectedIcon: {
        imageSource: require('../../../assets/variableIcons/icon_fill.png'),
      },
      iconResourceName: 'sym_action_email', // Android specific
      title: 'Tab3',
    },
    contentViewRenderFn: Tab3,
  },
  {
    tabScreenProps: {
      tabKey: 'Tab4',
      icon: {
        sfSymbolName: 'rectangle.stack',
      },
      selectedIcon: {
        sfSymbolName: 'rectangle.stack.fill',
      },
      iconResourceName: 'sym_action_chat', // Android specific
      title: 'Tab4',
      badgeValue: '',
    },
    contentViewRenderFn: Tab4,
  },
];

function App() {
  const [config, setConfig] = React.useState<Configuration>(
    DEFAULT_GLOBAL_CONFIGURATION,
  );

  return (
    <ConfigWrapperContext.Provider
      value={{
        config,
        setConfig,
      }}>
      <ScreenStack style={[StyleSheet.absoluteFill]}>
        <Screen isNativeStack activityState={2} screenId="hello">
          <BottomTabsContainer tabConfigs={TAB_CONFIGS} />
        </Screen>
      </ScreenStack>
    </ConfigWrapperContext.Provider>
  );
}

export default App;
```

</details>

## Checklist

- [ ] Ensured that CI passes

## Initial solution

See #3077
for problem description & solution suggestion.


Co-authored-by: Jakub Tkacz <[email protected]>

---------

Co-authored-by: Jakub Tkacz <[email protected]>
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.

2 participants