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

[android] Mounting multiple KeyboardAvoidingViews across different mounted screens cause conflicts with one another #267

Closed
erickreutz opened this issue Nov 11, 2023 · 10 comments · Fixed by #268
Assignees
Labels
🤖 android Android specific 🐛 bug Something isn't working 📚 components Anything related to the exported components of this library KeyboardAvoidingView 🧪 Anything related to KeyboardAvoidingView component

Comments

@erickreutz
Copy link

erickreutz commented Nov 11, 2023

  • Screen 1 uses a KeyboardAvoidingView to keep a button fixed at the bottom of the view when the keyboard appears.
  • Screen 1 pushes Screen 2
  • Screen 2 uses a second KeyboardAvoidingView to keep a button fixed at the bottom of the view when the keyboard appears but it does not render correctly due to conflicts with what I assume to be is some shared state from the KeyboardAvodiingView on the previously screen

If I set enabled={false} on screen 1 then screen 2 works as expected.

It should also be noted that this does not seem to be an issue on iOS.

Screen 1 Screen 1 (input focused) Screen 2 Screen 2 (with disabled avoiding view on the previous screen)
image image image image

Both Screen 1 and Screen 2 use the exact same code

import { TextInput, Text, View, StyleSheet } from "react-native";
import { RectButton } from "react-native-gesture-handler";
import { useNavigation, useIsFocused } from "@react-navigation/native";
import { KeyboardAvoidingView } from "react-native-keyboard-controller";

export function ScreenOne() {
  const navigation = useNavigation();

  const onPress = () => {
    navigation.navigate("Two");
  };

  return (
    <KeyboardAvoidingView
      style={{ flex: 1 }}
      behavior="height"
      keyboardVerticalOffset={80}
    >
      <View style={styles.root}>
        <View style={styles.inner}>
          <TextInput style={styles.input} placeholder="Input" />
        </View>
        <RectButton onPress={onPress} style={styles.button}>
          <Text>Next</Text>
        </RectButton>
      </View>
    </KeyboardAvoidingView>
  );
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
  inner: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center",
  },
  input: {
    borderWidth: 1,
    borderColor: "black",
    padding: 8,
    width: "80%",
  },
  button: {
    backgroundColor: "lightblue",
    padding: 24,
    alignItems: "center",
  },
});
import { StyleSheet } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { NotificationsProvider } from "./notifs";
import { KeyboardProvider } from "react-native-keyboard-controller";
import {
  SafeAreaProvider,
  initialWindowMetrics,
} from "react-native-safe-area-context";
import { NavigationContainer } from "@react-navigation/native";
import { RootStack } from "./RootStack";

export default function App() {
  return (
    <SafeAreaProvider initialMetrics={initialWindowMetrics}>
      <GestureHandlerRootView style={styles.root}>
        <KeyboardProvider>
          <NavigationContainer>
            <RootStack />
          </NavigationContainer>
        </KeyboardProvider>
        <NotificationsProvider />
      </GestureHandlerRootView>
    </SafeAreaProvider>
  );
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
  },
});
{
  "name": "my-test-app",
  "version": "1.0.0",
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo run:android",
    "ios": "expo run:ios",
    "web": "expo start --web"
  },
  "dependencies": {
    "@react-navigation/native": "^6.1.9",
    "@react-navigation/native-stack": "^6.9.17",
    "expo": "~49.0.15",
    "expo-dev-client": "~2.4.12",
    "expo-status-bar": "~1.6.0",
    "react": "18.2.0",
    "react-native": "0.72.6",
    "react-native-gesture-handler": "~2.12.0",
    "react-native-keyboard-controller": "^1.9.0",
    "react-native-reanimated": "^3.5.4",
    "react-native-safe-area-context": "4.6.3",
    "react-native-screens": "~3.22.0",
    "expo-splash-screen": "~0.20.5"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0"
  },
  "private": true
}
@erickreutz erickreutz changed the title Mounting multiple KeyboardAvoidingViews across different mounted screens cause conflicts with one another [android] Mounting multiple KeyboardAvoidingViews across different mounted screens cause conflicts with one another Nov 11, 2023
@kirillzyusko kirillzyusko added 🐛 bug Something isn't working 🤖 android Android specific 📚 components Anything related to the exported components of this library labels Nov 11, 2023
@kirillzyusko
Copy link
Owner

Interesting, thank you for your report @erickreutz

Mat I ask you which Android version did you test?

@erickreutz
Copy link
Author

@kirillzyusko Pixel 6 Pro API 31:5554

@kirillzyusko
Copy link
Owner

kirillzyusko commented Nov 12, 2023

@erickreutz Do I correctly understand, that you are using next steps to reproduce?

  • open Screen 1
  • set focus on input
  • press next button (keyboard must be opened)

If yes, then don't you mind to test #268 and see whether the issue has gone for you? 👀

@erickreutz
Copy link
Author

@kirillzyusko yes, you understand correctly - just tried your branch out in my test app and it's working as expected now - thank you!!

@kirillzyusko
Copy link
Owner

@erickreutz awesome! I'll fix CI tomorrow and will try to prepare a new release! 😎

@erickreutz
Copy link
Author

@kirillzyusko Great thank you! also, I thought I should let you know that after further testing the problem still seems to persist if you set autoFocus on the input inside the second screen. If you delay the autoFocus it seems to fix the issue.

@kirillzyusko
Copy link
Owner

I thought I should let you know that after further testing the problem still seems to persist if you set autoFocus on the input inside the second screen.

@erickreutz I can not reproduce it in my example app... Any ideas what I'm doing wrong?

Screen.Recording.2023-11-12.at.21.33.16.mov

@erickreutz
Copy link
Author

@kirillzyusko Hmm yeah I think it may have been a bug in my own code - sorry about that.

@kirillzyusko
Copy link
Owner

@erickreutz ah, okay 🙂 Then I'm going to close this issue - feel free to open new one if you discover any issues 👀

kirillzyusko added a commit that referenced this issue Nov 13, 2023
## 📜 Description

Compare size of keyboard in `onApplyWindowInsets` callback.

## 💡 Motivation and Context

When you have an opened keyboard and you trigger a navigation from
screen A to screen B, then you'll have a race condition between
`onApplyWindowInsets` and `onStart`/`onProgress`/`onEnd` callbacks.

For this particular case `onApplyWindowInsets` shouldn't emit events,
because in this method I'm simply detecting keyboard resize. With old
condition if-statement:

```kt
if (isKeyboardShown && !isMoving && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
```

is evaluated as `true` and because of that we're dispatching unnecessary
events.

Since this method was designed to detect keyboard layout
changes/resizing I thought that it would be good to add additional
condition that compares previous and current height. With this fix the
race condition will gone, because keyboard is not changes its size when
you perform navigation.

Another case that would be solved by this PR is when you open a
keyboard, let your screen to dim (become fully dark/turned off, but not
to be locked yet), and touch it to wake your phone - in this case
`onApplyWindowInsets` is also dispatching and before we were sending
unnecessary events. Now it'll not happen, because size of the keyboard
is the same 🙃

Closes
#267

## 📢 Changelog

### Android
- changed `DEFAULT_ANIMATION_TIME` to int;
- added `isKeyboardSizeEqual` variable;
- used `isKeyboardSizeEqual` variable as `!isKeyboardSizeEqual` in
if-statement.

## 🤔 How Has This Been Tested?

Tested manually on Pixel 3A (API 33).

## 📸 Screenshots (if appropriate):

|Before|After|
|-------|-----|
|<video
src="https://github.com/kirillzyusko/react-native-keyboard-controller/assets/22820318/3c36fe8a-1a73-4f8b-8220-de4757f6fa85">|<video
src="https://github.com/kirillzyusko/react-native-keyboard-controller/assets/22820318/cd4f9c2b-c013-46e5-8e5d-a41e9b5f51f0">|

## 📝 Checklist

- [x] CI successfully passed
@kirillzyusko
Copy link
Owner

Published under 1.9.1 version 👍

@kirillzyusko kirillzyusko added the KeyboardAvoidingView 🧪 Anything related to KeyboardAvoidingView component label May 29, 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 📚 components Anything related to the exported components of this library KeyboardAvoidingView 🧪 Anything related to KeyboardAvoidingView component
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants