Android bottom home bar covers bottom navigation

652 views Asked by At

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?

4

There are 4 answers

1
user18309290 On BEST ANSWER

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>
1
Thomasino73 On

Take a look at the following page

It lets your application know what OS your user is on, I think it could be handy for your problem. If your user is on Android, you can simply give it a larger height number or add padding / margin to the bottom

Small example

import { Platform } from 'react-native';
 const insets = useSafeAreaInsets();

  return (
    <SafeAreaView
      style={{
        paddingBottom: insets.bottom,
        flexDirection: 'row',
        height: Platform.OS !== "ios" ? 120 : 80,
        backgroundColor: theme.colors.white,
        alignItems: 'center',
      }}
    >

I'm using !== "ios" because when i'm testing with my OnePlus, I get linux instead of Android. I hope this helps you and feel free to ask questions if you have any!

2
Nensi Kasundra On

Directly give a tab to the screen and inside its components manage it's style. So It will be exactly fit to the screen. Also PFA SS for that.

import * as React from 'react';
import { SafeAreaView, Text, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

function HomeScreen() {
  return (
    <SafeAreaView style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Home!</Text>
    </SafeAreaView>
  );
}

function SettingsScreen() {
  return (
    <SafeAreaView style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Settings!</Text>
    </SafeAreaView>
  );
}

const Tab = createBottomTabNavigator();

function MyTabs() {
  return (
    <Tab.Navigator>
      <Tab.Screen name="Home" component={HomeScreen} />
      <Tab.Screen name="Settings" component={SettingsScreen} />
    </Tab.Navigator>
  );
}

export default function TabDemo() {
  return (
    <NavigationContainer>
      <MyTabs />
    </NavigationContainer>
  );
}

enter image description here

3
Dokt On

I don't know if you've found the solution to this already or not but I've ran into the same issue recently and I've came up with a solution of my own that maybe will help you or others.

Basically I thought that I needed to add padding to the bottom tab bar but it turned out that there is a prop called safeAreaInsets on the Tabs component.

Here is my solution :

import { Platform } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";

export default function TabLayout() {
   const insets = useSafeAreaInsets();
   return (
     <Tabs 
        safeAreaInSets={{
          bottom: Platform.OS === "android" ? 10 : insets.bottom
        }}
     >
     </Tabs>
   );
}

So to explain a bit further, you need to get the safeAreaInsets using "react-native-safe-area-context" then apply them to the Tabs component conditionally so that if the platform is Android, you set a fixed bottom inset (in my case it's 10 pixels but you can change it as you see fit) and keep the default behavior of the safeArea on iOS.

Hope it helped