Skip to main content

Integrate AppBar with react-navigation

Prerequisites

  • react-native-paper
  • react-navigation

We assume that you have already installed the mentioned libraries above, otherwise please check out the guides below.

React Native Paper - Getting Started

React Navigation - Getting Started

Stack Navigator

We will start with react-navigation by creating a basic navigation stack. Stack navigator gives us a possibility of transition between screens in our app and manage navigation's history. In a simple scenario where there is only one stack navigator present in the app, it resembles a navigation state in a browser. Screens are pushed and popped from the stack while the user navigates to a new screen or go back to the previous one.

Let's create two screens. A main screen named Home and details screen named Details.

import 'react-native-gesture-handler';
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();

export default function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}

At the moment our navigation stack contains two screens and will render HomeScreen or DetailsScreen components according to the current navigation state. We have not implemented those components yet, so let's do this now:

import React from 'react';
import {View, Text, Button, StyleSheet} from 'react-native';

function HomeScreen() {
return (
<View style={style.container}>
<Text>Home Screen</Text>
</View>
);
}

function DetailsScreen() {
return (
<View style={style.container}>
<Text>Details Screen</Text>
</View>
);
}

const style = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});

Once we have finished implementing the components, we can run the app and check how Stack looks like.

To navigate from HomeScreen to DetailsScreen we can use the navigation object provided by Stack.Screen component. Every component rendered by Stack.Screen has an access to the navigation object via props. Let's modify our HomeScreen component:

import { Button } from 'react-native-paper';

function HomeScreen({ navigation }) {
return (
<View style={style.container}>
<Text>Home Screen</Text>
<Button mode="contained" onPress={() => navigation.navigate('Details')}>
Go to details
</Button>
</View>
);
}

const style = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});

Our result:

As you can see, we can already navigate between two screens. In the next steps, we will show you how to use Paper's AppBar instead of the default header.

Adding AppBar

We can customize Stack's header by passing custom component:

export default function App() {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
header: (props) => <CustomNavigationBar {...props} />,
}}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}

Now we will implement CustomNavigationBar using AppBar component:

import { Appbar } from 'react-native-paper';

export default function CustomNavigationBar() {
return (
<Appbar.Header>
<Appbar.Content title="My awesome app" />
</Appbar.Header>
);
}

Current implementation of the CustomNavigationBar is simple - we just render a title inside of it, however it's hardcoded "My awesome app" text, so let's adjust it. To automatically get the correct title for the screen, we're going to take the advantage of getHeaderTitle helper available in @react-navigation/elements in the following way:

import { Appbar } from 'react-native-paper';
import { getHeaderTitle } from '@react-navigation/elements';

export default function CustomNavigationBar({ route, options }) {
const title = getHeaderTitle(options, route.name);

return (
<Appbar.Header>
<Appbar.Content title={title} />
</Appbar.Header>
);
}

You may notice there is no way to go back to the previous screen, because the back button is not visible in the header. Let's add it now and let's make sure it's visible on all Stack's screens except Home screen.

In order to achieve it, we firstly check, if the navigation bar receives a back prop. If it has, it means there is another screen on the stack beneath the current screen and we should render the back arrow button in such a case.

import { Appbar } from 'react-native-paper';
import { getHeaderTitle } from '@react-navigation/elements';

export default function CustomNavigationBar({ navigation, route, options, back }) {
const title = getHeaderTitle(options, route.name);

return (
<Appbar.Header>
{back ? <Appbar.BackAction onPress={navigation.goBack} /> : null}
<Appbar.Content title={title} />
</Appbar.Header>
);
}

Adding more items to Appbar

Another interesting pattern that can be implemented with react-native-paper and react-navigation is a "menu" button. Thanks to the Menu component we can add a nice looking pop-up to our Appbar. To implement this feature we need to make a couple of changes in CustomNavigationBar:

  • Render a Menu component
  • Pass Appbar.Action to the anchor prop
  • Add a state to control Menu visibility
note

To have properly working Menu component, remember to wrap your root component with the PaperProvider:

import { PaperProvider } from 'react-native-paper';

// ...

<PaperProvider>
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
header: (props) => <CustomNavigationBar {...props} />,
}}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
</PaperProvider>

We also want the menu to appear only on HomeScreen, which means we will render it conditionally based on the back prop.

import React from 'react';
import { Appbar, Menu } from 'react-native-paper';
import { getHeaderTitle } from '@react-navigation/elements';

export default function CustomNavigationBar({
navigation,
route,
options,
back,
}) {
const [visible, setVisible] = React.useState(false);
const openMenu = () => setVisible(true);
const closeMenu = () => setVisible(false);

const title = getHeaderTitle(options, route.name);

return (
<Appbar.Header>
{back ? <Appbar.BackAction onPress={navigation.goBack} /> : null}
<Appbar.Content title={title} />
{!back ? (
<Menu
visible={visible}
onDismiss={closeMenu}
anchor={
<Appbar.Action
icon="dots-vertical"
onPress={openMenu}
/>
}>
<Menu.Item
onPress={() => {
console.log('Option 1 was pressed');
}}
title="Option 1"
/>
<Menu.Item
onPress={() => {
console.log('Option 2 was pressed');
}}
title="Option 2"
/>
<Menu.Item
onPress={() => {
console.log('Option 3 was pressed');
}}
title="Option 3"
disabled
/>
</Menu>
) : null}
</Appbar.Header>
);
}

Final result:

That's all we need! We have app bar that contains everything we need to navigate through screens and access an additional menu on the main screen. As you can see, with Material design Appbar provided by react-native-paper used together with react-navigation we can easily create an app that looks and works great.

Grab the Expo snack if you want to check the whole code!