Khemraj Sharma
Khemraj Sharma

Reputation: 58974

FlatList Sticky header does not work inside ScrollView?

React native FlatList sticky header works well without ScrollView. But when i add a Parallax ScrollView (or even simple ScrollView) then sticky header of FlatList does not work. (Sticky header scroll with scrollview.) I am stuck in this issue since 1 day.

First i wanted to have Parallax ScrollView, when Parallax ScrollView did not work, i tried simple ScrollView.

I have already tried 5 of Parallax scrollView like this and this. But none worked.

If Parallax ScrollView is not possible with FlatList sticky header, then at least tell some trick to put FlatList in simple scrollview, That work with Android and iOS both. Here is my code with scrollview and FlatList.

import { Body, Left, ListItem, Right, Text, View } from "native-base";
import React from "react";
import { FlatList, ScrollView } from "react-native";
var _this;
export default class App extends React.Component {
  constructor() {
    super();
    _this = this;
    this.state = {
      data: [
        { name: "Movies", header: true },
        { name: "Interstellar", header: false },
        { name: "Dark Knight", header: false },
        { name: "Pop", header: false },
        { name: "Pulp Fiction", header: false },
        { name: "Burning Train", header: false },
        { name: "Music", header: true },
        { name: "Adams", header: false },
        { name: "Nirvana", header: false },
        { name: "Amrit Maan", header: false },
        { name: "Oye Hoye", header: false },
        { name: "Eminem", header: false },
        { name: "Places", header: true },
        { name: "Jordan", header: false },
        { name: "Punjab", header: false },
        { name: "Ludhiana", header: false },
        { name: "Jamshedpur", header: false },
        { name: "India", header: false },
        { name: "People", header: true },
        { name: "Jazzy", header: false },
        { name: "Appie", header: false },
        { name: "Baby", header: false },
        { name: "Sunil", header: false },
        { name: "Arrow", header: false },
        { name: "Things", header: true },
        { name: "table", header: false },
        { name: "chair", header: false },
        { name: "fan", header: false },
        { name: "cup", header: false },
        { name: "cube", header: false }
      ],
      stickyHeaderIndices: []
    };
  }
  componentDidMount() {
    var arr = [];
    this.state.data.map(obj => {
      if (obj.header) {
        arr.push(this.state.data.indexOf(obj));
      }
    });
    arr.push(0);
    this.setState({
      stickyHeaderIndices: arr
    });
  }

  renderItem = ({ item }) => {
    if (item.header) {
      return (
        <ListItem itemDivider>
          <Left />
          <Body style={{ marginRight: 40 }}>
            <Text style={{ fontWeight: "bold" }}>{item.name}</Text>
          </Body>
          <Right />
        </ListItem>
      );
    } else if (!item.header) {
      return (
        <ListItem style={{ marginLeft: 0 }}>
          <Body>
            <Text>{item.name}</Text>
          </Body>
        </ListItem>
      );
    }
  };

  showCartScreen() {
    _this.props.navigation.navigate("ScreenCart");
  }

  render() {
    return (
      <ScrollView style={{ flex: 1 }}>
        <View style={{height : 200}}></View>
        <FlatList
          data={this.state.data}
          renderItem={this.renderItem}
          keyExtractor={item => item.name}
          stickyHeaderIndices={this.state.stickyHeaderIndices}
        />
      </ScrollView>
    );
  }
}

Code with parallax scroll view, in which Flat-list is not fully scroll-able.

import { FlatList } from "react-native";
import { Text, ListItem, Left, Body, Icon, Right, Title } from "native-base";
import React, { Component } from "react";
import {
  Dimensions,
  Image,
  ListView,
  PixelRatio,
  StyleSheet,
  View
} from "react-native";

import ParallaxScrollView from "react-native-parallax-scroll-view";
export default class App extends React.Component {
  constructor() {
    super();
    this.state = {
      data: [
        { name: "Movies", header: true },
        { name: "Interstellar", header: false },
        { name: "Dark Knight", header: false },
        { name: "Pop", header: false },
        { name: "Pulp Fiction", header: false },
        { name: "Burning Train", header: false },
        { name: "Music", header: true },
        { name: "Adams", header: false },
        { name: "Nirvana", header: false },
        { name: "Amrit Maan", header: false },
        { name: "Oye Hoye", header: false },
        { name: "Eminem", header: false },
        { name: "Places", header: true },
        { name: "Jordan", header: false },
        { name: "Punjab", header: false },
        { name: "Ludhiana", header: false },
        { name: "Jamshedpur", header: false },
        { name: "India", header: false },
        { name: "People", header: true },
        { name: "Jazzy", header: false },
        { name: "Appie", header: false },
        { name: "Baby", header: false },
        { name: "Sunil", header: false },
        { name: "Arrow", header: false },
        { name: "Things", header: true },
        { name: "table", header: false },
        { name: "chair", header: false },
        { name: "fan", header: false },
        { name: "cup", header: false },
        { name: "cube", header: false }
      ],
      stickyHeaderIndices: [],
      dataSource: new ListView.DataSource({
        rowHasChanged: (r1, r2) => r1 !== r2
      }).cloneWithRows([
        "Simplicity Matters",
        "Hammock Driven Development",
        "Value of Values",
        "Are We There Yet?",
        "The Language of the System",
        "Design, Composition, and Performance",
        "Clojure core.async",
        "The Functional Database",
        "Deconstructing the Database",
        "Hammock Driven Development",
        "Value of Values"
      ])
    };
  }
  componentDidMount() {
    var arr = [];
    this.state.data.map(obj => {
      if (obj.header) {
        arr.push(this.state.data.indexOf(obj));
      }
    });
    arr.push(0);
    this.setState({
      stickyHeaderIndices: arr
    });
  }

  renderItem = ({ item }) => {
    if (item.header) {
      return (
        <ListItem itemDivider>
          <Left />
          <Body style={{ marginRight: 40 }}>
            <Text style={{ fontWeight: "bold" }}>{item.name}</Text>
          </Body>
          <Right />
        </ListItem>
      );
    } else if (!item.header) {
      return (
        <ListItem style={{ marginLeft: 0 }}>
          <Body>
            <Text>{item.name}</Text>
          </Body>
        </ListItem>
      );
    }
  };
  // render() {
  //   return (
  //     <FlatList
  //       data={this.state.data}
  //       renderItem={this.renderItem}
  //       keyExtractor={item => item.name}
  //       stickyHeaderIndices={this.state.stickyHeaderIndices}
  //     />
  //   );
  // }

  render() {
    const { onScroll = () => {} } = this.props;
    return (
      <FlatList
        ref="ListView"
        style={styles.container}
        data={this.state.data}
        renderItem={this.renderItem}
        keyExtractor={item => item.name}
        stickyHeaderIndices={this.state.stickyHeaderIndices}
        renderScrollComponent={props => (
          <ParallaxScrollView
            onScroll={onScroll}
            headerBackgroundColor="#333"
            stickyHeaderHeight={STICKY_HEADER_HEIGHT}
            parallaxHeaderHeight={PARALLAX_HEADER_HEIGHT}
            backgroundSpeed={10}
            renderBackground={() => (
              <View key="background">
                <Image
                  source={{
                    uri: "https://i.ytimg.com/vi/P-NZei5ANaQ/maxresdefault.jpg",
                    width: window.width,
                    height: PARALLAX_HEADER_HEIGHT
                  }}
                />
                <View
                  style={{
                    position: "absolute",
                    top: 0,
                    width: window.width,
                    backgroundColor: "rgba(0,0,0,.4)",
                    height: PARALLAX_HEADER_HEIGHT
                  }}
                />
              </View>
            )}
            renderForeground={() => (
              <View key="parallax-header" style={styles.parallaxHeader}>
                <Image
                  style={styles.avatar}
                  source={{
                    uri:
                      "https://pbs.twimg.com/profile_images/2694242404/5b0619220a92d391534b0cd89bf5adc1_400x400.jpeg",
                    width: AVATAR_SIZE,
                    height: AVATAR_SIZE
                  }}
                />
                <Text style={styles.sectionSpeakerText}>
                  Talks by Rich Hickey
                </Text>
                <Text style={styles.sectionTitleText}>
                  CTO of Cognitec, Creator of Clojure
                </Text>
              </View>
            )}
            renderStickyHeader={() => (
              <View key="sticky-header" style={styles.stickySection}>
                <Text style={styles.stickySectionText}>Rich Hickey Talks</Text>
              </View>
            )}
            renderFixedHeader={() => (
              <View key="fixed-header" style={styles.fixedSection}>
                <Text
                  style={styles.fixedSectionText}
                  onPress={() => this.refs.ListView.scrollTo({ x: 0, y: 0 })}
                >
                  Scroll to top
                </Text>
              </View>
            )}
          />
        )}
      />
    );
  }
}


const window = Dimensions.get("window");

const AVATAR_SIZE = 120;
const ROW_HEIGHT = 60;
const PARALLAX_HEADER_HEIGHT = 350;
const STICKY_HEADER_HEIGHT = 70;

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "black"
  },
  background: {
    position: "absolute",
    top: 0,
    left: 0,
    width: window.width,
    height: PARALLAX_HEADER_HEIGHT
  },
  stickySection: {
    height: STICKY_HEADER_HEIGHT,
    width: 300,
    justifyContent: "flex-end"
  },
  stickySectionText: {
    color: "white",
    fontSize: 20,
    margin: 10
  },
  fixedSection: {
    position: "absolute",
    bottom: 10,
    right: 10
  },
  fixedSectionText: {
    color: "#999",
    fontSize: 20
  },
  parallaxHeader: {
    alignItems: "center",
    flex: 1,
    flexDirection: "column",
    paddingTop: 100
  },
  avatar: {
    marginBottom: 10,
    borderRadius: AVATAR_SIZE / 2
  },
  sectionSpeakerText: {
    color: "white",
    fontSize: 24,
    paddingVertical: 5
  },
  sectionTitleText: {
    color: "white",
    fontSize: 18,
    paddingVertical: 5
  },
  row: {
    overflow: "hidden",
    paddingHorizontal: 10,
    height: ROW_HEIGHT,
    backgroundColor: "white",
    borderColor: "#ccc",
    borderBottomWidth: 1,
    justifyContent: "center"
  },
  rowText: {
    fontSize: 20
  }
});

Upvotes: 3

Views: 7741

Answers (1)

Pramod
Pramod

Reputation: 1940

In this block of code just remove the "ScrollView" with "View" it will work

<ScrollView style={{ flex: 1 }}>
  <View style={{height : 200}}></View>
  <FlatList data={this.state.data}
   renderItem={this.renderItem}
   keyExtractor={item => item.name}
   stickyHeaderIndices={this.state.stickyHeaderIndices} />
</ScrollView>

Reason: The flat list has default scroll view and you are putting it in a scroll view so on scrolling its not getting the top position to header to stick

Upvotes: 2

Related Questions