【Flutter】処理が成功した時だけシートを自動で閉じる


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

`showModalBottomSheet`の詳しい使い方については前回の記事をご覧ください。

https://qiita.com/jimborie/items/16a71ef407fffb588404


 # 目標
 モーダルボトムシートを開いた状態で、何らかの処理を行い、成功の場合だけシートを自動で閉じる。

 # 実装

前回記事より、`showModalBottomSheet`の基本的な実装は次の通りです。


```dart:showModalBottomSheetの使い方
  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(); //シートUI
          });
        },
    child: Container(
      padding: EdgeInsets.all(5),
      decoration: BoxDecoration(
        color: Colors.blue, borderRadius: BorderRadius.circular(4)),
      child: Text(
        'showModalBottomSheet',
        style: TextStyle(color: Colors.white),
      ),
    ),
  ),
```

ここでは何らかのメニューリストのイメージで、まずボトムシート状にリスト状にウィジェットを配置し、
このリストをタップすることで、ある処理が実行され成功した時のみシートが自動で閉じる、という動きを実装していきたいと思います。

## リストメニューの配置
ここではリストメニューを`Container`ウィジェットで作成していきます。
それぞれ`GestureDetector`でラップすることで、タップされた時の処理を実装できるようにします。

## 実行する処理の実装
例として以下のようなリストをタップされた時の処理を実装します。
リストの`GestureDetector`の`onTap`コールバックに任意の処理を指定します。
mathパッケージをインポートし、`nextBool`でランダムなブール値を生成します。TRUEなら処理成功、FALSEなら失敗とし、try-catchで囲み、エラーハンドリングを行えるようにしておきます。

## シートを閉じる動きの実装
ここまでの流れで、ボトムシート自体の実装およびシートを開く`showModalBottomSheet`の実装が完了しました。
しかしボトムシートを閉じる処理がありません。
実はボトムシートのクラスである`ModalBottomSheet`クラスは、Flutterにおける通常の画面遷移処理においても継承し利用されている`ModalRoute`クラスを継承しており、`Navigator`による画面遷移が可能となっております。

https://api.flutter.dev/flutter/widgets/Navigator/pop.html

https://github.com/flutter/flutter/blob/f468f3366c/packages/flutter/lib/src/widgets/navigator.dart#L2514

したがって`pop`を実行すれば、シートを閉じることができます。
以下の処理を追加し、処理成功時シートが自動で閉じるようにします。

```dart:
Navigator.of(context).pop();
```

## 成功時だけシートを閉じる

今回擬似的に処理の成功・不成功を発生させるため、ランダムにブール値を発生させ、TRUEなら成功、FALSEなら失敗、という処理を実装しました。
処理が失敗した時にはエラーメッセージを表示し、処理が成功した時にはシートが自動で閉じる（popされる）ようにします。

以上の実装結果がこちらです。

```dart:
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: ListView.builder(
        physics: const BouncingScrollPhysics(),
        itemCount: 3, //適当なメニュー数
        itemBuilder: (context, index) {
          return GestureDetector(
            behavior: HitTestBehavior.opaque,
            onTap: () {
              try {
                //メニューを押した時に実行したい処理
                //ここではランダムなBool値を生成し、TRUE：成功、FALSE:失敗とする
                if (math.Random().nextBool()) {
                  //成功したら画面を閉じる
                  Navigator.of(context).pop();
                } else {
                  throw Error();
                }
              } catch (_) {
                //処理失敗時
                await showDialog(
                  context: context,
                  builder: (context) {
                    return const SimpleDialog(
                      titlePadding: EdgeInsets.all(20),
                      title: Text('エラーが発生しました'),
                    );
                  },
                );
              }
            },
            child: Container(
              decoration: BoxDecoration(
                color: Colors.white,
                border: Border(
                  bottom: BorderSide(color: Colors.grey),
                ),
              ),
              width: double.infinity,
              padding: EdgeInsets.only(top: 20, bottom: 20),
              child: Center(
                child: Text(index.toString()), //適当なメニュー名
              ),
            ),
          );
        },
      ),
    );
  }
}
```


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3119758/ed923f1e-21fb-578b-3d00-6fb1ed32fb6d.png">
　　<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3119758/daaa6065-7418-52aa-9b1f-3ade046707c4.png">


# おわりに

今回の記事ではリストメニューが表示されたモーダルボトムシートを想定して、各メニューの処理実行が成功した時だけシートを閉じる実装方法をご紹介しました。
この動作は、処理が成功した時は自動でメニューを閉じ、実行結果がエラーの場合のみユーザにリトライ動作を促すことを意図して設計しております。
次回は成功・失敗よりもさらに詳細な処理結果に応じた画面遷移の方法をご紹介したいと思います。