【Flutter】showModalBottomSheetでiOS風のUIを実現する

# はじめに
Flutterにはマテリアルデザインのボトムシート(画面下端から上端にスライドして表示されるシート)を簡単に実装する`showModalBottomSheet`というメソッドが用意してあります。

本記事では`showModalBottomSheet`を使用して、シートの角の丸いiOS風UIにカスタムする方法を解説したいと思います。

# 目標
  `showModalBottomSheet`とボトムシートをカスタムし、iOS風のUIを実現する。

 # 実装

 サンプルを実際に動かした様子は次のとおりです。
画面中央の青いボタンを押下すると、画面下部からシートが出現します。

<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3119758/837693f0-2346-c0cc-eff8-356a38764cc8.png">

<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3119758/cb006660-5f3b-b7a8-58c5-8fffd5cb03af.png">


 サンプルコードは次の通りになります。順番に解説していきます。

 ```dart: サンプルコード
class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: GestureDetector(
          onTap: () async {
            await showModalBottomSheet<void>(
                context: context,
                backgroundColor: Colors.transparent,
                isScrollControlled: true,
                enableDrag: true,
                barrierColor: Colors.black.withOpacity(0.5),
                builder: (context) {
                  return _BottomSheet();
                });
          },
          child: Container(
            padding: EdgeInsets.all(5),
            decoration: BoxDecoration(
                color: Colors.blue, borderRadius: BorderRadius.circular(4)),
            child: Text(
              'showModalBottomSheet',
              style: TextStyle(color: Colors.white),
            ),
          ),
        ),
      ),
    );
  }
}

class _BottomSheet extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      height: double.infinity,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.only(
          topLeft: Radius.circular(10),
          topRight: Radius.circular(10),
        ),
      ),
      margin: EdgeInsets.only(top: 80),
      child: Center(
        child: Text('modal bottom sheet'),
      ),
    );
  }
}
 ```

 ## showModalBottomSheetの実行

ボトムシートを表示したい動作のコールバックに、`showModalBottomSheet`を設定します。
`showModalBottomSheet`はシートが閉じられる際に受け渡される、任意のresultの型を指定することができます。
今回のサンプルでは特にシートから結果が帰らないので`void`としています。
またこのケースではawaitする必要もありません。
詳しくはshowModalBottomSheetについての公式ドキュメントをご覧ください。

https://api.flutter.dev/flutter/material/showModalBottomSheet.html

## showModalBottomSheetのパラメータ

`showModalBottomSheet`では多くのパラメータを設定できます。
そのうち、サンプルで使用しているものについて解説します。

- context
BuildContextです。必須の引数となります。
contextについてはここでは多くを解説しませんが、大雑把に表現すると、ツリーのどこにそのウィジェットが配置されるべきかの情報をもつオブジェクトという理解で良いかと思います。

- builder
必須の引数です。
`showModalBottomSheet`によって表示するウィジェットのビルダーになります。

- backgroundColor
シート表示時、画面上で、`builder`のコールバックが返却するウィジェット以外の領域を塗りつぶす色の指定になります。
今回シート外の領域は透明であって欲しいので`Colors.transparent`を指定します。
注意したいのが次に説明する`barrierColor`との違いで、`backgroundColor`が指定するのはあくまで`showModalBottomSheet`によって表示される一枚のウィジェットの`builder`によって生成されるウィジェット以外の領域ということです。
`barrierColor`が指定するのは`showModalBottomSheet`によって表示されるウィジェットの外側の領域のカラーになります。
詳しくは下に示す図をご覧ください。スワイプでシートを押し下げた様子を示しています。黄色の部分が`backgroundColor`になります。

<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3119758/8feee818-1901-4e5b-f0a3-fcb0cbe008c9.jpeg">


- barrierColor
`showModalBottomSheet`によって表示されるウィジェットの外側の領域のカラーです。
通常のUIでは下の画面が見えるよう、透過がかけられたカラーになるかと思います。

- isScrollControlled
シートがスクローラブルなウィジェットの時、シートをスクロールのためのドラッグ可能にする設定値です。
シートの中で`ListView`ウィジェットなどを使用しているとき`true`に設定します。

- enableDrag
表示したシートを指でスワイプ操作できるかどうかのbool値です。


## ボトムシート
シートとして表示したいウィジェットを`builder`のコールバック内で返却します。
任意のウィジェットで構いませんが、サンプルのようにするとiOS風のシート上端の角が丸いシートが実現できます。
`top`とのマージンは、画面上端とシート間の隙間の大きさとなります。
`decoration`で上端の角のみ`BorderRadius`を指定することで、シート上端の角に丸みを持たせています。
サンプルでは対処しておりませんが、必要に応じて、`SafeArea`の対応を行った方が良いかと思います。


# おわりに
本記事では`showModalBottomSheet`の基本的な使用法をご紹介しました。
次回は`showModalBottomSheet`でresultが返って来るケースの利用法を解説したいと思います。