Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Opening Modal prevents proper resizing of layout #387

Open
krystianhub opened this issue Mar 15, 2024 · 3 comments
Open

Opening Modal prevents proper resizing of layout #387

krystianhub opened this issue Mar 15, 2024 · 3 comments
Assignees
Labels
🤖 android Android specific 🐛 bug Something isn't working 🌎 modal Anything that involves Modal usage

Comments

@krystianhub
Copy link

Describe the bug
When the keyboard is open, launching a Modal (from either the react-native or react-native-modal package) results in the keyboard closing but without correctly adjusting the layout size.

Code snippet

import { useState } from 'react';
import { Button,  FlatList,  StyleSheet, Text, TextInput, View } from 'react-native';
import { KeyboardAwareScrollView, KeyboardProvider } from 'react-native-keyboard-controller';
import Modal from 'react-native-modal';

const data = Array.from(Array(50).keys());

export default function App() {
  const [modalVisible, setModalVisible] = useState(false);

  const onPress = () => {
    // Partial workaround: uncomment me and set Modal coverScreen to false
    // KeyboardController.dismiss();
    setModalVisible(true);
  };

  return (
    <KeyboardProvider>

      <Modal isVisible={modalVisible} coverScreen={true}>
        <View style={{flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: 'black'}}>
          <Text style={{color: 'white'}}>Modal content here</Text>

          <Button title='Close modal' onPress={() => setModalVisible(false)} />
        </View>
      </Modal>

      <KeyboardAwareScrollView contentContainerStyle={styles.container} keyboardShouldPersistTaps={"always"}>

        <View style={{flex: 1}} />

        <Button title="Open modal"  onPress={onPress}/>


        <FlatList scrollEnabled={false} data={data} style={{height: 500}} renderItem={({index}) => <Text style={{textAlign: 'center'}}>{index}</Text>}/>

        <TextInput placeholder='text input' style={{backgroundColor: 'white', padding: 10}} />

        <View style={{flex: 1}} />

      </KeyboardAwareScrollView>
    </KeyboardProvider>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'white',
    alignItems: 'center',
    justifyContent: 'center',
    padding: 50,
  },
});

Repo for reproducing
https://github.com/krystianhub/RN-Modal-Issue - Expo Managed workflow; requires compiled app with expo-dev-client.
I can provide the apk if needed. Alternatively, to build it locally yourself:

eas build --profile development --platform android --local

To Reproduce
Steps to reproduce the behavior:

  1. Tap on text input element
  2. Keyboard is open
  3. Tap on open modal
  4. Modal is visible, keyboard is closed
  5. Tap on close modal
  6. Layout is incorrectly shifted upwards

Expected behavior
Keyboard should close and properly resize layout back to its default height.
Video of expected behaviour (recorded with disabled Keyboard Provider, same as default RN behaviour):
https://github.com/kirillzyusko/react-native-keyboard-controller/assets/1358334/afb4ac15-82a9-4e3e-86eb-a5524d50a4ba

Screenshots
Video of the current behaviour:
https://github.com/kirillzyusko/react-native-keyboard-controller/assets/1358334/ca572fdd-72f9-406d-88cd-cc3070a3a1e6

Smartphone (please complete the following information):

  • Desktop OS: Fedora Linux 39
  • Device: Xiaomi Mi 10T Pro 5G
  • OS: Android 13
  • RN version: 0.73.5
  • RN architecture: fabric
  • JS engine: Hermes
  • Library version: 1.11.3

Additional context
I discovered a partial solution by adjusting the "coverScreen" attribute to "false" within the Modal element and manually dismissing the keyboard when pressing the "Open Modal" button simultaneously. Nonetheless, when dealing with navigation and headers, the opacity of the Modal's background only extends to the parent view rather than the entire screen. Regrettably, this workaround doesn't fully meet my expectations.

Video of the workaround:
https://github.com/kirillzyusko/react-native-keyboard-controller/assets/1358334/bd90fd3d-aa04-44e7-ad99-e308e5c9f448

@krystianhub
Copy link
Author

I also attempted to use the KeyboardAvoidingView component instead, but encountered the same issue. It appears that when the Modal is displayed (with coverScreen=true), the animation breaks, leaving layout in the same position as when keyboard is open.

@krystianhub krystianhub changed the title Opening Modal preventing proper resizing of layout Opening Modal prevents proper resizing of layout Mar 15, 2024
@kirillzyusko kirillzyusko added 🐛 bug Something isn't working 🤖 android Android specific labels Mar 15, 2024
@kirillzyusko
Copy link
Owner

kirillzyusko commented Mar 16, 2024

Hi @krystianhub

Thank you for opening the issue ❤️

Indeed this is kind of known issue. The problem is that as soon as modal appears it handles keyboard on its own way and prevents events propagation to the root view - as a result I'm not receiving events that keyboard is hidden and because of that you see a space which is equal to keyboard size (even if keyboard is already closed).

The fix should be implemented on a native side, but right now there is no way to get a window of modal in RN (see #369 for more details).

So as a workaround you have 2 choices:

  • do not use Modal from react-native (react-native-modal is also based on implementation from RN) - you can create your own custom modals using portals -> in this case everything will live under the same rootView and all keyboard events will be dispatched correctly;
  • close a keyboard before modal gets shown - we wrote such helper (located in keyboard.ts):
import {
  KeyboardController,
  KeyboardEvents,
} from "react-native-keyboard-controller";

let isClosed = true;

KeyboardEvents.addListener("keyboardDidHide", _ => {
  isClosed = true;
});
KeyboardEvents.addListener("keyboardDidShow", _ => {
  isClosed = false;
});

const utils = {
  waitToBeClosed: () =>
    new Promise(resolve => {
      if (isClosed) {
        resolve(undefined);
        return;
      }

      const subscription = KeyboardEvents.addListener("keyboardDidHide", _ => {
        resolve(undefined);
        subscription.remove();
      });
      KeyboardController.dismiss();
    }),
};

export default utils;

and the before showing a modal we use it like:

import KeyboardUtils from './keyboard';
// ...
const onPress = async () => {
  await KeyboardUtils.waitToBeClosed();
  setModalVisible(true);
};

This is not the best solution, but gives quite pleasant UI and UX so... as a temporary solution we use it 🙂

As I said before - I'm planning to fix the integration with Modal windows, but it'll take some time, because I'll need to change a native code in Modal component from react-native (and it'll take time to review/merge my changes).

@krystianhub
Copy link
Author

Thank you for your quick response.
I had been considering a similar solution to yours (an async helper that waits until the keyboard is fully hidden), but your implementation is better. After some initial testing, I'm pleased with the result!

Thanks!

@kirillzyusko kirillzyusko added the 🌎 modal Anything that involves Modal usage label Mar 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🤖 android Android specific 🐛 bug Something isn't working 🌎 modal Anything that involves Modal usage
Projects
None yet
Development

No branches or pull requests

2 participants