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

Inconsistent NavigationBar Behavior with Edge-to-Edge Support (Android) #592

Open
wise-danya opened this issue Sep 22, 2024 · 6 comments
Open
Assignees
Labels
🤖 android Android specific 🐛 bug Something isn't working sponsor 💖 Someone pays money for the issue to be resolved 💸

Comments

@wise-danya
Copy link

wise-danya commented Sep 22, 2024

Description

On Android, when toggling the enabled prop of the <KeyboardProvider /> using useKeyboardController, the system NavigationBar behaves inconsistently due to the handling of edge-to-edge mode:

When enabled is set to true, the NavigationBar disappears.
When enabled is set to false, the NavigationBar reappears and causes layout issues by lifting the content.
This issue is primarily caused by toggling edge-to-edge mode in the enable() and disable() methods.

Steps to reproduce the behavior

  1. Integrate react-native-keyboard-controller and react-native-bars into a project.
  2. Use the useKeyboardController hook to toggle the enabled prop of <KeyboardProvider />.
  3. Toggle enabled to true, observe that the system NavigationBar disappears.
  4. Toggle enabled to false, observe that the NavigationBar reappears and lifts the content.

Expected behavior

The NavigationBar should remain hidden or translucent and should not affect the layout or content positioning, regardless of the enabled state of the KeyboardProvider.

Repo for reproducing

You can find a minimal reproducible example for this issue in the following repository: https://github.com/wise-danya/rn-keyboard-controller-issue-example

Video Demo

rnkc-demo.mov

Additional context

On some screens, when the library is disabled, the NavigationBar is visible and causes the content to be lifted, leading to layout issues. This makes it hard to gradually adopt the library across multiple screens in an app.

Environment:

  • Desktop OS: MacOS 14.6.1
  • Device: Pixel 7
  • OS: Android 14
  • RN version: Example: 0.75.3; Prod: 0.73.9
  • RN architecture: New Architecture (Fabric)
  • JS engine: Hermes
  • Library version: 1.13.4
@kirillzyusko
Copy link
Owner

Hey @wise-danya 👋 Thank you for providing a repo and high quality issue description!

Before digging into internals may I ask you what is the purpose of disabling the module and keeping edge-to-edge mode?

Basically the feature with disabling module was added to fallback to default Android adjustResize behavior. But if you keep edge-to-edge and just remove keyboard callbacks from native side, then you still will have edge-to-edge mode (i. e. automatic window resize will be disabled). So I'm kind of curious what is it the purpose you are following when trying to keep edge-to-edge and remove keyboard callbacks?

@kirillzyusko kirillzyusko added 🐛 bug Something isn't working 🤖 android Android specific labels Sep 23, 2024
@wise-danya
Copy link
Author

Hey @kirillzyusko 👋 Thanks for the quick response and for digging into the issue! Let me provide some additional context to clarify.

Purpose of Keeping Edge-to-Edge Mode

The reason I want to keep edge-to-edge mode enabled while using react-native-keyboard-controller is due to the usage of another library, react-native-bars. This library solves several issues with system bars and requires EdgeToEdge theme configuration in the styles.xml file:

<resources>
  <style name="AppTheme" parent="Theme.EdgeToEdge">
    <!---->
  </style>
</resources>

Since the app’s theme relies on EdgeToEdge, the NavigationBar is expected to stay hidden or translucent. When react-native-keyboard-controller disables edge-to-edge mode, it conflicts with this setup and causes the NavigationBar to reappear, disrupting the layout.

Purpose of Disabling the Module

As for disabling the module, the main reason is to allow a gradual adaptation of react-native-keyboard-controller in the project. Not all screens are ready to use the keyboard controller functionality, and enabling the module across the app requires careful adjustments for various keyboard usage scenarios.

I’m following the approach you suggested for gradual adoption:

"If you want to gradually adopt the functionality of this package in your app, you can use useKeyboardController hook. You can disable the module by default <KeyboardProvider enabled={false}> and enable it using useKeyboardController on screens where you want to use it."

This allows me to selectively enable the module on screens where it has been fully integrated, while the rest of the app uses the default Android adjustResize behavior. However, the conflict between react-native-bars and react-native-keyboard-controller prevents me from providing a consistent visual experience across all screens.

Any workaround or ideas on how to avoid this conflict between the two libraries would be really helpful!

@kirillzyusko
Copy link
Owner

kirillzyusko commented Sep 24, 2024

Thanks @wise-danya

I think now I understand better what you are trying to achieve.

Let me dig a little bit in internals and ask you about how you are going to handle them in your app 👀

1️⃣ edge-to-edge + adjustResize

As you pointed out earlier when you add react-native-bars it enters edge-to-edge mode. But when you refer to adjustResize behavior - it's kind of not truth adjustResize 😓

By default in Android when you activate edge-to-edge and adjustResize then you window will stop resizing 😱 react-native-bars handles that by implementing an additional logic and simulating old adjustResize behavior:

https://github.com/zoontek/react-native-bars/blob/2a7396f58282e30475a794e303a9fe93a920250d/android/src/main/java/com/zoontek/rnbars/RNBarsModuleImpl.java#L44-L63

So when you call RNBars.init(this); the Android OS doesn't resize the window, but react-native-bar will do that for you.

2️⃣ react-native-keyboard-controller integration

By default, when you wrap app with KeyboardProvider it shouldn't change the behavior (since you already in edge-to-edge mode). But! react-native-keyboard-controller and react-native-bars both setup onApplyWindowInsetsListener to rootView/decorView which may conflict with each other (i. e. when you disable/enable react-native-keyboard-controller).

3️⃣ react-native-bars keyboard handling

Honestly I didn't have a chance to clone your project and run reproduction. But I think there will be a conflict anyway, let's consider following cases:

  • disabling/enabling react-native-keyboard-controller overwrites the callback which is set by react-native-bars - in this case when you disable react-native-keyboard-controller keyboard handling will stop to work in the app at all;
  • disabling/enabling react-native-keyboard-controller doesn't overwrite the callback which is set by react-native-bars - in this case you will not be able to integrate react-native-keyboard-controller selectively, because react-native-bars will apply default keyboard handling on all screens (and you don't have an ability to turn it on/off on demand, right?). So if you add keyboard handling from (let's say you'll start to use KeyboardAvoidingView from RNKC), then there are high chances that both modules will handle keyboard appearance at the same time and you will get double resize 🙈

So taking all these restrictions I don't think it's really possible to use setEnabled method if you use react-native-bars 😔

The potential solution would be to disable react-native-bars keyboard handling (by RNBars.init(this, false)). It will not resize the window anymore. And then you can wrap your entire app with KeyboardAvoidingView - theoretically it will give you the same behavior as you had before (just with one small difference - keyboard transitions will be animated now). And then you can write your own solution for disabling global KeyboardAvoidingView and usage of specific keyboard handling on specific screens.

Let me know what do you think and whether we are on the same page or not 😅 I'll clone your project to play around it later today 👀

@RayKay91
Copy link

Hi, I think the issue was introduced from react-native-screens version 3.32.0. There is a fix that has been merged but not released yet that you can cherry pick to make it work as before, or patch it yourself with patch-package. I was getting a strange inset above and below that this PR fixes:

software-mansion/react-native-screens#2301

@kirillzyusko
Copy link
Owner

@RayKay91 the provided example doesn't use react-native-screens directly or native-stack navigator in any form, so I highly doubt that it can be fixed by changes from react-native-screens (just because react-native-screens is not used in reproduction example).

@kirillzyusko kirillzyusko added the sponsor 💖 Someone pays money for the issue to be resolved 💸 label Oct 9, 2024
@kirillzyusko
Copy link
Owner

@wise-danya did you solve this problem? Can I close this issue? 👀

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 sponsor 💖 Someone pays money for the issue to be resolved 💸
Projects
None yet
Development

No branches or pull requests

3 participants