Play nicely with translucent StatusBar on Android

In Expo we use a translucent StatusBar by default on Android, and some normal React Native projects may do this as well because it makes it easier to reason about cross-platform since the iOS StatusBar is always translucent.

We may need to upstream a constant to React Native that we can use to check if the StatusBar is translucent, and then add any necessary paddingTop to the navigation bar when it is.

16 thoughts on “Play nicely with translucent StatusBar on Android

  1. I just wanna +1 this because I just used react-navigation for the first time (with Exponent) and realized that I needed to handle android statusbar paddingTop unlike when I use ex-navigation where it handles it for me. It would be really nice for new users to get that functionality out of the box 👍🏿

  2. @lyahdav Here’s a few ways you can do it.

    TabNavigator
    Pass a second argument to the a Navigator with a custom config containing styles.
    Docs

    const BasicApp = TabNavigator({ Home : { screen: Home }, Manufacturers : { screen: Manufacturers }, }, { tabBarOptions: { style: { backgroundColor: 'orange', marginTop: 24 }, } });

    StackNavigator
    Inside a Screen add a header config containing your styles to navigationOptions.
    Docs

    static navigationOptions = { title: 'something else', header: { style: { backgroundColor: 'orange', marginTop: 24 } } }

    DrawerNavigator
    No example, but it also takes a second argument with a config where you can add styles.
    Docs

  3. The way I’ve been going for now is to have the statusBar as always transparent, with a color of transparent:

    <StatusBar
      backgroundColor={'transparent'}
      translucent
    />
    

    In the styles for the navigators, add the following. It allows for a different header color (Android):

      tabBarOptions: {
        style: {
          backgroundColor: 'orange', // Main color
          borderTopWidth: 24,
          borderTopColor: 'red', // StatusBar color
        },
      }
    

    EDIT: Seems this only works with TabNavigator for now.

  4. I’m web developer with a good experience in react, but completely new to react native and to native mobile development.
    When implementing my first project using create-react-native-app and using expo to test/debug my app as I develop, I’m facing the same problem as others described in this thread and from what I read here the general understanding is that people are trying to solve it from the perspective of react-navigation and by doing so incurring in various problems:

    1. The three navigators (StackNavigator, TabNavigator and DrawerNavigator) have different apis
    2. Applying style to the Navigator through navigationOptions might incur in duplication of code
    3. There are other case scenarios where the solutions proposed above don’t work, for example in my case I’m forcing my TabNavigator to stick to the bottom (event for android) with tabBarPosition: 'bottom' as documented, and all the solutions above seem to expect it on the top (default on android)

    My solution was to separate the concerns…

    import React from 'react';
    import { View, StatusBar, Platform } from 'react-native';
    import { Tabs } from './app-navigation';
    
    const App = () => {
        return (
          <View style={styles.container}>
            <View style={styles.statusBar}>
              <StatusBar
                backgroundColor={'transparent'}
                translucent
              />
            </View>
            <Tabs />
          </View>
        );
    };
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
      },
      statusBar: {
        height: (Platform.OS === 'ios' ? 20 :  StatusBar.currentHeight),
        backgroundColor: 'red',
      },
    });
    

    and here is my app-navigation.js

    import React from 'react';
    import { TabNavigator } from 'react-navigation';
    
    import Sample1 from './screen/sample1';
    import Sample2 from './screen/sample2';
    
    export const Tabs = TabNavigator({
      Sample1: {
        screen: Sampe1,
      },
      Sample2: {
        screen: Sample2,
      },
    }, {
      tabBarPosition: 'bottom',
    });
    

    This address all the issues for me, and I can use different navigators, or position them top or bottom and my status bar will be position and styled as expected. In the future if I want to modify the style (such as backgroundColor) as I navigate through the tabs I would try to extract the:

            <View style={styles.statusBar}>
              <StatusBar
                backgroundColor={'transparent'}
                translucent
              />
            </View>
    

    into its own component and use the react ways of component communication (props and callbacks) to perform any necessary changes.

    Since I’m new would love to know if this is a good solution or if not the reasons why it might not be.

  5. For Expo users looking for a simple code sample to make sure content appears under the status bar.

    const App = StackNavigator({
      Home: { screen: HomeScreen },
      About: { screen: AboutScreen },
      }, {
        navigationOptions: { headerStyle: { marginTop: Expo.Constants.statusBarHeight } }
      })
  6. On Android, to get this working really nicely I did the following:

    Edit android/app/src/main/res/values/styles.xml

    <resources>
        <!-- Base application theme. -->
        <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
            <!-- Customize your theme here. -->
            <item name="android:navigationBarColor">@android:color/transparent</item>
            <item name="android:statusBarColor">#1e000000</item>
            <item name="android:windowTranslucentNavigation">true</item>
        </style>
    </resources>
    

    Note: #1e000000 = rgba(0, 0, 0, 0.3)

    This makes the statusBar (and bottom navigation bar (soft keys) if present/needed) transparent.

    Now on each screen which uses the navigationOptions I add in the following:

    headerStyle: {
        height: 56 + StatusBar.currentHeight, // 56 = Header/Toolbar spec
        paddingTop: StatusBar.currentHeight, // StatusBar height
        backgroundColor: 'pink',
    }
    

    This now makes your Header act as normal, however your StatusBar area is actually transparent with a black opacity over the top. This now allows you to do what you want; change colors dynamically, have an image behind it etc.

  7. Getting the correct status bar underlap height is actually pretty complex.

    • iOS’ StatusBar height is normally hardcoded at 20 which is correct for all currently released iOS devices. It can grow during calls due to the call indicator, however iOS actually pushes the viewport down clipping the bottom of the viewport instead of growing the status bar’s height, so the relevant height stays at 20.
    • However Apple is releasing a new iPhone X which has a StatusBar height of 44. And they’re doing this alongside a release of an iPhone 8 that has as StatusBar height of 20. So iOS’ StatusBar height is now hardware dependent.
    • Android has different StatusBar heights, it was 25 up till Marshmallow (API level 23) when it changed to 24. Some devices like the Kindle Fire change the value. And it’s also dynamic, on tablets it can be 32px. Though as long as there is no circumstance where the "status_bar_height", "dimen", "android" trick doesn’t return a value StatusBar.currentHeight takes care of this for Android.
    • However while that’s all fine for getting the height of the StatusBar, than doesn’t actually tell you if the StatusBar is overlapping (is set to translucent), if it’s not translucent than the overlapping height is 0.
    • If the user is running vanilla react-native on Android with no StatusBar tweaks, the StatusBar is not translucent and the overlapping height is 0.
    • But even if the user sets the StatusBar to translucent, that doesn’t mean that it is actually translucent. react-native supports android all the way back to API level 16. Whether the StatusBar is translucent if you set it depends on the version of Android and how you set it to translucent.
      • Android introduced the ability to set the StatusBar to translucent statically using android:windowTranslucentStatus in KitKat (API level 19), this is how Expo does it.
      • But it wasn’t till Lollipop (API level 21) that it became possible to set it translucent dynamically, the way <StatusBar translucent /> does it.

    I think the long term solution is going to be to make react-native support StatusBar.currentHeight on all platforms and add an StatusBar.isTranslucent() + event emitter api.

    By the way, just to make everyone’s lives worse 😉. For awhile now Android’s soft keys (the back/home/recents buttons at the bottom of the screen which are software based on some devices) have had the ability to set them as translucent as well; which is fine since that’s only a problem if the app author does it (at which point they may complain about bottom aligned tabs). Except, the new iPhone X now has a soft home indicator instead of the home button which underlaps just like the StatusBar, and that underlapping footer is mandatory. And just to rub salt in that, in landscape mode you also need to inset from the edges of the screen, except this horizontal inset should be done in your content instead of the views that contain them (so things like dividers still span to the edges).

    So a <NavigationFooter /> api for soft keys and the soft home indicator that works like the proposed StatusBar API will probably be a good idea as well. And then something else to deal with the horizontal insets. Maybe something higher level as well like <StatusBar.Spacer /> to make this easy to deal with.

  8. I have found a quick inline fix to the android translucent status bar in stacknavigator.

    navigationOptions: {
          headerStyle: {
            ...Platform.OS === 'android' ? { paddingTop: StatusBar.currentHeight, height: StatusBar.currentHeight + 56 } : {},
    }
    

    The syntax is from es6, which works just like Object.assign. Without using this, there will be 3 types of styles to check and set separately (iphone, iphone x, android).


    There are currently 2 pull requests trying to fix this:
    #2446
    #3036

  9. Why is this closed when the issue is not properly resolved? I just started an Expo app and it definitely does not play nicely with the top bar.

  10. Pingback: fake rolex