Search code examples
androidreact-nativeexposafeareaview

Android bottom home bar covers bottom navigation


Why does the Android bottom home bar cover the React-Navigation-bottom-tabs element?

On iOS 🍏, it looks fine:

ios bottom nav bar tabs

On Android 🤖, it looks like this:

android bottom nav bar tabs

I have the SafeAreaProvider wrapping the app:

<SafeAreaProvider onLayout={onLayoutRootView}>
      <ThemeProvider theme={theme}>
        <ColorModeSwitch>
          <StatusBar style="auto" />
          <MessageProvider>
            <SWRConfig value={{ shouldRetryOnError: false }}>
              <AuthProvider>
                <Stack
                  screenOptions={{
                    headerShown: false,
                    headerLeft: undefined,
                  }}
                />
              </AuthProvider>
            </SWRConfig>
          </MessageProvider>
        </ColorModeSwitch>
      </ThemeProvider>
    </SafeAreaProvider>

I tried adding SafeAreaView to different parts of the app. Inside the ColorModeSwitch, I thought it would be good to use a single SafeAreaView which wraps the whole app. Is this considered OK, or should it wrap each page view? The problem with this approach is that it's adding bottom padding to the iOS view bottom-navigation-tabs, which I don't want.

I tried an implementation of SafeAreaView into my custom PageView which is a wrapper component around each Screen's View, inside the Stack &/or Tab navigation screen. This too did not change the bottom tabs:

const PageView = ({ children, style }: Props) => {
  const styles = useStyles();
  return (
    <SafeAreaView style={[styles.container, style]}>{children}</SafeAreaView>
  );
};

I am using Expo & Expo-Router for my navigation & Tabs:

export default function TabLayout() {
  return (
    <>
      <Tabs
        initialRouteName="home"
        screenOptions={{
          headerShown: false,
        }}
        tabBar={props => (
          <Shadow distance={60} offset={[0, 20]} stretch>
            <TabBar {...props} />
          </Shadow>
        )}
      >
        <Tabs.Screen
          name="index"
          options={{
            tabBarLabel: 'Home',
          }}
        />
        <Tabs.Screen
          listeners={({ navigation }: BottomTabScreenProps<any>) => ({
            tabPress: (e: EventArg<'tabPress', true>) => {
              e.preventDefault();
              navigation.navigate('scanModal');
              return;
            },
          })}
          name="scan"
          options={{
            tabBarLabel: 'Scan',
          }}
        />
        <Tabs.Screen
          name="profile"
          options={{
            tabBarLabel: 'Profile',
          }}
        />
      </Tabs>
    </>
  );
}

So I tried adding SafeAreaView as the Wrapper to my custom bottom nav tab elements:

 const insets = useSafeAreaInsets();

  return (
    <SafeAreaView
      style={{
        paddingBottom: insets.bottom,
        flexDirection: 'row',
        height: 80,
        backgroundColor: theme.colors.white,
        alignItems: 'center',
      }}
    >

But this made it worse:

bottom tabs nav, wrapped with SafeAreaProvider

What is an option to fix this on Android?


Solution

  • Use useSafeAreaInsets to get the bottom inset and set the value as paddingBottom in a custom tab bar. Something like this:

    import { useSafeAreaInsets } from 'react-native-safe-area-context';
    
    function CustomTabBar({ state, descriptors, navigation }) {
      const insets = useSafeAreaInsets();
      return (
        <View style={{ paddingBottom: insets.bottom }}>
    ...
        </View>
      );
    }
    
    <Tab.Navigator
      tabBar={(props) => <CustomTabBar {...props} />}>
    ...
    </Tab.Navigator>