diff --git a/packages/react-navigation/examples/NavigationPlayground/js/App.js b/packages/react-navigation/examples/NavigationPlayground/js/App.js
index a5abafca..c091ca6c 100644
--- a/packages/react-navigation/examples/NavigationPlayground/js/App.js
+++ b/packages/react-navigation/examples/NavigationPlayground/js/App.js
@@ -34,6 +34,7 @@ import StackWithTranslucentHeader from './StackWithTranslucentHeader';
import SimpleTabs from './SimpleTabs';
import SwitchWithStacks from './SwitchWithStacks';
import TabsWithNavigationFocus from './TabsWithNavigationFocus';
+import KeyboardHandlingExample from './KeyboardHandlingExample';
const ExampleInfo = {
SimpleStack: {
@@ -114,6 +115,11 @@ const ExampleInfo = {
name: 'withNavigationFocus',
description: 'Receive the focus prop to know when a screen is focused',
},
+ KeyboardHandlingExample: {
+ name: 'Keyboard Handling Example',
+ description:
+ 'Demo automatic handling of keyboard showing/hiding inside StackNavigator',
+ },
};
const ExampleRoutes = {
@@ -143,6 +149,7 @@ const ExampleRoutes = {
path: 'settings',
},
TabsWithNavigationFocus,
+ KeyboardHandlingExample,
};
type State = {
diff --git a/packages/react-navigation/examples/NavigationPlayground/js/KeyboardHandlingExample.js b/packages/react-navigation/examples/NavigationPlayground/js/KeyboardHandlingExample.js
new file mode 100644
index 00000000..7a0674a5
--- /dev/null
+++ b/packages/react-navigation/examples/NavigationPlayground/js/KeyboardHandlingExample.js
@@ -0,0 +1,62 @@
+import React from 'react';
+import { StatusBar, View, TextInput, InteractionManager } from 'react-native';
+import { createStackNavigator, withNavigationFocus } from 'react-navigation';
+import { Button } from './commonComponents/ButtonWithMargin';
+
+class ScreenOne extends React.Component {
+ static navigationOptions = {
+ title: 'Home',
+ };
+
+ render() {
+ const { navigation } = this.props;
+ return (
+
+
+ );
+ }
+}
+
+class ScreenTwo extends React.Component {
+ static navigationOptions = {
+ title: 'Screen w/ Input',
+ };
+
+ componentDidMount() {
+ InteractionManager.runAfterInteractions(() => {
+ this._textInput.focus();
+ });
+ }
+
+ render() {
+ const { navigation } = this.props;
+ return (
+
+
+ (this._textInput = c)}
+ style={{
+ backgroundColor: 'white',
+ height: 24,
+ width: 150,
+ borderColor: '#555',
+ borderWidth: 1,
+ }}
+ />
+
+ navigation.pop()} title="Pop" />
+
+ );
+ }
+}
+
+export default createStackNavigator({
+ ScreenOne,
+ ScreenTwo: withNavigationFocus(ScreenTwo),
+});
diff --git a/packages/react-navigation/src/navigators/createKeyboardAwareNavigator.js b/packages/react-navigation/src/navigators/createKeyboardAwareNavigator.js
new file mode 100644
index 00000000..8da2d5bf
--- /dev/null
+++ b/packages/react-navigation/src/navigators/createKeyboardAwareNavigator.js
@@ -0,0 +1,49 @@
+import React from 'react';
+import { TextInput } from 'react-native';
+
+export default Navigator =>
+ class KeyboardAwareNavigator extends React.Component {
+ static router = Navigator.router;
+ _previouslyFocusedTextInput = null;
+
+ render() {
+ return (
+
+ );
+ }
+
+ _handleGestureBegin = () => {
+ this._previouslyFocusedTextInput = TextInput.State.currentlyFocusedField();
+ if (this._previouslyFocusedTextInput) {
+ TextInput.State.blurTextInput(this._previouslyFocusedTextInput);
+ }
+ this.props.onGestureBegin && this.props.onGestureBegin();
+ };
+
+ _handleGestureCanceled = () => {
+ if (this._previouslyFocusedTextInput) {
+ TextInput.State.focusTextInput(this._previouslyFocusedTextInput);
+ }
+ this.props.onGestureFinish && this.props.onGestureFinish();
+ };
+
+ _handleGestureFinish = () => {
+ this._previouslyFocusedTextInput = null;
+ this.props.onGestureCanceled && this.props.onGestureCanceled();
+ };
+
+ _handleTransitionStart = (transitionProps, prevTransitionProps) => {
+ const currentField = TextInput.State.currentlyFocusedField();
+ if (currentField) {
+ TextInput.State.blurTextInput(currentField);
+ }
+ this.props.onTransitionStart &&
+ this.props.onTransitionStart(transitionProps, prevTransitionProps);
+ };
+ };
diff --git a/packages/react-navigation/src/navigators/createNavigator.js b/packages/react-navigation/src/navigators/createNavigator.js
index 7d0e16d7..6401aeb6 100644
--- a/packages/react-navigation/src/navigators/createNavigator.js
+++ b/packages/react-navigation/src/navigators/createNavigator.js
@@ -95,6 +95,7 @@ function createNavigator(NavigatorView, router, navigationConfig) {
return (
{
this._isResponding = false;
this._reset(index, 0);
+ this.props.onGestureCanceled && this.props.onGestureCanceled();
},
onPanResponderGrant: () => {
position.stopAnimation((value: number) => {
this._isResponding = true;
this._gestureStartValue = value;
});
+ this.props.onGestureBegin && this.props.onGestureBegin();
},
onMoveShouldSetPanResponder: (event, gesture) => {
if (index !== scene.index) {
@@ -345,10 +347,12 @@ class StackViewLayout extends React.Component {
// If the speed of the gesture release is significant, use that as the indication
// of intent
if (gestureVelocity < -0.5) {
+ this.props.onGestureCanceled && this.props.onGestureCanceled();
this._reset(immediateIndex, resetDuration);
return;
}
if (gestureVelocity > 0.5) {
+ this.props.onGestureFinish && this.props.onGestureFinish();
this._goBack(immediateIndex, goBackDuration);
return;
}
@@ -356,8 +360,10 @@ class StackViewLayout extends React.Component {
// Then filter based on the distance the screen was moved. Over a third of the way swiped,
// and the back will happen.
if (value <= index - POSITION_THRESHOLD) {
+ this.props.onGestureFinish && this.props.onGestureFinish();
this._goBack(immediateIndex, goBackDuration);
} else {
+ this.props.onGestureCanceled && this.props.onGestureCanceled();
this._reset(immediateIndex, resetDuration);
}
});