FlutterBlocでダイアログ表示やtoast表示

`flutter_bloc` による状態管理 + プログレスダイアログ表示、Toast表示のようなサンプルが見つからなかったので書いておきます。

### 実現したいこと

`flutter_bloc` で状態管理をしている画面で↓のようなシンプルな入力フォームをつくりたかった。
![sample.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510027/7c514f88-24ff-4f43-0681-b77d31f1e67d.gif)

### 使用したライブラリ
[flutter_bloc](https://pub.dev/packages/flutter_bloc)
[bloc](https://pub.dev/packages/bloc)
[fluttertoast](https://pub.dev/packages/fluttertoast)


### ソースコード

ダイアログ表示やToast表示は上位にBlocListnerを作れば簡単に出来そうでした。
`fetch` や `insert` はダミーです。



○SamplePage

```dart:sample_page.dart
class SamplePage extends StatelessWidget {
  const SamplePage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocProvider(
        create: (context) => SampleBloc(repository: SampleFirestoreRepositry()),
        child: const _SampleBody(),
      ),
    );
  }
}

class _SampleBody extends StatelessWidget {
  const _SampleBody({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocListener<SampleBloc, SampleState>(
      bloc: BlocProvider.of<SampleBloc>(context)..add(InitEvent()),
      listener: (context, state) {
        // ダイアログ表示
        if (state is SaveProgressState) {
          _showLoadingDialog(context);
        }

        // ダイアログ閉じてToast表示
        if (state is SaveSuccessState) {
          Navigator.pop(context);
          Fluttertoast.showToast(msg: "保存が完了しました");
        }
      },
      child: BlocBuilder(
        bloc: BlocProvider.of<SampleBloc>(context),
        condition: (prev, state) {
          return state is! SaveProgressState && state is! SaveSuccessState;
        },
        builder: (context, state) {
          if (state is DispSuccessState) {
            return _buildSuccess(context);
          }

          return Center(
            child: CircularProgressIndicator(),
          );
        },
      ),
    );
  }

  Widget _buildSuccess(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text("何かしらの入力フォーム"),
        TextFormField(onSaved: (_text) => {}),
        RaisedButton(
          onPressed: () =>
              BlocProvider.of<SampleBloc>(context).add(SaveEvent()),
          child: Text("保存"),
        )
      ],
    );
  }

  void _showLoadingDialog(BuildContext context) {
    showDialog(
      context: context,
      barrierDismissible: false,
      builder: (BuildContext context) {
        return new Dialog(
          child: Container(
            margin: EdgeInsets.all(20.0),
            child: new Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                new CircularProgressIndicator(),
                new Container(
                    margin: new EdgeInsets.all(10.0), child: new Text("保存中")),
              ],
            ),
          ),
        );
      },
    );
  }
}

```

○SampleBloc

```dart:sample_bloc.dart
class SampleBloc extends Bloc<SampleEvent, SampleState> {
  final SampleRepository _repository;

  SampleBloc({SampleRepository repository})
      : assert(repository != null),
        _repository = repository,
        super();

  @override
  SampleState get initialState => InitState();

  @override
  Stream<SampleState> mapEventToState(SampleEvent event) async* {
    if (event is InitEvent) {
      yield* _init(event);
    } else if (event is SaveEvent) {
      yield* _save(event);
    }
  }

  Stream<SampleState> _init(InitEvent event) async* {
    yield DispProgressState();

    try {
  
      // 2秒待つだけ
      await _repository.fetch();

      yield DispSuccessState();
    } catch (_) {
      yield DispFailureState(_);
    }
  }

  Stream<SampleState> _save(SaveEvent event) async* {
    yield SaveProgressState();

    try {

      // 2秒待つだけ
      await _repository.insert();
      yield SaveSuccessState();
    } catch (_) {
      yield SaveFailureState(_);
    }
  }
}
```




### 他力本願寺:shinto_shrine:

Flutter勉強中です。
勉強中の気づきや指摘等頂いた際には、内容を更新していきます。
（訳：キレイな書き方教えてください:bow:）