Browse files

BREAKING: Fix modal resizing on keyboard show

This changes modal behavior to resize when the keyboard appears/disappears.
Previously, the modal would not react in any way, or it would pan above to bring the
TextInput into view. Resizing is the correct behavior for android.

This is not trivial, as in, setting the flag, because of the combination of
react native laying out all views and the system reacting to the keyboard
appearance in a weird way. Namely:
- if `windowTranslucentStatus` is not set, the system will just call
  `onSizeChanged` on the dialog's content view, and everything works nicely
- with `windowTranslucentStatus` set, the system will consider the dialog as a
  full screen view that doesn't resize. In order for it to resize, the base
  view of the layout needs to have
  `setFitsSystemWindows(true)` called on it. This is needed, so that the system
  can call layout on that base view with the new value of `paddingBottom` that
  coincides with the height of the keyboard. Neat.

We fix this by wrapping our existing content view (mHostView) in a simple
FrameLayout that has `setFitsSystemWindows` set. That way, `mHostView` will have
`onSizeChanged` called on itself with the correct new size of the dialog.

This has the fortunate consequence of our layout now also getting `paddingTop` as the size of the
status bar, which means that we can remove the JS `top` hack in Modal, which
was necessary for no view getting drawn under the status bar.

This behavior is set as default, since that is the default correct Android behavior.

Reviewed By: astreet

Differential Revision: D3913784

fbshipit-source-id: 4378ada21f466dc7ac6e357abeca10b88009ca3f
  • Loading branch information...
1 parent fc62b00 commit 404b7cc069471cc8e0277d398751305665f0d3e1 @andreicoman11 andreicoman11 committed with Facebook Github Bot 1 Sep 28, 2016
@@ -16,10 +16,9 @@ const Platform = require('Platform');
const PropTypes = require('react/lib/ReactPropTypes');
const React = require('React');
const StyleSheet = require('StyleSheet');
-const UIManager = require('UIManager');
const View = require('View');
-const deprecatedPropType = require('deprecatedPropType');
+const deprecatedPropType = require('deprecatedPropType');
const requireNativeComponent = require('requireNativeComponent');
const RCTModalHostView = requireNativeComponent('RCTModalHostView', null);
@@ -136,7 +135,6 @@ class Modal extends React.Component {
const containerStyles = {
backgroundColor: this.props.transparent ? 'transparent' : 'white',
- top: Platform.OS === 'android' && Platform.Version >= 19 ? UIManager.RCTModalHostView.Constants.StatusBarHeight : 0,
let animationType = this.props.animationType;
@@ -17,13 +17,13 @@
import android.content.Context;
import android.content.DialogInterface;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
+import android.widget.FrameLayout;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.R;
@@ -204,7 +204,7 @@ protected void showOrUpdate() {
mDialog = new Dialog(getContext(), theme);
- mDialog.setContentView(mHostView);
+ mDialog.setContentView(getContentView());
@@ -236,10 +236,24 @@ public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
+ mDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);;
+ * Returns the view that will be the root view of the dialog. We are wrapping this in a
+ * FrameLayout because this is the system's way of notifying us that the dialog size has changed.
+ * This has the pleasant side-effect of us not having to preface all Modals with
+ * "top: statusBarHeight", since that margin will be included in the FrameLayout.
+ */
+ private View getContentView() {
+ FrameLayout frameLayout = new FrameLayout(getContext());
+ frameLayout.addView(mHostView);
+ frameLayout.setFitsSystemWindows(true);
+ return frameLayout;
+ }
+ /**
* updateProperties will update the properties that do not require us to recreate the dialog
* Properties that do require us to recreate the dialog should set mPropertyRequiresNewDialog to
* true when the property changes
@@ -284,9 +298,8 @@ protected void onSizeChanged(final int w, final int h, int oldw, int oldh) {
new Runnable() {
public void run() {
- Point modalSize = ModalHostHelper.getModalHostSize(getContext());
((ReactContext) getContext()).getNativeModule(UIManagerModule.class)
- .updateNodeSize(getChildAt(0).getId(), modalSize.x, modalSize.y);
+ .updateNodeSize(getChildAt(0).getId(), w, h);

0 comments on commit 404b7cc

Please sign in to comment.