Expo React Native FlatList でカルーセルUI

code

はじめに

こちらの記事は、React Native の FlatListでカルーセルを実装する備忘録です. 

動作のイメージはこのような感じです.

環境はほぼデフォルトですので省略します. スライドショーの画像はUnsplashからお借りしました.

Version情報

  • expo: ~47.0.9
  • React Native: 0.70.5

各コンポーネントの作成

最初に表示するスライドショーのファイルを作成します. TypeScriptプロジェクトなのでイImageの型定義用にImageSourcePropTypeをimportしています.

slide.ts

import { ImageSourcePropType } from 'react-native'

export interface ISlide {
  id: string,
  title: string,
  description: string,
  image: ImageSourcePropType
}

const slides: ISlide[] = [
  {
  id: '1',
  title: 'beef',
  description: 'grilled beef on the plate',
  image: require('../assets/beef.jpg')
  },
  {
    id: '2',
    title: 'chicken',
    description: 'roasted chicken on the plate',
    image: require('../assets/chicken.jpg')
  },
  {
    id: '3',
    title: 'fish',
    description: 'pan fried fish on the plate',
    image: require('../assets/fish.jpg')
  },
  {
    id: '4',
    title: 'lamb',
    description: 'roasted lamb on the plate',
    image: require('../assets/lamb.jpg')
  },
  {
    id: '5',
    title: 'pasta',
    description: 'carbonara pasta on the plate',
    image: require('../assets/pasta.jpg')
  }
];

export default slides;

次は、カルーセルのコンポーネントです.

まとめると次のようになります.

Onboarding.tsx

import {
  StyleSheet,
  View,
  Image,
  useWindowDimensions,
  FlatList,
  ListRenderItemInfo,
  Animated,
  ImageSourcePropType,
  ViewToken
} from 'react-native'
import React, { useState, useRef, SetStateAction } from 'react'
import slides, { ISlide } from './slide'


const Onboarding = () => {

  const [currentIndex, setCurrentIndex] = useState<number>(0);
  const scrollX = useRef(new Animated.Value(0)).current;
  const slidesRef = useRef(null);

  const { width } = useWindowDimensions();

  const Item = ({ item }: { item: ImageSourcePropType }) => {
    return (
      <View style={[styles.container, { width }]}>
        <Image
          source={item}
          style={[styles.image, { width, resizeMode: 'contain' }]}
        />
      </View>
    );
  };

  const _renderItem = (listRenderItemInfo: ListRenderItemInfo<ISlide>) => {
    return <Item item={listRenderItemInfo.item.image} />;
  }

  const viewableItemsChanged = useRef(({ viewableItems }: {viewableItems: any}) => {
    setCurrentIndex(viewableItems[0])
  }).current;

  const viewConfig = useRef({ viewAreaCoveragePercentThreshold: 50 }).current;

  return (
    <View style={styles.container}>
      <FlatList
        data={slides}
        renderItem={_renderItem}
        keyExtractor={item => item.id}
        horizontal
        showsHorizontalScrollIndicator
        pagingEnabled
        bounces={false}
        onScroll={Animated.event([{ nativeEvent: { contentOffset: { x: scrollX } } }], {
          useNativeDriver: false,
        })}
        scrollEventThrottle={32}
        onViewableItemsChanged={viewableItemsChanged}
        viewabilityConfig={viewConfig}
        ref={slidesRef}
      />
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  image: {
    flex: 1,
    backgroundColor: 'white',
    zIndex: 1,
    justifyContent: 'center'
  }
});


export default Onboarding;

そしてOnboardingコンポーネントをApp.tsxに入れます.

App.tsx

import { StatusBar } from 'expo-status-bar';
import { StyleSheet, SafeAreaView, Platform } from 'react-native';
import Onboarding from './components/Onboarding';


export default function App() {
  return (
    <SafeAreaView style={styles.container}>
      <Onboarding />
      <StatusBar style="auto" />
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

以上です. 最後までご覧いただきありがとうございます.

参考情報

この記事では下記の動画を参考にしました.

Onboarding tutorial for React Native – Animated Carousel #1