Things of interesting

プログラミングに関する技術ネタの備忘録

Flutterでfirebase+Blocなアプリケーションを作る(その2)

やること

Flutter入門としてサンプルを使ったデモアプリを作成します。

はじめに

前回からの変更点

前回flutter_blocというライブラリを使ってデモアプリを動かしてみたのですが、かなりReduxよりのアーキテクチャになっていて、 例えばFirestoreのSnapshotを参照したりすると結構面倒な実装になりました。。。

blocクラスとのIFがEvent/Stateになっていて直接Widget側にはStreamを渡せない) hnak.hatenablog.jp

複雑な状態管理を行うのであればいいのかも知れませんが、簡単なアプリを作るにはやりすぎな気もするので シンプルなbloc_providerのライブラリをベースに作り直すことにしました。

こちらのライブラリを使わせていただきました、今回のデモアプリもexample/complexをベースしています。 pub.dev

作ったもの

Githubにあげてます。 github.com

Firebase ログインの実装フロー

Step1. ログインボタンの呼び出し

lib/screens/login/login_form.dart
このクラスでログイン画面を構成しています。
下の方でGoogleLoginButtonのWidgetを作っています。
※通常のログインは適当実装なので動くかわかりません、Googleログインを使ってください。

lib/screens/login/google_login_button.dart このクラスでGoogleログインボタンを作り、ボタンを押すことでloginBlocのSinkにデータが流れます。

      onPressed: () {
        loginBloc.googleLoginSink.add(null);
      },

Step2. ログインのビジネスロジック処理

lib/bloc/login/login_bloc.dart
ここではログインの状態管理とGoogleログインの実行を行なっています。
signInWithGoogleが成功するとログイン状態(LoginState)がsuccessになります。

    _googleLoginController.stream.listen((onData) async {
      try {
        _loginState.add(LoginState.loading());
        await userRepository.signOut();
        await userRepository.signInWithGoogle();
        _loginState.add(LoginState.success());
      } catch (_) {
        _loginState.add(LoginState.failure());
      }
    });

Step3. ログインの状態管理と画面遷移

lib/bloc/login/login_state.dart LoginStateという状態管理用のクラスを定義していますが、flutter_blocライブラリのStateクラスではなくてStreamを流れるただのImmutableなオブジェクトです。
LoginFormではこのLoginStateの状態を監視していて状態が変わることで処理が動くようになっています。
初期化処理の中でLoginStateがSuccessになると別の画面へ遷移するようにしています。

  @override
  void initState() {
    super.initState();
    loginBloc = LoginProvider.of(context);
    loginBloc.loginStateStream.listen((onData) {
      if (onData.isSuccess) {
        Navigator.of(context).pushNamed(ProductScreen.routeName);
      }
    });
    _emailController.addListener(_onEmailChanged);
    _passwordController.addListener(_onPasswordChanged);
  }

このLoginStateは画面コンポーネントのバリデーションにも使用しており、例えばパスワードのInputでは以下のように処理しています。
少しわかりにくいですが、bulderに記述されたsnapshotの中身は、streamで記載されたloginStateStreamから渡されるオブジェクトになります。
なので snapshot.data.isPasswordValid は LoginStateのisPasswordValidを呼び出すことになります。

StreamBuilder<LoginState>(
                    stream: loginBloc.loginStateStream,
                    initialData: loginBloc.loginStateStream.value,
                    builder: (context, snapshot) => TextFormField(
                          controller: _passwordController,
                          decoration: const InputDecoration(
                            icon: Icon(Icons.lock),
                            labelText: 'Password',
                          ),
                          obscureText: true,
                          autovalidate: true,
                          autocorrect: false,
                          validator: (_) {
                            return !snapshot.data.isPasswordValid
                                ? 'Invalid Password'
                                : null;
                          },
                        ),
                  ),

さいごに

こんな感じでBlocなアプリケーションにもFirebaseを組み込むことができました。
あんまりFlutterの勉強時間が取れていませんが、これからFlutterを使って色々なアプリを作ってみたいと思います!