kirillzyusko / react-native-keyboard-controller

Keyboard manager which works in identical way on both iOS and Android
https://kirillzyusko.github.io/react-native-keyboard-controller/
MIT License
1.54k stars 61 forks source link

KeyboardProvider changes windowSoftInputMode #378

Closed itsramiel closed 5 months ago

itsramiel commented 5 months ago

Describe the bug I was following the installation instructions and one of them is to wrap the app with KeyboardProvider, but after I did that inputs started acting as if windowSoftInput was changed from adjustResize(default setting) to adjustPan. I am not sure if that is exactly the case, but why the different behavior? I did not mean that. What I expected is that the KeyboardProvider is there to manage and connect to other pieces of the library that I use throught the app but not to change the behavior of the app immediately.

My use case is that I want to use the KeyboardAvoidingView component in one of my screens, so I followed the installation instructions and now all inputs in the app are no longer causing the window to resize.

Here is a small repro I did, simply adding the provider no longer resizes the window:

function App() {

  return (
    <KeyboardProvider>
      <View style={{ flex: 1 }}>
        <Pressable
          style={{ flex: 1, backgroundColor: 'yellow' }}
          onPress={() => KeyboardController.dismiss()}
        />
        <TextInput style={{ padding: 48, backgroundColor: 'green' }} />
      </View>
    </KeyboardProvider>
  );
}

export default App;

https://github.com/kirillzyusko/react-native-keyboard-controller/assets/80689446/e8480b7f-221f-42a2-bc4a-efaba27d14e7

Another weird thing that I do not understand is that when I explicitly set the windowSoftInputMode to adjustPan(because I wanted to use the KeyboardAvoidingView and I want the window not to resize) then the window resizes, which is the opposite of what I was looking for:

function App() {
  useEffect(() => {
    KeyboardController.setInputMode(
      AndroidSoftInputModes.SOFT_INPUT_ADJUST_PAN,
    );

    return () => {
      KeyboardController.setDefaultMode();
    };
  }, []);

  return (
    <KeyboardProvider>
      <View style={{ flex: 1 }}>
        <Pressable
          style={{ flex: 1, backgroundColor: 'yellow' }}
          onPress={() => KeyboardController.dismiss()}
        />
        <TextInput style={{ padding: 48, backgroundColor: 'green' }} />
      </View>
    </KeyboardProvider>
  );
}

https://github.com/kirillzyusko/react-native-keyboard-controller/assets/80689446/911d544d-ecba-49b4-91c9-f6c0f200a80d

Smartphone (please complete the following information):

kirillzyusko commented 5 months ago

Hey @itsramiel

When you wrap app in KeyboardProvider it will move app in edge-to-edge mode on Android (and in this mode app is not resizing window automatically - this is your first video).

[!NOTE] If you want to return to default behavior, then you'll need to use useKeyboardController and disable/enable module where you need calling setEnabled(false) or setEnabled(true) (on Provider level you can disable module initially and enable only where you need).

Regarding adjustPan behavior - yes it'll affect how window is resized. Again if you want to match iOS behavior and write cross platform code, you need to go to edge-to-edge mode and set windowSoftInputMode to adjustResize (and in this case you need to handle keyboard appearance on your own).

I hope I answered on your question - but if not, then please feel free to ask additional questions 😊

Maybe documentation can be somehow improved?

itsramiel commented 5 months ago

Hey @kirillzyusko

Hmm okay so I think I got the first point and that is when you wrap the app with the provider that app stops resizing because that how android behaves in edge to edge.

However I still don't get the second point. If wrapping the app with the provider stops the resizing why does it go back to resizing when I was setting the windowSoftInputMode to adjustPan? From what I understood that windowSoftInputMode will not longer affect and it will always pan.

Now just a general comment here. I think integrating this library to an existing brownfield project is quite cumbersome annoying. I would have loved if the library only affects where it is used. If I need a KeyboardAvoidView then I just use it in that screen while knowing my windowSoftInputMode is not altered. I see how this can be worked around by enablind/disabling the module but yeah I would have loved it if it was less intrusive

kirillzyusko commented 5 months ago

However I still don't get the second point. If wrapping the app with the provider stops the resizing why does it go back to resizing when I was setting the windowSoftInputMode to adjustPan? From what I understood that windowSoftInputMode will not longer affect and it will always pan.

Actually it will affect. adjustPan will move a content. To understand the difference between soft input modes you can have a look on this post.

When you enter edge-to-edge and specify adjustResize -> window will not be resized anymore:

https://github.com/kirillzyusko/react-native-keyboard-controller/assets/22820318/9a123f73-1685-464e-a0ce-27449e2189dc

But If you are in edge-to-edge and use adjustPan - it will translate the content of the screen:

https://github.com/kirillzyusko/react-native-keyboard-controller/assets/22820318/4f2df94b-cb16-4f96-861a-561af1992468

I think I wrote slightly incorrect information in documentation? Would you mind to point me to the documentation where I wrote such information (that window with edge-to-edge and adjustPan will not move the content? I remember I wrote something somewhere, but can not find it right now...).

Now just a general comment here. I think integrating this library to an existing brownfield project is quite cumbersome annoying. I would have loved if the library only affects where it is used. If I need a KeyboardAvoidView then I just use it in that screen while knowing my windowSoftInputMode is not altered. I see how this can be worked around by enablind/disabling the module but yeah I would have loved it if it was less intrusive

The problem is that in react-native you can not simply rely on the component usage. For example if you are using react-navigation and use KeyboardAvoidingView on screen A. Then you go in stack navigator to screen B (and in this screen you want to use default resizing mechanisms). On screen A the KeyboardAvoidingView is still mounted - does it mean that you want to use library functionality? No, because you want to use default resizing mechanism based on windowSoftInputMode.

That's why this library provides primitives (enabling/disabling module, setting windowSoftInputMode in runtime) so that you can implement any logic inside your app (with any navigation library) and assure a gradual adoption.

If you want to write the component that will enable library when it gets mounted, you can create your own component, for example:

const KeyboardAvoidingViewWithAutoActivation = ({children}) => {
  const {setEnabled} = useKeyboardController();

  // or any other condition for activation, for example when screen becomes focused/unfocused
  useEffect(() => {
    setEnabled(true);

    return () => setEnabled(false);
  }, []);

  return (
    <KeyboardAvoidingView>{children}</KeyboardAvoidingView>
  )
}
itsramiel commented 5 months ago

Thank you @kirillzyusko. I think I had a wrong understanding of adjustPan. And I think I get your point for the goal of the library.

Thank your for the clarification 🙏. I'll be closing this as it is not really a bug.

kirillzyusko commented 5 months ago

@itsramiel my pleasure 😊

By the way, if you have an suggestions for documentation improvements - I'll be happy to hear them 😊