React Native Navigation
App-wide support for 100% native navigation with an easy cross-platform interface. For iOS, this package is a wrapper around react-native-controllers, but provides a simplified more abstract API over it. This abstract API will be unified with the Android solution which is currently work in progress.
Overview
Installation - iOS
Note: We recommend using npm ver 3+. If you insist on using npm ver 2 please notice that the location for react-native-controllers in node_modules will be under the react-native-navigation folder and you'll need to change the paths in Xcode below accordingly.
-
Add the native files of the dependency react-native-controllers to your Xcode project:
-
In Xcode, in Project Navigator (left pane), right-click on the Libraries
> Add files to [project name]
. Add ./node_modules/react-native-controllers/ios/ReactNativeControllers.xcodeproj
(screenshots)
-
In Xcode, in Project Navigator (left pane), click on your project (top) and select the Build Phases
tab (right pane). In the Link Binary With Libraries
section add libReactNativeControllers.a
(screenshots)
-
In Xcode, in Project Navigator (left pane), click on your project (top) and select the Build Settings
tab (right pane). In the Header Search Paths
section add $(SRCROOT)/../node_modules/react-native-controllers/ios
. Make sure on the right to mark this new path recursive
(screenshots)
-
In Xcode, under your project files, modify AppDelegate.m
according to this example
Installation - Android
Coming soon, not yet supported
Usage
If you don't like reading, just jump into the fully working example projects:
- example - Example project showing the best practice use of this package. Shows many navigation features.
- redux-example - Best practice use of this package in a redux-based. project
Step 1 - Change the way your app starts
This would normally go in your index.ios.js
import { Navigation } from 'react-native-navigation';
import { registerScreens } from './screens';
registerScreens();
Navigation.startTabBasedApp({
tabs: [
{
label: 'One',
screen: 'example.FirstTabScreen',
icon: require('../img/one.png'),
selectedIcon: require('../img/one_selected.png'),
title: 'Screen One'
},
{
label: 'Two',
screen: 'example.SecondTabScreen',
icon: require('../img/two.png'),
selectedIcon: require('../img/two_selected.png'),
title: 'Screen Two'
}
]
});
Step 2 - Register all of your screen components
Every screen that you want to be able to place in a tab, push to the navigation stack or present modally needs to be registered. We recommend doing this in a central place, like screens/index.js
.
Note: Since your screens will potentially be bundled with other packages, your registered name must be unique! Follow a namespacing convention like packageName.ScreenName
.
import { Navigation } from 'react-native-navigation';
import FirstTabScreen from './FirstTabScreen';
import SecondTabScreen from './SecondTabScreen';
import PushedScreen from './PushedScreen';
export function registerScreens() {
Navigation.registerComponent('example.FirstTabScreen', () => FirstTabScreen);
Navigation.registerComponent('example.SecondTabScreen', () => SecondTabScreen);
Navigation.registerComponent('example.PushedScreen', () => PushedScreen);
}
Top Level API
Navigation
import { Navigation } from 'react-native-navigation';
- registerComponent(screenID, generator, store = undefined, Provider = undefined)
Every screen component in your app must be registered with a unique name. The component itself is a traditional React component extending React.Component
.
Navigation.registerComponent('example.FirstTabScreen', () => FirstTabScreen);
Navigation.registerComponent('example.FirstTabScreen', () => FirstTabScreen, store, Provider);
Change your app root into an app based on several tabs (usually 2-5), a very common pattern in iOS (like Facebook app or the iOS Contacts app). Every tab has its own navigation stack with a native nav bar.
Navigation.startTabBasedApp({
tabs: [
{
label: 'One',
screen: 'example.FirstTabScreen',
icon: require('../img/one.png'),
selectedIcon: require('../img/one_selected.png'),
title: 'Screen One',
navigatorStyle: {}
},
{
label: 'Two',
screen: 'example.SecondTabScreen',
icon: require('../img/two.png'),
selectedIcon: require('../img/two_selected.png'),
title: 'Screen Two'
}
],
drawer: {
left: {
screen: 'example.FirstSideMenu'
},
right: {
screen: 'example.SecondSideMenu'
}
}
});
- startSingleScreenApp(params)
Change your app root into an app based on a single screen (like the iOS Calendar or Settings app). The screen will receive its own navigation stack with a native nav bar
Navigation.startSingleScreenApp({
screen: {
screen: 'example.WelcomeScreen',
title: 'Welcome',
navigatorStyle: {}
},
drawer: {
left: {
screen: 'example.FirstSideMenu'
},
right: {
screen: 'example.SecondSideMenu'
}
}
});
Show a screen as a modal.
Navigation.showModal({
screen: "example.ModalScreen",
title: "Modal",
passProps: {},
navigatorStyle: {},
animationType: 'slide-up'
});
- dismissModal(params = {})
Dismiss the current modal.
Navigation.dismissModal({
animationType: 'slide-down'
});
- registerScreen(screenID, generator)
This is an internal function you probably don't want to use directly. If your screen components extend Screen
directly (import { Screen } from 'react-native-navigation'
), you can register them directly with registerScreen
instead of with registerComponent
. The main benefit of using registerComponent
is that it wraps your regular screen component with a Screen
automatically.
Navigation.registerScreen('example.AdvancedScreen', () => AdvancedScreen);
Screen API
This API is relevant when in a screen component context - it allows a screen to push other screens, pop screens, change its navigator style, etc. Access to this API is available through the navigator
object that is passed to your component through props
.
Push a new screen into this screen's navigation stack.
this.props.navigator.push({
screen: 'example.ScreenThree',
title: undefined,
passProps: {},
animated: true,
backButtonTitle: undefined,
navigatorStyle: {}
});
Pop the top screen from this screen's navigation stack.
this.props.navigator.pop({
animated: true
});
Pop all the screens until the root from this screen's navigation stack.
this.props.navigator.popToRoot({
animated: true
});
Reset the screen's navigation stack to a new screen (the stack root is changed).
this.props.navigator.resetTo({
screen: 'example.ScreenThree',
title: undefined,
passProps: {},
animated: true,
navigatorStyle: {}
});
Show a screen as a modal.
this.props.navigator.showModal({
screen: "example.ModalScreen",
title: "Modal",
passProps: {},
navigatorStyle: {},
animationType: 'slide-up'
});
- dismissModal(params = {})
Dismiss the current modal.
this.props.navigator.dismissModal({
animationType: 'slide-down'
});
- setOnNavigatorEvent(callback)
Set a handler for navigator events (like nav button press). This would normally go in your component constructor.
this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
Set buttons dynamically on the navigator. If your buttons don't change during runtime, see "Adding buttons to the navigator" below to add them using static navigatorButtons = {...};
.
this.props.navigator.setButtons({
leftButtons: [],
rightButtons: [],
animated: true
});
Set the nav bar title dynamically. If your title doesn't change during runtime, set it when the screen is defined / pushed.
this.props.navigator.setTitle({
title: "Dynamic Title"
});
- toggleDrawer(params = {})
Toggle the side menu drawer assuming you have one in your app.
this.props.navigator.toggleDrawer({
side: 'left',
animated: true
});
Styling the navigator
You can style the navigator appearance and behavior by passing a navigatorStyle
object. This object can be passed when the screen is originally created; can be defined per-screen by setting static navigatorStyle = {};
on the screen component; and can be overridden when a screen is pushed.
The easiest way to style your screen is by adding static navigatorStyle = {};
to your screen React component definition.
export default class StyledScreen extends Component {
static navigatorStyle = {
drawUnderNavBar: true,
navBarTranslucent: true
};
constructor(props) {
super(props);
}
render() {
return (
<View style={{flex: 1}}>...</View>
);
}
Style object format
{
navBarTextColor: '#000000',
navBarBackgroundColor: '#f7f7f7',
navBarButtonColor: '#007aff',
navBarHidden: false,
navBarHideOnScroll: false,
navBarTranslucent: false,
drawUnderNavBar: false,
drawUnderTabBar: false,
statusBarBlur: false,
navBarBlur: false,
tabBarHidden: false,
statusBarHideWithNavBar: false
statusBarHidden: false,
statusBarTextColorScheme: 'dark'
}
Note: If you set any styles related to the Status Bar, make sure that in Xcode > project > Info.plist, the property View controller-based status bar appearance
is set to YES
.
All supported styles are defined here. There's also an example project there showcasing all the different styles.
Adding buttons to the navigator
Nav bar buttons can be defined per-screen by adding static navigatorButtons = {...};
on the screen component definition. Handle onPress events for the buttons by setting your handler with navigator.setOnNavigatorEvent(callback)
.
class FirstTabScreen extends Component {
static navigatorButtons = {
rightButtons: [
{
title: 'Edit',
id: 'edit',
testID: 'e2e_rules'
},
{
icon: require('../../img/navicon_add.png'),
id: 'add'
}
]
};
constructor(props) {
super(props);
this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
}
onNavigatorEvent(event) {
if (event.id == 'edit') {
AlertIOS.alert('NavBar', 'Edit button pressed');
}
if (event.id == 'add') {
AlertIOS.alert('NavBar', 'Add button pressed');
}
}
render() {
return (
<View style={{flex: 1}}>...</View>
);
}
Buttons object format
{
rightButtons: [{
title: 'Edit',
icon: require('../../img/navicon_edit.png'),
id: 'compose',
testID: 'e2e_is_awesome'
}],
leftButtons: []
}
License
The MIT License.
See LICENSE