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

Animated keyboard values do not update if the focussed TextInput exists within a modal #369

Open
Braden1996 opened this issue Feb 25, 2024 · 14 comments
Assignees
Labels
🤖 android Android specific 🐛 bug Something isn't working 🌎 modal Anything that involves Modal usage

Comments

@Braden1996
Copy link

Braden1996 commented Feb 25, 2024

Describe the bug
Animated keyboard values do not update if the focussed TextInput exists within a modal.

Code snippet
Repo for reproducing
Here is a test case I've been using which plugs into your example app:

import React, { useState } from "react";
import { Button, Modal, StyleSheet, View } from "react-native";
import {
  GestureHandlerRootView,
  TouchableWithoutFeedback,
} from "react-native-gesture-handler";
import { KeyboardProvider } from "react-native-keyboard-controller";

import TextInput from "../../../components/TextInput";
import KeyboardAnimation from "../KeyboardAnimation";

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

  return (
    <View style={styles.container}>
      <Button
        title={"Show Modal"}
        onPress={() => setModalVisible(true)}
        testID="toggle_button"
      />

      <View style={styles.animationContainer}>
        <KeyboardAnimation />
      </View>

      <Modal
        transparent
        visible={modalVisible}
        onDismiss={() => setModalVisible(false)}
        onRequestClose={() => setModalVisible(false)}
      >
        <GestureHandlerRootView style={styles.modalContainer}>
            <TouchableWithoutFeedback onPress={() => setModalVisible(false)}>
              <TextInput
                testID="modal_example_text_input"
                style={styles.textInput}
              />
            </TouchableWithoutFeedback>
        </GestureHandlerRootView>
      </Modal>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "stretch",
  },
  modalContainer: {
    flex: 1,
    backgroundColor: "rgba(0,0,0,0.2)",
    justifyContent: "center",
    alignItems: "center",
  },
  animationContainer: {
    flex: 1,
  },
  textInput: {
    width: 200,
    marginTop: 50,
    height: 50,
    backgroundColor: "yellow",
  },
});

To Reproduce
Steps to reproduce the behavior:

  1. Create a TextInput element inside of a React Native Modal.
  2. See that any keyboard height observers no longer seem to receive updates.

Expected behavior
RN Keyboard Controller's utilities should continue to work for TextInputs inside Modals.

Screenshots

Screen.Feb.25.mp4
@kirillzyusko
Copy link
Owner

@Braden1996 interesting, I will have a look on it on Monday 👀

@kirillzyusko kirillzyusko added 🐛 bug Something isn't working 🤖 android Android specific labels Feb 25, 2024
@kirillzyusko
Copy link
Owner

@Braden1996 one small question/ask from my side - can you add info about your env? I.e. which library version was used, which Android OS version was used, etc 🙏

Also did you test only Android 14? Have you checked the behavior on Android 9/10?

@Braden1996
Copy link
Author

@Braden1996 one small question/ask from my side - can you add info about your env? I.e. which library version was used, which Android OS version was used, etc 🙏

Also did you test only Android 14? Have you checked the behavior on Android 9/10?

Hey! Appreciate the quick reply.

I tested this on my physical Pixel 6a Android 13 device as well as a Pixel 7 Pro Android 14 in an Android Studio emulator.
The library version I used was 1.11.1, although I've noticed this issue for a few major release (we use this library in production at Attio for our bottom sheets). The video recording I sent was from cloning this repo last night.
Additionally, I've noticed Reanimated's useAnimatedKeyboard suffers from the same issue.

@kirillzyusko
Copy link
Owner

I quickly checked - the issue happens because a Modal lives in a separate window:

image

And this window handles insets on its own - it even doesn't have an enabled edge-to-edge mode 🤔 And the problem is that it's kind of 3rd party component (provided by RN, but not by this library) so it's not very easy to get an access to that window.

But anyway I'll try to search for solutions 👀

@kirillzyusko
Copy link
Owner

@Braden1996 one small question - do you expect that views which are behind modal will be moving? Or you need to move elements inside of the modal?

I'm just thinking about a possibility to mount KeyboardProvider inside of the Modal, but it looks like that elements outside of the Modal will not get any events in this case 🤔

@Braden1996
Copy link
Author

@Braden1996 one small question - do you expect that views which are behind modal will be moving? Or you need to move elements inside of the modal?

I'm just thinking about a possibility to mount KeyboardProvider inside of the Modal, but it looks like that elements outside of the Modal will not get any events in this case 🤔

That's a good question!

Product-wise, I think that most of the time views behind the modal shouldn't react to a keyboard focussed within the modal.

However, in terms of developer experience, I'd prefer it if all the keyboard events and reanimated values were global and it was the consumer's job to customise how components should behave. It's hard enough thinking about a global keyboard state, let alone numerous individual keyboard states all interweaving.

In our specific case, we have bottom-sheets rendered inside a Modal but also as a separate React Navigation screen. This is great for composability. But would become tricky to reason about if we now had diverging "keyboard side-effects".

Also, it's fairly easy to toggle a reaction based on an expected TextInput being focused than it is to communicate keyboard state back in and out of Modals.

@kirillzyusko
Copy link
Owner

@Braden1996 just a quick update - I spent some time, and unfortunately right now there is no way to get mDialog variable and access a window.

I've had a look on other repositories who is using edge-to-edge mode and they recommend to not use Modal from react-native and instead create your own solution in JS (using portals for example).

While this is an intermediate solution I still feel like it's more like a workaround, rather than a real fix. I'll try to cooperate with FB team and propose several solutions to them on how to fix this concrete issue. But right now it looks like it'll not be resolved anytime soon (at least fix will be available only for new react-native versions, and it typically requires 3 months to prepare a new release) 😓

@Braden1996
Copy link
Author

@Braden1996 just a quick update - I spent some time, and unfortunately right now there is no way to get mDialog variable and access a window.

I've had a look on other repositories who is using edge-to-edge mode and they recommend to not use Modal from react-native and instead create your own solution in JS (using portals for example).

While this is an intermediate solution I still feel like it's more like a workaround, rather than a real fix. I'll try to cooperate with FB team and propose several solutions to them on how to fix this concrete issue. But right now it looks like it'll not be resolved anytime soon (at least fix will be available only for new react-native versions, and it typically requires 3 months to prepare a new release) 😓

Hey @kirillzyusko, thanks for the update - appreciate it!

Shame to hear about it not being a quick fix. Regarding your suggestion of Portals, unfortunately they aren't available in React Native and the open-source solutions are a bit of awkward as they just use a context to hoist your React nodes higher in the tree (which has nasty side effects like escaping out of parent context).

@kirillzyusko
Copy link
Owner

@Braden1996 actually a real implementation of portal does exist in RN world, for example - https://github.com/mfrachet/rn-native-portals 👀

@kirillzyusko kirillzyusko added the 🌎 modal Anything that involves Modal usage label Mar 20, 2024
@kirillzyusko
Copy link
Owner

A small update from my side - I've seen ReactModalHostView has been converted to kotlin code and now inner class DialogRootViewGroup is a public one and it looks like it has a reference to its parent class (i. e. it's not static anymore).

I hope I can experiment with these changes further and I hope we can overcome this problem 💪

@thespacemanatee
Copy link

@kirillzyusko Does this sister PR from react-native-reanimated help? software-mansion/react-native-reanimated#5916

@kirillzyusko
Copy link
Owner

kirillzyusko commented Apr 29, 2024

Hey @thespacemanatee

Thank you for the link.

This approach will work, but on Fabric I can not resolve a correct tag (it will crash app immediately) 😔 I'll see what I can do here.

But in general I don't like the appraoch proposed in the PR just because we rely on transitive dependencies (Modal is mounted -> it dispatches onShow event -> we attach a listener to all RN events and listen to onShow -> if onShow has been dispatched then we look up into a View and attach our keyboard callbacks to this view). Though I understand that in current codebase it's the only one way to achieve the desired result.

But anyway, on fabric it crashes the app as soon as modal gets present.

And yeah, thank you for the link and reminder 👀

@Braden1996
Copy link
Author

@Braden1996 actually a real implementation of portal does exist in RN world, for example - https://github.com/mfrachet/rn-native-portals 👀

I forgot to mention, but I tried this library (and some similarly slightly more recently updated fork) and it didn't seem to work at all (portal content simply didn't appear). I didn't investigate further, however. Although proper Portals would be great!

@kirillzyusko
Copy link
Owner

@Braden1996 well, yeah, maybe - I used this library a year ago and it was working, but it's not maintained, so yeah, maybe something is broken and not compatible with new RN APIs

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

3 participants