Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Play nicely with translucent StatusBar on Android #12

Closed
brentvatne opened this issue Jan 27, 2017 · 35 comments

Comments

@brentvatne
Copy link
Member

commented Jan 27, 2017

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.

@FiberJW

This comment has been minimized.

Copy link
Contributor

commented Feb 23, 2017

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 👍🏿

@lyahdav

This comment has been minimized.

Copy link

commented Mar 13, 2017

@datwheat I ran into this same issue with react-navigation and Exponent. Can you provide an example of your code that fixed this? I'm not understanding where to add the paddingTop. I basically followed the instructions here to use react-navigation.

@kingr

This comment has been minimized.

Copy link

commented Mar 13, 2017

@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

@Ehesp

This comment has been minimized.

Copy link
Contributor

commented Mar 22, 2017

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.

@dantman

This comment has been minimized.

Copy link
Contributor

commented Apr 21, 2017

For handling this within react-navigation, it would probably be easiest if control of the statusBar as a navigation option was implemented in react-navigation first. Then instead of needing to ask for a React Native constant or some other sort of input we could just say that the transparent status bar should be applied using react-navigation's statusBar props.

@laxgoalie392

This comment has been minimized.

Copy link

commented May 2, 2017

Any news on this? I could only get the padding doing something like the following and the status bar does not reflect the color of the header

    static navigationOptions = {
        title: "Welcome",
        headerStyle: { marginTop: Constants.statusBarHeight },
    };

(Constants comes from expo)

@DavidBadura

This comment has been minimized.

Copy link

commented May 13, 2017

@laxgoalie392 you should use paddingTop instead of marginTop ;-)

{
    headerStyle: {
      paddingTop: Constants.statusBarHeight,
      height: 60 + Constants.statusBarHeight,
    }
}
@raarts

This comment has been minimized.

Copy link
Contributor

commented May 30, 2017

I have the same problem, but in iOS. The given solution works on Android, but not on iOS. Here's my main.js:

import Expo from 'expo';
import React from 'react';
import {
  StatusBar,
  StyleSheet,
  View,
  Text,
} from 'react-native';
import { TabNavigator } from "react-navigation";

class RecentChatsScreen extends React.Component {
  render() {
    return (
       <View style={styles.container}>
         <Text>List of recent chats!!</Text>
       </View>
    )
  }
}

class AllContactsScreen extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text>List of all contacts</Text>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  // App container
  container: {
    flex: 1,
    //paddingTop: Expo.Constants.statusBarHeight,
  },
});

const MainScreenNavigator = TabNavigator({
  Recent: { screen: RecentChatsScreen },
  All: { screen: AllContactsScreen },
}, {
  tabBarOptions: {
    style: {
      backgroundColor: 'orange',
      marginTop: 24
    },
  },
});

Expo.registerRootComponent(MainScreenNavigator);

Uncommenting paddingTop in styles.container works, but this moves reponsability for fixing this into each separate screen.

Here's my package.json:

{
  "name": "nav-test",
  "version": "0.0.0",
  "description": "Hello Expo!",
  "author": null,
  "private": true,
  "main": "main.js",
  "dependencies": {
    "expo": "17.0.0",
    "react": "16.0.0-alpha.6",
    "react-native": "https://github.com/expo/react-native/archive/sdk-17.0.0.tar.gz",
    "react-navigation": "^1.0.0-beta.11"
  }
}

On iOS this looks like this:

screen shot 2017-05-30 at 22 42 17

@awitherow

This comment has been minimized.

Copy link

commented Jun 17, 2017

@brentvatne is there a corresponding PR for this in Expo or is the work going to be handled here?

@awitherow

This comment has been minimized.

Copy link

commented Jun 17, 2017

I see that there are solutions such as

#1168 (comment)

and #1168 (comment) but this is not very optimal.

@brentvatne

This comment has been minimized.

Copy link
Member Author

commented Jun 18, 2017

@awitherow - it will be handled on here, @skevy will work on it soon

@Palisand

This comment has been minimized.

Copy link

commented Jun 23, 2017

@DavidBadura Why are you using 60 instead of 56 for height? I ask because react-navigation's APPBAR_HEIGTH constant is 56 for android.

Until skevy resolves this issue, I am using:

import {Constants} from "expo";

headerStyle: {
  height: Constants.statusBarHeight + (Platform.OS === "ios" ? 44 : 56),
  paddingTop: Platform.OS === "ios" ? 20 : Constants.statusBarHeight,
}
@marcos-abreu

This comment has been minimized.

Copy link

commented Jul 30, 2017

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.

@Palisand

This comment has been minimized.

Copy link

commented Jul 30, 2017

@marcos-abreu I have a couple months worth of react-native experience and a few more of react. For what it's worth, your solution seems just fine to me. As for why mine wasn't working, I recently made an edit to my answer without remembering that StatusBar.currentHeight was Android only. I've since re-edited, using Expo's constant instead. Only including a headerStyle is the probably the fastest way to resolve the header bar height issue, but certainly not the most optimal. However, considering a solution is in the works, I figure there is no need for further improvement just to have it reverted once the official solution is released.

Also, I am not experiencing problem 3 with my posted changes.

@marcos-abreu

This comment has been minimized.

Copy link

commented Jul 30, 2017

Hey @Palisand, thanks for replying and reviewing my solution. As for the problems I experienced when testing your solution (specially regarding 3) was that headerStyle is only available for StackNavigator (as documented here) but not on TabNavigator (as documented here) and even if I adjust the styles from your solution to a property that TabNavigatior accepts it results in something like the image bellow: (I've used the color red to indicate where the style is being applied)

screenshot_20170730-133724

@Palisand

This comment has been minimized.

Copy link

commented Jul 30, 2017

@marcos-abreu Hmmm... you're absolutely right. My TabNavigator was nested in a StackNavigator that had the styles set, which is why it was working for me. So it looks like your solution takes care of most, if not all, issues. The only suggestion I have now is that if you end up extracting the view-wrapped status bar into its own component, you might want to use redux to control its state, considering it is used app-wide.

@ricardoalcantara

This comment has been minimized.

Copy link

commented Aug 5, 2017

Hey people I might have found a way to solve that problem. I was having a terrible time trying to configure the "keyboard adjustresize" with Textinput and in the expo.io forum I discovered that if you fill the app.json with the field

    "androidStatusBar": {
        "backgroundColor": "#999999"
    }

It not only makes the "keyboard adjustresize" works but also does fix this translucent issue.

Update:
You need only the AndroidStatusBar -> backgroundColor

Update 2:
Remember to close and open the app again, I took so long to realize that I had to kill the app to apply the change.

@ro-savage

This comment has been minimized.

Copy link

commented Aug 20, 2017

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 } }
  })
@Ehesp

This comment has been minimized.

Copy link
Contributor

commented Aug 30, 2017

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.

@ricardoalcantara

This comment has been minimized.

Copy link

commented Aug 30, 2017

@ro-savage I guess if you go that way, when you need the "keyboard adjustresize" (that auto scroll to the inputfield when the keyboard appear), and you fix with that thing I said, you will get double sized status bar. But it happens only with the new create-native-react-app using expo framework.

@dantman

This comment has been minimized.

Copy link
Contributor

commented Sep 16, 2017

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.

@FMCorz

This comment has been minimized.

Copy link

commented Oct 6, 2017

Is this a terrible idea?

Edit: yes it is.

import { Constants } from 'expo';

const Navigation = StackNavigator({
  Home: {
    screen: HomeScreen
  }
});

export default class App extends React.Component {
  render() {
    return <Navigation style={{ paddingTop: Constants.statusBarHeight }} />;
  }
}

So surely the content will not flow underneath the bar, but at least it saves me from having to add padding to all my screens.

@leethree

This comment has been minimized.

Copy link
Contributor

commented Nov 6, 2017

It'll be great if SafeAreaView could support translucent status bar on Android.

@JuanCAlpizar

This comment has been minimized.

Copy link

commented Nov 7, 2017

@leethree sorry, I just found this when I was trying to find a solution to my status bar problem, but I don't quite understand, what´s SafeAreaView, is it some under the hood implementation or do we need to implement it in our current code?

@leethree

This comment has been minimized.

Copy link
Contributor

commented Nov 7, 2017

@JuanCAlpizar SafeAreaView was introduced in #2833 to support iPhone X paddings. Also see this comment from @davepack :
#2889 (comment)

@carsonwah

This comment has been minimized.

Copy link

commented Nov 22, 2017

This should be a pretty old issue. Is there any update on this?

I was using the following code to fix it (can't remember why 64):

headerStyle: {
        // padding height for translucent status bar
        height: Platform.OS === "ios" ? 64 : (56 + StatusBar.currentHeight),
        paddingTop: Platform.OS === "ios" ? 20 : StatusBar.currentHeight,
}

But it is not working desirably after that iphone x support update. (64 height in ios becomes too large)

Either way I will fix it manually. But I think this should be supported as soon as possible as Expo is translucent by default as @brentvatne said.

@carsonwah

This comment has been minimized.

Copy link

commented Nov 22, 2017

I am guessing that the source of the extra height comes from here:
(some updates from 1.0.0-beta.19, or actually 1.0.0-beta.18)
d93acdd#diff-7d1ace708bab0c8203594be64846ebecR47
https://github.com/react-community/react-navigation/releases/tag/v1.0.0-beta.19

Just FYI, for anyone having the same issue after the update.

@carsonwah

This comment has been minimized.

Copy link

commented Nov 30, 2017

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

@brentvatne

This comment has been minimized.

Copy link
Member Author

commented Jan 12, 2018

resolved in 1.0.0-beta.26

@inform880

This comment has been minimized.

Copy link

commented Jan 28, 2018

Does anybody have a quick fix for someone running with expo + crna? @carsonwah's fix didn't work for me.

@abhishek0058

This comment has been minimized.

Copy link

commented Mar 5, 2018

+1

@ken0x0a

This comment has been minimized.

Copy link

commented Jul 2, 2018

I'm using

"expo": "^28.0.0",
"react-navigation": "~2.3.1",

With this versions, to avoid navbar height overwrap on ANDROID, I'm using code below.
I hope it helps someone.

const HomeTabScreen = createStackNavigator(
  RouteConfig,
  {
    mode: 'card',
    navigationOptions: {
      headerTitleAllowFontScaling: true,
      ...Platform.select({
        android: {
          headerStyle: {
            marginTop: -Expo.Constants.statusBarHeight,
          },
        },
      }),
    },
  },
)
@Darker

This comment has been minimized.

Copy link

commented Sep 11, 2018

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.

@kemenesbalazs

This comment has been minimized.

Copy link

commented Mar 5, 2019

I can confirm it is definitely still an issue! same here with CRNA

@brentvatne

This comment has been minimized.

Copy link
Member Author

commented Mar 5, 2019

@Darker @kemenesbalazs - can you elaborate in a new issue? thanks!

@react-navigation react-navigation locked and limited conversation to collaborators Mar 5, 2019

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
You can’t perform that action at this time.