1. 程式人生 > >Building a React Native App With Complex Navigation Using React Navigation

Building a React Native App With Complex Navigation Using React Navigation

In May 2018 my startup switched from the Ionic framework to React Native because the app we are developing needs versatile navigation. Ionic’s way of handling navigation was just too restrictive¹. The officially endorsed library for React Native is React Navigation.Soon after discovering the library, I fell in love with React Navigation

, because it’sjust so flexible, fast and easy to use if you know JavaScript.

Note about the library’s drawbacks: People tend to critique this library because its API can be a little confusing. Keep in mind, no navigation library is perfect (yet ?). That is why there are so many.

1. Planning the App

Now, let’s jump right into it ??! Here is what we are going to build.

The layout for our application’s navigation ?

In this diagram, solid lines represent navigators (switch, tabs, drawer, and stack) and dotted lines represent the different screens.

Our root navigator will be a switch navigator so that we can jump between the LoadingScreen, the authentication screens and the main screens of our app.The authentication screens will consist of a screen to log in, a screen to reset the password and a screen to register. The PasswordResetScreen

should be pushed onto the LoginScreen with a card transition.After logging in, the user should be able to access the main screens, which are the HomeScreen, the OptionsScreen, the DetailScreen, and the SettingsScreen.On Android, the user usually accesses the settings through a burger menu, while on iOS, it is more common to have a tabs layout. We will use the respective platform’s specific design dynamically. Additionally, within the HomeStack we will display the OptionsScreen using a modal transition on iOS.

Overall the app will have eight screens. The app represents a standard layout where the user sees a list of items on the HomeScreen, which he can filter using the OptionsScreen. And he can take a closer look at specific items on the DetailScreen. Note: We will not add the list interface in this tutorial.

(Optional) If You Want to Code Along, …

… you can either create your own new project or preferably clone this repository.

Then inside the root directory of the cloned project, install the packages.

npm install

Since I’m using the repository mentioned above as a basis for this tutorial, I will use TypeScript. If you are not using it, don’t bother about TypeScript. You can simply ignore the types. Merely remember to replace all .ts and .tsx endings with .js.

If you created your own project move your App.js inside a folder called app/ that should be inside your root directory. Remember to modify the import in index.js accordingly.

(Optional) Challenges

I strongly favor action taking over theory ?. Therefore this post includes challenges so you can immediately practice.

Reminder:If you struggle with a challenge, look at this post’s GitHub repository. Be sure to try hard first. Otherwise, you might diminish your learning experience!

2. Preparing the Screens

Before we get to code the navigation logic, we have to set up the screens. Let’s do it! ??

Note: If you dislike setting up the screens yourself there is a commit including the screen preparation on this post’s repository. You can use it to skip this part.

Create a directory called screens/ inside the app/ folder. In it, create a folder for each of the eight screens.

mkdir app/screens/Loading && mkdir app/screens/Login && mkdir app/screens/PasswordReset && mkdir app/screens/Register && mkdir app/screens/Home && mkdir app/screens/Options && mkdir app/screens/Detail && mkdir app/screens/Settings

When creating components, I like to have my styling in a separate styles.ts file and use an index.ts file to simplify my imports.So create three files in each folder. A styles.ts, an index.ts and a NameScreen.tsx (e.g. LoginScreen.tsx for the Login/ folder etc.).

Each styles.ts should look like this.

We will use this style to display text in the middle of the device telling us on which screen we are. Modify each NameScreen.tsx to look like this, and replace the words NameScreen with the actual files name.

We also need to export each screen from index.ts.

This might seem a bit redundant, which it is. I still go for this because I enjoy the benefits of having tidy, easy to read imports.

Eventually, we’ll also need buttons on some screens. We could create buttons ourselves using <TouchableOpacity />, but I prefer using libraries because they have high code quality and are easy and fast to use.

Install the React Native Elements library. We’ll use the beta version because it has better components. We’ll also need to install vector icons and link them.

npm install --save [email protected] install --save react-native-vector-iconsreact-native link react-native-vector-icons

If you’re using TypeScript add the types for the icons. React Native Elements already comes with types.

npm install --save-dev @types/react-native-vector-icons

That’s it. We are ready to build our navigation layout ?.

3. Using React Navigation

First, we have to install React Navigation.

npm install --save react-navigation

Add types if you are using TypeScript.

npm install --save-dev @types/react-navigation

3.1 Routing

Next, create a folder called navigator/ inside the app/ directory. In it create a file called Navigator.ts, in which we will do all the routing for our screens. We start by importing all our main screens and the LoadingScreen. We leave out the auth screens and will come back to them later.

Since you need to have defined the objects that we pass into our navigators, we’ll go from right to left in the diagram and start with the HomeStack. Following the documentation, we’ll pass it the Detail-, Home- and OptionsScreen.

Afterward, depending on the platform, we will need either a drawer or a tabs navigator. We’ll take care of the platform specific code later and just start with a tabs navigator, which contains the HomeStack and the SettingsScreen.

gg wp; a gist for a oneliner ?

Subsequently, we need the RootSwitch containing the LoadingScreen and the MainTabs.

3.1 a). Platform Specific Navigators

Let’s tackle the platform-specific code. We can import Platform and use its select() function to programmatically use a different navigator based on the device. Also specify an initial route, since we are not using the LoadingScreen (yet).

Navigator.ts should now look like this

For the routing, it’s as easy as that ??. There is still some work left later though.

3.1 b). Platform Specific Transitions

As mentioned earlier, for iOS it would be ideal to open the options using a modal transition, where the screen pops up from bottom to top. How do you use a modal transition on iOS, but not on Android? When I first needed a solution for this, I struggled with this question, too. It turns out there is an easy answer.

When you look at the stack navigator’s documentation, you will find a setting for its mode. But if we would just set it to “modal”, the DetailScreen will also have that transition, and we don’t want that. Being able to set the mode dynamically would be optimal.

The solution is to create a custom screen transition that knows which screens are modals. In our case, we will declare them in an array called IOS_MODAL_ROUTES.

Remember to import StackViewTransitionConfigs ☝?

Done. Now we can decide which transition is used on a page to page basis.

3.1 c). Adding the Navigator Into The App

The last thing we have to do is actually using the Navigator. Render the Navigator inside App.tsx.

You can safely run the app now. While you don’t have all buttons to utilize our routes and adequately navigate between all screens, it at least won’t crash.

3.2 Making the Screens Functional

If you launched the app, you would see some odd things.On Android, the only way to open the drawer is by swiping from the left side of the screen towards the middle. Wouldn’t it be nice to have a burger menu button inside the header?Furthermore, on both platforms, there is no way to access the OptionsScreen, and there is no header on the SettingsScreen. Screens that have headers have weird titles like “HomeScreen” instead of just “Home”. And, since we added the routes in the HomeStack alphabetically, the DetailScreen is rendered first instead of the HomeScreen.

The last problem can be addressed quickly by either changing the order of screens inside the HomeStack or by adding an initialRouteName setting. I won’t show you how to do this, this is your first challenge. Spoiler: We’ve already done it in this tutorial for a different navigator.

Now the DetailScreen is inaccessible too. We’ll address this further down.

3.2 a). Header Configuration

Before we begin configuring our headers, the SettingsScreen needs one. With React Navigation there’s only one way to add a header to a screen. The screen has to be embedded within a stack. This is your second challenge. Create a stack navigator called SettingsStack, insert the SettingsScreen in it and integrate the SettingsStack inside the respective platform’s main navigator.

Let’s configure the headers now. We can set the headers using the setting of each screen. We need to give the headers proper titles. And we need to include a logout button on iOS and a burger menu button to open the drawer on Android to both the HomeScreen and the SettingsScreen.

We set the title of the screen to “Home”². We used React Native Element’s to render a burger menu icon with which we can open and close the drawer. The styles.icon property is just paddingLeft: 10 to put some space between the edge of the screen and the icon.

Now, as your third challenge do the same for the SettingsScreen, but also give it a logout button on iOS. Note: For now, use onPress={() => {}}. You will give it better logout logic later.

3.2 b). Configuring the Drawer And the Tab Bar

On iOS, the tab bar lacks icons and proper titles. The same goes for the items inside the drawer on Android. We’ll fix that now³.

Remember to import React now ⚛️

Note: If you are using TypeScript and are experiencing linting errors after importing React, rename Navigator.ts to Navgitor.tsx.

In the navigationOptions of each stack inside the main navigator, we set the title and icon for both the tab bar and the drawer.

3.2 c). Using the Platform Specific Transitions

We still have to add a way to access the DetailScreen and the OptionsScreen. We create two buttons using React Native Elements on the HomeScreen, which will navigate to the respective screens.

You can modify justifyContent in styles.ts to “space-evenly” so that it looks decent. That’s all ?. Now our dynamicModalTransition will take care of the respective transition’s animation.

But wait. On iOS, we have a logout icon on the SettingsScreen. But there is no way to log out on Android, yet. What we need is a …

3.2 d). Logout Button For the Burger Menu ?

Create a new folder inside app/ called components/ and create another a new folder called BurgerMenu/ within that. Inside BurgerMenu/ we create a new component for our custom drawer that includes a logout button.

styles.container is just flex: 1 ?

Import it in Navigator.ts and add it as a to the drawer navigator. Now we have a logout button at the bottom of the drawer on Android. It will always be pushed to the bottom by the <ScrollView />. There is still an issue with the drawer though.

3.2 e). Fixing the Drawer Behavior

Currently, if you go on the Detail- or the OptionsScreen, you can open the drawer by swiping from the left corner of the screen to the right. We don’ t want our drawer to expand on these screens. To fix this, we have to lock our drawer on individual screens.

This is it. We can no longer open our drawer on screens pushed to the HomeStack.

3.2 f). Loading Screen Logic

We have not used the loading screen yet. Since this application is just to demonstrate React Navigation’s capabilities, there’s no purpose for the loading screen. You can learn here how you would use it.

3.3 Challenges

I will leave you with four challenges to polish this application ?. If you struggle with them, peek at the GitHub repository. Remember that practice makes perfect. If you cheat, you will lose your learning benefits.

Note: If you didn’t code along, but want to do these challenges there is a commit for this point in the article’s GitHub repository. Using this, you will have to create the RegisterScreen manually.

  1. Complete the auth screens. We did everything you need to know for this challenge already in this tutorial. Use a button on the LoginScreen to push the PasswordResetScreen on to the LoginStack.
  2. Add pseudo login and logout functionality⁴: Create a login button on the login screen. Make this button navigate to the HomeScreen. Make the platform’s respective logout buttons direct to the LoginScreen. Hint: For the drawer you will need .
  3. The logout button inside the drawer looks terrible. See if you can get its styling the same as the DrawerItems‘. Hint: You can find the code for the DrawerItems here.
  4. On iOS the tab bar is showing on the Detail- and OptionsScreen and on the PasswordResetScreen if you did the auth challenge. Hide it on pushed screens.
  5. Optional: Use localization to translate the strings.

Summary

  1. We planned the structure of our app.
  2. We build an app with complex routing that could very well be used for a real-world production application.
  3. We handled different platform-specific navigations and transitions.

Here is what the finished application looks like on iOS and on Android ?.

My name is Jan Hesters. I’m a Full Stack Developer specializing in JavaScript and Python, studied physicist, psychology-enthusiast, and alongside Nikolas, a founder of Full Stack Founders.
If you enjoyed this article, you might want to clap for a bunch of seconds, because it helps me out a lot.Thank you ?