React Native で下から引っ張るようなモーダル(BottomDrawer)を実装する方法

下から引っ張るような形のモーダル(Bottom Drawer)を実装する手順を紹介します。

イメージは以下のとおりです。

shell
npm i react-native-modal

react-native-modalswipeDirection'down' に設定すれば、下から引っ張るような動作にできます。

BottomDrawer.tsx
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { Text, View, StyleSheet } from 'react-native';
import Modal from 'react-native-modal';

interface RegisterModalProps {
  isOpen: boolean;
  setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
  children?: React.ReactNode;
}

export default function BottomDrawer({ isOpen, setIsOpen, children }: RegisterModalProps) {
  const toggleModal = () => {
    setIsOpen(!isOpen);
  };
  return (
    <Modal
      isVisible={isOpen}
      onBackButtonPress={toggleModal}
      onSwipeComplete={toggleModal}
      swipeDirection={'down'}
      style={styles.bottomModal}
    >
      <View style={styles.modalContent}>
        <View style={styles.modalCloseButton}>
          <MaterialCommunityIcons name="close" size={24} color="black" onPress={toggleModal} />
        </View>
        {children}
      </View>
    </Modal>
  );
}

const styles = StyleSheet.create({
  bottomModal: {
    justifyContent: 'flex-end',
    margin: 0,
  },
  modalContent: {
    flex: 0.9,
    backgroundColor: 'white',
    padding: 22,
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 4,
    borderColor: 'rgba(0, 0, 0, 0.1)',
  },
  modalCloseButton: {
    position: 'absolute',
    top: 10,
    right: 10,
  },
});

BottomDrawer を使う側は、以下のように書けばOKです。

SampleScreen.tsx
import BottomDrawer from '../timeline/BottomDrawer';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { FAB } from 'react-native-paper';

export const SampleScreen = () => {
  const [isOpen, setIsOpen] = React.useState(false);
  return (
    <View style={styles.container}>
      <Text>ベースとなるスクリーンです。</Text>

      <BottomDrawer isOpen={isOpen} setIsOpen={setIsOpen}>
        <Text>モーダル</Text>
      </BottomDrawer>
      <FAB
        style={styles.fab}
        small
        icon="menu"
        onPress={() => {
          setIsOpen(!isOpen);
        }}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#a29bfe',
    alignItems: 'center',
    justifyContent: 'center',
  },
  fab: {
    position: 'absolute',
    margin: 16,
    right: 0,
    bottom: 0,
  },
});

この記事を書いているときに作っていたアプリ