mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-12 22:51:18 +08:00
Initial public release of React Navigation
This commit is contained in:
32
.babelrc
Normal file
32
.babelrc
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"env": {
|
||||
// For RN example development
|
||||
"development": {
|
||||
"presets": ["react-native"],
|
||||
"plugins": [
|
||||
"transform-flow-strip-types",
|
||||
],
|
||||
},
|
||||
// For Jest
|
||||
"test": {
|
||||
"presets": ["react-native"],
|
||||
"plugins": [
|
||||
"transform-flow-strip-types",
|
||||
],
|
||||
},
|
||||
// For publishing to NPM for RN
|
||||
"publish-rn": {
|
||||
"presets": ["react-native-syntax"],
|
||||
"plugins": [
|
||||
"transform-flow-strip-types",
|
||||
],
|
||||
},
|
||||
// For publishing to NPM for RN
|
||||
"publish-web": {
|
||||
"presets": ["es2015", "stage-1", "react"],
|
||||
"plugins": [
|
||||
"transform-flow-strip-types",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
77
.eslintrc
Normal file
77
.eslintrc
Normal file
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"extends": "airbnb",
|
||||
"parser": "babel-eslint",
|
||||
"plugins": [
|
||||
"flowtype"
|
||||
],
|
||||
"env": {
|
||||
"jasmine": true
|
||||
},
|
||||
"rules": {
|
||||
"no-underscore-dangle": 0,
|
||||
"no-use-before-define": 0,
|
||||
"no-unused-expressions": 0,
|
||||
"new-cap": 0,
|
||||
"no-plusplus": 0,
|
||||
"no-class-assign": 0,
|
||||
"no-duplicate-imports": 0,
|
||||
|
||||
"import/extensions": 0,
|
||||
"import/no-extraneous-dependencies": 0,
|
||||
"import/no-unresolved": 0,
|
||||
|
||||
"react/jsx-filename-extension": [
|
||||
0, { "extensions": [".js", ".jsx"] }
|
||||
],
|
||||
"react/forbid-prop-types": 0,
|
||||
"react/sort-comp": 0,
|
||||
"react/prefer-stateless-function": 0,
|
||||
|
||||
"flowtype/boolean-style": [
|
||||
2,
|
||||
"boolean"
|
||||
],
|
||||
"flowtype/define-flow-type": 1,
|
||||
"flowtype/generic-spacing": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"flowtype/no-weak-types": 1,
|
||||
"flowtype/require-parameter-type": 2,
|
||||
"flowtype/require-return-type": [
|
||||
0,
|
||||
"always",
|
||||
{
|
||||
"annotateUndefined": "never"
|
||||
}
|
||||
],
|
||||
"flowtype/require-valid-file-annotation": 2,
|
||||
"flowtype/semi": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"flowtype/space-after-type-colon": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"flowtype/space-before-generic-bracket": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"flowtype/space-before-type-colon": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"flowtype/union-intersection-spacing": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"flowtype/use-flow-type": 1,
|
||||
"flowtype/valid-syntax": 1
|
||||
},
|
||||
"settings": {
|
||||
"flowtype": {
|
||||
"onlyFilesWithFlowAnnotation": false
|
||||
}
|
||||
}
|
||||
}
|
||||
55
.flowconfig
Normal file
55
.flowconfig
Normal file
@@ -0,0 +1,55 @@
|
||||
[ignore]
|
||||
; We fork some components by platform
|
||||
.*/*[.]android.js
|
||||
|
||||
; Ignore templates for 'react-native init'
|
||||
.*/local-cli/templates/.*
|
||||
|
||||
; Ignore the website subdir
|
||||
.*/node_modules/react-native/website/.*
|
||||
|
||||
; Ignore "BUCK" generated dirs
|
||||
.*/node_modules/react-native/\.buckd/
|
||||
|
||||
; Ignore unexpected extra "@providesModule"
|
||||
.*/node_modules/.*/node_modules/fbjs/.*
|
||||
|
||||
; Ignore duplicate module providers
|
||||
; For RN Apps installed via npm, "Libraries" folder is inside
|
||||
; "node_modules/react-native" but in the source repo it is in the root
|
||||
.*/node_modules/react-native/Libraries/react-native/React.js
|
||||
.*/node_modules/react-native/Libraries/react-native/ReactNative.js
|
||||
|
||||
<PROJECT_ROOT>/lib
|
||||
<PROJECT_ROOT>/lib-rn
|
||||
<PROJECT_ROOT>/examples
|
||||
<PROJECT_ROOT>/website
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
node_modules/react-native/Libraries/react-native/react-native-interface.js
|
||||
node_modules/react-native/flow/
|
||||
flow/
|
||||
|
||||
[options]
|
||||
module.system=haste
|
||||
|
||||
experimental.strict_type_args=true
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FixMe
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-7]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-7]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
|
||||
unsafe.enable_getters_and_setters=true
|
||||
|
||||
[version]
|
||||
^0.35.0
|
||||
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# VSCode
|
||||
.vscode/
|
||||
jsconfig.json
|
||||
|
||||
# NodeJS
|
||||
npm-debug.log
|
||||
node_modules
|
||||
lib-rn
|
||||
lib
|
||||
yarn-error.log
|
||||
|
||||
# OS X
|
||||
.DS_Store
|
||||
|
||||
# Exponent
|
||||
.exponent
|
||||
1
.npmignore
Normal file
1
.npmignore
Normal file
@@ -0,0 +1 @@
|
||||
examples
|
||||
1
.watchmanconfig
Normal file
1
.watchmanconfig
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
26
LICENSE
Normal file
26
LICENSE
Normal file
@@ -0,0 +1,26 @@
|
||||
BSD License
|
||||
|
||||
For React Navigation software
|
||||
|
||||
Copyright (c) 2016-present, React Navigation Contributors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
17
PULL_REQUEST_TEMPLATE.md
Normal file
17
PULL_REQUEST_TEMPLATE.md
Normal file
@@ -0,0 +1,17 @@
|
||||
Please provide enough information so that others can review your pull request:
|
||||
|
||||
Explain the **motivation** for making this change. What existing problem does the pull request solve?
|
||||
|
||||
Prefer **small pull requests**. These are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise split it.
|
||||
|
||||
**Test plan (required)**
|
||||
|
||||
Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI.
|
||||
|
||||
Make sure you test on both platforms if your change affects both platforms.
|
||||
|
||||
The code must pass tests and shouldn't add more Flow errors.
|
||||
|
||||
**Code formatting**
|
||||
|
||||
Look around. Match the style of the rest of the codebase.
|
||||
42
README.md
Normal file
42
README.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# React Navigation [](https://circleci.com/gh/reactjs/react-navigation)
|
||||
|
||||
*Learn once, navigate anywhere.*
|
||||
|
||||
Browse the docs on [reactnavigation.org](https://navigate:navigate@reactnavigation.org/). (The username/pass is `navigate:navigate`)
|
||||
|
||||
## [Getting started](https://reactnavigation.org/docs/intro/)
|
||||
|
||||
1. Create a new React Native App
|
||||
```
|
||||
react-native init SimpleApp
|
||||
cd SimpleApp
|
||||
```
|
||||
|
||||
2. Install the latest version of react-navigation from npm
|
||||
```
|
||||
yarn add react-navigation
|
||||
```
|
||||
or
|
||||
```
|
||||
npm install --save react-navigation
|
||||
```
|
||||
|
||||
3. Run the new app
|
||||
```
|
||||
react-native run-android # or:
|
||||
react-native run-ios
|
||||
```
|
||||
|
||||
## Advanced guide
|
||||
|
||||
- [Redux integration](https://reactnavigation.org/docs/guides/redux)
|
||||
- [Web integration](https://reactnavigation.org/docs/guides/web)
|
||||
- [Deep linking](https://reactnavigation.org/docs/guides/linking)
|
||||
- [Contributors guide](https://reactnavigation.org/docs/guides/contributors)
|
||||
|
||||
## React Navigation API
|
||||
|
||||
- [Navigators](https://reactnavigation.org/docs/navigators/)
|
||||
- [Routers](https://reactnavigation.org/docs/routers/)
|
||||
- [Views](https://reactnavigation.org/docs/views/)
|
||||
|
||||
23
circle.yml
Normal file
23
circle.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
machine:
|
||||
node:
|
||||
version: 6.1.0
|
||||
pre:
|
||||
- mkdir ~/.yarn-cache
|
||||
dependencies:
|
||||
pre:
|
||||
# Install Yarn
|
||||
- sudo apt-key adv --fetch-keys http://dl.yarnpkg.com/debian/pubkey.gpg
|
||||
- echo "deb http://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -y -qq yarn
|
||||
cache_directories:
|
||||
- "~/.yarn-cache"
|
||||
override:
|
||||
- yarn install
|
||||
- cd website && yarn install
|
||||
deployment:
|
||||
website-prod:
|
||||
branch: master
|
||||
commands:
|
||||
- yarn run build-docs
|
||||
- ./scripts/deploy-website.sh
|
||||
120
docs/api/navigators/DrawerNavigator.md
Normal file
120
docs/api/navigators/DrawerNavigator.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# DrawerNavigator
|
||||
|
||||
Used to easily set up a screen with a drawer navigation.
|
||||
|
||||
```js
|
||||
class MyHomeScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
drawer: () => ({
|
||||
label: 'Home',
|
||||
icon: ({ tintColor }) => (
|
||||
<Image
|
||||
source={require('./chats-icon.png')}
|
||||
style={[styles.icon, {tintColor: tintColor}]}
|
||||
/>
|
||||
),
|
||||
}),
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Button
|
||||
onPress={() => this.props.navigation.navigate('Notifications')}
|
||||
label="Go to notifications"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyNotificationsScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
drawer: () => ({
|
||||
label: 'Notifications',
|
||||
icon: ({ tintColor }) => (
|
||||
<Image
|
||||
source={require('./notif-icon.png')}
|
||||
style={[styles.tabIcon, {tintColor: tintColor}]}
|
||||
/>
|
||||
),
|
||||
}),
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Button
|
||||
onPress={() => this.props.navigation.goBack()}
|
||||
label="Go back home"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
icon: {
|
||||
width: 24,
|
||||
height: 24,
|
||||
},
|
||||
});
|
||||
|
||||
const MyApp = DrawerNavigator({
|
||||
Home: {
|
||||
screen: MyHomeScreen,
|
||||
},
|
||||
Notifications: {
|
||||
screen: MyNotificationsScreen,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
To open and close drawer, navigate to `'DrawerOpen'` and `'DrawerClose'` respectively.
|
||||
|
||||
```js
|
||||
this.props.navigation.navigate('DrawerOpen'); // open drawer
|
||||
this.props.navigation.navigate('DrawerClose'); // close drawer
|
||||
```
|
||||
|
||||
## API Definition
|
||||
|
||||
```js
|
||||
DrawerNavigator(RouteConfigs, DrawerNavigatorConfig)
|
||||
```
|
||||
|
||||
### Drawer Navigator Options
|
||||
|
||||
- `drawerWidth` - Width of the drawer
|
||||
- `contentComponent` - Component to use to render the navigation items. Receives the `navigation` prop for the drawer. Defaults to `DrawerView.Items`.
|
||||
- `contentOptions` - Configure the drawer content, see below.
|
||||
|
||||
Several options get passed to the underlying router to modify navigation logic:
|
||||
|
||||
- `initialRouteName` - The routeName for the initial route.
|
||||
- `order` - Array of routeNames which defines the order of the drawer items.
|
||||
- `paths` - Provide a mapping of routeName to path config, which overrides the paths set in the routeConfigs.
|
||||
- `backBehavior` - Should the back button cause switch to the initial route? If yes, set to `initialRoute`, otherwise `none`. Defaults to `initialRoute` behavior.
|
||||
|
||||
### `contentOptions` for `DrawerView.Items`
|
||||
|
||||
- `activeTintColor` - label and icon color of the active label
|
||||
- `activeBackgroundColor` - background color of the active label
|
||||
- `inactiveTintColor` - label and icon color of the inactive label
|
||||
- `inactiveBackgroundColor` - background color of the inactive label
|
||||
- `style` - style object for the content section
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
contentOptions: {
|
||||
activeTintColor: '#e91e63',
|
||||
style: {
|
||||
marginVertical: 0,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Navigator Props
|
||||
|
||||
The navigator component created by `DrawerNavigator(...)` takes the following props,
|
||||
|
||||
- `screenProps` - Props to pass to each child screen
|
||||
|
||||
21
docs/api/navigators/Navigators.md
Normal file
21
docs/api/navigators/Navigators.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Navigators
|
||||
|
||||
Navigators allow you define you application's navigation structure. Navigators also render common elements such as headers and tab bars which you can configure.
|
||||
|
||||
Under the hood, navigators are plain React components.
|
||||
|
||||
## Built-in Navigators
|
||||
|
||||
`react-navigation` includes the following functions to help you create navigators:
|
||||
|
||||
- [StackNavigator](/docs/navigators/stack) - Renders one screen at a time and provides transitions between screens. When a new screen is opened it is placed on top of the stack.
|
||||
- [TabNavigator](/docs/navigators/tab) - Renders a tab bar that lets the user switch between several screens
|
||||
- [DrawerNavigator](/docs/navigators/drawer) - Provides a drawer that slides in from the left of the screen
|
||||
|
||||
## Rendering screens with Navigators
|
||||
|
||||
The navigators render application screens which are just React components.
|
||||
|
||||
To learn how to create screens, read about:
|
||||
- [Screen `navigation` prop](/docs/navigators/navigation-prop) to allow the screen to dispatch navigation actions, such as opening another screen
|
||||
- [Screen `navigationOptions`](/docs/navigators/navigation-options) to customize how the screen gets presented by the navigator (e.g. header title, tab label)
|
||||
130
docs/api/navigators/StackNavigator.md
Normal file
130
docs/api/navigators/StackNavigator.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# StackNavigator
|
||||
|
||||
Provides a way for your app to transition between screens where each new screen is placed on top of a stack.
|
||||
|
||||
By default the StackNavigator is configured to have the familiar iOS and Android look & feel: new screens slide in from the right on iOS, fade in from the bottom on Android. On iOS the StackNavigator can also be configured to a modal style where screens slide in from the bottom.
|
||||
|
||||
```jsx
|
||||
|
||||
class MyHomeScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
title: 'Home',
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Button
|
||||
onPress={() => this.props.navigation.navigate('Profile', {name: 'Lucy'})}
|
||||
label="Go to Lucy's profile"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const ModalStack = StackNavigator({
|
||||
Home: {
|
||||
screen: MyHomeScreen,
|
||||
},
|
||||
Profile: {
|
||||
path: 'people/:name',
|
||||
screen: MyProfileScreen,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## API Definition
|
||||
|
||||
```js
|
||||
StackNavigator(RouteConfigs, StackNavigatorConfig)
|
||||
```
|
||||
|
||||
### RouteConfigs
|
||||
|
||||
The route configs object is a mapping from route name to a route config, which tells the navigator what to present for that route.
|
||||
|
||||
```js
|
||||
StackNavigator({
|
||||
|
||||
// For each screen that you can navigate to, create a new entry like this:
|
||||
Profile: {
|
||||
|
||||
// `ProfileScreen` is a React component that will be the main content of the screen.
|
||||
screen: ProfileScreen,
|
||||
// When `ProfileScreen` is loaded by the StackNavigator, it will be given a navigation prop.
|
||||
|
||||
// Optional: When deep linking or using react-navigation in a web app, this path is used:
|
||||
path: 'people/:username',
|
||||
// The action and route params are extracted from the path.
|
||||
|
||||
// Optional: Override the navigation options for the screen
|
||||
navigationOptions: {
|
||||
title: ({state}) => `${state.params.username}'s Profile'`,
|
||||
},
|
||||
},
|
||||
|
||||
...MyOtherRoutes,
|
||||
});
|
||||
```
|
||||
|
||||
### StackNavigatorConfig
|
||||
|
||||
Options for the router:
|
||||
|
||||
- `initialRouteName` - Sets the default screen of the stack. Must match one of the keys in route configs.
|
||||
- `initialRouteParams` - The params for the initial route
|
||||
- `paths` - A mapping of overrides for the paths set in the route configs
|
||||
|
||||
Visual options:
|
||||
|
||||
- `mode` - Defines the style for rendering and transitions:
|
||||
- `card` - Use the standard iOS and Android screen transitions. This is the default.
|
||||
- `modal` - Make the screens slide in from the bottom which is a common iOS pattern. Only works on iOS, has no effect on Android.
|
||||
- `headerMode` - Specifies how the header should be rendered:
|
||||
- `float` - Render a single header that stays at the top and animates as screens are changed. This is a common pattern on iOS.
|
||||
- `screen` - Each screen has a header attached to it and the header fades in and out together with the screen. This is a common pattern on Android.
|
||||
- `none` - No header will be rendered.
|
||||
|
||||
|
||||
### Screen Navigation Options
|
||||
|
||||
Usually you define static `navigationOptions` on your screen component. For example:
|
||||
|
||||
```jsx
|
||||
class ProfileScreen extends React.Component {
|
||||
|
||||
static navigationOptions = {
|
||||
|
||||
title: `${state.params.name}'s Profile!`,
|
||||
|
||||
header: ({ state, setParams }) => ({
|
||||
// Render a button on the right side of the header
|
||||
// When pressed switches the screen to edit mode.
|
||||
right: (
|
||||
<Button
|
||||
title={state.params.editing ? 'Done' : 'Edit'}
|
||||
onPress={() => setParams({editing: state.params.editing ? false : true})}
|
||||
/>
|
||||
),
|
||||
}),
|
||||
};
|
||||
...
|
||||
```
|
||||
|
||||
All `navigationOptions` for the `StackNavigator`:
|
||||
|
||||
- `title` - a title (string) displayed in the header
|
||||
- `header` - a config object for the header bar:
|
||||
- `visible` - Boolean toggle of header visibility. Only works when `headerMode` is `screen`.
|
||||
- `title` - Title string used by the navigation bar, or a custom React component
|
||||
- `right` - Custom component displayed on the right side of the header
|
||||
|
||||
### Examples
|
||||
|
||||
See the examples [SimpleStack.js](https://github.com/reactjs/react-navigation/tree/master/examples/NavigationPlayground/js/SimpleStack.js) and [ModalStack.js](https://github.com/reactjs/react-navigation/tree/master/examples/NavigationPlayground/js/ModalStack.js) which you can run locally as part of the [NavigationPlayground](https://github.com/reactjs/react-navigation/tree/master/examples/NavigationPlayground) app.
|
||||
|
||||
|
||||
### Navigator Props
|
||||
|
||||
The navigator component created by `StackNavigator(...)` takes the following props,
|
||||
|
||||
- `screenProps` - Props to pass to each child screen
|
||||
143
docs/api/navigators/TabNavigator.md
Normal file
143
docs/api/navigators/TabNavigator.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# TabNavigator
|
||||
|
||||
Used to easily set up a screen with several tabs with a TabRouter.
|
||||
|
||||
```js
|
||||
class MyHomeScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
tabBar: {
|
||||
label: 'Home',
|
||||
// Note: By default the icon is only shown on iOS. Search the showIcon option below.
|
||||
icon: ({ tintColor }) => (
|
||||
<Image
|
||||
source={require('./chats-icon.png')}
|
||||
style={[styles.icon, {tintColor: tintColor}]}
|
||||
/>
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Button
|
||||
onPress={() => this.props.navigation.navigate('Notifications')}
|
||||
label="Go to notifications"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyNotificationsScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
tabBar: {
|
||||
label: 'Notifications',
|
||||
icon: ({ tintColor }) => (
|
||||
<Image
|
||||
source={require('./notif-icon.png')}
|
||||
style={[styles.tabIcon, {tintColor: tintColor}]}
|
||||
/>
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Button
|
||||
onPress={() => this.props.navigation.goBack()}
|
||||
label="Go back home"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
icon: {
|
||||
width: 26,
|
||||
height: 26,
|
||||
},
|
||||
});
|
||||
|
||||
const MyApp = TabNavigator({
|
||||
Home: {
|
||||
screen: MyHomeScreen,
|
||||
},
|
||||
Notifications: {
|
||||
screen: MyNotificationsScreen,
|
||||
},
|
||||
}, {
|
||||
tabBarOptions: {
|
||||
activeTintColor: '#e91e63',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Tab Navigator Options
|
||||
|
||||
- `tabBarComponent` - component to use as the tab bar, e.g. `TabView.TabBarBottom`
|
||||
(this is the default on iOS), `TabView.TabBarTop`
|
||||
(this is the default on Android)
|
||||
- `tabBarPosition` - position of the tab bar, can be `'top'` or `'bottom'`
|
||||
- `swipeEnabled` - whether to allow swiping between tabs
|
||||
- `animationEnabled` - whether to animate when changing tabs
|
||||
- `lazyLoad` - whether to lazily render tabs as needed as opposed to rendering them upfront
|
||||
- `tabBarOptions` - configure the tab bar, see below.
|
||||
|
||||
Several options get passed to the underlying router to modify navigation logic:
|
||||
|
||||
- `initialTab` - The routeName for the initial tab route when first loading
|
||||
- `order` - Array of routeNames which defines the order of the tabs
|
||||
- `paths` - Provide a mapping of routeName to path config, which overrides the paths set in the routeConfigs.
|
||||
- `backBehavior` - Should the back button cause a tab switch to the initial tab? If yes, set to `initialTab`, otherwise `none`. Defaults to `initialTab` behavior.
|
||||
|
||||
### `tabBarOptions` for `TabBarBottom` (default tab bar on iOS)
|
||||
|
||||
- `activeTintColor` - label and icon color of the active tab
|
||||
- `activeBackgroundColor` - background color of the active tab
|
||||
- `inactiveTintColor` - label and icon color of the inactive tab
|
||||
- `inactiveBackgroundColor` - background color of the inactive tab
|
||||
- `style` - style object for the tab bar
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
tabBarOptions: {
|
||||
activeTintColor: '#e91e63',
|
||||
style: {
|
||||
backgroundColor: 'blue',
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `tabBarOptions` for `TabBarTop` (default tab bar on Android)
|
||||
|
||||
- `activeTintColor` - label and icon color of the active tab
|
||||
- `inactiveTintColor` - label and icon color of the inactive tab
|
||||
- `showIcon` - whether to show icon for tab, default is false
|
||||
- `showLabel` - whether to show label for tab, default is true
|
||||
- `upperCaseLabel` - whether to make label uppercase, default is true
|
||||
- `pressColor` - color for material ripple (Android >= 5.0 only)
|
||||
- `pressOpacity` - opacity for pressed tab (iOS and Android < 5.0 only)
|
||||
- `scrollEnabled` - whether to enable scrollable tabs
|
||||
- `tabStyle` - style object for the tab
|
||||
- `indicatorStyle` - style object for the tab indicator (line at the bottom of the tab)
|
||||
- `labelStyle` - style object for the tab label
|
||||
- `style` - style object for the tab bar
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
tabBarOptions: {
|
||||
labelStyle: {
|
||||
fontSize: 12,
|
||||
},
|
||||
style: {
|
||||
backgroundColor: 'blue',
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Navigator Props
|
||||
|
||||
The navigator component created by `TabNavigator(...)` takes the following props,
|
||||
|
||||
- `screenProps` - Props to pass to each child screen
|
||||
130
docs/api/routers/Routers.md
Normal file
130
docs/api/routers/Routers.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# Routers
|
||||
|
||||
Routers define a component's navigation state, and they allow the developer to define paths and actions that can be handled.
|
||||
|
||||
|
||||
## Built-In Routers
|
||||
|
||||
`react-navigation` ships with a few standard routers:
|
||||
|
||||
- [StackRouter](/docs/routers/stack)
|
||||
- [TabRouter](/docs/routers/tabs)
|
||||
|
||||
|
||||
## Using Routers
|
||||
|
||||
To make a navigator manually, put a static `router` on a component. (To quickly make a navigator with a built-in component, it may be easier to use a [Navigator Factory](/docs/navigators) instead)
|
||||
|
||||
```js
|
||||
class MyNavigator extends React.Component {
|
||||
static router = StackRouter(routes, config);
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Now you can use this component as a `screen` in another navigator, and the navigation logic for `MyNavigator` will be defined by this `StackRouter`.
|
||||
|
||||
|
||||
## Customizing Routers
|
||||
|
||||
See the [Custom Router API spec](/docs/routers/api) to learn about the API of `StackRouter` and `TabRouter`. You can override the router functions as you see fit:
|
||||
|
||||
### Custom Navigation Actions
|
||||
|
||||
To override navigation behavior, you can override the navigation state logic in `getStateForAction`, and manually manipulate the `routes` and `index`.
|
||||
|
||||
```js
|
||||
const MyApp = StackNavigator({
|
||||
Home: { screen: HomeScreen },
|
||||
Profile: { screen: ProfileScreen },
|
||||
}, {
|
||||
initialRouteName: 'Home',
|
||||
})
|
||||
const MyApp.router = {
|
||||
...MyApp.router,
|
||||
getStateForAction(action, state) {
|
||||
if (state && action.type === 'PushTwoProfiles') {
|
||||
const routes = [
|
||||
...state.routes,
|
||||
{key: 'A', routeName: 'Profile', params: { name: action.name1 }},
|
||||
{key: 'B', routeName: 'Profile', params: { name: action.name2 }},
|
||||
];
|
||||
return {
|
||||
...state,
|
||||
routes,
|
||||
index: routes.length - 1,
|
||||
};
|
||||
}
|
||||
return MyApp.router.getStateForAction(action, state);
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Blocking Navigation Actions
|
||||
|
||||
Sometimes you may want to prevent some navigation activity, depending on your route.
|
||||
|
||||
```js
|
||||
const MyStackRouter = StackRouter({
|
||||
Home: { screen: HomeScreen },
|
||||
Profile: { screen: ProfileScreen },
|
||||
}, {
|
||||
initialRouteName: 'Home',
|
||||
})
|
||||
const MyAppRouter = {
|
||||
...MyStackRouter,
|
||||
getStateForAction(action, state) {
|
||||
if (
|
||||
state &&
|
||||
action.type === 'Back' &&
|
||||
state.routes[state.index].params.isEditing
|
||||
) {
|
||||
// Returning null from getStateForAction means that the action
|
||||
// has been handled/blocked, but there is not a new state
|
||||
return null;
|
||||
}
|
||||
return MyStackRouter.getStateForAction(action, state);
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### Handling Custom URIs
|
||||
|
||||
Perhaps your app has a unique URI which the built-in routers cannot handle. You can always extend the router `getActionForPathAndParams`.
|
||||
|
||||
```js
|
||||
|
||||
const MyApp = StackNavigator({
|
||||
Home: { screen: HomeScreen },
|
||||
Profile: { screen: ProfileScreen },
|
||||
}, {
|
||||
initialRouteName: 'Home',
|
||||
})
|
||||
MyApp.router = {
|
||||
...MyApp.router,
|
||||
getActionForPathAndParams(path, params) {
|
||||
if (
|
||||
path === 'my/custom/path' &&
|
||||
params.magic === 'yes'
|
||||
) {
|
||||
// returns a profile navigate action for /my/custom/path?magic=yes
|
||||
return {
|
||||
type: 'Navigate',
|
||||
routeName: 'Profile',
|
||||
action: {
|
||||
// This child action will get passed to the child router
|
||||
// ProfileScreen.router.getStateForAction to get the child
|
||||
// navigation state.
|
||||
type: 'Navigate',
|
||||
routeName: 'Friends',
|
||||
},
|
||||
};
|
||||
return null;
|
||||
}
|
||||
return MyApp.router.getStateForAction(action, state);
|
||||
},
|
||||
};
|
||||
```
|
||||
101
docs/api/routers/RoutersAPI.md
Normal file
101
docs/api/routers/RoutersAPI.md
Normal file
@@ -0,0 +1,101 @@
|
||||
## Custom Router API
|
||||
|
||||
You can make your own router by building an object with the following functions:
|
||||
|
||||
```js
|
||||
const MyRouter = {
|
||||
getStateForAction: (action) => ({}),
|
||||
getActionForPathAndParams: (path, params) => null,
|
||||
getPathAndParamsForState: (state) => null,
|
||||
getComponentForState: (state) => MyScreen,
|
||||
getComponentForRouteName: (routeName) => MyScreen,
|
||||
};
|
||||
|
||||
// Now, you can make a navigator by putting the router on it:
|
||||
class MyNavigator extends React.Component {
|
||||
static router = MyRouter;
|
||||
render() {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
### `getStateForAction(action, state)`
|
||||
|
||||
Defines the navigation state in response to a given action. This function will be run when an action gets passed into `props.navigation.dispatch(`, or when any of the helper functions are called, like `navigation.navigate(`.
|
||||
|
||||
Typically this should return a navigation state, with the following form:
|
||||
|
||||
```
|
||||
{
|
||||
index: 1, // identifies which route in the routes array is active
|
||||
routes: [
|
||||
{
|
||||
// Each route needs a name to identify the type.
|
||||
routeName: 'MyRouteName',
|
||||
|
||||
// A unique identifier for this route in the routes array:
|
||||
key: 'myroute-123',
|
||||
// (used to specify the re-ordering of routes)
|
||||
|
||||
// Routes can have any data, as long as key and routeName are correct
|
||||
...randomRouteData,
|
||||
},
|
||||
...moreRoutes,
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
If the router has handled the action externally, or wants to swallow it without changing the navigation state, this function will return `null`.
|
||||
|
||||
### `getComponentRouteName(routeName)`
|
||||
|
||||
Returns the child component or navigator for the given route name.
|
||||
|
||||
Say a router `getStateForAction` outputs a state like this:
|
||||
```js
|
||||
{
|
||||
index: 1,
|
||||
routes: [
|
||||
{ key: 'A', routeName: 'Foo' },
|
||||
{ key: 'B', routeName: 'Bar' },
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
Based on the routeNames in the state, the router is responsible for returning valid components when calling `router.getComponentRouteName('Foo')` or `router.getComponentRouteName('Bar')`.
|
||||
|
||||
### `getComponentForState(state)`
|
||||
|
||||
Returns the active component for a deep navigation state.
|
||||
|
||||
### `getActionForPathAndParams`
|
||||
|
||||
Returns an optional navigation action that should be used when the user navigates to this path and provides optional query parameters.
|
||||
|
||||
### `getPathAndParamsForState`
|
||||
|
||||
Returns the path and params that can be put into the URL to link the user back to the same spot in the app.
|
||||
|
||||
The path/params that are output from this should form an action when passed back into the router's `getActionForPathAndParams`. That action should take you to a similar state once passed through `getStateForAction`.
|
||||
|
||||
### `getScreenConfig`
|
||||
|
||||
Used to retrieve the navigation options for a route. Must provide the screen's current navigation prop, and the name of the option to be retrieved.
|
||||
|
||||
- `navigation` - This is the navigation prop that the screen will use, where the state refers to the screen's route/state. Dispatch will trigger actions in the context of that screen.
|
||||
- `optionName` - What named option is being fetched, such as 'title'
|
||||
|
||||
Inside an example view, perhaps you need to fetch the configured title:
|
||||
```js
|
||||
// First, prepare a navigation prop for your child, or re-use one if already available.
|
||||
const childNavigation = addNavigationHelpers({
|
||||
// In this case we use navigation.state.index because we want the title for the active route.
|
||||
state: navigation.state.routes[navigation.state.index],
|
||||
dispatch: navigation.dispatch,
|
||||
})
|
||||
const screenTitle = this.props.router.getScreenConfig(childNavigation, 'title');
|
||||
```
|
||||
64
docs/api/routers/StackRouter.md
Normal file
64
docs/api/routers/StackRouter.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# StackRouter
|
||||
|
||||
Manage the logical navigation stack, including pushing, popping, and handling path parsing to create a deep stack.
|
||||
|
||||
Let's take a look at a simple stack router:
|
||||
|
||||
```js
|
||||
const MyApp = StackRouter({
|
||||
Home: { screen: HomeScreen },
|
||||
Profile: { screen: ProfileScreen },
|
||||
}, {
|
||||
initialRouteName: 'Home',
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
### RouteConfig
|
||||
|
||||
A basic stack router have a route config. Here is an example configuration:
|
||||
|
||||
```js
|
||||
const MyApp = StackRouter({ // This is the RouteConfig:
|
||||
Home: {
|
||||
screen: HomeScreen,
|
||||
path: '',
|
||||
},
|
||||
Profile: {
|
||||
screen: ProfileScreen,
|
||||
path: 'profile/:name',
|
||||
},
|
||||
Settings {
|
||||
// This can be handy to lazily require a screen:
|
||||
getScreen: () => require('Settings').default,
|
||||
// Note: Child navigators cannot be configured using getScreen because
|
||||
// the router will not be accessible. Navigators must be configured
|
||||
// using `screen: MyNavigator`
|
||||
path: 'settings',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Each item in the config may have the following:
|
||||
|
||||
- `path` - Specify the path and params to be parsed for item in the stack
|
||||
- `screen` - Specify the screen component or child navigator
|
||||
- `getScreen` - Set a lazy getter for a screen component (but not navigators)
|
||||
|
||||
|
||||
### StackConfig
|
||||
|
||||
Config options that are also passed to the stack router.
|
||||
|
||||
- `initalRouteName` - The routeName for the default route when the stack first loads
|
||||
- `initialRouteParams` - Default params of the initial route
|
||||
- `paths` - Provide a mapping of routeName to path config, which overrides the paths set in the routeConfigs.
|
||||
|
||||
### Supported Actions
|
||||
|
||||
The stack router may respond to the following navigation actions. The router will generally delegate the action handling to a child router, if possible.
|
||||
|
||||
- Navigate - Will push a new route on the stack if the routeName matches one of the router's routeConfigs
|
||||
- Back - Goes back (pops)
|
||||
- Reset - Clears the stack and provides new actions to create a fully new navigation state
|
||||
- SetParams - An action that a screen dispatches to change the params of the current route.
|
||||
60
docs/api/routers/TabRouter.md
Normal file
60
docs/api/routers/TabRouter.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# TabRouter
|
||||
|
||||
Manage a set of tabs in the application, handle jumping to tabs, and handle the back button press to jump to the initial tab.
|
||||
|
||||
Let's take a look at a simple tabs router:
|
||||
|
||||
```js
|
||||
const MyApp = TabRouter({
|
||||
Home: { screen: HomeScreen },
|
||||
Settings: { screen: SettingsScreen },
|
||||
}, {
|
||||
initialTab: 'Home',
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
### RouteConfig
|
||||
|
||||
A tabs router has a routeConfig for each possible tab:
|
||||
|
||||
```js
|
||||
const MyApp = TabRouter({ // This is the RouteConfig:
|
||||
Home: {
|
||||
screen: HomeScreen,
|
||||
path: 'main',
|
||||
},
|
||||
Settings: {
|
||||
// This can be handy to lazily require a tab:
|
||||
getScreen: () => require('./SettingsScreen').default,
|
||||
// Note: Child navigators cannot be configured using getScreen because
|
||||
// the router will not be accessible. Navigators must be configured
|
||||
// using `screen: MyNavigator`
|
||||
path: 'settings',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Each item in the config may have the following:
|
||||
|
||||
- `path` - Specify the path for each tab
|
||||
- `screen` - Specify the screen component or child navigator
|
||||
- `getScreen` - Set a lazy getter for a screen component (but not navigators)
|
||||
|
||||
|
||||
### Tab Router Config
|
||||
|
||||
Config options that are also passed to the router.
|
||||
|
||||
- `initialTab` - The routeName for the initial tab route when first loading
|
||||
- `order` - Array of routeNames which defines the order of the tabs
|
||||
- `paths` - Provide a mapping of routeName to path config, which overrides the paths set in the routeConfigs.
|
||||
- `backBehavior` - Should the back button cause a tab switch to the initial tab? If yes, set to `initialTab`, otherwise `none`. Defaults to `initialTab` behavior.
|
||||
|
||||
### Supported Actions
|
||||
|
||||
The tabs router may respond to the following navigation actions. The router will generally delegate the action handling to a child router, if possible.
|
||||
|
||||
- Navigate - Will jump to the routeName if it matches a tab
|
||||
- Back - Goes to the first tab, if not already selected
|
||||
- SetParams - An action that a screen dispatches to change the params of the current route.
|
||||
191
docs/api/views/Transitioner.md
Normal file
191
docs/api/views/Transitioner.md
Normal file
@@ -0,0 +1,191 @@
|
||||
# Transitioner
|
||||
|
||||
`Transitioner` is a React component that helps manage transitions for complex animated components. It manages the timing of animations and keeps track of various screens as they enter and leave, but it doesn't know what anything looks like, because rendering is entirely deferred to the developer.
|
||||
|
||||
Under the covers, `Transitioner` is used to implement `CardStack`, and hence the `StackNavigator`.
|
||||
|
||||
The most useful thing `Transitioner` does is to take in a prop of the current navigation state. When routes are removed from that navigation state, `Transitioner` will coordinate the transition away from those routes, keeping them on screen even though they are gone from the navigation state.
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
```jsx
|
||||
class MyNavView extends Component {
|
||||
...
|
||||
render() {
|
||||
return (
|
||||
<Transitioner
|
||||
configureTransition={this._configureTransition}
|
||||
navigationState={this.props.navigation.state}
|
||||
render={this._render}
|
||||
onTransitionStart={this.onTransitionStart}
|
||||
onTransitionEnd={this.onTransitionEnd}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
### `configureTransition` function
|
||||
|
||||
Invoked on `Transitioner.componentWillReceiveProps`, this function allows customization of animation parameters such as `duration`. The value returned from this function will be fed into a timing function, by default `Animated.timing()`, as its config.
|
||||
|
||||
#### Examples
|
||||
|
||||
```js
|
||||
_configureTransition(transitionProps, prevTransitionProps) {
|
||||
return {
|
||||
// duration in milliseconds, default: 250
|
||||
duration: 500,
|
||||
// An easing function from `Easing`, default: Easing.inOut(Easing.ease)
|
||||
easing: Easing.bounce,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note: `duration` and `easing` are only applicable when the timing function is `Animated.timing`. We can also use a different timing function and its corresponding config parameters, like so:
|
||||
|
||||
```js
|
||||
_configureTransition(transitionProps, prevTransitionProps) {
|
||||
return {
|
||||
// A timing function, default: Animated.timing.
|
||||
timing: Animated.spring,
|
||||
// Some parameters relevant to Animated.spring
|
||||
friction: 1,
|
||||
tension: 0.5,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Flow definition
|
||||
|
||||
```js
|
||||
configureTransition: (
|
||||
transitionProps: NavigationTransitionProps,
|
||||
prevTransitionProps: ?NavigationTransitionProps,
|
||||
) => NavigationTransitionSpec,
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
- `transitionProps`: the current [NavigationTransitionProps](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L273) created from the current navigation state and props
|
||||
- `prevTransitionProps`: the previous [NavigationTransitionProps](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L273) created from the previous navigation state and props
|
||||
|
||||
#### Returns
|
||||
- An object of type [NavigationTransitionSpec](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L316) that will be fed into an Animated timing function as its config
|
||||
|
||||
|
||||
### `navigationState` object
|
||||
A plain object that represents the navigation state
|
||||
|
||||
#### Example value
|
||||
|
||||
```js
|
||||
{
|
||||
// Index refers to the active child route in the routes array.
|
||||
index: 1,
|
||||
routes: [
|
||||
{ key: 'DF2FGWGAS-12', routeName: 'ContactHome' },
|
||||
{ key: 'DF2FGWGAS-13', routeName: 'ContactDetail', params: { personId: 123 } }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Flow definition
|
||||
```js
|
||||
export type NavigationState = {
|
||||
index: number,
|
||||
routes: Array<NavigationRoute>,
|
||||
};
|
||||
```
|
||||
|
||||
For more information about the `NavigationRoute` type, check out its [flow definition](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L32).
|
||||
|
||||
### `render` function
|
||||
Invoked from `Transitioner.render()`. This function performs the actual rendering delegated from `Transitioner`. In this function, we can use the information included in the `transitionProps` and `prevTransitionProps` parameters to render scenes, create animations and handle gestures.
|
||||
|
||||
There are a few important properties of the `transitionProps` and `prevTransitionProps` parameters that are useful for the tasks mentioned above:
|
||||
|
||||
- `scenes: Array<NavigationScene>` - a list of all available scenes
|
||||
- `position: NavigationAnimatedValue` - the progressive index of the transitioner's navigation state
|
||||
- `progress: NavigationAnimatedValue` - the value that represents the progress of the transition when navigation state changes from one to another. Its numberic value will range from 0 to 1.
|
||||
|
||||
For the complete list of properties of `NavigationTransitionProps`, check out its [flow definition](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L273).
|
||||
|
||||
#### Examples
|
||||
|
||||
`transitionProps.scenes` is the list of all available scenes. It is up to the implementor to determine how to lay them out on the screen. For example, we can render the scenes as a stack of cards like so:
|
||||
|
||||
```jsx
|
||||
_render(transitionProps, prevTransitionProps) {
|
||||
const scenes = transitionProps.scenes.map(scene => this._renderScene(transitionProps, scene));
|
||||
return (
|
||||
<View style={styles.stack}>
|
||||
{scenes}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
We can then use an `Animated.View` to animate the transition. To create necessary animated style properties, such as `opacity`, we can interpolate on `position` and `progress` values that come with `transitionProps`:
|
||||
|
||||
```jsx
|
||||
_renderScene(transitionProps, scene) {
|
||||
const { position } = transitionProps;
|
||||
const { index } = scene;
|
||||
const opacity = position.interpolate({
|
||||
inputRange: [index-1, index, index+1],
|
||||
outputRange: [0, 1, 0],
|
||||
});
|
||||
// The prop `router` is populated when we call `createNavigator`.
|
||||
const Scene = this.props.router.getComponent(scene.route.routeName);
|
||||
return (
|
||||
<Animated.View style={{ opacity }}>
|
||||
{ Scene }
|
||||
</Animated.View>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The above code creates a cross fade animation during transition.
|
||||
|
||||
For a comprehensive tutorial on how to create custom transitions, see this [blog post](http://www.reactnativediary.com/2016/12/20/navigation-experimental-custom-transition-1.html).
|
||||
|
||||
#### Flow definition
|
||||
```js
|
||||
render: (transitionProps: NavigationTransitionProps, prevTransitionProps: ?NavigationTransitionProps) => React.Element<*>,
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
- `transitionProps`: the current [NavigationTransitionProps](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L273) created from the current state and props
|
||||
- `prevTransitionProps`: the previous [NavigationTransitionProps](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L273) created from the previous state and props
|
||||
|
||||
#### Returns
|
||||
- A ReactElement, which will be used to render the Transitioner component
|
||||
|
||||
### `onTransitionStart` function
|
||||
Invoked when the transition animation is about to start.
|
||||
|
||||
#### Flow definition
|
||||
```js
|
||||
onTransitionStart: (transitionProps: NavigationTransitionProps, prevTransitionProps: ?NavigationTransitionProps) => void,
|
||||
```
|
||||
#### Parameters
|
||||
- `transitionProps`: the current [NavigationTransitionProps](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L273) created from the current state and props
|
||||
- `prevTransitionProps`: the previous [NavigationTransitionProps](https://github.com/reactjs/react-navigation/blob/master/src/TypeDefinition.js#L273) created from the previous state and props
|
||||
|
||||
#### Returns
|
||||
- none.
|
||||
|
||||
### `onTransitionEnd` function
|
||||
Invoked once the transition animation completes.
|
||||
|
||||
#### Flow definition
|
||||
```js
|
||||
onTransitionEnd: () => void
|
||||
```
|
||||
#### Parameters
|
||||
- none.
|
||||
|
||||
#### Returns
|
||||
- none.
|
||||
17
docs/api/views/Views.md
Normal file
17
docs/api/views/Views.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Views
|
||||
|
||||
Navigation views are presentation components that take a [`router`](/docs/api/routers) and a [`navigation`](/docs/navigators/navigation-prop) prop, and can display several screens, as specified by the `navigation.state`.
|
||||
|
||||
Navigation views are controlled React components that can present the current navigation state. They manage switching of screens, animations and gestures. They also present persistent navigation views such as tab bars and headers.
|
||||
|
||||
## Built in Views
|
||||
|
||||
- [CardStack](https://github.com/reactjs/react-navigation/blob/master/src/views/CardStack.js) - Present a stack that looks suitable on any platform
|
||||
+ [Card](https://github.com/reactjs/react-navigation/blob/master/src/views/Card.js) - Present one card from the card stack, with gestures
|
||||
+ [Header](https://github.com/reactjs/react-navigation/blob/master/src/views/Header.js) - The header view for the card stack
|
||||
- [Tabs](https://github.com/reactjs/react-navigation/blob/master/src/views/TabView) - A configurable tab switcher / pager
|
||||
- [Drawer](https://github.com/reactjs/react-navigation/tree/master/src/views/Drawer) - A view with a drawer that slides from the left
|
||||
|
||||
## [Transitioner](/docs/views/transitioner)
|
||||
|
||||
`Transitioner` manages the animations during the transition and can be used to build fully custom navigation views. It is used inside the `CardStack` view. [Learn more about Transitioner here.](/docs/views/transitioner)
|
||||
61
docs/blog/2017-01-Introducing-React-Navigation.md
Normal file
61
docs/blog/2017-01-Introducing-React-Navigation.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Introducing React Navigation for React Native
|
||||
|
||||
Today we're excited to introduce React Navigation, a flexible navigation library for React Native and web, including customizable views for React Native, routers for any platform, and navigators that make it super easy to get started. We aim to provide a simple and extensible solution which enables developers to share one navigation paradigm for all of their React apps.
|
||||
|
||||
|
||||
## Start Quick with pre-built Navigators
|
||||
|
||||
A navigator is a React component with a static `.router` declared on it. To make it super easy to get started, React Navigation ships with a few navigator factories, pairing common views with routers.
|
||||
|
||||
For example, the provided `StackNavigator` makes it easy to use a `CardStack` view and a `StackRouter` together:
|
||||
|
||||
```js
|
||||
const MyApp = StackNavigator({
|
||||
Home: {screen: HomeScreen},
|
||||
Profile: {screen: ProfileScreen},
|
||||
});
|
||||
```
|
||||
|
||||
Each of these screens are just React components, and they can easily set their own title:
|
||||
|
||||
```js
|
||||
class HomeScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
title: 'Home',
|
||||
};
|
||||
render() {
|
||||
const { navigate } = this.props.navigation;
|
||||
return (
|
||||
<Button
|
||||
onPress={() => navigate('Profile', { name: 'A' })}
|
||||
title="Go to A's profile"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To learn more, [continue with the getting started guide](/docs/intro).
|
||||
|
||||
|
||||
## Performant Views on React Native
|
||||
|
||||
Animations and gestures are critical for smooth navigation in a mobile app. React Navigation utilizes React Native's Animated library to provide 60fps animations that are driven from the native thread.
|
||||
|
||||
The views are designed to be highly extensible. For your app, you may want to build a custom modal, fork the stack header, or even utilize the underlying `<Transitioner>` component to build an entirely custom navigation presentation.
|
||||
|
||||
|
||||
## Routers for Every Platform
|
||||
|
||||
In React Navigation, routers manage the [relationship between actions, state, and URIs](/docs/routers/api). The routers are cross-platform, and there is example code for iOS, Android, and web. Several routers are included, including [`TabRouter`](/docs/routers/tab) and [`StackRouter`](/docs/routers/stack), and it is encouraged to [override their behavior as needed](/docs/routers).
|
||||
|
||||
The routers are composable and can be useful for structuring your app. A common navigation structure in iOS is to have an independent navigation stack for each tab, where all tabs can be covered by a modal. This is three layers of router: a card stack, within tabs, all within a modal stack. So unlike our experience on web apps, the navigation state of mobile apps is too complex to encode into a single URI. Routers in `react-navigation` map from URIs to navigation actions, which are then used to compute navigation state.
|
||||
|
||||
|
||||
## Future
|
||||
|
||||
React Navigation is born from the React Native community's need for an extensible yet easy-to-use navigation solution. It replaces and improves upon several navigation libraries in the ecosystem, including Ex-Navigation and React Native's Navigator and NavigationExperimental components.
|
||||
|
||||
Until the community lands on one navigation solution that works well on the web and React Native, we will forever be destined to re-invent navigation. We are extremely sensitive about the burden of change that accompanies a new navigation library, so we aim to provide a solution that will work long into the future. We are excited to support React Navigation for any platform, including cutting-edge frontiers like hybrid native apps, web server rendering, and ReactVR.
|
||||
|
||||
The first beta of React Navigation is available today on npm and GitHub, and you can [get started here](/docs/intro). We're excited to hear feedback from the React community, and together we still have a long way to go before our dream is realized. We'd love to see the community flourish with beautiful navigation views, custom router integrations, and more easy-to-use navigators. All of these individual contributions can work together seamlessly. If you have improvements for the built-in components, please [follow the contributors guide](/docs/guides/contributors) and dive right in!
|
||||
229
docs/guides/Common-Navigation-Spec.md
Normal file
229
docs/guides/Common-Navigation-Spec.md
Normal file
@@ -0,0 +1,229 @@
|
||||
# Common Navigation Spec
|
||||
|
||||
### Introduction
|
||||
|
||||
It is useful to have “one standard way” to handle navigation in a React app. Unfortunately, we've learned that a single navigation library cannot be the right fit for every application. There is often a tradeoff between several useful features:
|
||||
|
||||
* Simplicity
|
||||
* Supporting complex animations
|
||||
* Navigating between natively-implemented screens and JavaScript screens
|
||||
* Precise fidelity to the native UI controls
|
||||
* Default support for deep linking and the Android back button
|
||||
|
||||
Although the React Native community will need more than one navigation library, we can make them work nicely together. The goal of this document is to specify a common API for navigation libraries. Consistency will make several things easier for React Native developers:
|
||||
|
||||
* Combining multiple navigation libraries in one application
|
||||
* Switching out navigation libraries when requirements change
|
||||
* Learning navigation once, and applying that knowledge in different applications
|
||||
|
||||
Navigation libraries don't have to implement all of this API - it's just a recommendation.
|
||||
|
||||
(TODO: When we actually publish this spec, we should mention here what libraries are supporting it, what libraries will support it, and (link to?) how to migrate from other now-discouraged navigation libraries.)
|
||||
|
||||
|
||||
## Key Concepts
|
||||
|
||||
#### Navigation Container
|
||||
|
||||
The parent component which hosts a navigation-aware component. It must provide the `navigation` prop, and usually uses the child component's static `router` to determine navigation state.
|
||||
|
||||
#### Navigation-Aware Component
|
||||
|
||||
A React component which can observe and initiate navigation in an app. It uses the navigation prop to see navigation state and request actions. It may expose a router to define navigation state and URI handling.
|
||||
|
||||
The card stack of your application may be a navigation-aware component. Also, one screen of your app that handles the Android back button is a navigation-aware component.
|
||||
|
||||
#### Navigation State
|
||||
|
||||
The object that defines the navigation state of your component, passed in as a prop. A router can define the state, which optionally specifies the title and URI of the component.
|
||||
|
||||
#### Action
|
||||
|
||||
A JSON object used to request changes in the app's navigation state.
|
||||
|
||||
#### Router
|
||||
|
||||
Defines the navigation behavior of a component by defining navigation state as a function of actions, and allows URIs to be optionally converted into an action that can be handled.
|
||||
|
||||
#### Navigator
|
||||
|
||||
A navigation-aware component that hosts other navigation-aware components. Most navigators are expected to delegate all router logic, manage child navigation state, and pass up actions as they are dispatched.
|
||||
|
||||
## Specification
|
||||
|
||||
### The `navigation` prop
|
||||
|
||||
The navigation prop should be provided to components who need access to navigation. If provided, it must follow this interface:
|
||||
|
||||
```javascript
|
||||
type BackAction = {type: 'Back'};
|
||||
type URIAction = {type: 'URI', uri: string};
|
||||
|
||||
interface Navigation<S, A> {
|
||||
dispatch(action: (A | BackAction | URIAction)): boolean;
|
||||
state: S;
|
||||
}
|
||||
```
|
||||
|
||||
#### navigation.state
|
||||
|
||||
The controlled navigation state prop, as requested by the parent.
|
||||
|
||||
```javascript
|
||||
const MyView = ({ navigation }) => {
|
||||
switch (navigation.state.myRequestedView) {
|
||||
case 'ViewA': return <ViewA />;
|
||||
case 'ViewB': return <ViewB />;
|
||||
default: return <OtherView />;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### navigation.dispatch(action)
|
||||
|
||||
The channel that a component can call to request navigation from its parent. When calling `dispatch`, you must provide an action object with a `type`. There are two special action types: 'Back' and 'URI'.
|
||||
|
||||
```javascript
|
||||
const MyLink = ({ navigation }) => (
|
||||
<Button onPress={() => {
|
||||
navigation.dispatch({
|
||||
type: 'MyNavigationRequest',
|
||||
myParam: 42,
|
||||
});
|
||||
}>
|
||||
Press me to navigate
|
||||
</Button>
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
### The static `router`
|
||||
|
||||
A router object may be statically defined on your component. If defined, it must follow this interface:
|
||||
|
||||
```javascript
|
||||
type BackAction = {type: 'Back'};
|
||||
type URIAction = {type: 'URI', uri: string};
|
||||
|
||||
interface Router<S, A> {
|
||||
getStateForAction(action: (A | BackAction | URIAction), lastState: ?S): ?S;
|
||||
getActionForURI(uri: string): ?A;
|
||||
}
|
||||
```
|
||||
|
||||
The state and action types of the static router must match the state and action types associated with the navigation prop passed into the component.
|
||||
|
||||
#### router.getStateForAction(action, lastState)
|
||||
|
||||
This function is defined on the static router and is used to define the expected navigation state.
|
||||
|
||||
```javascript
|
||||
class ScreenWithEditMode extends React.Component {
|
||||
static router = {
|
||||
getStateForAction: (action, prevState) => {
|
||||
return { isEditing: true };
|
||||
},
|
||||
};
|
||||
render() {
|
||||
// this.props.navigation.state.isEditing === true
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`getStateForAction` must **always** return a navigation state that can be rendered by the component when passed in as the `navigation.state` prop.
|
||||
|
||||
If null is returned, we are signaling that the previous navigation state has not changed, but the action is handled. This is usually used in cases where the action is being swallowed.
|
||||
|
||||
|
||||
#### router.getActionForURI(uri)
|
||||
|
||||
Return an action if a URI can be handled, otherwise return `null`
|
||||
|
||||
|
||||
|
||||
### Special Actions
|
||||
|
||||
There are two special actions that can be fired into `navigation.dispatch` and can be handled by your `router.getStateForAction`.
|
||||
|
||||
#### Back Action
|
||||
|
||||
This action means the same thing as an Android back button press.
|
||||
|
||||
```
|
||||
type BackAction = { type: 'Back' };
|
||||
```
|
||||
|
||||
#### URI Open Action
|
||||
|
||||
Used to request the enclosing app or OS to open a link at a particular URI. If it is a web URI like `http` or `https`, the app may open a WebView to present the page. Or the app may open the URI in a web browser. In some cases, an app may choose to block a URI action or handle it differently.
|
||||
|
||||
```
|
||||
type URIAction = { type: 'URI', uri: string };
|
||||
```
|
||||
|
||||
|
||||
### Special Navigation State
|
||||
|
||||
The state defined by `router.getStateForAction` can contain special navigation properties that may be relevant to your app. The title and current URI of a component may change over time, and the parent often needs to observe the behavior.
|
||||
|
||||
#### `state.title`
|
||||
|
||||
If the navigation state contains 'title', it will be used as the title for the given component. This is relevant for top-level components on the web to update the browser title, and is relevant in mobile apps where a title is shown in the header.
|
||||
|
||||
#### `state.uri`
|
||||
|
||||
A URI can also be put in `state.uri`, which will signal to the parent how it may be possible to deep link into a similar navigation state. In web apps, this will be used to keep the URI bar in sync with the current navigation state of the app.
|
||||
|
||||
|
||||
## Use Cases
|
||||
|
||||
### "Block the Android back button on one screen of my app"
|
||||
|
||||
To block the Android back button:
|
||||
|
||||
```
|
||||
class Foo extends React.Component {
|
||||
static router = {
|
||||
getStateForAction(action, prevState = {}) {
|
||||
if (action.type === 'Back') return null;
|
||||
else return prevState;
|
||||
},
|
||||
};
|
||||
render() {
|
||||
...
|
||||
```
|
||||
|
||||
Because we return null, we signal to our container that the action has been handled but the state does not change. The parent should not handle the back behavior at this point, and nothing should be re-rendered.
|
||||
|
||||
### "Link deeply into one screen of my app"
|
||||
|
||||
```
|
||||
class Foo extends React.Component {
|
||||
static router = {
|
||||
getStateForAction(action, prevState = {deep: false}) {
|
||||
if (action.type === 'GoDeep') return { deep: true };
|
||||
else return prevState;
|
||||
},
|
||||
getActionForURI(uri) {
|
||||
if (uri === 'myapp://foo')
|
||||
return {type: 'Go'};
|
||||
else if (uri === 'myapp://foo_deep')
|
||||
return {type: 'GoDeep'};
|
||||
return null;
|
||||
},
|
||||
};
|
||||
render() {
|
||||
// this.props.navigation.state.deep may be true or false
|
||||
...
|
||||
```
|
||||
|
||||
Based on the state URI we may decide to return an action. If an action is returned, `getStateForAction` is expected to output the correct state for a deep link.
|
||||
|
||||
## Reference Implementations
|
||||
|
||||
A library to that helps easily produce navigation-aware components: https://github.com/reactjs/react-navigation . (Also uses a HOC to provide navigation containers when needed.)
|
||||
|
||||
A simple navigation container: https://gist.github.com/ericvicenti/77d190e2ec408012255937400e34bdb1
|
||||
|
||||
A web implementation of a navigation container: https://gist.github.com/ericvicenti/55bef95fcd8558029a3bae8483baea6c
|
||||
79
docs/guides/Contributors.md
Normal file
79
docs/guides/Contributors.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Contributors Guide
|
||||
|
||||
## Environment
|
||||
|
||||
React navigation was initially developed on macOS 10.12, with node 7+, and react-native v0.39+. Please open issues when uncovering problems in different environments.
|
||||
|
||||
## Development
|
||||
|
||||
### 0. Basic Install
|
||||
|
||||
```
|
||||
git clone git@github.com:reactjs/react-navigation.git
|
||||
cd react-navigation
|
||||
npm install
|
||||
```
|
||||
|
||||
### 1. Run the native playground
|
||||
|
||||
```
|
||||
cd examples/NavigationPlayground
|
||||
npm install
|
||||
cd ../..
|
||||
npm start
|
||||
|
||||
# In a seperate terminal tab:
|
||||
npm run run-playground-android
|
||||
npm run run-playground-ios
|
||||
```
|
||||
|
||||
### 2. Run the website
|
||||
|
||||
For development mode and live-reloading:
|
||||
|
||||
```
|
||||
cd website
|
||||
npm install
|
||||
npm run start
|
||||
```
|
||||
|
||||
To run the website in production mode with server rendering:
|
||||
|
||||
```
|
||||
npm run prod
|
||||
```
|
||||
|
||||
### 3. Run tests, run flow
|
||||
|
||||
```
|
||||
jest
|
||||
flow
|
||||
```
|
||||
|
||||
Tests must pass for your changes to be accepted and merged.
|
||||
|
||||
Flow is not yet passing, but your code should be flow checked and we expect that your changes do not introduce any flow errors.
|
||||
|
||||
|
||||
### 4. Developing Docs
|
||||
|
||||
The docs are indexed in [App.js](https://github.com/reactjs/react-navigation/blob/master/website/src/App.js), where all the pages are declared alongside the titles. To test the docs, follow the above instructions for running the website. Changing existing markdown files should not require any testing.
|
||||
|
||||
The markdown from the `docs` folder gets generated and dumped into a json file as a part of the build step. To see updated docs appear in the website, re-run the build step by running `npm run build-docs` from the `react-navigation` root folder.
|
||||
|
||||
|
||||
## Submitting Contributions
|
||||
|
||||
### New views or unique features
|
||||
|
||||
Often navigation needs are specific to certain apps. If your changes are unique to your app, you may want to fork the view or router that has changed. You can keep the source code in your app, or publish it on npm as a `react-navigation` compatible view or router.
|
||||
|
||||
This library is intended to include highly standard and generic navigation patterns. But it
|
||||
|
||||
### Major Changes
|
||||
|
||||
Before embarking on any major changes, please file an issue describing the suggested change and motivation. We may already have thought about it and we want to make sure we all are on the same page before starting on any big changes.
|
||||
|
||||
### Minor Bugfixes
|
||||
|
||||
Simple bug fixes are welcomed in pull requests! Please check for duplicate PRs before posting.
|
||||
110
docs/guides/Custom-Navigators.md
Normal file
110
docs/guides/Custom-Navigators.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# Custom Navigators
|
||||
|
||||
A navigator is any React component that has a [router](/docs/routers/) on it. Here is a basic one, which uses the [router's API](/docs/routers/api) to get the active component to render:
|
||||
|
||||
```js
|
||||
class MyNavigator extends React.Component {
|
||||
static router = MyRouter;
|
||||
render() {
|
||||
const { state, dispatch } = this.props.navigation;
|
||||
const { routes, index } = state;
|
||||
|
||||
// Figure out what to render based on the navigation state and the router:
|
||||
const Component = MyRouter.getComponentForState(state);
|
||||
|
||||
// The state of the active child screen can be found at routes[index]
|
||||
let childNavigation = { dispatch, state: routes[index] };
|
||||
// If we want, we can also tinker with the dispatch function here, to limit
|
||||
// or augment our children's actions
|
||||
|
||||
// Assuming our children want the convenience of calling .navigate() and so on,
|
||||
// we should call addNavigationHelpers to augment our navigation prop:
|
||||
childNavigation = addNavigationHelpers(childNavigation);
|
||||
|
||||
return <Component navigation={childNavigation} />;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Navigation Prop
|
||||
|
||||
The navigation prop passed down to a navigator only includes `state` and `dispatch`. This is the current state of the navigator, and an event channel to send action requests.
|
||||
|
||||
All navigators are controlled components: they always display what is coming in through `props.navigation.state`, and their only way to change the state is to send actions into `props.navigation.dispatch`.
|
||||
|
||||
Navigators can specify custom behavior to parent navigators by [customizing their router](/docs/routers/). For example, a navigator is able to specify when actions should be blocked by returning null from `router.getStateForAction`. Or a navigator can specify custom URI handling by overriding `router.getActionForPathAndParams` to output a relevant navigation action, and handling that action in `router.getStateForAction`.
|
||||
|
||||
### Navigation State
|
||||
|
||||
The navigation state that is passed into a navigator's `props.navigation.state` has the following structure:
|
||||
|
||||
```
|
||||
{
|
||||
index: 1, // identifies which route in the routes array is active
|
||||
routes: [
|
||||
{
|
||||
// Each route needs a name, which routers will use to associate each route
|
||||
// with a react component
|
||||
routeName: 'MyRouteName',
|
||||
|
||||
// A unique id for this route, used to keep order in the routes array:
|
||||
key: 'myroute-123',
|
||||
|
||||
// Routes can have any additional data. The included routers have `params`
|
||||
...customRouteData,
|
||||
},
|
||||
...moreRoutes,
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Navigation Dispatchers
|
||||
|
||||
A navigator can dispatch navigation actions, such as 'Go to a URI', 'Go back'.
|
||||
|
||||
The dispatcher will return `true` if the action was successfully handled, otherwise `false`.
|
||||
|
||||
## Navigation Containers
|
||||
|
||||
The built in navigators can automatically behave like top-level navigators when the navigation prop is missing. This functionality provides a transparent navigation container, which is where the top-level navigation prop comes from.
|
||||
|
||||
When rendering one of the included navigators, the navigation prop is optional. When it is missing, the container steps in and manages its own navigation state. It also handles URLs, external linking, and Android back button integration.
|
||||
|
||||
For the purpose of convenience, the built-in navigators have this ability because behind the scenes they use `createNavigationContainer`. Usually, navigators require a navigation prop in order to function.
|
||||
|
||||
### `containerOptions`
|
||||
|
||||
These options can be used to configure a navigator when it is used at the top level.
|
||||
|
||||
An error will be thrown if a navigator is configured with `containerOptions` and also receives a `navigation` prop, because in that case it would be unclear if the navigator should handle its own state.
|
||||
|
||||
- `URIPrefix` - The prefix of the URIs that the app might handle. This will be used when handling a [deep link](/docs/guides/linking) to extract the path passed to the router.
|
||||
|
||||
## API for building custom navigators
|
||||
|
||||
To help developers implement custom navigators, the following utilities are provided with React Navigation:
|
||||
|
||||
### `createNavigator`
|
||||
|
||||
This utility combines a [router](/docs/routers/) and a [navigation view](/docs/views/) together in a standard way:
|
||||
|
||||
```js
|
||||
const MyApp = createNavigator(MyRouter)(MyView);
|
||||
```
|
||||
|
||||
All this does behind the scenes is:
|
||||
|
||||
```js
|
||||
const MyApp = ({ navigation }) => (
|
||||
<MyView router={MyRouter} navigation={navigation} />
|
||||
);
|
||||
MyApp.router = MyRouter;
|
||||
```
|
||||
|
||||
### `addNavigationHelpers`
|
||||
|
||||
Takes in a bare navigator navigation prop with `state` and `dispatch`, and augments it with all the various functions in a screen navigation prop, such as `navigation.navigate()` and `navigation.goBack()`. These functions are simply helpers to create the actions and send them into `dispatch`.
|
||||
|
||||
### `createNavigationContainer`
|
||||
|
||||
If you want your navigator to be usable as a top-level component, (without a navigation prop being passed in), you can use `createNavigationContainer`. This utility will make your navigator act like a top-level navigator when the navigation prop is missing. It will manage the app state, and integrate with app-level nav features, like handling incoming and outgoing links, and Android back button behavior.
|
||||
53
docs/guides/Customizing-Navigation.md
Normal file
53
docs/guides/Customizing-Navigation.md
Normal file
@@ -0,0 +1,53 @@
|
||||
## Customizing Navigation Views
|
||||
|
||||
Modify the presentation of navigation, including styles, animations and gestures.
|
||||
|
||||
## Customizing Routers
|
||||
|
||||
Building a custom router allows you to change the navigation logic of your component, manage navigation state, and define behavior for URIs.
|
||||
|
||||
|
||||
A router can be defined like this:
|
||||
|
||||
```
|
||||
class MyNavigationAwareComponent extends React.Component {
|
||||
|
||||
static router = {
|
||||
|
||||
// Defines the navigation state for a component:
|
||||
getStateForAction: (action: {type: string}, lastState?: any) => {
|
||||
const state = lastState = { myMode: 'default' };
|
||||
if (action.type === 'MyAction') {
|
||||
return { myMode: 'action' };
|
||||
} else if (action.type === 'Back') {
|
||||
return { myMode: 'blockBackButton' };
|
||||
} else {
|
||||
return state;
|
||||
}
|
||||
},
|
||||
|
||||
// Defines if a component can handle a particular URI.
|
||||
// If it does, return an action to be passed to `getStateForAction`
|
||||
|
||||
getActionForURI: (uri: string) => {
|
||||
if (uri === 'myapp://myAction') {
|
||||
return { type: 'MyAction' };
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
render() {
|
||||
// render something based on this.props.navigation.state
|
||||
...
|
||||
}
|
||||
|
||||
onButtonPress = () => {
|
||||
this.props.navigation.dispatch({ type: 'MyAction' });
|
||||
};
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
```
|
||||
107
docs/guides/Deep-Linking.md
Normal file
107
docs/guides/Deep-Linking.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Deep Linking
|
||||
|
||||
In this guide we will set up our app to handle external URIs. Let's start with the SimpleApp that [we created in the getting started guide](/docs/intro).
|
||||
|
||||
In this example, we want a URI like `mychat://chats/Taylor` to open our app and link straight into Taylor's chat page.
|
||||
|
||||
## Configuration
|
||||
|
||||
Previously, we had defined a navigator like this:
|
||||
|
||||
```js
|
||||
const SimpleApp = StackNavigator({
|
||||
Home: { screen: HomeScreen },
|
||||
Chat: { screen: ChatScreen },
|
||||
});
|
||||
```
|
||||
|
||||
We want paths like `chat/Taylor` to link to a "Chat" screen with the `user` passed as a param. Let's re-configure our chat screen with a `path` that tells the router what relative path to match against, and what params to extract. This path spec would be `chat/:user`.
|
||||
|
||||
```js
|
||||
const SimpleApp = StackNavigator({
|
||||
Home: { screen: HomeScreen },
|
||||
Chat: {
|
||||
screen: ChatScreen,
|
||||
path: 'chat/:user',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
### URI Prefix
|
||||
|
||||
Next, let's configure our navigation container to extract the path from the app's incoming URI. When configuring a top-level navigator, we can provide `containerOptions`:
|
||||
|
||||
```js
|
||||
const SimpleApp = StackNavigator({
|
||||
...
|
||||
}, {
|
||||
containerOptions: {
|
||||
// on Android, the URI prefix typically contains a host in addition to scheme
|
||||
URIPrefix: Platform.OS == 'android' ? 'mychat://mychat/' : 'mychat://',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## iOS
|
||||
|
||||
Let's configure the native iOS app to open based on the `mychat://` URI scheme.
|
||||
|
||||
In `SimpleApp/ios/SimpleApp/AppDelegate.m`:
|
||||
|
||||
```
|
||||
// Add the header at the top of the file:
|
||||
#import <React/RCTLinkingManager.h>
|
||||
|
||||
// Add this above the `@end`:
|
||||
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
|
||||
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
|
||||
{
|
||||
return [RCTLinkingManager application:application openURL:url
|
||||
sourceApplication:sourceApplication annotation:annotation];
|
||||
}
|
||||
```
|
||||
|
||||
In Xcode, open the project at `SimpleApp/ios/SimpleApp.xcodeproj`. Select the project in sidebar and navigate to the info tab. Scroll down to "URL Types" and add one. In the new URL type, set the identifier and the url scheme to your desired url scheme.
|
||||
|
||||

|
||||
|
||||
Now you can press play in Xcode, or re-build on the command line:
|
||||
|
||||
```sh
|
||||
react-native run-ios
|
||||
```
|
||||
|
||||
To test the URI in iOS, open safari and type `mychat://chat/Taylor`.
|
||||
|
||||
## Android
|
||||
|
||||
To configure the external linking in Android, you can create a new intent in the manifest.
|
||||
|
||||
In `SimpleApp/android/app/src/main/AndroidManifest.xml`, add the new `VIEW` type `intent-filter` inside the `MainActivity` entry:
|
||||
|
||||
```
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="mychat"
|
||||
android:host="mychat" />
|
||||
</intent-filter>
|
||||
```
|
||||
|
||||
Now, re-install the app:
|
||||
|
||||
```sh
|
||||
react-native run-android
|
||||
```
|
||||
|
||||
To test the intent handling in Android, run the following:
|
||||
|
||||
```
|
||||
adb shell am start -W -a android.intent.action.VIEW -d "mychat://mychat/chat/Taylor" com.simpleapp
|
||||
```
|
||||
|
||||
```phone-example
|
||||
linking
|
||||
```
|
||||
98
docs/guides/Guide-Headers.md
Normal file
98
docs/guides/Guide-Headers.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Configuring the Header
|
||||
|
||||
In the previous example, we created a StackNavigator to display severals screens in our app.
|
||||
|
||||
|
||||
When navigating to a chat screen, we can specify params for the new route by providing them to the navigate function. In this case we want to provide the name of the person on the chat screen:
|
||||
|
||||
```js
|
||||
this.props.navigation.navigate('Chat', { user: 'Lucy' });
|
||||
```
|
||||
|
||||
The `user` param can be accessed from the profile screen:
|
||||
|
||||
```js
|
||||
class ChatScreen extends React.Component {
|
||||
const { params } = this.props.navigation.state;
|
||||
render() {
|
||||
return <Text>Chat with {params.user}</Text>;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Setting the Header Title
|
||||
|
||||
Next, the header title can be configured to use the screen param:
|
||||
|
||||
```js
|
||||
class ChatScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
// // Title may be a simple string:
|
||||
// title: 'Hello',
|
||||
|
||||
// Or the title string may be a function of the navigation prop:
|
||||
title: ({ navigation }) => `Chat with ${navigation.state.params.user}`
|
||||
};
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
```phone-example
|
||||
basic-header
|
||||
```
|
||||
|
||||
|
||||
### Adding a Right Button
|
||||
|
||||
Then we can add a [`header` navigation option](/docs/navigators/navigation-options#Stack-Navigation-Options) that allows us to add a custom right button:
|
||||
|
||||
```js
|
||||
static navigationOptions = {
|
||||
header: {
|
||||
right: <Button title="Info" />,
|
||||
},
|
||||
...
|
||||
```
|
||||
|
||||
```phone-example
|
||||
header-button
|
||||
```
|
||||
|
||||
Just like `title`, the `header` option can be defined as a function of the [navigation prop](/docs/navigators/navigation-prop). Lets render a different button based on the route params, and set up the button to call `navigation.setParams` when pressed.
|
||||
|
||||
```js
|
||||
static navigationOptions = {
|
||||
title: ({ state }) => {
|
||||
if (state.params.mode === 'info') {
|
||||
return `${state.params.user}'s Contact Info`;
|
||||
}
|
||||
return `Chat with ${state.params.user}`;
|
||||
},
|
||||
header: ({ state, setParams }) => {
|
||||
// The navigation prop has functions like setParams, goBack, and navigate.
|
||||
let right = (
|
||||
<Button
|
||||
title={`${state.params.user}'s info`}
|
||||
onPress={() => setParams({ mode: 'info' }}
|
||||
/>
|
||||
);
|
||||
if (state.params.mode === 'info') {
|
||||
right = (
|
||||
<Button
|
||||
title="Done"
|
||||
onPress={() => setParams({ mode: 'none' }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return { right };
|
||||
},
|
||||
...
|
||||
```
|
||||
|
||||
Now, the header can interact with the screen route/state:
|
||||
|
||||
```phone-example
|
||||
header-interaction
|
||||
```
|
||||
|
||||
To see the rest of the header options, see the [navigation options document](/docs/navigators/navigation-options#Stack-Navigation-Options).
|
||||
124
docs/guides/Guide-Intro.md
Normal file
124
docs/guides/Guide-Intro.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# Hello Mobile Navigation
|
||||
|
||||
Let's use React Navigation to build a simple chat application for Android and iOS.
|
||||
|
||||
## Setup and Installation
|
||||
|
||||
First, make sure you're [all set up to use React Native](http://facebook.github.io/react-native/docs/getting-started.html). Next, create a new project and add `react-navigation`:
|
||||
|
||||
|
||||
```sh
|
||||
# Create a new React Native App
|
||||
react-native init SimpleApp
|
||||
cd SimpleApp
|
||||
|
||||
# Install the latest version of react-navigation from npm
|
||||
npm install --save react-navigation
|
||||
|
||||
# Run the new app
|
||||
react-native run-android # or:
|
||||
react-native run-ios
|
||||
```
|
||||
|
||||
Verify that you can successfully see the bare sample app run on iOS and/or Android:
|
||||
|
||||
```phone-example
|
||||
bare-project
|
||||
```
|
||||
|
||||
We want to share code on iOS and Android, so lets delete the contents of `index.ios.js` and `index.android.js` and replace it with `import './App';`.
|
||||
|
||||
Now lets create the new file for our app implementation, `App.js`.
|
||||
|
||||
## Introducing Stack Navigator
|
||||
|
||||
For our app, we want to use the `StackNavigator` because we want a conceptual 'stack' navigation, where each new screen is put on the top of the stack, and going back removes a screen from the top of the stack. Lets start with just one screen:
|
||||
|
||||
```js
|
||||
import React from 'react';
|
||||
import {
|
||||
AppRegistry,
|
||||
Text,
|
||||
} from 'react-native';
|
||||
import { StackNavigator } from 'react-navigation';
|
||||
|
||||
class HomeScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
title: 'Welcome',
|
||||
};
|
||||
render() {
|
||||
return <Text>Hello, World!</Text>;
|
||||
}
|
||||
}
|
||||
|
||||
const SimpleApp = StackNavigator({
|
||||
Home: { screen: HomeScreen },
|
||||
});
|
||||
|
||||
AppRegistry.registerComponent('SimpleApp', () => SimpleApp);
|
||||
```
|
||||
|
||||
The `title` of the screen is configurable on the [static `navigationOptions`](/docs/navigators/navigation-options), where many options can be set to configure the presentation of the screen in the navigator.
|
||||
|
||||
Now the same screen should appear on both iPhone and Android apps:
|
||||
|
||||
```phone-example
|
||||
first-screen
|
||||
```
|
||||
|
||||
## Adding a New Screen
|
||||
|
||||
Lets create a button in the `HomeScreen` component that links to our second page:
|
||||
|
||||
```js
|
||||
class HomeScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
title: 'Welcome',
|
||||
};
|
||||
render() {
|
||||
const { navigate } = this.props.navigation;
|
||||
return (
|
||||
<View>
|
||||
<Text>Hello, Chat App!</Text>
|
||||
<Button
|
||||
onPress={() => navigate('Chat', { user: 'Lucy' })}
|
||||
title="Chat with Lucy"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We're using the navigate function from the [screen navigation prop](/docs/navigators/navigation-prop). In addition to specifying the target `routeName`, we can also pass params that will be put into the new route.
|
||||
|
||||
Now let's create the Chat screen that displays the `name` param passed in through the route:
|
||||
|
||||
```js
|
||||
class ChatScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
// Nav options can be defined as a function of the navigation prop:
|
||||
title: ({ state }) => `Chat with ${state.params.user}`,
|
||||
};
|
||||
render() {
|
||||
// The screen's current route is passed in to `props.navigation.state`:
|
||||
const { params } = this.props.navigation.state;
|
||||
return (
|
||||
<View>
|
||||
<Text>Chat with {params.user}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const SimpleApp = StackNavigator({
|
||||
Home: { screen: HomeScreen },
|
||||
Chat: { screen: ChatScreen },
|
||||
});
|
||||
```
|
||||
|
||||
Now you can navigate to your new screen, and go back:
|
||||
|
||||
```phone-example
|
||||
first-navigation
|
||||
```
|
||||
72
docs/guides/Guide-Nested.md
Normal file
72
docs/guides/Guide-Nested.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Nesting Navigators
|
||||
|
||||
It is common in mobile apps to compose various forms of navigation. The routers and navigators in React Navigation are composable, which allows you to define a complicated navigation structure for your app.
|
||||
|
||||
For our chat app, we want to put several tabs on the first screen, to view recent chat threads or all contacts.
|
||||
|
||||
## Introducing Tab Navigator
|
||||
|
||||
Lets create a new `TabNavigator` in our `App.js`:
|
||||
|
||||
```js
|
||||
class RecentChatsScreen extends React.Component {
|
||||
render() {
|
||||
return <Text>List of recent chats</Text>
|
||||
}
|
||||
}
|
||||
|
||||
class AllContactsScreen extends React.Component {
|
||||
render() {
|
||||
return <Text>List of all contacts</Text>
|
||||
}
|
||||
}
|
||||
|
||||
const MainScreenNavigator = TabNavigator({
|
||||
Recent: { screen: RecentChatsScreen },
|
||||
All: { screen: AllContactsScreen },
|
||||
});
|
||||
```
|
||||
|
||||
If the `MainScreenNavigator` was rendered as the top-level navigator component, it would look like this:
|
||||
|
||||
```phone-example
|
||||
simple-tabs
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Nesting a Navigator in a screen
|
||||
|
||||
We want these tabs to be visible in the first screen of the app, but new screens in the stack should cover the tabs.
|
||||
|
||||
Lets add our tabs navigator as a screen in our top-level `StackNavigator` that we set up in the [previous step](/docs/intro/).
|
||||
|
||||
```js
|
||||
const SimpleApp = StackNavigator({
|
||||
Home: { screen: MainScreenNavigator },
|
||||
Chat: { screen: ChatScreen },
|
||||
});
|
||||
```
|
||||
|
||||
Because `MainScreenNavigator` is being used as a screen, we can give it `navigationOptions`:
|
||||
|
||||
```js
|
||||
MainScreenNavigator.navigationOptions = {
|
||||
title: 'My Chats',
|
||||
};
|
||||
```
|
||||
|
||||
Lets also add a button to each tab that links to a chat:
|
||||
|
||||
```js
|
||||
<Button
|
||||
onPress={() => this.props.navigation.navigate('Chat', { user: 'Lucy' })}
|
||||
title="Chat with Lucy"
|
||||
/>
|
||||
```
|
||||
|
||||
Now we have put one navigator inside another, and we can `navigate` between navigators:
|
||||
|
||||
```phone-example
|
||||
nested
|
||||
```
|
||||
174
docs/guides/Hybrid-Navigation.md
Normal file
174
docs/guides/Hybrid-Navigation.md
Normal file
@@ -0,0 +1,174 @@
|
||||
## Common React Navigation API - Hybrid Integration
|
||||
|
||||
This is a purely speculative API that demonstrates how it may be possible to integrate the [JS navigation API](./Common-Navigation-Spec.md) in a hybrid app.
|
||||
|
||||
## Setting up a screen
|
||||
|
||||
It should be possible to register new screens from JS into native. In your main bundle:
|
||||
|
||||
```
|
||||
const HybridNavigationModule = require('NativeModules').HybridNavigation;
|
||||
|
||||
HybridNavigationModule.registerScreens([
|
||||
{
|
||||
type: 'Marketplace',
|
||||
screen: MarketplaceScreen,
|
||||
},
|
||||
{
|
||||
type: 'Product',
|
||||
screen: ProductScreen,
|
||||
},
|
||||
]);
|
||||
```
|
||||
|
||||
## Linking to JS
|
||||
|
||||
Now, your native code can open a react screen by type name:
|
||||
|
||||
```
|
||||
// please pretend this is Obj-C or Java syntax:
|
||||
CoreHybridNavigation.openReactScreen('Profile', {id: 123});
|
||||
```
|
||||
|
||||
## Linking to Native
|
||||
|
||||
If JS product code wants to request navigation to a screen that may *or may not* be in native, it can do this:
|
||||
|
||||
```
|
||||
const MarketplaceScreen = ({ navigation }) => (
|
||||
<View>
|
||||
<Button onPress={() => navigation.dispatch({
|
||||
type: 'Product',
|
||||
id: 42,
|
||||
})}>
|
||||
See product 42
|
||||
</Button>
|
||||
</View>
|
||||
);
|
||||
```
|
||||
|
||||
Inside the infra:
|
||||
|
||||
```
|
||||
class InfraScreen extends React.Component {
|
||||
constructor() {
|
||||
const {initURI, type} = this.props;
|
||||
const ScreenView = ScreenRegistry[type].screen;
|
||||
const router = ScreenView.router;
|
||||
const deepLinkAction = router.getActionForURI(initURI);
|
||||
const initAction = deepLinkAction || {type: 'init'}
|
||||
const nav = router.getStateForAction(initAction);
|
||||
this.state = {
|
||||
nav,
|
||||
};
|
||||
HybridNavigationModule.setNavOptions(this.state.nav);
|
||||
}
|
||||
componentWillUpdate() {
|
||||
HybridNavigationModule.setNavOptions(this.state.nav);
|
||||
}
|
||||
dispatch = (action) => {
|
||||
const {type} = this.props;
|
||||
const ScreenView = ScreenRegistry[type].screen;
|
||||
const {getStateForAction} = ScreenView.router;
|
||||
const newNavState = getStateForAction(action, this.state.nav);
|
||||
if (newNavState !== this.state.nav) {
|
||||
this.setState({ nav: newNavState });
|
||||
return true;
|
||||
}
|
||||
if (action.type === 'URI') {
|
||||
HybridNavigationModule.openURI(action.uri);
|
||||
return true;
|
||||
}
|
||||
if (action.type === 'Back') {
|
||||
HybridNavigationModule.goBack();
|
||||
return true;
|
||||
}
|
||||
HybridNavigationModule.openAction(action);
|
||||
return true;
|
||||
}
|
||||
render() {
|
||||
const {type} = this.props;
|
||||
const ScreenView = ScreenRegistry[type].screen;
|
||||
const navigation = {
|
||||
dispatch: this.dispatch,
|
||||
state: this.state.nav,
|
||||
};
|
||||
return <ScreenView navigation={navigation} />;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Setting title
|
||||
|
||||
```
|
||||
MarketplaceScreen.router = {
|
||||
getStateForAction(action, lastState) {
|
||||
return lastState || {title: 'Marketplace Home'};
|
||||
},
|
||||
};
|
||||
```
|
||||
A HOC could be used to make this feel more elegant.
|
||||
|
||||
|
||||
## Disabling/Enabling the right button
|
||||
|
||||
```
|
||||
const TestScreen = ({ navigation }) => (
|
||||
<View>
|
||||
<Button onPress={() => navigation.dispatch({
|
||||
type: 'ToggleMyButtonPressability',
|
||||
})}>
|
||||
{navigation.state.rightButtonEnabled ? 'Disable' : 'Enable'} right button
|
||||
</Button>
|
||||
<Text>Pressed {navigation.state} times</Text>
|
||||
</View>
|
||||
);
|
||||
TestScreen.router = {
|
||||
getStateForAction(action, lastState = {}) {
|
||||
let state = lastState || {
|
||||
rightButtonEnabled: true,
|
||||
rightButtonTitle: 'Tap Me',
|
||||
pressCount: 0,
|
||||
};
|
||||
if (action.type === 'ToggleMyButtonPressability') {
|
||||
state = {
|
||||
...state,
|
||||
rightButtonEnabled: !state.rightButtonEnabled,
|
||||
};
|
||||
} else if (action.type === 'RightButtonPress') {
|
||||
state = {
|
||||
...state,
|
||||
pressCount: state.pressCount + 1,
|
||||
};
|
||||
}
|
||||
return state;
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
## Before JS starts
|
||||
|
||||
A JSON file could be defined for native to consume before JS spins up:
|
||||
|
||||
```
|
||||
{
|
||||
"screens": [
|
||||
{
|
||||
"type": "Profile",
|
||||
"path": "/users/:id?name=:name",
|
||||
"params": {
|
||||
"name": "string",
|
||||
"id": "number"
|
||||
},
|
||||
"title": "%name%' s Profile",
|
||||
"rightButtonTitle": "Message %name%"
|
||||
},
|
||||
{
|
||||
...
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
This seems like a pain to set up, so we can statically analyze our JS and autogenerate this JSON! If the JS in an app changes, there could be a way for JS to report the new routing configuration to native for use on the next cold start.
|
||||
46
docs/guides/Redux-Integration.md
Normal file
46
docs/guides/Redux-Integration.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Redux Integration
|
||||
|
||||
To handle your app's navigation state in redux, you can pass your own `navigation` prop to a navigator. Your navigation prop must provide the current state, as well as access to a dispatcher to handle navigation options.
|
||||
|
||||
With redux, your app's state is defined by a reducer. Each navigation router effectively has a reducer, called `getStateForAction`. The following is a minimal example of how you might use navigators within a redux application:
|
||||
|
||||
```
|
||||
const AppNavigator = StackNavigator(AppRouteConfigs);
|
||||
|
||||
const appReducer = combineReducers({
|
||||
nav: (state, action) => (
|
||||
AppNavigator.router.getStateForAction(action, state)
|
||||
),
|
||||
...
|
||||
});
|
||||
|
||||
@connect(state => {
|
||||
nav: state.nav,
|
||||
})
|
||||
class AppWithNavigationState extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<AppNavigator navigation={{
|
||||
dispatch: this.props.dispatch,
|
||||
state: this.props.nav,
|
||||
}} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const store = createStore(appReducer);
|
||||
|
||||
class App extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<AppWithNavigationState />
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, your navigation state is stored with redux, and you can fire navigation actions using redux.
|
||||
|
||||
When a navigator is given a `navigation` prop, it relinquishes control of the state. So you are now responsible for persisting state, handling deep linking, integrating the back button, etc.
|
||||
57
docs/guides/Screen-Nav-Options.md
Normal file
57
docs/guides/Screen-Nav-Options.md
Normal file
@@ -0,0 +1,57 @@
|
||||
|
||||
# Screen Navigation Options
|
||||
|
||||
Each screen can configure several aspects about how it gets presented in parent navigators.
|
||||
|
||||
#### Two Ways to specify each option
|
||||
|
||||
**Static configuration:** Each navigation option can either be directly assigned:
|
||||
|
||||
```js
|
||||
class MyScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
title: 'Great',
|
||||
};
|
||||
...
|
||||
```
|
||||
|
||||
**Dynamic Configuration**
|
||||
|
||||
Or, each option can be a function that takes the following arguments, and returns the value of the option.
|
||||
|
||||
- `navigation` - the [navigation prop](/docs/intro/navigation-prop) for the screen, with the screen's route at `navigation.state`
|
||||
- `childRouter` - The child router, if the screen is a navigator
|
||||
|
||||
```js
|
||||
class ProfileScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
title: (navigation, childRouter) => {
|
||||
return navigation.state.params.name + "'s Profile!";
|
||||
},
|
||||
};
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
#### Generic Navigation Options
|
||||
|
||||
The `title` navigation option is generic between every navigator. It is used to set the title string for a given screen.
|
||||
|
||||
```js
|
||||
class MyScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
title: 'Great',
|
||||
};
|
||||
...
|
||||
```
|
||||
|
||||
Unlike the other nav options which are only utilized by the navigator view, the title option can be used by the environment to update the title in the browser window or app switcher.
|
||||
|
||||
|
||||
## Tab Navigation Options
|
||||
|
||||
Coming Soon
|
||||
|
||||
## Stack Navigation Options
|
||||
|
||||
Coming Soon
|
||||
142
docs/guides/Screen-Navigation-Prop.md
Normal file
142
docs/guides/Screen-Navigation-Prop.md
Normal file
@@ -0,0 +1,142 @@
|
||||
|
||||
# Screen Navigation Prop
|
||||
|
||||
Each screen in your app will recieve a navigation prop, which contains the following:
|
||||
|
||||
|
||||
## `navigate` - Link to other screens
|
||||
|
||||
Call this to link to another screen in your app. Takes the following arguments:
|
||||
|
||||
- `routeName` - A destination routeName that has been registered somewhere in the app's router
|
||||
- `params` - Params to merge into the destination route
|
||||
- `action` - (advanced) The sub-action to run in the child router, if the screen is a navigator.
|
||||
|
||||
```js
|
||||
class HomeScreen extends React.Component {
|
||||
render() {
|
||||
const {navigate} = this.props.navigation;
|
||||
|
||||
return (
|
||||
<View>
|
||||
<Text>This is the home screen of the app</Text>
|
||||
<Button
|
||||
onPress={() => navigate('Profile', {name: 'Brent'})}
|
||||
title="Go to Brent's profile"
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `state` - The screen's current state/route
|
||||
|
||||
A screen has access to it's route via `this.props.navigation.state`. Each will contain:
|
||||
|
||||
- `routeName` - the name of the route config in the router
|
||||
- `key` - a unique identifier used to sort routes
|
||||
- `params` - an optional object of string options for this screen
|
||||
|
||||
```js
|
||||
class ProfileScreen extends React.Component {
|
||||
render() {
|
||||
const {state} = this.props.navigation;
|
||||
// state.routeName === 'Profile'
|
||||
return (
|
||||
<Text>Name: {state.params.name}</Text>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## `setParams` - Make changes to route params
|
||||
|
||||
Firing the `setParams` action allows a screen to change the params in the route, which is useful for updating the header buttons and title.
|
||||
|
||||
```js
|
||||
class ProfileScreen extends React.Component {
|
||||
render() {
|
||||
const {setParams} = this.props.navigation;
|
||||
return (
|
||||
<Button
|
||||
onPress={() => setParams({name: 'Lucy'})}
|
||||
title="Set title name to 'Lucy'"
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `goBack` - Close the active screen and move back
|
||||
|
||||
```js
|
||||
class HomeScreen extends React.Component {
|
||||
render() {
|
||||
const {navigate} = this.props.navigation;
|
||||
return (
|
||||
<View>
|
||||
<Button
|
||||
onPress={() => goBack()}
|
||||
title="Go back from this HomeScreen"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => goBack(null)}
|
||||
title="Go back anywhere"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => goBack('screen-123')}
|
||||
title="Go back from screen-123"
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Optionally provide a key, which specifies the route to go back from. By default, goBack will close the route that it is called from. If the goal is to go back *anywhere*, without specifying what is getting closed, call `.goBack(null);`
|
||||
|
||||
|
||||
## `dispatch` - Send an action to the router
|
||||
|
||||
Use dispatch to send any navigation action to the router. The other navigation functions use dispatch behind the scenes.
|
||||
|
||||
The following actions are supported:
|
||||
|
||||
### Navigate
|
||||
```js
|
||||
{
|
||||
type: 'Navigate',
|
||||
routeName: 'Profile',
|
||||
params: {},
|
||||
|
||||
// navigate can have a nested navigate action that will be run inside the child router
|
||||
action: {type: 'Navigate', routeName: 'SubProfileRoute'}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Reset
|
||||
|
||||
The `Reset` action wipes the whole navigation state and replaces it with the result of several actions.
|
||||
|
||||
```js
|
||||
{
|
||||
type: 'Reset',
|
||||
actions: ,
|
||||
}
|
||||
```
|
||||
|
||||
### SetParams
|
||||
|
||||
When dispatching `SetParams`, the router will produce a new state that has changed the params of a particular route, as identified by the key
|
||||
|
||||
```js
|
||||
{
|
||||
type: 'SetParams',
|
||||
params: {}, // these are the new params that will be merged into the existing route params
|
||||
// The key of the route that should get the new params
|
||||
key: 'screen-123',
|
||||
}
|
||||
```
|
||||
16
docs/guides/Web-Integration.md
Normal file
16
docs/guides/Web-Integration.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Web Integration
|
||||
|
||||
React navigation routers work on web and allow you to share navigation logic with native apps. The views currently bundled in `react-navigation` currently only work on React Native, but that may change with future-facing projects like [react-primitives](https://github.com/lelandrichardson/react-primitives).
|
||||
|
||||
## Example App
|
||||
|
||||
[This website](https://github.com/reactjs/react-navigation/blob/master/website/) is built with react navigation, specifically using `createNavigator` and `TabRouter`.
|
||||
|
||||
See the source code of the site here: [App.js](https://github.com/reactjs/react-navigation/blob/master/website/src/App.js).
|
||||
|
||||
To see how the app gets rendered on the server, see [Server.js](https://github.com/reactjs/react-navigation/blob/master/website/src/Server.js). On the browser, the App wakes up and gets rendered with [BrowserAppContainer.js](https://github.com/reactjs/react-navigation/blob/master/website/src/BrowserAppContainer.js).
|
||||
|
||||
|
||||
## More Coming Soon
|
||||
|
||||
Soon this guide will be replaced with a more thorough walkthrough of react-navigation usage on the web.
|
||||
3
examples/HelloHybrid/.babelrc
Normal file
3
examples/HelloHybrid/.babelrc
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": ["react-native"]
|
||||
}
|
||||
6
examples/HelloHybrid/.buckconfig
Normal file
6
examples/HelloHybrid/.buckconfig
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
[android]
|
||||
target = Google Inc.:Google APIs:23
|
||||
|
||||
[maven_repositories]
|
||||
central = https://repo1.maven.org/maven2
|
||||
44
examples/HelloHybrid/.flowconfig
Normal file
44
examples/HelloHybrid/.flowconfig
Normal file
@@ -0,0 +1,44 @@
|
||||
[ignore]
|
||||
; We fork some components by platform
|
||||
.*/*[.]android.js
|
||||
|
||||
; Ignore "BUCK" generated dirs
|
||||
<PROJECT_ROOT>/\.buckd/
|
||||
|
||||
; Ignore unexpected extra "@providesModule"
|
||||
.*/node_modules/.*/node_modules/fbjs/.*
|
||||
|
||||
; Ignore duplicate module providers
|
||||
; For RN Apps installed via npm, "Libraries" folder is inside
|
||||
; "node_modules/react-native" but in the source repo it is in the root
|
||||
.*/Libraries/react-native/React.js
|
||||
.*/Libraries/react-native/ReactNative.js
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
node_modules/react-native/Libraries/react-native/react-native-interface.js
|
||||
node_modules/react-native/flow
|
||||
flow/
|
||||
|
||||
[options]
|
||||
module.system=haste
|
||||
|
||||
experimental.strict_type_args=true
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FixMe
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-6]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-6]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
|
||||
unsafe.enable_getters_and_setters=true
|
||||
|
||||
[version]
|
||||
^0.36.0
|
||||
1
examples/HelloHybrid/.gitattributes
vendored
Normal file
1
examples/HelloHybrid/.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.pbxproj -text
|
||||
53
examples/HelloHybrid/.gitignore
vendored
Normal file
53
examples/HelloHybrid/.gitignore
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
# OSX
|
||||
#
|
||||
.DS_Store
|
||||
|
||||
# Xcode
|
||||
#
|
||||
build/
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.xcuserstate
|
||||
project.xcworkspace
|
||||
|
||||
# Android/IntelliJ
|
||||
#
|
||||
build/
|
||||
.idea
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
|
||||
# node.js
|
||||
#
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
|
||||
# BUCK
|
||||
buck-out/
|
||||
\.buckd/
|
||||
android/app/libs
|
||||
*.keystore
|
||||
|
||||
# fastlane
|
||||
#
|
||||
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
|
||||
# screenshots whenever they are needed.
|
||||
# For more information about the recommended setup visit:
|
||||
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
|
||||
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots
|
||||
1
examples/HelloHybrid/.watchmanconfig
Normal file
1
examples/HelloHybrid/.watchmanconfig
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
117
examples/HelloHybrid/HybridContainer.js
Normal file
117
examples/HelloHybrid/HybridContainer.js
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
NativeModules,
|
||||
} from 'react-native';
|
||||
import {
|
||||
addNavigationHelpers,
|
||||
} from 'react-navigation';
|
||||
|
||||
const {
|
||||
HybridNavigationManager,
|
||||
} = NativeModules;
|
||||
|
||||
const HybridContainer = (ReactScreens) => {
|
||||
class HybridAppScreen extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { name, params, rootTag } = props;
|
||||
let ScreenView = ReactScreens[name];
|
||||
let screenKey = name;
|
||||
let navState = null;
|
||||
const action = { type: 'Navigate', routeName: name, params };
|
||||
if (!ScreenView) {
|
||||
// Deep linking magic here. Try each screen to see if the state changes
|
||||
// in response to this action. The first screen who returns
|
||||
// a new state for the action is used
|
||||
Object.keys(ReactScreens).forEach(screenId => {
|
||||
if (!ScreenView) {
|
||||
const V = ReactScreens[screenId];
|
||||
if (!V || !V.router || !V.router.getStateForAction) {
|
||||
return;
|
||||
}
|
||||
const baseState = V.router.getStateForAction({ type: 'Init' });
|
||||
const linkedState = V.router.getStateForAction(action, baseState);
|
||||
if (baseState !== linkedState) {
|
||||
ScreenView = V;
|
||||
navState = linkedState;
|
||||
screenKey = screenId;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!ScreenView) {
|
||||
screenKey = 'NotFoundComponent';
|
||||
ScreenView = ReactScreens.NotFoundComponent;
|
||||
}
|
||||
if (!navState) {
|
||||
const router = ScreenView.router;
|
||||
navState = router && router.getStateForAction(action);
|
||||
}
|
||||
let title = null;
|
||||
const defaultNavState = { routeName: name, key: `screen-${rootTag}`, params };
|
||||
if (navState) {
|
||||
const { routes, index } = navState;
|
||||
const router = ScreenView.router;
|
||||
const routeConfig = router && router.getScreenConfig({
|
||||
state: routes[index], dispatch: () => {}
|
||||
}, index, true);
|
||||
title = routeConfig && routeConfig.title;
|
||||
}
|
||||
this.state = {
|
||||
navState: navState || defaultNavState,
|
||||
screenKey,
|
||||
};
|
||||
HybridNavigationManager.setTitle(rootTag, title || name);
|
||||
}
|
||||
|
||||
dispatch = (action) => {
|
||||
const { name, rootTag } = this.props;
|
||||
const ScreenView = ReactScreens[this.state.screenKey] || ReactScreens.NotFoundComponent;
|
||||
const router = ScreenView.router;
|
||||
const navState = router && router.getStateForAction(action, this.state.navState);
|
||||
if (navState && navState !== this.state.navState) {
|
||||
this.setState({ navState });
|
||||
return true;
|
||||
}
|
||||
if (action.type === 'Navigate') {
|
||||
HybridNavigationManager.navigate(action.routeName, action.params || {});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
render() {
|
||||
const Component = ReactScreens[this.state.screenKey] || ReactScreens.NotFoundComponent;
|
||||
const navigation = addNavigationHelpers({
|
||||
state: this.state.navState,
|
||||
dispatch: this.dispatch,
|
||||
});
|
||||
return (
|
||||
<Component navigation={navigation} />
|
||||
);
|
||||
}
|
||||
componentWillUpdate(props, state) {
|
||||
const { name, rootTag } = props;
|
||||
const ScreenView = ReactScreens[name];
|
||||
if (!ScreenView) {
|
||||
console.log('Experiencing an error! Fix me!')
|
||||
}
|
||||
let title = null;
|
||||
if (state.navState) {
|
||||
const { routes, index } = state.navState;
|
||||
const router = ScreenView.router;
|
||||
const routeConfig = router && router.getScreenConfig({
|
||||
state: routes[index], dispatch: () => {}
|
||||
}, index, true);
|
||||
title = (routeConfig && routeConfig.title) || route.routeName;
|
||||
}
|
||||
HybridNavigationManager.setTitle(rootTag, title || name);
|
||||
}
|
||||
}
|
||||
return HybridAppScreen;
|
||||
};
|
||||
|
||||
export default HybridContainer;
|
||||
17
examples/HelloHybrid/ReadMe.md
Normal file
17
examples/HelloHybrid/ReadMe.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Hello Hybrid Example
|
||||
|
||||
A reference implementation for integrating react-navigation into a native app.
|
||||
|
||||
iOS only right now. Help needed to support Android!
|
||||
|
||||
## Setup:
|
||||
|
||||
```
|
||||
cd react-navigation
|
||||
npm install
|
||||
cd examples/HelloHybrid
|
||||
npm install
|
||||
open ios/HelloHybrid.xcodeproj
|
||||
cd ../..
|
||||
react-native start
|
||||
```
|
||||
12
examples/HelloHybrid/__tests__/index.android.js
Normal file
12
examples/HelloHybrid/__tests__/index.android.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'react-native';
|
||||
import React from 'react';
|
||||
import Index from '../index.android.js';
|
||||
|
||||
// Note: test renderer must be required after react-native.
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
it('renders correctly', () => {
|
||||
const tree = renderer.create(
|
||||
<Index />
|
||||
);
|
||||
});
|
||||
12
examples/HelloHybrid/__tests__/index.ios.js
Normal file
12
examples/HelloHybrid/__tests__/index.ios.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'react-native';
|
||||
import React from 'react';
|
||||
import Index from '../index.ios.js';
|
||||
|
||||
// Note: test renderer must be required after react-native.
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
it('renders correctly', () => {
|
||||
const tree = renderer.create(
|
||||
<Index />
|
||||
);
|
||||
});
|
||||
66
examples/HelloHybrid/android/app/BUCK
Normal file
66
examples/HelloHybrid/android/app/BUCK
Normal file
@@ -0,0 +1,66 @@
|
||||
import re
|
||||
|
||||
# To learn about Buck see [Docs](https://buckbuild.com/).
|
||||
# To run your application with Buck:
|
||||
# - install Buck
|
||||
# - `npm start` - to start the packager
|
||||
# - `cd android`
|
||||
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
|
||||
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
|
||||
# - `buck install -r android/app` - compile, install and run application
|
||||
#
|
||||
|
||||
lib_deps = []
|
||||
for jarfile in glob(['libs/*.jar']):
|
||||
name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile)
|
||||
lib_deps.append(':' + name)
|
||||
prebuilt_jar(
|
||||
name = name,
|
||||
binary_jar = jarfile,
|
||||
)
|
||||
|
||||
for aarfile in glob(['libs/*.aar']):
|
||||
name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile)
|
||||
lib_deps.append(':' + name)
|
||||
android_prebuilt_aar(
|
||||
name = name,
|
||||
aar = aarfile,
|
||||
)
|
||||
|
||||
android_library(
|
||||
name = 'all-libs',
|
||||
exported_deps = lib_deps
|
||||
)
|
||||
|
||||
android_library(
|
||||
name = 'app-code',
|
||||
srcs = glob([
|
||||
'src/main/java/**/*.java',
|
||||
]),
|
||||
deps = [
|
||||
':all-libs',
|
||||
':build_config',
|
||||
':res',
|
||||
],
|
||||
)
|
||||
|
||||
android_build_config(
|
||||
name = 'build_config',
|
||||
package = 'com.hellohybrid',
|
||||
)
|
||||
|
||||
android_resource(
|
||||
name = 'res',
|
||||
res = 'src/main/res',
|
||||
package = 'com.hellohybrid',
|
||||
)
|
||||
|
||||
android_binary(
|
||||
name = 'app',
|
||||
package_type = 'debug',
|
||||
manifest = 'src/main/AndroidManifest.xml',
|
||||
keystore = '//android/keystores:debug',
|
||||
deps = [
|
||||
':app-code',
|
||||
],
|
||||
)
|
||||
139
examples/HelloHybrid/android/app/build.gradle
Normal file
139
examples/HelloHybrid/android/app/build.gradle
Normal file
@@ -0,0 +1,139 @@
|
||||
apply plugin: "com.android.application"
|
||||
|
||||
import com.android.build.OutputFile
|
||||
|
||||
/**
|
||||
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
|
||||
* and bundleReleaseJsAndAssets).
|
||||
* These basically call `react-native bundle` with the correct arguments during the Android build
|
||||
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
|
||||
* bundle directly from the development server. Below you can see all the possible configurations
|
||||
* and their defaults. If you decide to add a configuration block, make sure to add it before the
|
||||
* `apply from: "../../node_modules/react-native/react.gradle"` line.
|
||||
*
|
||||
* project.ext.react = [
|
||||
* // the name of the generated asset file containing your JS bundle
|
||||
* bundleAssetName: "index.android.bundle",
|
||||
*
|
||||
* // the entry file for bundle generation
|
||||
* entryFile: "index.android.js",
|
||||
*
|
||||
* // whether to bundle JS and assets in debug mode
|
||||
* bundleInDebug: false,
|
||||
*
|
||||
* // whether to bundle JS and assets in release mode
|
||||
* bundleInRelease: true,
|
||||
*
|
||||
* // whether to bundle JS and assets in another build variant (if configured).
|
||||
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
|
||||
* // The configuration property can be in the following formats
|
||||
* // 'bundleIn${productFlavor}${buildType}'
|
||||
* // 'bundleIn${buildType}'
|
||||
* // bundleInFreeDebug: true,
|
||||
* // bundleInPaidRelease: true,
|
||||
* // bundleInBeta: true,
|
||||
*
|
||||
* // the root of your project, i.e. where "package.json" lives
|
||||
* root: "../../",
|
||||
*
|
||||
* // where to put the JS bundle asset in debug mode
|
||||
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
|
||||
*
|
||||
* // where to put the JS bundle asset in release mode
|
||||
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in debug mode
|
||||
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in release mode
|
||||
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
|
||||
*
|
||||
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
|
||||
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
|
||||
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
|
||||
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
|
||||
* // for example, you might want to remove it from here.
|
||||
* inputExcludes: ["android/**", "ios/**"],
|
||||
*
|
||||
* // override which node gets called and with what additional arguments
|
||||
* nodeExecutableAndArgs: ["node"]
|
||||
*
|
||||
* // supply additional arguments to the packager
|
||||
* extraPackagerArgs: []
|
||||
* ]
|
||||
*/
|
||||
|
||||
apply from: "../../node_modules/react-native/react.gradle"
|
||||
|
||||
/**
|
||||
* Set this to true to create two separate APKs instead of one:
|
||||
* - An APK that only works on ARM devices
|
||||
* - An APK that only works on x86 devices
|
||||
* The advantage is the size of the APK is reduced by about 4MB.
|
||||
* Upload all the APKs to the Play Store and people will download
|
||||
* the correct one based on the CPU architecture of their device.
|
||||
*/
|
||||
def enableSeparateBuildPerCPUArchitecture = false
|
||||
|
||||
/**
|
||||
* Run Proguard to shrink the Java bytecode in release builds.
|
||||
*/
|
||||
def enableProguardInReleaseBuilds = false
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.1"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.hellohybrid"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 22
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
}
|
||||
}
|
||||
splits {
|
||||
abi {
|
||||
reset()
|
||||
enable enableSeparateBuildPerCPUArchitecture
|
||||
universalApk false // If true, also generate a universal APK
|
||||
include "armeabi-v7a", "x86"
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled enableProguardInReleaseBuilds
|
||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
}
|
||||
}
|
||||
// applicationVariants are e.g. debug, release
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
// For each separate APK per architecture, set a unique version code as described here:
|
||||
// http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
|
||||
def versionCodes = ["armeabi-v7a":1, "x86":2]
|
||||
def abi = output.getFilter(OutputFile.ABI)
|
||||
if (abi != null) { // null for the universal-debug, universal-release variants
|
||||
output.versionCodeOverride =
|
||||
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||
compile "com.android.support:appcompat-v7:23.0.1"
|
||||
compile "com.facebook.react:react-native:+" // From node_modules
|
||||
}
|
||||
|
||||
// Run this once to be able to run the application with BUCK
|
||||
// puts all compile dependencies into folder libs for BUCK to use
|
||||
task copyDownloadableDepsToLibs(type: Copy) {
|
||||
from configurations.compile
|
||||
into 'libs'
|
||||
}
|
||||
66
examples/HelloHybrid/android/app/proguard-rules.pro
vendored
Normal file
66
examples/HelloHybrid/android/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Disabling obfuscation is useful if you collect stack traces from production crashes
|
||||
# (unless you are using a system that supports de-obfuscate the stack traces).
|
||||
-dontobfuscate
|
||||
|
||||
# React Native
|
||||
|
||||
# Keep our interfaces so they can be used by other ProGuard rules.
|
||||
# See http://sourceforge.net/p/proguard/bugs/466/
|
||||
-keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
|
||||
-keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
|
||||
-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
|
||||
|
||||
# Do not strip any method/class that is annotated with @DoNotStrip
|
||||
-keep @com.facebook.proguard.annotations.DoNotStrip class *
|
||||
-keep @com.facebook.common.internal.DoNotStrip class *
|
||||
-keepclassmembers class * {
|
||||
@com.facebook.proguard.annotations.DoNotStrip *;
|
||||
@com.facebook.common.internal.DoNotStrip *;
|
||||
}
|
||||
|
||||
-keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
|
||||
void set*(***);
|
||||
*** get*();
|
||||
}
|
||||
|
||||
-keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }
|
||||
-keep class * extends com.facebook.react.bridge.NativeModule { *; }
|
||||
-keepclassmembers,includedescriptorclasses class * { native <methods>; }
|
||||
-keepclassmembers class * { @com.facebook.react.uimanager.UIProp <fields>; }
|
||||
-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp <methods>; }
|
||||
-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup <methods>; }
|
||||
|
||||
-dontwarn com.facebook.react.**
|
||||
|
||||
# okhttp
|
||||
|
||||
-keepattributes Signature
|
||||
-keepattributes *Annotation*
|
||||
-keep class okhttp3.** { *; }
|
||||
-keep interface okhttp3.** { *; }
|
||||
-dontwarn okhttp3.**
|
||||
|
||||
# okio
|
||||
|
||||
-keep class sun.misc.Unsafe { *; }
|
||||
-dontwarn java.nio.file.*
|
||||
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
|
||||
-dontwarn okio.**
|
||||
@@ -0,0 +1,31 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.hellohybrid"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="16"
|
||||
android:targetSdkVersion="22" />
|
||||
|
||||
<application
|
||||
android:name=".MainApplication"
|
||||
android:allowBackup="true"
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.hellohybrid;
|
||||
|
||||
import com.facebook.react.ReactActivity;
|
||||
|
||||
public class MainActivity extends ReactActivity {
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript.
|
||||
* This is used to schedule rendering of the component.
|
||||
*/
|
||||
@Override
|
||||
protected String getMainComponentName() {
|
||||
return "HelloHybrid";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.hellohybrid;
|
||||
|
||||
import android.app.Application;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.shell.MainReactPackage;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class MainApplication extends Application implements ReactApplication {
|
||||
|
||||
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
|
||||
@Override
|
||||
protected boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
return Arrays.<ReactPackage>asList(
|
||||
new MainReactPackage()
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public ReactNativeHost getReactNativeHost() {
|
||||
return mReactNativeHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">HelloHybrid</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
24
examples/HelloHybrid/android/build.gradle
Normal file
24
examples/HelloHybrid/android/build.gradle
Normal file
@@ -0,0 +1,24 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.3.1'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
maven {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url "$rootDir/../node_modules/react-native/android"
|
||||
}
|
||||
}
|
||||
}
|
||||
20
examples/HelloHybrid/android/gradle.properties
Normal file
20
examples/HelloHybrid/android/gradle.properties
Normal file
@@ -0,0 +1,20 @@
|
||||
# Project-wide Gradle settings.
|
||||
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
android.useDeprecatedNdk=true
|
||||
BIN
examples/HelloHybrid/android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
examples/HelloHybrid/android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
examples/HelloHybrid/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
examples/HelloHybrid/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
|
||||
164
examples/HelloHybrid/android/gradlew
vendored
Executable file
164
examples/HelloHybrid/android/gradlew
vendored
Executable file
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >&-
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
90
examples/HelloHybrid/android/gradlew.bat
vendored
Normal file
90
examples/HelloHybrid/android/gradlew.bat
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
8
examples/HelloHybrid/android/keystores/BUCK
Normal file
8
examples/HelloHybrid/android/keystores/BUCK
Normal file
@@ -0,0 +1,8 @@
|
||||
keystore(
|
||||
name = 'debug',
|
||||
store = 'debug.keystore',
|
||||
properties = 'debug.keystore.properties',
|
||||
visibility = [
|
||||
'PUBLIC',
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,4 @@
|
||||
key.store=debug.keystore
|
||||
key.alias=androiddebugkey
|
||||
key.store.password=android
|
||||
key.alias.password=android
|
||||
3
examples/HelloHybrid/android/settings.gradle
Normal file
3
examples/HelloHybrid/android/settings.gradle
Normal file
@@ -0,0 +1,3 @@
|
||||
rootProject.name = 'HelloHybrid'
|
||||
|
||||
include ':app'
|
||||
53
examples/HelloHybrid/index.android.js
Normal file
53
examples/HelloHybrid/index.android.js
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Sample React Native App
|
||||
* https://github.com/facebook/react-native
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
AppRegistry,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
export default class HelloHybrid extends Component {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.welcome}>
|
||||
Welcome to React Native!
|
||||
</Text>
|
||||
<Text style={styles.instructions}>
|
||||
To get started, edit index.android.js
|
||||
</Text>
|
||||
<Text style={styles.instructions}>
|
||||
Double tap R on your keyboard to reload,{'\n'}
|
||||
Shake or press menu button for dev menu
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F5FCFF',
|
||||
},
|
||||
welcome: {
|
||||
fontSize: 20,
|
||||
textAlign: 'center',
|
||||
margin: 10,
|
||||
},
|
||||
instructions: {
|
||||
textAlign: 'center',
|
||||
color: '#333333',
|
||||
marginBottom: 5,
|
||||
},
|
||||
});
|
||||
|
||||
AppRegistry.registerComponent('HelloHybrid', () => HelloHybrid);
|
||||
43
examples/HelloHybrid/index.ios.js
Normal file
43
examples/HelloHybrid/index.ios.js
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
AppRegistry,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
import HybridContainer from './HybridContainer';
|
||||
|
||||
const NotFoundComponent = () => (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.welcome}>
|
||||
Screen not found!
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
const HelloHybrid = HybridContainer({
|
||||
Settings: require('./screens/Settings'),
|
||||
Story: require('./screens/Story'),
|
||||
NotFoundComponent,
|
||||
});
|
||||
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
welcome: {
|
||||
fontSize: 20,
|
||||
textAlign: 'center',
|
||||
margin: 10,
|
||||
},
|
||||
});
|
||||
|
||||
AppRegistry.registerComponent('HelloHybrid', () => HelloHybrid);
|
||||
1113
examples/HelloHybrid/ios/HelloHybrid.xcodeproj/project.pbxproj
Normal file
1113
examples/HelloHybrid/ios/HelloHybrid.xcodeproj/project.pbxproj
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,129 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0620"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "NO"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "83CBBA2D1A601D0E00E9B192"
|
||||
BuildableName = "libReact.a"
|
||||
BlueprintName = "React"
|
||||
ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "HelloHybrid.app"
|
||||
BlueprintName = "HelloHybrid"
|
||||
ReferencedContainer = "container:HelloHybrid.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
|
||||
BuildableName = "HelloHybridTests.xctest"
|
||||
BlueprintName = "HelloHybridTests"
|
||||
ReferencedContainer = "container:HelloHybrid.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
|
||||
BuildableName = "HelloHybridTests.xctest"
|
||||
BlueprintName = "HelloHybridTests"
|
||||
ReferencedContainer = "container:HelloHybrid.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "HelloHybrid.app"
|
||||
BlueprintName = "HelloHybrid"
|
||||
ReferencedContainer = "container:HelloHybrid.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "HelloHybrid.app"
|
||||
BlueprintName = "HelloHybrid"
|
||||
ReferencedContainer = "container:HelloHybrid.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "HelloHybrid.app"
|
||||
BlueprintName = "HelloHybrid"
|
||||
ReferencedContainer = "container:HelloHybrid.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
13
examples/HelloHybrid/ios/HelloHybrid/AppDelegate.h
Normal file
13
examples/HelloHybrid/ios/HelloHybrid/AppDelegate.h
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <React/RCTRootView.h>
|
||||
#import "AppNavigationDelegate.h"
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, AppNavigationDelegate>
|
||||
|
||||
@property (strong, nonatomic) UIWindow *window;
|
||||
@property (strong, nonatomic) UIViewController *rootController;
|
||||
@property (strong, nonatomic) UINavigationController *nav;
|
||||
@property (strong, nonatomic) RCTBridge *bridge;
|
||||
|
||||
@end
|
||||
54
examples/HelloHybrid/ios/HelloHybrid/AppDelegate.m
Normal file
54
examples/HelloHybrid/ios/HelloHybrid/AppDelegate.m
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import "HomeViewController.h"
|
||||
#import "ProfileViewController.h"
|
||||
#import "ReactViewController.h"
|
||||
#import "HybridNavigationManager.h"
|
||||
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
_bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions: launchOptions];
|
||||
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
||||
_rootController = [[HomeViewController alloc] initWithDelegate:self];
|
||||
_nav = [[UINavigationController alloc] initWithRootViewController:self.rootController];
|
||||
[_window addSubview:_nav.view];
|
||||
[_window setRootViewController:_nav];
|
||||
[_window makeKeyAndVisible];
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark AppNavigationDelegate
|
||||
|
||||
- (void)openViewWithName:(NSString *)name andParams:(NSDictionary *)params
|
||||
{
|
||||
if ([name isEqualToString:@"Profile"]) {
|
||||
ProfileViewController *profileView = [[ProfileViewController alloc] initWithDelegate: self];
|
||||
[_nav pushViewController:profileView animated:true];
|
||||
return;
|
||||
}
|
||||
|
||||
ReactViewController *reactView = [[ReactViewController alloc] initWithDelegate:self bridge:_bridge viewName:name viewParams:params];
|
||||
[_nav pushViewController:reactView animated:true];
|
||||
}
|
||||
|
||||
#pragma mark RCTBridgeDelegate
|
||||
|
||||
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"examples/HelloHybrid/index.ios" fallbackResource:nil];
|
||||
}
|
||||
|
||||
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge;
|
||||
{
|
||||
return @[
|
||||
[[HybridNavigationManager alloc] initWithBridge:_bridge navigationDelegate:self],
|
||||
];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,9 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@protocol AppNavigationDelegate <NSObject>
|
||||
|
||||
- (void)openViewWithName:(NSString *)name andParams:(NSDictionary *)params;
|
||||
|
||||
@optional
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Powered by React Native" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
|
||||
<rect key="frame" x="20" y="439" width="441" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="HelloHybrid" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
|
||||
<rect key="frame" x="20" y="140" width="441" height="43"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
|
||||
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
|
||||
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
|
||||
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
|
||||
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="548" y="455"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
12
examples/HelloHybrid/ios/HelloHybrid/HomeViewController.h
Normal file
12
examples/HelloHybrid/ios/HelloHybrid/HomeViewController.h
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "AppNavigationDelegate.h"
|
||||
|
||||
@interface HomeViewController : UIViewController {
|
||||
id<AppNavigationDelegate> navigation;
|
||||
}
|
||||
|
||||
- (id) initWithDelegate:(id<AppNavigationDelegate>)delegate;
|
||||
|
||||
@end
|
||||
|
||||
57
examples/HelloHybrid/ios/HelloHybrid/HomeViewController.m
Normal file
57
examples/HelloHybrid/ios/HelloHybrid/HomeViewController.m
Normal file
@@ -0,0 +1,57 @@
|
||||
|
||||
#import "HomeViewController.h"
|
||||
|
||||
@interface HomeViewController ()
|
||||
@end
|
||||
@implementation HomeViewController
|
||||
|
||||
- (id)initWithDelegate:(id<AppNavigationDelegate>)delegate;
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
navigation = delegate;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)loadView {
|
||||
CGRect rect = [UIScreen mainScreen].bounds;
|
||||
self.view = [[UIView alloc] initWithFrame:rect];
|
||||
self.view.backgroundColor = [UIColor whiteColor];
|
||||
self.navigationItem.title = @"Home Screen";
|
||||
|
||||
UILabel *labelView = [[UILabel alloc] initWithFrame:CGRectMake(5,80,self.view.frame.size.width,50)];
|
||||
labelView.text = @"This screen is native";
|
||||
labelView.textAlignment = NSTextAlignmentCenter;
|
||||
labelView.textColor = [UIColor blackColor];
|
||||
|
||||
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||
[button addTarget:self
|
||||
action:@selector(openProfile:)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
[button setTitle:@"Open Profile" forState:UIControlStateNormal];
|
||||
button.frame = CGRectMake(10.0, 120.0, self.view.frame.size.width, 40.0);
|
||||
|
||||
UIButton *button2 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||
[button2 addTarget:self
|
||||
action:@selector(openSettings:)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
[button2 setTitle:@"Open Settings" forState:UIControlStateNormal];
|
||||
button2.frame = CGRectMake(10.0, 160.0, self.view.frame.size.width, 40.0);
|
||||
|
||||
[self.view addSubview:button];
|
||||
[self.view addSubview:button2];
|
||||
[self.view addSubview:labelView];
|
||||
}
|
||||
|
||||
- (void) openProfile:(UIButton*)sender
|
||||
{
|
||||
[navigation openViewWithName:@"Profile" andParams:@{}];
|
||||
}
|
||||
|
||||
- (void) openSettings:(UIButton*)sender
|
||||
{
|
||||
[navigation openViewWithName:@"Settings" andParams:@{}];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,9 @@
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import "AppNavigationDelegate.h"
|
||||
|
||||
|
||||
@interface HybridNavigationManager : NSObject <RCTBridgeModule>
|
||||
|
||||
- (id)initWithBridge:(RCTBridge *)bridge navigationDelegate:(id<AppNavigationDelegate>)delegate;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,42 @@
|
||||
#import "AppNavigationDelegate.h"
|
||||
#import "HybridNavigationManager.h"
|
||||
#import <React/RCTLog.h>
|
||||
#import <React/RCTRootView.h>
|
||||
#import <React/RCTUIManager.h>
|
||||
|
||||
@implementation HybridNavigationManager {
|
||||
__weak id<AppNavigationDelegate> _delegate;
|
||||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
- (id)initWithBridge:(RCTBridge *)bridge navigationDelegate:(id<AppNavigationDelegate>)delegate;
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_bridge = bridge;
|
||||
_delegate = delegate;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
- (dispatch_queue_t)methodQueue
|
||||
{
|
||||
return dispatch_get_main_queue();
|
||||
}
|
||||
|
||||
|
||||
RCT_EXPORT_METHOD(navigate:(NSString *)name params:(NSDictionary *)params)
|
||||
{
|
||||
[_delegate openViewWithName:name andParams:params];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setTitle:(nonnull NSNumber *)rootTag title:(nonnull NSString *)title)
|
||||
{
|
||||
RCTRootView *rootView = (RCTRootView *)[_bridge.uiManager viewForReactTag: rootTag];
|
||||
rootView.reactViewController.navigationItem.title = title;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
54
examples/HelloHybrid/ios/HelloHybrid/Info.plist
Normal file
54
examples/HelloHybrid/ios/HelloHybrid/Info.plist
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<!--See http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/ -->
|
||||
<dict>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>localhost</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
12
examples/HelloHybrid/ios/HelloHybrid/ProfileViewController.h
Normal file
12
examples/HelloHybrid/ios/HelloHybrid/ProfileViewController.h
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "AppNavigationDelegate.h"
|
||||
|
||||
@interface ProfileViewController : UIViewController {
|
||||
id<AppNavigationDelegate> navigation;
|
||||
}
|
||||
|
||||
- (id) initWithDelegate:(id<AppNavigationDelegate>)delegate;
|
||||
|
||||
@end
|
||||
|
||||
70
examples/HelloHybrid/ios/HelloHybrid/ProfileViewController.m
Normal file
70
examples/HelloHybrid/ios/HelloHybrid/ProfileViewController.m
Normal file
@@ -0,0 +1,70 @@
|
||||
|
||||
#import "ProfileViewController.h"
|
||||
|
||||
@interface ProfileViewController ()
|
||||
@end
|
||||
@implementation ProfileViewController
|
||||
|
||||
- (id)initWithDelegate:(id<AppNavigationDelegate>)delegate;
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
navigation = delegate;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)loadView {
|
||||
self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
self.view.backgroundColor = [UIColor whiteColor];
|
||||
self.navigationItem.title = @"Profile Screen";
|
||||
|
||||
UILabel *labelView = [[UILabel alloc] initWithFrame:CGRectMake(5,80,self.view.frame.size.width,50)];
|
||||
labelView.text = @"This screen is native";
|
||||
labelView.textAlignment = NSTextAlignmentCenter;
|
||||
labelView.textColor = [UIColor blackColor];
|
||||
|
||||
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||
[button addTarget:self
|
||||
action:@selector(openStory:)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
[button setTitle:@"Open Story" forState:UIControlStateNormal];
|
||||
button.frame = CGRectMake(10.0, 120.0, self.view.frame.size.width, 40.0);
|
||||
|
||||
UIButton *button2 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||
[button2 addTarget:self
|
||||
action:@selector(openSettings:)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
[button2 setTitle:@"Open Settings" forState:UIControlStateNormal];
|
||||
button2.frame = CGRectMake(10.0, 160.0, self.view.frame.size.width, 40.0);
|
||||
|
||||
UIButton *button3 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||
[button3 addTarget:self
|
||||
action:@selector(openAdvancedSettings:)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
[button3 setTitle:@"Open Advanced Settings" forState:UIControlStateNormal];
|
||||
button3.frame = CGRectMake(10.0, 200.0, self.view.frame.size.width, 40.0);
|
||||
|
||||
[self.view addSubview:button];
|
||||
[self.view addSubview:button2];
|
||||
[self.view addSubview:button3];
|
||||
[self.view addSubview:labelView];
|
||||
}
|
||||
|
||||
- (void)openStory:(UIButton*)sender
|
||||
{
|
||||
[navigation openViewWithName:@"Story" andParams:@{ @"id": @"4242" }];
|
||||
}
|
||||
|
||||
- (void)openSettings:(UIButton*)sender
|
||||
{
|
||||
[navigation openViewWithName:@"Settings" andParams:@{}];
|
||||
}
|
||||
|
||||
- (void)openAdvancedSettings:(UIButton*)sender
|
||||
{
|
||||
[navigation openViewWithName:@"AdvancedSettings" andParams:@{}];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
16
examples/HelloHybrid/ios/HelloHybrid/ReactViewController.h
Normal file
16
examples/HelloHybrid/ios/HelloHybrid/ReactViewController.h
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "AppNavigationDelegate.h"
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
@interface ReactViewController : UIViewController {
|
||||
id<AppNavigationDelegate> navigation;
|
||||
NSString* name;
|
||||
NSDictionary* params;
|
||||
RCTBridge* bridge;
|
||||
}
|
||||
|
||||
- (id)initWithDelegate:(id<AppNavigationDelegate>)delegate bridge:(RCTBridge *)bridge viewName:(NSString *)name viewParams:(NSDictionary *)params;
|
||||
|
||||
@end
|
||||
|
||||
34
examples/HelloHybrid/ios/HelloHybrid/ReactViewController.m
Normal file
34
examples/HelloHybrid/ios/HelloHybrid/ReactViewController.m
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
#import "ReactViewController.h"
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
@interface ReactViewController ()
|
||||
@end
|
||||
@implementation ReactViewController
|
||||
|
||||
- (id)initWithDelegate:(id<AppNavigationDelegate>)delegate bridge:(RCTBridge *)inBridge viewName:(NSString *)inName viewParams:(NSDictionary *)inParams
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
navigation = delegate;
|
||||
name = inName;
|
||||
params = inParams;
|
||||
bridge = inBridge;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)loadView {
|
||||
self.navigationItem.title = @"";
|
||||
self.view = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"HelloHybrid" initialProperties: @{ @"name": name, @"params": params }];
|
||||
self.view.backgroundColor = [UIColor whiteColor];
|
||||
}
|
||||
|
||||
- (void)requestClose:(UIButton*)sender
|
||||
{
|
||||
[self dismissViewControllerAnimated:true completion:^{}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
10
examples/HelloHybrid/ios/HelloHybrid/main.m
Normal file
10
examples/HelloHybrid/ios/HelloHybrid/main.m
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
@autoreleasepool {
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||
}
|
||||
}
|
||||
62
examples/HelloHybrid/ios/HelloHybridTests/HelloHybridTests.m
Normal file
62
examples/HelloHybrid/ios/HelloHybridTests/HelloHybridTests.m
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import <React/RCTLog.h>
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
#define TIMEOUT_SECONDS 600
|
||||
#define TEXT_TO_LOOK_FOR @"Welcome to React Native!"
|
||||
|
||||
@interface HelloHybridTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation HelloHybridTests
|
||||
|
||||
- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
|
||||
{
|
||||
if (test(view)) {
|
||||
return YES;
|
||||
}
|
||||
for (UIView *subview in [view subviews]) {
|
||||
if ([self findSubviewInView:subview matching:test]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)testRendersWelcomeScreen
|
||||
{
|
||||
UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
|
||||
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
|
||||
BOOL foundElement = NO;
|
||||
|
||||
__block NSString *redboxError = nil;
|
||||
RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
|
||||
if (level >= RCTLogLevelError) {
|
||||
redboxError = message;
|
||||
}
|
||||
});
|
||||
|
||||
while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
|
||||
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||
|
||||
foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
|
||||
if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}];
|
||||
}
|
||||
|
||||
RCTSetLogFunction(RCTDefaultLogFunction);
|
||||
|
||||
XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
|
||||
XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
24
examples/HelloHybrid/ios/HelloHybridTests/Info.plist
Normal file
24
examples/HelloHybrid/ios/HelloHybridTests/Info.plist
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
25
examples/HelloHybrid/package.json
Normal file
25
examples/HelloHybrid/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "HelloHybrid",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node node_modules/react-native/local-cli/cli.js start",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^15.4.2",
|
||||
"react-native": "^0.40.0",
|
||||
"react-navigation": "file:../.."
|
||||
},
|
||||
"jest": {
|
||||
"preset": "jest-react-native"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-jest": "16.0.0",
|
||||
"babel-preset-react-native": "1.9.0",
|
||||
"jest": "16.0.2",
|
||||
"jest-react-native": "16.0.0",
|
||||
"react-test-renderer": "15.3.2",
|
||||
"whatwg-fetch": "1.0.0"
|
||||
}
|
||||
}
|
||||
83
examples/HelloHybrid/screens/Settings.js
Normal file
83
examples/HelloHybrid/screens/Settings.js
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
SegmentedControlIOS,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
import {
|
||||
TabNavigator,
|
||||
} from 'react-navigation';
|
||||
|
||||
const BasicSettingsScreen = () => (
|
||||
<Text style={styles.welcome}>
|
||||
Settings, Built in React!
|
||||
</Text>
|
||||
);
|
||||
|
||||
const AdvancedSettingsScreen = () => (
|
||||
<Text style={styles.welcome}>
|
||||
Advanced settings - also React!
|
||||
</Text>
|
||||
);
|
||||
|
||||
const TabView = ({children, navigation, tabs}) => (
|
||||
<View style={styles.container}>
|
||||
<SegmentedControlIOS
|
||||
style={styles.tabBar}
|
||||
values={tabs.map(tab => tab.myLabel)}
|
||||
selectedIndex={navigation.state.index}
|
||||
onChange={({nativeEvent}) => {
|
||||
navigation.dispatch({
|
||||
type: tabs[nativeEvent.selectedSegmentIndex].key,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
|
||||
const SettingsScreen = TabNavigator({
|
||||
Settings: {
|
||||
screen: BasicSettingsScreen,
|
||||
navigationOptions: {
|
||||
title: () => 'Settings',
|
||||
},
|
||||
},
|
||||
AdvancedSettings: {
|
||||
screen: AdvancedSettingsScreen,
|
||||
navigationOptions: {
|
||||
title: () => 'Advanced Settings',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
paddingTop: 80,
|
||||
},
|
||||
tabBar: {
|
||||
alignSelf: 'stretch',
|
||||
height: 40,
|
||||
margin: 10,
|
||||
},
|
||||
welcome: {
|
||||
fontSize: 20,
|
||||
textAlign: 'center',
|
||||
margin: 10,
|
||||
},
|
||||
link: {
|
||||
textAlign: 'center',
|
||||
color: '#0A5FFF',
|
||||
fontSize: 16,
|
||||
marginVertical: 10,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = SettingsScreen;
|
||||
59
examples/HelloHybrid/screens/Story.js
Normal file
59
examples/HelloHybrid/screens/Story.js
Normal file
@@ -0,0 +1,59 @@
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
ScrollView,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
const Story = ({navigation}) => (
|
||||
<View style={styles.container}>
|
||||
<ScrollView>
|
||||
<Text style={styles.welcome}>
|
||||
React Screen. Story ID: {JSON.stringify(navigation.state)}
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
navigation.navigate('Profile', {id: '9876'});
|
||||
}}>
|
||||
<Text style={styles.link}>Navigate to native profile</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
navigation.navigate('Settings');
|
||||
}}>
|
||||
<Text style={styles.link}>Navigate to react settings</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
navigation.navigate('RandomLink');
|
||||
}}>
|
||||
<Text style={styles.link}>Navigate to unimpemented page</Text>
|
||||
</TouchableOpacity>
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
welcome: {
|
||||
fontSize: 20,
|
||||
textAlign: 'center',
|
||||
margin: 10,
|
||||
marginTop: 80,
|
||||
},
|
||||
link: {
|
||||
textAlign: 'center',
|
||||
color: '#0A5FFF',
|
||||
fontSize: 16,
|
||||
marginVertical: 10,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = Story;
|
||||
3924
examples/HelloHybrid/yarn.lock
Normal file
3924
examples/HelloHybrid/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
3
examples/LinkingExample/.babelrc
Normal file
3
examples/LinkingExample/.babelrc
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": ["react-native"]
|
||||
}
|
||||
6
examples/LinkingExample/.buckconfig
Normal file
6
examples/LinkingExample/.buckconfig
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
[android]
|
||||
target = Google Inc.:Google APIs:23
|
||||
|
||||
[maven_repositories]
|
||||
central = https://repo1.maven.org/maven2
|
||||
44
examples/LinkingExample/.flowconfig
Normal file
44
examples/LinkingExample/.flowconfig
Normal file
@@ -0,0 +1,44 @@
|
||||
[ignore]
|
||||
; We fork some components by platform
|
||||
.*/*[.]android.js
|
||||
|
||||
; Ignore "BUCK" generated dirs
|
||||
<PROJECT_ROOT>/\.buckd/
|
||||
|
||||
; Ignore unexpected extra "@providesModule"
|
||||
.*/node_modules/.*/node_modules/fbjs/.*
|
||||
|
||||
; Ignore duplicate module providers
|
||||
; For RN Apps installed via npm, "Libraries" folder is inside
|
||||
; "node_modules/react-native" but in the source repo it is in the root
|
||||
.*/Libraries/react-native/React.js
|
||||
.*/Libraries/react-native/ReactNative.js
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
node_modules/react-native/Libraries/react-native/react-native-interface.js
|
||||
node_modules/react-native/flow
|
||||
flow/
|
||||
|
||||
[options]
|
||||
module.system=haste
|
||||
|
||||
experimental.strict_type_args=true
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FixMe
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-6]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-6]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
|
||||
unsafe.enable_getters_and_setters=true
|
||||
|
||||
[version]
|
||||
^0.36.0
|
||||
1
examples/LinkingExample/.gitattributes
vendored
Normal file
1
examples/LinkingExample/.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.pbxproj -text
|
||||
53
examples/LinkingExample/.gitignore
vendored
Normal file
53
examples/LinkingExample/.gitignore
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
# OSX
|
||||
#
|
||||
.DS_Store
|
||||
|
||||
# Xcode
|
||||
#
|
||||
build/
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.xcuserstate
|
||||
project.xcworkspace
|
||||
|
||||
# Android/IntelliJ
|
||||
#
|
||||
build/
|
||||
.idea
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
|
||||
# node.js
|
||||
#
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
|
||||
# BUCK
|
||||
buck-out/
|
||||
\.buckd/
|
||||
android/app/libs
|
||||
*.keystore
|
||||
|
||||
# fastlane
|
||||
#
|
||||
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
|
||||
# screenshots whenever they are needed.
|
||||
# For more information about the recommended setup visit:
|
||||
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
|
||||
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots
|
||||
1
examples/LinkingExample/.watchmanconfig
Normal file
1
examples/LinkingExample/.watchmanconfig
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
48
examples/LinkingExample/App.js
Normal file
48
examples/LinkingExample/App.js
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
Linking,
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
export default class LinkingExample extends Component {
|
||||
state = { initUrl: null };
|
||||
|
||||
async componentDidMount() {
|
||||
const initUrl = await Linking.getInitialURL();
|
||||
this.setState({ initUrl });
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.welcome}>
|
||||
Welcome to React Native!
|
||||
|
||||
{this.state.initUrl}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F5FCFF',
|
||||
},
|
||||
welcome: {
|
||||
fontSize: 20,
|
||||
textAlign: 'center',
|
||||
margin: 10,
|
||||
},
|
||||
instructions: {
|
||||
textAlign: 'center',
|
||||
color: '#333333',
|
||||
marginBottom: 5,
|
||||
},
|
||||
});
|
||||
12
examples/LinkingExample/__tests__/index.android.js
Normal file
12
examples/LinkingExample/__tests__/index.android.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'react-native';
|
||||
import React from 'react';
|
||||
import Index from '../index.android.js';
|
||||
|
||||
// Note: test renderer must be required after react-native.
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
it('renders correctly', () => {
|
||||
const tree = renderer.create(
|
||||
<Index />
|
||||
);
|
||||
});
|
||||
12
examples/LinkingExample/__tests__/index.ios.js
Normal file
12
examples/LinkingExample/__tests__/index.ios.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'react-native';
|
||||
import React from 'react';
|
||||
import Index from '../index.ios.js';
|
||||
|
||||
// Note: test renderer must be required after react-native.
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
it('renders correctly', () => {
|
||||
const tree = renderer.create(
|
||||
<Index />
|
||||
);
|
||||
});
|
||||
66
examples/LinkingExample/android/app/BUCK
Normal file
66
examples/LinkingExample/android/app/BUCK
Normal file
@@ -0,0 +1,66 @@
|
||||
import re
|
||||
|
||||
# To learn about Buck see [Docs](https://buckbuild.com/).
|
||||
# To run your application with Buck:
|
||||
# - install Buck
|
||||
# - `npm start` - to start the packager
|
||||
# - `cd android`
|
||||
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
|
||||
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
|
||||
# - `buck install -r android/app` - compile, install and run application
|
||||
#
|
||||
|
||||
lib_deps = []
|
||||
for jarfile in glob(['libs/*.jar']):
|
||||
name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile)
|
||||
lib_deps.append(':' + name)
|
||||
prebuilt_jar(
|
||||
name = name,
|
||||
binary_jar = jarfile,
|
||||
)
|
||||
|
||||
for aarfile in glob(['libs/*.aar']):
|
||||
name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile)
|
||||
lib_deps.append(':' + name)
|
||||
android_prebuilt_aar(
|
||||
name = name,
|
||||
aar = aarfile,
|
||||
)
|
||||
|
||||
android_library(
|
||||
name = 'all-libs',
|
||||
exported_deps = lib_deps
|
||||
)
|
||||
|
||||
android_library(
|
||||
name = 'app-code',
|
||||
srcs = glob([
|
||||
'src/main/java/**/*.java',
|
||||
]),
|
||||
deps = [
|
||||
':all-libs',
|
||||
':build_config',
|
||||
':res',
|
||||
],
|
||||
)
|
||||
|
||||
android_build_config(
|
||||
name = 'build_config',
|
||||
package = 'com.linkingexample',
|
||||
)
|
||||
|
||||
android_resource(
|
||||
name = 'res',
|
||||
res = 'src/main/res',
|
||||
package = 'com.linkingexample',
|
||||
)
|
||||
|
||||
android_binary(
|
||||
name = 'app',
|
||||
package_type = 'debug',
|
||||
manifest = 'src/main/AndroidManifest.xml',
|
||||
keystore = '//android/keystores:debug',
|
||||
deps = [
|
||||
':app-code',
|
||||
],
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user