react-native-keyboard-controller icon indicating copy to clipboard operation
react-native-keyboard-controller copied to clipboard

Multiline TextInput Auto-growing and Scrolling Behavior Issues with KeyboardAwareScrollView

Open NyoriK opened this issue 11 months ago • 8 comments

Overview I've identified several inconsistencies in the TextInput component's auto-growing behavior when used with KeyboardAwareScrollView, affecting both iOS and Android platforms. These issues vary depending on prop configurations and component wrapping.

Detailed Findings

Base Case (No value/onChangeText props)

  • TextInput doesn't auto-grow regardless of multiline prop
  • Height only respects minHeight or height style properties
  • Behavior consistent across iOS and Android

https://github.com/user-attachments/assets/2f822e38-a325-45b2-936d-64f700c7cbb6

With value and onChangeText props

  1. Height Behavior:
  • Respects minHeight and auto-grows after reaching minimum
  • Fixed height when explicitly set; no auto-growing beyond specified height
  1. Scrolling Issues:
  • Noticeable "jumpy" behavior when:

    • Cursor reaches end of last line
    • First character on new line triggers internal scroll
    • Second character triggers height auto-grow and ScrollView adjustment

https://github.com/user-attachments/assets/dc777281-5c3a-44c6-800b-810d45170821

  • Less noticeable when cursor is in middle of content

TextInput Wrapped in View

  • Auto-growing works without value/onChangeText props
  • Platform-specific differences:
    • iOS: Minimal jumping, hardly noticeable
    • Android: Jumping behavior persists, similar to non-wrapped version

https://github.com/user-attachments/assets/8de0311e-15d3-43eb-9d50-f7fd09d81f47

Comparison with React Native's ScrollView Native ScrollView demonstrates superior behavior:

  • Consistent auto-growing without requiring value/onChangeText props
  • Significantly reduced jumping compared to KeyboardAwareScrollView with View wrapper

https://github.com/user-attachments/assets/645eadd1-dc1a-40b3-be95-a53005e00254

Code Examples I've included three implementation scenarios to demonstrate these issues and one with react native ScrollView:

  1. Basic Implementation
<KeyboardAwareScrollView>
  <TextInput 
    multiline
    placeholder="TextInput"
    placeholderTextColor="black"
    style={styles.textInput}
  />
</KeyboardAwareScrollView>
  1. With Props
<KeyboardAwareScrollView>
  <TextInput 
    multiline
    value={value}
    onChangeText={onChange}
    placeholder="TextInput"
    placeholderTextColor="black"
    style={styles.textInput}
  />
</KeyboardAwareScrollView>
  1. With View Wrapper
<KeyboardAwareScrollView>
  <View>
  <TextInput 
    multiline
    placeholder="TextInput"
    placeholderTextColor="black"
    style={styles.textInput}
  />
  </View>
</KeyboardAwareScrollView>
  1. React Native ScrollView
<ScrollView>
  <TextInput 
    multiline
    placeholder="TextInput"
    placeholderTextColor="black"
    style={styles.textInput}
  />
</ScrollView>

Devices tested: Android:

  • Physical: Redmi Note 10 Pro
  • Emulator: Pixel_8_API_35

iOS:

  • Physical: iPhone 15

Development Environment:

  • Desktop OS: MacOS Sonoma

Environment:

  • expo: "~52.0.28"
  • expo: "~52.0.28"
  • react-native: "0.76.6"
  • react-native-keyboard-controller: "^1.16.1"

NyoriK avatar Feb 01 '25 12:02 NyoriK

@NyoriK do you test new architecture or old?

kirillzyusko avatar Feb 01 '25 14:02 kirillzyusko

@kirillzyusko Yes.

app.json:

{
    "expo": {
        "name": "multiline-app",
        "slug": "multiline-app",
        "version": "1.0.0",
        "orientation": "portrait",
        "icon": "./assets/images/icon.png",
        "scheme": "myapp",
        "userInterfaceStyle": "automatic",
        "newArchEnabled": true,
        "ios": {
            "supportsTablet": true,
            "bundleIdentifier": "com.knyoridev.multiline-app"
        },
        "android": {
            "adaptiveIcon": {
                "foregroundImage": "./assets/images/adaptive-icon.png",
                "backgroundColor": "#ffffff"
            },
            "package": "com.knyoridev.multilineapp"
        },
        "web": {
            "bundler": "metro",
            "output": "static",
            "favicon": "./assets/images/favicon.png"
        },
        "plugins": [
            "expo-router",
            [
                "expo-splash-screen",
                {
                    "image": "./assets/images/splash-icon.png",
                    "imageWidth": 200,
                    "resizeMode": "contain",
                    "backgroundColor": "#ffffff"
                }
            ]
        ],
        "experiments": {
            "typedRoutes": true
        }
    }
}

NyoriK avatar Feb 02 '25 05:02 NyoriK

@NyoriK would you mind to try this PR? And let me know if it fixes the problem for you?

In general it works, but the main downside is that it doesn't work on Android - I opened a PR in react-native. Let's see if it gets accepted/merged 👀

kirillzyusko avatar Feb 03 '25 19:02 kirillzyusko

@kirillzyusko I've tested the PR, and can confirm it works as intended on iOS.

The behavior is now much closer to native ScrollView. There's still some minor jumpiness when typing at the end of the TextInput, but this appears to be the same as with native ScrollView and is expected due to the combination of TextInput's internal scroll and asynchronous height adjustments (as discussed in #708).

Looking forward to the Android fix once the React Native PR is merged. Thanks for working on this!

NyoriK avatar Feb 04 '25 07:02 NyoriK

@kirillzyusko Just tested again on Android and can confirm - the fix doesn't work on Android. When typing reaches the keyboard area, the KeyboardAwareScrollView doesn't scroll up, and the content being typed goes underneath the keyboard. Will await the Android fix once the React Native PR is merged.

NyoriK avatar Feb 04 '25 07:02 NyoriK

Just tested again on Android and can confirm - the fix doesn't work on Android. When typing reaches the keyboard area, the KeyboardAwareScrollView doesn't scroll up, and the content being typed goes underneath the keyboard. Will await the Android fix once the React Native PR is merged.

Yeah, an intermediate solution can be combining "additional view" approach on Android and using contentInset on iOS.

But it looks like PR has been merged in upstream, so maybe in RN 0.79 we'll get this prop available on Android 🙈

kirillzyusko avatar Feb 04 '25 15:02 kirillzyusko

Hi, I'm experiencing the same problems as describe in the issue chain. I'm using RN 0.79.2 and r-n-keyboard-controller 1.17.1. I'm testing on iOS (device and simulator). Curious is there a workaround for now?

The testing code I'm using is pretty simple. I'm trying to have an automatically expanding TextInput and when it grows the scrolling behaviour jumps to the top, or doesn't scroll to the caret.

return (
    <KeyboardAwareScrollView bottomOffset={50}>
      <TextInput value={text} onChangeText={setText} multiline style={styles.textInput} textAlignVertical="top" />
    </KeyboardAwareScrollView>
  );

mostlygeek avatar May 13 '25 05:05 mostlygeek

One of the last issues with this lifesaver of a library. Subscribing.

rikur avatar May 17 '25 23:05 rikur