facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
118.17k stars 24.21k forks source link

KeyboardAvoidingView has no effect on multiline TextInput #16826

Open peacechen opened 6 years ago

peacechen commented 6 years ago

KeyboardAvoidingView only works with single-line TextInputs. When the multiline prop is set, KeyboardAvoidingView does not shift the TextInput at all.

Is this a bug report?

Yes

Have you read the Contributing Guidelines?

Yes

Environment

Environment: OS: macOS Sierra 10.12.6 Node: 7.0.0 npm: 3.10.8 Watchman: 4.7.0 Xcode: 9.1

Packages: (wanted => installed) react-native: 0.49.3 react: 16.0.0-beta.5

Target Platform: iOS (10.3)

Steps to Reproduce

  1. Use a <TextInput> component with multiline prop set.
  2. Wrap this in a ScrollView
  3. Wrap that in a KeyboardAvoidingView.

Expected Behavior

Multiline TextInput should scroll above the soft keyboard.

Actual Behavior

Soft keyboard covers multiline TextInput.

Reproducible Demo

import React, { Component } from 'react';
import { Text, TextInput, View, ScrollView, KeyboardAvoidingView, Keyboard} from 'react-native';

...

    render() {
        return (
            <KeyboardAvoidingView style={{flex:1}} behavior="padding" keyboardVerticalOffset={64}>
                <ScrollView keyboardShouldPersistTaps={'handled'}>
                    <View style={{padding: 12}}>
                        // various content to fill the page
                        <Text style={{fontSize: 20, padding: 40}}>MESSAGE 1</Text>
                        <Text style={{fontSize: 20, padding: 40}}>MESSAGE 2</Text>
                        <Text style={{fontSize: 20, padding: 40}}>MESSAGE 3</Text>
                        <Text style={{fontSize: 20, padding: 40}}>MESSAGE 4</Text>
                        <Text style={{fontSize: 20, padding: 40}}>MESSAGE 5</Text>
                        <Text style={{fontSize: 20, padding: 40}}>MESSAGE 6</Text>
                        <Text style={{fontSize: 20, padding: 40}}>MESSAGE 7</Text>
                        <Text style={{fontSize: 20, padding: 40}}>MESSAGE 8</Text>
                        <Text style={{fontSize: 20, padding: 40}}>MESSAGE 9</Text>
                        <Text style={{fontSize: 20, padding: 40}}>MESSAGE 10</Text>
                    </View>
                    <TextInput
                        style={{padding: 4}}
                        multiline={true}
                        placeholder={'Type something here...'}
                        onChangeText={this.updateMessage}
                        value={this.state.message}
                    />
                </ScrollView>
            </KeyboardAvoidingView>
        );
    }
vanenshi commented 7 months ago

@fabOnReact Thanks for the great details on the problem, Fabrizio this is a 7-year-old problem, if you manage to fix it, all of us can remove the nasty workaround in our code and start using the pure component.

fabOnReact commented 7 months ago

Thanks, but I decided to not contribute anymore to facebook/react-native.

Paraschoudhary176 commented 6 months ago

`<KeyboardAvoidingView style={{ flex: 1 }} behavior="padding"

<ScrollView ref={component => { this.myScrollView = component; }}> <TextInput multiline onFocus={() => this.myScrollView.scrollTo({ x: 0, y: 750, animated: true })} /> `

This is how this will work. You can also try keyboard aware scroll view , or in android manifiest.xml can change the adjust keyboard. or if nothing works then we have antother alternative a prop called tabBarHideOnKeyboard: true.

pein892 commented 6 months ago

@Paraschoudhary176 Thanks for your thoughts, and here is my solution:

const App = () => {
  const scrollViewRef = useRef(null)
  const textInputRef = useRef(null)

  const onFocus = () => {
    if (Platform.OS === 'ios') {
      if (scrollViewRef.current && textInputRef.current) {
        textInputRef.current.measureLayout(
          scrollViewRef.current,
          (x, y, width, height) => {
            setTimeout(() => {
              scrollViewRef.current.scrollTo({y: y, animated: true});
            }, 500);  // 500ms just work fine.
          },
          () => {},
        );
      }
    }
  }

  return (
    <KeyboardAvoidingView>
      <ScrollView ref={scrollViewRef}>
        <TextInput 
          ref={textInputRef} 
          multiline={true} 
          numberOfLines={3} 
          onFocus={onFocus} />
      </ScrollView>
    </KeyboardAvoidingView>
  )
}
kirillzyusko commented 2 months ago

Hello, maintainer and creator of react-native-keyboard-controller is here 👋

I think KeyboardAwareScrollView from react-native-keyboard-controller should solve this problem - it automatically detects a moment when focused TextInput is growing and automatically scrolling to the necessary coordinates.

Theoretically similar behavior can be backported to KeyboardAvoidingView component from RNKC because this functionality is powered by another hook (layout changes detection) - but I hardly can imagine a layout where KeyboardAvoidingView+multiline is more preferable option over KeyboardAwareScrollView+multiline.

Anyway, if you have such examples and if you think, that something is not working or should work in a different way - feel free to open issues in react-native-keyboard-controller repository 👀

Energieman commented 1 month ago

in 2024, Here is my working fix, scrollEnabled={false} did most of the magic. I honestly hope it helps someone here.

<KeyboardAwareScrollView enableAutomaticScroll={true} innerRef = {(ref) => setScrollViewRef(ref)} style={{backgroundColor: 'green'}}> <TextInput scrollEnabled={false} //2 ref={editFieldRef} editable={noteEditable} placeholder="Enter note..." placeholdercolor={BaseColor.blackColor} onChangeText={text => {setNoteText(text);}} //avoidGoingBack(); value={noteText} defaultValue={noteText} multiline = {true} numberOfLines = {10} autoFocus={true} forcedStyle={{fontSize: 20}} //custom TextInput component prop style={{ minHeight: '100%', //for ios because number of lines dosent work with it color: BaseColor.unchangeableClack, backgroundColor: colorPaleteToUse, }} />

alainib commented 1 month ago

in 2024, Here is mu working fix, scrollEnabled={false} did most o the magic. I honestly hope it helps someone here.

<KeyboardAwareScrollView enableAutomaticScroll={true} innerRef = {(ref) => setScrollViewRef(ref)} style={{backgroundColor: 'green'}}> <TextInput scrollEnabled={false} //2 ref={editFieldRef} editable={noteEditable} placeholder="Enter note..." placeholdercolor={BaseColor.blackColor} onChangeText={text => {setNoteText(text);}} //avoidGoingBack(); value={noteText} defaultValue={noteText} multiline = {true} numberOfLines = {10} autoFocus={true} forcedStyle={{fontSize: 20}} //custom TextInput component prop style={{ minHeight: '100%', //for ios because number of lines dosent work with it color: BaseColor.unchangeableClack, backgroundColor: colorPaleteToUse, }} />

disabling scroll on a scrollview .... not a fix at all , if you have like 10 inpunt inside to show ? (

shuo-hiwintech commented 1 month ago

+1

chrisdev3001 commented 1 month ago

I dont wanna depends on another library for this bug, here is my solution:

Custom hook:

import { useRef } from 'react'
// import { doSomethingAtSecondX } from '@core/utils'

interface DoSomethingAtSecondX {
  callback: () => void
  seconds: number
}

export const doSomethingAtSecondX = (params: DoSomethingAtSecondX) => {
  const { callback, seconds } = params
  setTimeout(callback, seconds * 1000)
}

interface MoveScrollTo {
  x: number
  y: number
}

export const useMoveScroll = () => {
  const scrollRef = useRef() as any

  const moveScrollTo = ({ x, y }: MoveScrollTo) =>
    scrollRef.current.scrollTo({ x, y, animated: true })

  const moveScrollToDown = (pxToDown: number) => {
    if (scrollRef.current) {
      doSomethingAtSecondX({
        callback: () => moveScrollTo({ x: 0, y: pxToDown }),
        seconds: 0.35
      })
    }
  }

  return {
    scrollRef,
    moveScrollToDown
  }
}

Component:

const { scrollRef, moveScrollToDown } = useMoveScroll()

return (
  <ScrollView
      ref={scrollRef}
      showsVerticalScrollIndicator={false}
      ....
  />

      <Input
        multiline
        numberOfLines={5}
        placeholder='write comments here...'
        onFocus={() => moveScrollToDown(450)}
        onChangeText={setComments}
      />

  </ScrollView>

)

Hope this help 🐰

aman-technyx commented 3 weeks ago

@chrisdev3001 can you tell me where i can get this "doSomethingAtSecondX" import { doSomethingAtSecondX } from '@core/utils'

HemalSPatel commented 3 weeks ago

+1

chrisdev3001 commented 3 weeks ago

@chrisdev3001 can you tell me where i can get this "doSomethingAtSecondX" import { doSomethingAtSecondX } from '@core/utils'

I have updated the code... it was just a little helper function 👀

ftaibi commented 6 days ago

why is so hard to work with keyboards in rn???? :(((((

aman-technyx commented 4 days ago

@ftaibi this code works for me !

`import React from 'react'; import { Platform, Keyboard, TouchableWithoutFeedback, ViewStyle, } from 'react-native'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';

interface Props { children: React.ReactNode; style?: ViewStyle; }

const KeyboardAvoidingComponent: React.FC = ({ children, style }) => { return ( <KeyboardAwareScrollView showsVerticalScrollIndicator={false} style={style} extraHeight={Platform.OS === 'ios' ? 150 : 0} // adjust height for iOS enableOnAndroid={true}>

{children}
    </KeyboardAwareScrollView>
);

};

export default KeyboardAvoidingComponent; `

use as

<KeyboardAvoidingComponent style={{flex: 1}}> . // no need to use scrollview here.