背景の見えるポップアップスクリーンの作り方

背景が見えるモーダル画面って個人的には好きです。
今回はその作成方法を紹介します。
![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43625/d11951e0-2431-cd72-d221-ea7e87981907.gif)

# 使用ライブラリ

- `@react-navigation/stack`
- `@react-navigation/native`
- `react-native-safe-area-context`
- `react-native-linear-gradient`
- `styled-components`

# 透過するモーダルページ（外枠）の作成

```tsx
const TransparentModalPage: React.FunctionComponent = ({ children }) => {
  const navigation = useNavigation();
  return (
    <LinearGradient
      colors={['rgba(0,0,0,0)', 'rgba(0,0,0,0.9)']}
      style={{ flex: 1 }}
    >
      <TouchableWithoutFeedback
        style={{ flex: 1, alignSelf: 'stretch' }}
        onPress={() => navigation.goBack()}
      >
        <View style={{ flex: 1 }} />
      </TouchableWithoutFeedback>

      <View style={{ justifyContent: 'center' }}>{children}</View>
    </LinearGradient>
  );
};
```

`ToucableWithoutFeedback`の部分で、子要素のモーダルコンテンツ（`children`）以外をタップした場合に、ページを閉じるように設定しています。

# モーダルのコンテンツを作成

```tsx
const RoundView = styled(View)`
  border-top-left-radius: 10px;
  border-top-right-radius: 10px;
  background-color: 'rgba(255,255,255,1)';
`;

export const ModalCard: React.FunctionComponent = ({ children }) => {
  return (
    <RoundView>
      <View style={{ paddingVertical: 16 }}>
        <View
          style={{
            alignSelf: 'center',
            width: 50,
            borderBottomWidth: 3,
            borderBottomColor: 'rgba(0,0,0,1)',
            borderRadius: 8,
          }}
        />
      </View>

      <View style={{ paddingHorizontal: 32, paddingBottom: 32 }}>
        {children}
      </View>

      {/* To avoid a typescript error. */}
      <SafeAreaView>
        <></>
      </SafeAreaView>
    </RoundView>
  );
};
```

`SafeAreaView`は、モーダル部分が下に詰めすぎないように制御しています。

**SafeAreaView あり／なし**
![Simulator Screen Shot - iPhone 11 Pro Max - 2020-03-22 at 08.08.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43625/6c897bcf-e2f8-3715-af15-f9bd5f87e68e.png)![Simulator Screen Shot - iPhone 11 Pro Max - 2020-03-22 at 09.21.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43625/7f9313b3-8cdd-0910-6346-7772af148ea4.png)

# 画面コンポーネントの作成

```tsx
const ModalScreen = () => (
  <TransparentModalPage>
    <ModalCard>
      <View
        style={{ height: 50, alignItems: 'center', justifyContent: 'center' }}
      >
        <Text>Modal Area</Text>
      </View>
    </ModalCard>
  </TransparentModalPage>
);
```

作成した`TransparentModalPage`, `ModalCard`に子要素を設定するだけ完了です。

# ナビゲーター・スクリーンの作成

```tsx
const Stack = createStackNavigator();
const StackNavigator = () => {
  return (
    <Stack.Navigator mode="modal" headerMode="screen">
      ...
      <Stack.Screen
        name="ModalScreen"
        component={ModalScreen}
        options={{
          headerShown: false,
          cardStyle: { backgroundColor: 'transparent', opacity: 1 },
        }}
      />
    </Stack.Navigator>
  );
};
```

`ModalScreen`の画面オプションは、`cardStyle`で背景を透明設定にします。
この設定がないと、モーダル画面にグラデーションはかかりますが、背景が表示されません。

以上で設定は完了です。

# おわりに

ここでは紹介しませんでしたが、 Animation や GestureHandler を用いると、より「アプリらしい」挙動をさせることができます。
組み合わせ次第で、いろんなモーダル画面が作れるので、ぜひ試してみてください。