Scroll to end of FlatList after displaying the keyboard

Viewed 42122

I have a FlatList inside a KeyboardAvoidingView. When the keyboard is displayed I would like to scroll to the end of the FlatList.

I am listening for the 'keyboardDidShow' event which does get fired, but it may be fired too early as the FlatList is not scrolled to the end after calling scrollToEnd.

I have looked into the onLayout event of KeyboardAvoidingView however just setting the onLayout event to trigger a function seems to stop the KeyboardAvoidingView from adjusting it's size when the Keyboard is shown.

<KeyboardAvoidingView behavior='padding' style={{ flex: 1}} onLayout={this._scrollEnd}>

Code:

import React from 'react';
import {Image, Linking, Platform, ScrollView, StyleSheet, Text, TouchableOpacity, View, Button, Alert, FlatList, TextInput, KeyboardAvoidingView, Keyboard} from 'react-native';
import { MonoText } from '../components/StyledText';

export default class HomeScreen extends React.Component {
  constructor() {
    super();
    this.state = {
      messages: getMessages()
    };

    this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._scrollEnd);
    this.keyboardDidShowListener = Keyboard.addListener('keyboardDidHide', this._scrollEnd);
  }

  _scrollEnd = (evt) => {
    this.refs.flatList1.scrollToEnd();
  }

  render() {
    return (
      <KeyboardAvoidingView behavior='padding' style={{ flex: 1}} >
        <FlatList
          style={{ flex:1}}
          ref="flatList1"
          data={this.state.messages}
          renderItem={({ item }) => <Text>{item.text}</Text>}
        />
      </KeyboardAvoidingView>
    );
  }
}
5 Answers

actually if you always want to scroll to the end, mean you always want see latest message, am I right?

then use new version of react-native. and add inverted to change upside down of the flat list.

<FlatList
      inverted
      style={{ flex:1}}
      ref="flatList1"
      data={this.state.messages}
      renderItem={({ item }) => <Text>{item.text}</Text>}
    />

then rearrange your this.state.messages upside-down. then your latest message will always shown at the bottom of flatlist

For my case, I doesn't need to use KeyboardAvoidingView

Some of the users (@Nathileo) asked for a hooks-based approach to scrolling to the end of a FlatList.

  1. First, you need to implement React's useRef hook:

    import {useRef} from 'react';

    const yourRef = useRef(null);

  2. Second, the FlatList tag must be equipped with a reference and the desired functions:

    <FlatList
      ref={yourRef}
      onContentSizeChange={() => yourRef.current.scrollToEnd() }
      onLayout={() => yourRef.current.scrollToEnd() }
    />
    

I've been using this little component I made for managing the flatlist height with the keyboard. This use the renderProps pattern so you can reuse it :)

import { PureComponent } from 'react';
import { Keyboard, Dimensions, Animated } from 'react-native';

const DURATION = 200;

class ListSpacer extends PureComponent {
  state = {
    screenHeight: Dimensions.get('window').height,
    flatListHeight: new Animated.Value(Dimensions.get('window').height),
  };

  componentDidMount() {
    this._keyboardDidShowListener = Keyboard.addListener(
      'keyboardDidShow',
      this._keyBoardDidShow,
    );
    this._keyboardDidHideListener = Keyboard.addListener(
      'keyboardDidHide',
      this._keyBoardDidHide,
    );
  }

  componentWillUnmount() {
    this._keyboardDidShowListener.remove();
    this._keyboardDidHideListener.remove();
  }

  _keyBoardDidShow = e => {
    Animated.timing(this.state.flatListHeight, {
      toValue: Dimensions.get('window').height - e.endCoordinates.height,
      duration: DURATION,
    }).start();
  };

  _keyBoardDidHide = () => {
    Animated.timing(this.state.flatListHeight, {
      toValue: Dimensions.get('window').height,
      duration: DURATION,
    }).start();
  };

  render() {
    const renderProps = {
      flatListHeight: this.state.flatListHeight,
    };

    if (this.props.children) {
      return this.props.children(renderProps);
    }

    return this.props.render(renderProps);
  }
}

export default ListSpacer;

Here we listen for keyboard event, and the endcoordinates give you the keyboard height. This way you can make the flatlist height with it.

import {
  FlatList,
  KeyboardAvoidingView,
  Animated,
} from 'react-native';

const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);

return (
  <ListSpacer>
    {({ flatListHeight }) => (
      <KeyboardAvoidingView
        behavior="padding"
        keyboardVerticalOffset={INPUT_HEIGHT}
      >
        <AnimatedFlatList
          inverted
          style={{ height: flatListHeight }}
          data={data.comments}
          keyExtractor={this._keyExtractor}
          renderItem={this._renderItem}
          contentContainerStyle={styles.contentList}
        />
      </KeyboardAvoidingView>
    )}
  </ListSpacer>
);

Here I have this tutorials where I show what that does if you are more visual :)

https://youtu.be/2QnPZXCIN44?t=28m43s

Related