React-native-keyboard-aware-scroll-view: [KeyboardAwareFlatList] Not working with multiline TextInput

Created on 15 Feb 2018  路  27Comments  路  Source: APSL/react-native-keyboard-aware-scroll-view

When multiline prop is set in the TextInput it doesn't scroll properly

enhancement help wanted

Most helpful comment

Try setting scrollEnabled={false} on TextInput. Allows keyboard-scroll-view take over it seems :)

All 27 comments

Can you set up a repro case in order to be able to solve it? Thanks!

Just ran into this with KeyboardAwareScrollView too.

Reproduction case:

<KeyboardAwareFlatList
          data={optionsList}
          renderItem={this._renderItem.bind(this)}
          keyExtractor={this._keyExtractor}
          showsVerticalScrollIndicator={false}
        />

And in _renderItem function there is an item that renders a text field (TextInput) as following:

 <TextInput
            {...this.props}
            multiline={true} />

When the multiline prop is set to _false_ it works fine and scrolls up to text field as intended, but when it is set to _true_ , it fails to scroll up/center the text field as in last case.

Solution/Workaround: I added

extraHeight

prop like this extraHeight={texInputHeight} for now.

Ugh, yes, this has been an ongoing issue since first versions. I will try to get a solution as soon as I can.

I can confirm this is an issue. The workaround is nice, thanks @omar-salah-elboredy . Sadly, it adds an empty block which has a height of extraHeight below the scrollView, which can not be reached :/

I have same issue with @WaldoJeffers . Please help.

One solution is #250. componentDidUpdate() fires an .update() event and this repositions the TextInput directly above the keyboard.

update = () => {
  const currentlyFocusedField = TextInput.State.currentlyFocusedField()
  const responder = this.getScrollResponder()

  if (!currentlyFocusedField || !responder) {
    return
  }

  this._scrollToFocusedInputWithNodeHandle(
    currentlyFocusedField
  )
}

Update: There's now a PR waiting for review that fixes this issue among others (ideally any time the TextInput goes behind the keyboard. Please check out #251

On RN 0.56 #251 didn't work

[KeyboardAwareFlatList]
At the bottom, the TextInput gets the focus and it can be achieved at the top. But when the keyboard shrinks, it doesn't restore its original position

qq20180727-160005

What is the status on this issue?

@bugs181 Any updates Levi ? 馃槃

Just encountered this issue on RN 0.56. For anyone finding this I was able to workaround this by hacking it together like so:

render() {
  <KeyboardAwareScrollView  
        innerRef={ref => {
          this.scroll = ref;
        }} />
            <TextInput 
                  multiline={true} 
                  onFocus={(event: any) => {
                        this.scroll.props.scrollToFocusedInput(ReactNative.findNodeHandle(event.target));
                  }} />
</KeyboardAwareScrollView>
}

@kylethielk This worked for me, but I had to call without props

this.scroll.scrollToFocusedInput(...);

import { findNodeHandle } from 'react-native';

innerRef={ref => {
this.scroll = ref;
}}>

                <TextInput
                    multiline={true}

                    onFocus={(event: any) => {
                        this.scroll.props.scrollToFocusedInput(findNodeHandle(event.target))
                    }}/>

            </KeyboardAwareScrollView>

Just encountered this issue on RN 0.56. For anyone finding this I was able to workaround this by hacking it together like so:

render() {
  <KeyboardAwareScrollView  
        innerRef={ref => {
          this.scroll = ref;
        }} />
            <TextInput 
                  multiline={true} 
                  onFocus={(event: any) => {
                        this.scroll.props.scrollToFocusedInput(ReactNative.findNodeHandle(event.target));
                  }} />
</KeyboardAwareScrollView>
}

Just encountered this issue on RN 0.56. For anyone finding this I was able to workaround this by hacking it together like so:

render() {
  <KeyboardAwareScrollView  
        innerRef={ref => {
          this.scroll = ref;
        }} />
            <TextInput 
                  multiline={true} 
                  onFocus={(event: any) => {
                        this.scroll.props.scrollToFocusedInput(ReactNative.findNodeHandle(event.target));
                  }} />
</KeyboardAwareScrollView>
}

onFocus gets not fired when the multiline TextInput changes its height because of user input

enableOnAndroid={true}
extraHeight={100}
enableAutomaticScroll={true}
extraScrollHeight={100}
>



@mitevdev I found the solution.

render() {
  <KeyboardAwareScrollView  
        innerRef={ref => {
          this.scroll = ref;
        }} />
            <TextInput 
                  multiline={true} 
                  onContentSizeChange={(event) => {
                        this.scroll.props.scrollToEnd();
                  }} />
</KeyboardAwareScrollView>
}

hmmm how do we reference a ref to scrollview if TextInput lives within a component instead of nicely inline.

Event as a prop I spose


Update: worked with passing event as prop and the line this.scroll.props.scrollToFocusedInput(ReactNative.findNodeHandle(event.target));
plus i set extraHeight={100} on KeyboardAwareFlatList just to bump it up a bit.

So strange: it works as expected on _one_ of my multiline TextInputs, but not on another. I know I'm not adding anything to the discussion. But it has me baffled.

the scrollToFocusedInput takes a second parameter which is extraHeight but it doesn't work seems whatever you pass to it, it will be overwritten in a update method with the default extraHeight or props.

Hey guys,

Just incase this helps anyone I solved this nicely using hooks in the following way:

  1. I wrap all of my screens in a Layout component (I use React Navigation).
  2. If that Layout contains inputs then I pass avoidKeyboard into that component. If the layout also scrolls then I pass scrollEnabled.
  3. I use react-native-keyboard-aware-scroll-view wrapped in a component called KeyboardAvoidingScrollView. This uses the React context API to provide access to the ref.
  4. I have a KeyboardAwareInput component which accesses the ref via the context. If the ref exists then I scrollToFocusedInput in the onContentSizeChange handler using findNodeHandle on the event target.

This is working perfectly for me. Here is the code:

Layout.tsx

import React from 'react'
import { View } from 'react-native'
import { makeStyles } from '../styles/makeStyles'
import { KeyboardAvoidingScrollView } from './KeyboardAvoidingScrollView'

interface IProps {
  avoidKeyboard?: boolean
  scrollEnabled?: boolean
}

export const Layout: React.FunctionComponent<IProps> = ({
  avoidKeyboard = false,
  scrollEnabled = false,
  children,
}) => {
  const Container = <View style={styles.container}>{children}</View>

  return avoidKeyboard ? (
    <KeyboardAvoidingScrollView scrollEnabled={scrollEnabled}>
      {Container}
    </KeyboardAvoidingScrollView>
  ) : (
    Container
  )
}

const styles = makeStyles(theme => ({
  container: {
    ...theme.LayoutContainer.containerStyle,
  },
}))

KeyboardAvoidingScrollView.tsx

import React, { createContext, useContext, useRef } from 'react'
import {
  KeyboardAwareScrollView,
  KeyboardAwareScrollViewProps,
} from 'react-native-keyboard-aware-scroll-view'
import layout from '../constants/Layout'

interface IContext {
  scrollViewRef: React.MutableRefObject<KeyboardAwareScrollView>
}

const KeyboardAvoidingScrollViewContext = createContext<IContext>({
  scrollViewRef: null,
})

export const useKeyboardAvoidingScrollView = () => {
  return useContext(KeyboardAvoidingScrollViewContext)
}

interface IProps extends KeyboardAwareScrollViewProps {
  scrollEnabled?: boolean
}

export const KeyboardAvoidingScrollView: React.FunctionComponent<IProps> = ({
  scrollEnabled = false,
  children,
  ...scrollViewProps
}) => {
  const scrollViewRef = useRef(null)

  return (
    <KeyboardAwareScrollView
      ref={scrollViewRef}
      resetScrollToCoords={{ x: 0, y: 0 }}
      contentContainerStyle={{ flex: 1 }}
      scrollEnabled={scrollEnabled}
      extraHeight={layout.window.height / 4}
      {...scrollViewProps}
    >
      <KeyboardAvoidingScrollViewContext.Provider value={{ scrollViewRef }}>
        {children}
      </KeyboardAvoidingScrollViewContext.Provider>
    </KeyboardAwareScrollView>
  )
}

KeyboardAwareInput.tsx

import React from 'react'
import { findNodeHandle } from 'react-native'
import { Input, InputProps } from 'react-native-elements'
import { useKeyboardAvoidingScrollView } from '../components/KeyboardAvoidingScrollView'

export const KeyboardAwareInput: React.FunctionComponent<
  InputProps
> = inputProps => {
  const { scrollViewRef } = useKeyboardAvoidingScrollView()

  return (
    <Input
      {...inputProps}
      onContentSizeChange={event => {
        if (scrollViewRef && scrollViewRef.current) {
          scrollViewRef.current.scrollToFocusedInput(
            findNodeHandle(event.target)
          )
        }
      }}
    />
  )
}

And then an actual usage example:

ArrangeInPersonMeeting.tsx
_FYI: This is referenced from a TabNavigator, and in this case inside a Modal route_

import React from 'react'
import { NavigationStackScreenComponent } from 'react-navigation-stack'
import { Layout } from '../../../components/Layout'
import { ModalBottomButton } from '../../../components/ModalBottomButton'
import { IMember } from '../../company/interfaces'
import { EnterLocationDetails } from './EnterLocationDetails'
import { ProposedTimes } from './ProposedTimes'

export const ArrangeInPersonMeeting: NavigationStackScreenComponent = ({
  navigation,
}) => {
  const member: IMember = navigation.dangerouslyGetParent().getParam('member')
  return (
    <Layout avoidKeyboard>
      <ProposedTimes withMember={member} />
      <EnterLocationDetails />
      <ModalBottomButton
        title="Arrange Meeting"
        icon={{ type: 'material-community', name: 'calendar-check' }}
      />
    </Layout>
  )
}

EnterLocationDetails.tsx

import React, { useState } from 'react'
import { View } from 'react-native'
import { CheckBox } from 'react-native-elements'
import { HeaderBar } from '../../../components/HeaderBar'
import { KeyboardAwareInput } from '../../../components/KeyboardAwareInput'
import { Paper } from '../../../components/Paper'

export const EnterLocationDetails: React.FunctionComponent = () => {
  const [meetAtOffice, setMeetAtOffice] = useState(false)
  const [postalCode, setPostalCode] = useState<string>()
  const [details, setDetails] = useState<string>()

  return (
    <View style={{ flex: 1 }}>
      <HeaderBar label="Location" />
      <Paper>
        <View
          style={{
            flexDirection: 'row',
            alignItems: 'center',
          }}
        >
          <KeyboardAwareInput
            placeholder="Enter Postal Code"
            onChangeText={setPostalCode}
            value={postalCode}
            numberOfLines={1}
            maxLength={20}
            containerStyle={{
              flex: 0.5,
            }}
          />
          <CheckBox
            title="Meet at your office"
            checked={meetAtOffice}
            onPress={() => setMeetAtOffice(!meetAtOffice)}
            containerStyle={{
              flex: 0.5,
            }}
          />
        </View>
        <View>
          <KeyboardAwareInput
            placeholder="Please specify any additional place details or directions..."
            onChangeText={setDetails}
            value={details}
            maxLength={500}
            textAlignVertical="top"
            numberOfLines={3}
            multiline
          />
        </View>
      </Paper>
    </View>
  )
}

I hope that helps someone resolve this issue in a clean fashion.

Happy coding!

onContentSizeChange={(event) => {
this.scroll.props.scrollToEnd();
}}

I am able to fix this issue as suggested by @sanddy-man but with little change. Instead of using this.scroll.props.scrollToEnd();, I used this.scrollRef.props.scrollToFocusedInput(findNodeHandle(event.target)); as below:

render() {
  <KeyboardAwareScrollView  
        innerRef={ref => {
          this.scroll = ref;
        }}
  />
            <TextInput 
                  multiline={true} 
                  onContentSizeChange={(event) => {
                       this.scrollRef.props.scrollToFocusedInput(findNodeHandle(event.target));
                  }}
             />
   </KeyboardAwareScrollView>
}

Thank you @ajaykumar97 this is exactly what I was looking for 馃憤

I'll close this for being very old. Thanks for all the comments 馃樆
A solution seems to be here: https://github.com/APSL/react-native-keyboard-aware-scroll-view/issues/227#issuecomment-554641817

The solution in the comment above does not work for functional components. While one solution was provided using hooks, it is quite involved and is not an easy fix.

Could we keep this issue open, since the expected behavior of the KeyboardAwareScrollView component is to scroll to selected inputs, whether multiline or not? If the current behavior is intended (only scrolling to non-multiline inputs), I suggest the documentation should be updated to make this clear, as it could be a stumbling block for many developers.

Try setting scrollEnabled={false} on TextInput. Allows keyboard-scroll-view take over it seems :)

Was this page helpful?
0 / 5 - 0 ratings