How to use SafeAreaView for Android notch devices?

AndroidReact Native

Android Problem Overview


I'm developing an app with React Native and I'm testing with my OnePlus 6 and it has a notch. The SafeAreaView is a solution for the iPhone X but for Android, it seems there is no solution.

How to solve this kind of issue?

Android Solutions


Solution 1 - Android

Do something like

import { StyleSheet, Platform, StatusBar } from "react-native";

export default StyleSheet.create({
  AndroidSafeArea: {
    flex: 1,
    backgroundColor: "white",
    paddingTop: Platform.OS === "android" ? StatusBar.currentHeight : 0
  }
});

And then In your App.js

import SafeViewAndroid from "./components/SafeViewAndroid";

 <SafeAreaView style={SafeViewAndroid.AndroidSafeArea}>
          <Layout screenProps={{ navigation: this.props.navigation }} /> //OR whatever you want to render
        </SafeAreaView>

This should work good as get height will take care of the knotch in android device by calculating the statusBar height and it will arrange accordingly.

Solution 2 - Android

A work around I had to use recently:

GlobalStyles.js:

import { StyleSheet, Platform } from 'react-native';
export default StyleSheet.create({
    droidSafeArea: {
        flex: 1,
        backgroundColor: npLBlue,
        paddingTop: Platform.OS === 'android' ? 25 : 0
    },
});

It is applied like so:

App.js

import GlobalStyles from './GlobalStyles';
import { SafeAreaView } from "react-native";

render() {
    return (
      <SafeAreaView style={GlobalStyles.droidSafeArea}>
          //More controls and such
      </SafeAreaView>
    );
  }
}

You'll probably need to adjust it a bit to fit whatever screen you're working on, but this got my header just below the icon strip at the top.

Solution 3 - Android

You could also create helper component with this style applied right away like this

import React from 'react';
import { StyleSheet, Platform, StatusBar, SafeAreaView } from 'react-native';

export default props => (
  <SafeAreaView style={styles.AndroidSafeArea} {...props} >
    {props.children}
  </SafeAreaView>
);

const styles = StyleSheet.create({
  AndroidSafeArea: {
    paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight : 0
  }
});

Make note that I also deleted unnecessary styles which breaks natural behavior of SafeAreaView which in my case broke styling.

As for use you simply use it like normal SafeAreaView:

import React from 'react';
import SafeAreaView from "src/Components/SafeAreaView";

render() {
    return (
      <SafeAreaView>
          // Rest of your app
      </SafeAreaView>
    );
  }
}

Solution 4 - Android

Late 2020 answer: For anyone stumbling across this issue themselves, they have added support for this.

Follow this documentation page

Solution 5 - Android

if you're seeing this in 2020 and you also need the web support with the Android and iOS, type this in your terminal. expo install react-native-safe-area-context this will install the updated safe area context. Then import the following stuffs into your app.js

import { SafeAreaView, SafeAreaProvider} from "react-native-safe-area-context";

add <SafeAreaProvider> before all the tags in your main function in app.js, also remember to close it at the end. and finally, instead of view, add SafeAreaView. Read more at the official expo website : SafeAreaContext

Solution 6 - Android

for more consistency import:

import { Platform, StatusBar } from "react-native";

and then use it like so:

paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight : 0

Solution 7 - Android

Although the docs says it is relevant only for iOS, when I used React's SafeAreaView it acted differently on different screens on Android.

I managed to fix the problem by implementing my version of SafeAreaView:

import React from "react";
import { Platform, View, StatusBar } from "react-native";
import { GeneralStyle } from "../styles";

export function SaferAreaView({ children }) {
  if (Platform.OS == "ios") {
    return <SaferAreaView style={{ flex: 1 }}>{children}</SaferAreaView>;
  }
  if (Platform.OS == "android") {
    return <View style={{flex: 1, paddingTop: StatusBar.currentHeight}}>{children}</View>;
  }
}

This was tested on an old device (with hardware navigation) and new notch devices (with software navigation) - different screen sizes.

Solution 8 - Android

1 - expo install expo-constants

2- and do like this for example

import React from "react";
import Constants from "expo-constants";
import { Text, StyleSheet, SafeAreaView, View } from "react-native";

export default function HeaderTabs({ style }) {
    return (
        <SafeAreaView style={[styles.screen, style]}>
            <View style={[styles.view, style]}>
                <Text>Hello this is status bar</Text>
            </View>
        </SafeAreaView>
    );
}


const styles = StyleSheet.create({
    screen: {
        paddingTop: Constants.statusBarHeight,
        flex: 1,
    },
    view: {
        flex: 1,
    },
});

Solution 9 - Android

In the SafeAreaView Docs was told:

> It is currently only applicable to iOS devices with iOS version 11 or later.

So now I definitely use it in my project but I use Platform to recognize device platform and for Android, I make a manual safe area for the status bar.

Solution 10 - Android

you can use react-native-device-info for device info and apply styling also with a notch

Solution 11 - Android

Instead of using Platform API, you can use expo constants.

npm i expo-constants

then import it in your component as

import Constants from "expo-constants"

and then in the styles you can use it like this

const styles = StyleSheet.create({
container: {
paddingTop: Constants.statusBarHeight
} });

To see all the properties of Constants console log it you will find some more useful things.

Solution 12 - Android

Well, I had the same problem. I solved this using this lib React Native Status Bar Height, and I recommend because it´s a piece of cake to use.
And if you are using style-components you can add the getStatusBarHeight() on your styles.js like I did on the example below:

import styled from 'styled-components/native';

import { getStatusBarHeight} from 'react-native-status-bar-height';

export const Background = styled.View`
 flex:1;
 background:#131313;
 margin-top: ${getStatusBarHeight()};

`

Solution 13 - Android

This is currently the best or easiest way to implement SafeAreaView on Android and ios for both vanilla RN and Expo.

import { SafeAreaView } from 'react-native-safe-area-context';

function SomeComponent() {
  return (
    <SafeAreaView>
      <View />
    </SafeAreaView>
  );
}

Solution 14 - Android

Expo solution(docs - android only):

import { setStatusBarTranslucent } from 'expo-status-bar';

Then in the component you can use useEffect hook:

  useEffect(() => {
    setStatusBarTranslucent(false)
  },[])

for iOS you can use the <SafeAreaView> component from react-native.

Solution 15 - Android

ENRICO SECCO was right (i cant comment due to my stackoverflow reputation lol)! any safeareaview thingy doesn't work for me as well, so i get around with import { getStatusBarHeight} from 'react-native-status-bar-height';

here how execute it, keep in mind that this is in my app.js, where i put all my stack.navigator + bottomtab.navigator

export default function App() {

//IGNORE ALL OF THIS, JUMP TO THE RETURN() FUNCTION!

  const [appIsReady, setAppIsReady] = useState(false);
  useEffect(() => {
    async function prepare() {
      try {
        await SplashScreen.preventAutoHideAsync();
        await Font.loadAsync(AntDesign.font);
        await Font.loadAsync({
          'Montserrat-Bold': require('./assets/fonts/Montserrat-Bold.ttf'),
          'Montserrat-Regular': require('./assets/fonts/Montserrat-Regular.ttf'),
          'Montserrat-Light': require('./assets/fonts/Montserrat-Light.ttf'),
        });
        await new Promise(resolve => setTimeout(resolve, 2000));
      } catch (e) {
        console.warn(e);
      } finally {
        // Tell the application to render
        setAppIsReady(true);
      }
    }

    prepare();
  }, []);

  const onLayoutRootView = useCallback(async () => {
    if (appIsReady) {
      await SplashScreen.hideAsync();
    }
  }, [appIsReady]);

  if (!appIsReady) {
    return null;
  }
  return (
    
  //HERE!
    <NavigationContainer> 
      <View  style = {{
        flex: 1, <- TO MAKE IT FULL SCREEN (PLEASE DELETE THIS)
        marginTop: getStatusBarHeight(), <- TO PUSH IT DOWN FROM OFF SCREEN, MINE RAN OFF TO THE TOP LMAO (PLEASE DELETE THIS)
      }} onLayout={onLayoutRootView}>
      <Tabs/>
      </View>
    </NavigationContainer>  
  );
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionThomas DesdoitsView Question on Stackoverflow
Solution 1 - AndroidRishav KumarView Answer on Stackoverflow
Solution 2 - AndroidZexks MarquiseView Answer on Stackoverflow
Solution 3 - AndroidMarcin ZaborowskiView Answer on Stackoverflow
Solution 4 - AndroidNilsView Answer on Stackoverflow
Solution 5 - AndroidAbhisek GangulyView Answer on Stackoverflow
Solution 6 - AndroidYoussef ZidanView Answer on Stackoverflow
Solution 7 - AndroideyalyoliView Answer on Stackoverflow
Solution 8 - AndroidOmar FaracheView Answer on Stackoverflow
Solution 9 - AndroidAmerllicAView Answer on Stackoverflow
Solution 10 - AndroidUmair RiazView Answer on Stackoverflow
Solution 11 - AndroidSumitView Answer on Stackoverflow
Solution 12 - AndroidENRICO SECCOView Answer on Stackoverflow
Solution 13 - AndroidquesterstudiosView Answer on Stackoverflow
Solution 14 - AndroidPaweł StaneckiView Answer on Stackoverflow
Solution 15 - AndroidRayyan Eka PutraView Answer on Stackoverflow