How to create a custom hook to change status bar styles for every screen using React Navigation

Published on Mar 13, 2020

6 min read

REACT-NATIVE

React Native has a component called StatusBar that is used to control the app status bar. Using the react-navigation library you might have a scenario where you don't have a header bar and on different screens, you would like to ensure the color of the status bar is correctly rendered. Such as on the light background, a dark status bar is displayed and on a darker background of the screen, a light status bar is displayed.

In this tutorial, let us create a custom hook that is going to keep track of the status bar color change whenever a screen changes. For this, you are also going to create mock screens with different background colors and integrate a tab bar.

I am going to use Expo to create a new React Native app but you can use React Native cli to generate a new project too.

Requirements

🔗

Ensure your dev environment includes the following required packages:

  • Node.js above 10.x.x installed on your local machine
  • JavaScript/ES6 basics
  • expo-cli

Installing and configuring react-navigation

🔗

Start by creating a new project using expo-cli. Navigate inside the project directory when the CLI has finished generating the new project. Then install all the required dependencies to integrate react-navigation library and bottom tabs.

expo init customStatusBarHook
cd customStatusBarHook
yarn add @react-navigation/native @react-navigation/bottom-tabs
expo install react-native-gesture-handler
react-native-reanimated react-native-screens
react-native-safe-area-context
@react-native-community/masked-view

That's it to configure the react-navigation library.

Create bottom Tabs

🔗

Create a new file called AppTabs.js inside src/navigation/ directory. This file is going to be the sole routes file for this demo. Inside it, you are going to create two tab components called HomeScreen and `SettingsScreen.

Start by importing all the necessary components.

1import React from 'react';
2import { NavigationContainer } from '@react-navigation/native';
3import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
4import { View, Text } from 'react-native';
5import { Ionicons } from '@expo/vector-icons';

I am using @expo/vector-icons to display icons for each tab but if you are using react-native cli to generate this project, you will have to install react-native-vector-icons library.

Create the functional component HomeScreen with a View and a Text as shown in the snippet below. This is going to be the first tab screen in the tab navigator.

1function HomeScreen() {
2 return (
3 <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
4 <Text style={{ fontSize: 20, color: '#333333' }}>Home Screen</Text>
5 </View>
6 );
7}

Also, add the following code snippet for the tab screen, SettingsScreen.

1function SettingsScreen() {
2 useStatusBar('light-content');
3 return (
4 <View
5 style={{
6 flex: 1,
7 justifyContent: 'center',
8 alignItems: 'center',
9 backgroundColor: '#be79df'
10 }}
11 >
12 <Text style={{ fontSize: 20, color: 'white' }}>Settings Screen</Text>
13 </View>
14 );
15}

Next, add the following snippet to create the tab navigator with the previous two screens. The following tab navigator is also going to use have tab icons that are going to have different tint colors based on whether being active or not. This can be done by using screenOptions.

1const Tabs = createBottomTabNavigator();
2
3export default function AppTabs() {
4 return (
5 <NavigationContainer>
6 <Tabs.Navigator
7 screenOptions={({ route }) => ({
8 tabBarIcon: ({ focused, color, size }) => {
9 let iconName;
10
11 if (route.name === 'Home') {
12 iconName = focused
13 ? 'ios-information-circle'
14 : 'ios-information-circle-outline';
15 } else if (route.name === 'Settings') {
16 iconName = focused ? 'ios-list-box' : 'ios-list';
17 }
18 return <Ionicons name={iconName} size={size} color={color} />;
19 }
20 })}
21 tabBarOptions={{
22 activeTintColor: 'tomato',
23 inactiveTintColor: 'gray'
24 }}
25 >
26 <Tabs.Screen name="Home" component={HomeScreen} />
27 <Tabs.Screen name="Settings" component={SettingsScreen} />
28 </Tabs.Navigator>
29 </NavigationContainer>
30 );
31}

Go to the terminal window and trigger the command expo start. You are going to get the following output in a simulator.

ss3

As you can notice from the above demo that on each the tab screen the color of the status bar is dark. On the second tab, since it has a darker background than the first tab, there should be a way to change the status bar for each screen component as it is mounted.

Create a custom Status bar hook

🔗

The react-navigation library provides a hook called useFocusEffect that helps to run side-effects when a specific screen is focused.

It is similar to useEffect hook from React with the difference being between the two is that side-effects in useFocusEffect run only when a screen component is focused.

Also, it is important to wrap the side-effect in React.useCallback hook to avoid triggering the effect after every render when the screen is focused.

Create a new file called Hooks.js inside src/utils/ directory. Import the following statements.

1import React, { useCallback } from 'react';
2import { StatusBar } from 'react-native';
3import { useFocusEffect } from '@react-navigation/native';

Then export a custom function called useStatusBar that is going to provide a simple way to change the color of the status bar when applied. Pass the style as the only parameter.

1export const useStatusBar = style => {
2 useFocusEffect(
3 useCallback(() => {
4 StatusBar.setBarStyle(style);
5 }, [])
6 );
7};

Apply custom hook to change the status bar color

🔗

Open src/navigation/AppTabs.js file and import useStatusBar. Also, inside both function components, add the following statements with appropriate bar style value.

1// after other import statements
2import { useStatusBar } from '../utils/Hooks';
3
4function HomeScreen() {
5 useStatusBar('dark-content');
6 // rest of the code remains same
7}
8
9function SettingsScreen() {
10 useStatusBar('light-content');
11 // rest of the code remains same
12}

Go back to the simulator or Expo client and you are going to notice the changes now.

ss4

For a better transition between two tabs, you can pass on another parameter called animate with a default value of boolean true in the useStatusBar custom hook.

Open src/utils/Hooks.js and add the following.

1export const useStatusBar = (style, animated = true) => {
2 useFocusEffect(
3 useCallback(() => {
4 StatusBar.setBarStyle(style, animated);
5 }, [])
6 );
7};

Now, go back to the Expo client to see the changes.

ss5

Conclusion

🔗

To read more about the useFocusEffect hook provided by the react-navigation library take a look at this link.

You can also set a status bar configuration based on different routes when using react-navigation. Take a look at this link to read more.

I hope this short tutorial was useful to you. Thanks for reading it!

Share this article on Twitter


More Posts

Browse all posts

Join 1300+ devs & subscribe to my newsletter!

NEWSLETTER

🔗 Subscribe on Revue

AMAN MITTAL author

Software Developer and Technical Writer since 2017. Loves learning and writing about Node.js, React, React Native & Expo. Previously, a Developer Advocate at Draftbit.


Copyright ©  2019-2022 Aman Mittal · All Rights Reserved.