アプリ開発

【集約】flutterでfirebaseAuthを使ったログイン機能の実装

今回は、flutterでfirebaseAuthを用いた、

・googleログイン

・appleログイン

・emailログイン

・匿名ログイン

の実装方法を解説していきます。

ログインをしてから、firebaseAuthのユーザーidを取得するまでをそれぞれ解説していきます。

公式ドキュメントの例だとわかりにくく、ログイン後にユーザーidを取得して、いろいろ処理したかったのに、いろいろ検索かけたので。。

ここに書き残そうと思いました。

firebaseの導入がまだの方は、以下の記事に従って行ってください。

公式ドキュメント

Qiitaの記事では、これを参考にした気がします。

flutterとfirebaseを連携させる事ができてから以降の手順を解説していきます。

Flutter× FireabaseAuthでのGoogleログイン

まずは、FirebaseAuthを用いたGoogleログインについて解説していきます。

firebaseAuth・パッケージの導入

とりあえず、3つのパッケージをインストールしましょう。

firebase_coreとfirebase_authのインストールはごくシンプルです。

公式ドキュメントに従えば、1分ずつでできるでしょう。


 

Googleサインインを実装する前に必要な手順が以下のサイトに載っています。

僕はこれを飛ばすと、どこかでうまくいかなくなると思います^^;

ここでやることは以下の3つ

1.Podfileに以下を追加してpod install

pod 'GoogleSignIn'

 

2.OAuth Cliant IDの取得

こちらもリンクを貼った上記のサイトから取得できます。

 

3.URL schemeをプロジェクトに追加

2を完了後にclient IDが発行されます。

そのclient IDの順序を入れ替えたもの(上記の記事では、The reversed client ID)をXcodeに追加する必要があります。

  1. Open your project configuration: double-click the project name in the left tree view. Select your app from the TARGETS section, then select the Info tab, and expand the URL Types section.
  2. Click the + button, and add your reversed client ID as a URL scheme.The reversed client ID is your client ID with the order of the dot-delimited fields reversed. For example:

Runner⇨TARGETS(黄色いアプリのアイコンのとこ)⇨Infoタブ⇨URL Typesの中の+ボタンの中にThe reversed client IDを入れます。

これで下準備は完了です!

 

google_sign_inのみは、iosで以下の設定が必要です。

<!-- Put me in the [my_project]/ios/Runner/Info.plist file -->
<!-- Google Sign-in Section -->
<key>CFBundleURLTypes</key>
<array>
	<dict>
		<key>CFBundleTypeRole</key>
		<string>Editor</string>
		<key>CFBundleURLSchemes</key>
		<array>
			<!-- TODO Replace this value: -->
			<!-- Copied from GoogleService-Info.plist key REVERSED_CLIENT_ID -->
			<string>com.googleusercontent.apps.861823949799-vc35cprkp249096uujjn0vvnmcvjppkn</string>
		</array>
	</dict>
</array>
<!-- End of the Google Sign-in Section -->

プロジェクトのios内の/Runner/Info.plistのなかに、上記の追加が必要です。

また、完全コピペでは動かないので注意です。

(いろいろコメントが入ってますが、消してもらってOKです。)

<!-- Copied from GoogleService-Info.plist key REVERSED_CLIENT_ID -->

とコードの中に書いてある通り、

com.googleusercontent……の部分は、あなたのGoogleService-Info.plistの中の<key>REVERSED_CLIENT_ID</key>からコピペしてくる必要があります。

下準備で発行してもらったものでもOK!

そんなに大変ではありませんね。

firebaseAuthの設定

グーグルログインを実装する上ので注意点は以下の2つです。

・サポートメールが必須

・androidの場合は、SHA 証明書フィンガープリントの追加が必須

まずは、firebaseのAuthenticationのSign-in methodからGoogleを有効にしてください。

その時に以下のような画面が出ると思います。

モザイクが雑ですが、丸で囲った部分がよく引っかかるポイントです。

両方必須なので気をつけてください。

SHA1フィンガープリントを追加しておかないと、androidでgoogleログインができません。

取得方法

・Mac

keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

・Windows

keytool -list -v -keystore "\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android

 

SHA1を追加する場所は、左のメニューの

設定⇨プロジェクトを設定

のページの下の方にあります。

これで、SHA1フィンガープリントを追加したら、設定完了です。

Googleログインのコード例

classごと貼り付けておきます。

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';

class GoogleSignInScreen extends StatefulWidget {
  @override
  _GoogleSignInScreenState createState() => _GoogleSignInScreenState();
}

class _GoogleSignInScreenState extends State<GoogleSignInScreen> {
  final GoogleSignIn _googleSignIn = GoogleSignIn();
  final FirebaseAuth _auth = FirebaseAuth.instance;
  String userId;

  Future<String> _handleSignIn() async {
    GoogleSignInAccount googleCurrentUser = _googleSignIn.currentUser;
    try {
      googleCurrentUser ??= await _googleSignIn.signInSilently();
      googleCurrentUser ??= await _googleSignIn.signIn();
      if (googleCurrentUser == null) {
        return null;
      }

      final GoogleSignInAuthentication googleAuth =
          await googleCurrentUser.authentication;
      final AuthCredential credential = GoogleAuthProvider.getCredential(
        accessToken: googleAuth.accessToken,
        idToken: googleAuth.idToken,
      );
      final FirebaseUser user =
          (await _auth.signInWithCredential(credential)).user;
      print('signed in ${user.displayName}');

      return user.uid;
    } catch (e) {
      print(e);
      return null;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('googleログイン'),
        centerTitle: true,
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            OutlineButton(
              child: Text('googleログイン'),
              onPressed: () async {
                final result = await _handleSignIn();
                setState(() {
                  userId = result;
                });
              },
            ),
            if(userId != null)
              Text(userId)
          ],
        ),
      ),
    );
  }
}

main.dartのMaterialAppの中のhomeにGoogleSignInScreen()を入れていただければ動きます。

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
//ここ
        home: GoogleSignInScreen());
  }
}

iosシミュレータの画面は以下のような感じに。

初期状態

上部にgoogleログインボタンがあります。

ボタンを押して、googleにログインすると、firebaseAuthでのユーザーidがボタンの下に表示されるようになります。

ログイン状態

こんな感じです。

これでGoogleログインは完了です。

続いて、Appleログインを紹介します。

Flutter× FireabaseAuthでのAppleログイン

現在2020年8月25日ですが、つい最近からiosでは、Appleログインが必須になりました。

最初は知らずに、普通にRejectされました^^;

僕が苦しんだところ経験なども交えて、iosでのAppleログインについて解説していきます。

パッケージの導入(sign_in_with_apple)

firebaseAuthのパッケージの導入などは、最初に紹介したGoogleログインで触ったので、これ以降は、Packageの導入から説明を開始していきます。

FirebaseAuthでAppleを有効にしておいてください。

そして、以下のパッケージ(sign_in_with_apple)をインストールしてください。

別のパッケージ(apple_sign_in)もあったのですが、flutter側のおすすめがsign_in_with_appleのようなので、今回は、sign_in_with_appleを使用しています。

公式ドキュメントに沿ってインストールしましょう。

軽くこっちでも説明しますね。

1.pubspec.yamlに以下を追加。

dependencies: sign_in_with_apple: ^2.5.2

 

2.コマンドラインでflutter pub get

 

3.import 'package:sign_in_with_apple/sign_in_with_apple.dart';をインポート。

これからが重要です。

Apple Developerに登録する

あなたが普段からアプリを開発しているなら、おそらく加入していると思いますが、

Apple Developerに登録していないと、使えないです。

Before you can start integrating (or even testing) Sign in with Apple you need a paid membership to the Apple Developer Program. Sign in with Apple is one of the restricted services which is not available for free with just an Apple ID (source).

登録していない方は、まずは登録からですね。

Certificates, Identifiers & Profilesにアプリを追加

ここにアプリを追加する必要があります。

まず、画面左上にある「identifiers+」を押します。

その後、App Idsを選択して、Continueを押す。

その次のSelect TypeはAppを選択。

その後の画面では、

・アプリのBundle IDの入力

・desctiptionの入力 (自分のアプリをわかれば良い)

・Capablitiesの中のSign in With Appleにチェック。

Continueしてから、登録を完了してください。

もしも、すでにアプリが登録されている場合は、CapablitiesのSign in With Appleが選択されているかだけ確認してください

その後、XcodeでもRunnerのSigning & CapabilitiesにSign In WIth Appleが追加されているか確認してください。

XcodeのRunnerの「+Capabilities」から同様にSign In With Appleを追加できます。(上の写真を参考に)

この過程をやっていなくて、うまくいかないという例が山のようにあったので、気をつけてください。

ここまでやって、やっとコードに入っていきます。

Appleログインの実装コード例

コピペで動かせるコードは以下の通りです。

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart';

class AppleSignInArea extends StatefulWidget {
  @override
  _AppleSignInAreaState createState() => _AppleSignInAreaState();
}

class _AppleSignInAreaState extends State<AppleSignInArea> {
  final FirebaseAuth _auth = FirebaseAuth.instance;
  String userId;

  //ログインに失敗したらスナックバーを出す
  void showSnackBar(BuildContext context) {
    final snackBar = SnackBar(
      content: const Text('ログインに失敗ました'),
      backgroundColor: Colors.red,
    );
    Scaffold.of(context).showSnackBar(snackBar);
  }

  Future<String> _appleSignIn(BuildContext context) async {
    try {
      final appleIdCredential = await SignInWithApple.getAppleIDCredential(
        scopes: [
          AppleIDAuthorizationScopes.email,
          AppleIDAuthorizationScopes.fullName,
        ],
      );

      const oAuthProvider = OAuthProvider(providerId: 'apple.com');
      final credential = oAuthProvider.getCredential(
        idToken: appleIdCredential.identityToken,
        accessToken: appleIdCredential.authorizationCode,
      );
      final authResult = await _auth.signInWithCredential(credential);
      final firebaseUserId = authResult.user.uid;
      return firebaseUserId;
    } catch (e) {
      print(e);
      showSnackBar(context);
      return null;
    }
  }

  @override
  Widget build(BuildContext context) {
    //スナックバーを表示するため
    return Builder(
      builder: (context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Sign in with apple'),
          ),
          body: Column(
            children: <Widget>[
              Container(
                width: 300,
                child: SignInWithAppleButton(
                    borderRadius: const BorderRadius.all(Radius.circular(30)),
                    onPressed: () async {
                      final uid = await _appleSignIn(context);
                      setState(() {
                        userId = uid;
                      });
                    }),
              ),
              if (userId != null) Text(userId)
            ],
          ),
        );
      },
    );
  }
}

classごと載せているので、コピってもらって構いません。

ログイン部分だけ抜粋します。

final appleIdCredential = await SignInWithApple.getAppleIDCredential(
        scopes: [
          AppleIDAuthorizationScopes.email,
          AppleIDAuthorizationScopes.fullName,
        ],
      );

      const oAuthProvider = OAuthProvider(providerId: 'apple.com');
      final credential = oAuthProvider.getCredential(
        idToken: appleIdCredential.identityToken,
        accessToken: appleIdCredential.authorizationCode,
      );

とりあえず、この部分がログインですね。

FirebaseAuthのユーザーidまで欲しい場合は、全体コードを真似てもらえば取得できます。

僕が10時間くらいハマったポイント

テストはiosSimulatorではなく、実機で行ってください

実機でログインできなかったら、何かしらの工程が抜けているかもしれません。

しかし、どうも完璧にやったと思っても、Simulatorでは、原因不明のエラーが何回もでてしまいました。。

エラー内容もunknownだったので、どうしようもなかったです。

しかし、実機でテストしたらうまくいきました。

ただ一つ言えることは、

実機でテストしてください。

Flutter×FirebaseAuthでのEmailログイン

ログインの王道のGoogleログイン、iosで必須のAppleログインと同様によくあるのが、メールでのログインです。

google_sign_in、sign_in_with_appleよりも簡単です。

firebaseAuthでEmailを有効に

メール / パスワードというところです。

僕は、メールリンク(パスワードなしでログイン)は有効にはしていません。

また、パッケージとしては、firebaseAuthのみでOKです。

その他に必要なパッケージはないので、そのまま実装していきます。

メールログインの実装コード例

以下で紹介するコードをコピペしていただければ、以下のようになります。

メールとパスワードを入力するフォームがあり、パスワードは、見えないようにもできる仕組みです。よくあるやつです。

今回は、アカウントを持っていないので、Emailで登録を押すと、firebaseAuthからuserIdを取得でき、以下のようにユーザーIDが表示されます。

登録ボタンの下に表示されている文字列がfirebaseAuthでのユーザーIDです。

コードは以下の通り。

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

class EmailSignInScreen extends StatefulWidget {
  @override
  _EmailSignInScreenState createState() => _EmailSignInScreenState();
}

class _EmailSignInScreenState extends State<EmailSignInScreen> {
  final FirebaseAuth _auth = FirebaseAuth.instance;
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  String userId;
  bool _success;
  bool _isObscure = true;

  //ログイン
  void _login(BuildContext context) async {
    try {
      final AuthResult result = await FirebaseAuth.instance
          .signInWithEmailAndPassword(
              email: _emailController.text, password: _passwordController.text);

      if (result.user != null) {
        setState(() {
          _success = true;
          if (_success == true) {
            setState(() {
              userId = result.user.uid;
            });
          }
        });
      } else {
        _success = false;
      }
    } catch (e) {
      print(e);
    }
  }

  //新規登録
  void _register(BuildContext context) async {
    try {
      final FirebaseUser user = (await _auth.createUserWithEmailAndPassword(
        email: _emailController.text,
        password: _passwordController.text,
      ))
          .user;

      if (user != null) {
        setState(() {
          _success = true;
          if (_success == true) {
            userId = user.uid;
          }
        });
      } else {
        _success = false;
      }
    } catch (e) {
      print(e);
    }
  }

  Widget _buildEmailInputField() {
    return Container(
      color: Colors.white,
      child: TextFormField(
        keyboardType: TextInputType.emailAddress,
        controller: _emailController,
        decoration: const InputDecoration(
            labelText: 'Email', contentPadding: EdgeInsets.only(left: 16)),
        validator: (String value) {
          if (value.isEmpty) {
            return 'メールアドレスを入力してください';
          } else if (!value.contains('@') || !value.contains('.')) {
            return '正しいフォーマットを入力してください 例) xxxx@yyy.com';
          }
          return null;
        },
      ),
    );
  }

  Widget _buildPasswordInputField() {
    return Stack(
      children: <Widget>[
        Container(
          color: Colors.white,
          child: TextFormField(
            keyboardType: TextInputType.visiblePassword,
            obscureText: _isObscure,
            controller: _passwordController,
            decoration: const InputDecoration(
                labelText: 'Password',
                contentPadding: EdgeInsets.only(left: 16)),
            validator: (String value) {
              if (value.isEmpty) {
                return 'パスワードを入力してください';
              } else if (value.length < 6) {
                return '6文字以上で設定してください';
              }
              return null;
            },
          ),
        ),
        Positioned(
          right: 8,
          child: IconButton(
            icon: Icon(Icons.remove_red_eye),
            onPressed: () {
              setState(() {
                _isObscure = !_isObscure;
              });
            },
          ),
        )
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Emailログイン'),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            SizedBox(
              height: 20,
            ),
            _buildEmailInputField(),
            SizedBox(
              height: 20,
            ),
            _buildPasswordInputField(),
            OutlineButton(
              child: Text('Emailでログイン'),
              onPressed: () {
                _login(context);
              },
            ),
            OutlineButton(
              child: Text('Emailで登録'),
              onPressed: () {
                _register(context);
              },
            ),
            if (userId != null) Text(userId)
          ],
        ),
      ),
    );
  }
}

全コードを表示すると少し長いですね。。

要所だけまとめます。

ログイン部分

final AuthResult result = await FirebaseAuth.instance
          .signInWithEmailAndPassword(
              email: _emailController.text, password: _passwordController.text);

firebaseのユーザーIDの取得やエラーハンドリング無しの、ただのログインだけならば、ここだけでOKです。

email、passwordのところは各自で入れてくださいね。

 

ユーザー登録部分

final FirebaseUser user = (await _auth.createUserWithEmailAndPassword(
        email: _emailController.text,
        password: _passwordController.text,
      )).user;

これでOKです。

_auth.のあとが違うだけで、送るパラメータも一緒です。

これは簡単でしたね。

Flutter×FirebaseAuthでの匿名ログイン

上記で紹介した3つに加え、匿名ログインも便利です。

がっつりログイン機能を実装する気はないけど、

とりあえず、ユーザーを識別したい。

ユーザーごとに何かしらのデータを保存しておきたい。

などという時に、ユーザーにとって煩わしいログインをさせず、ユーザーIDを作れちゃいます。

しれっとログインさせる機能です。

上で紹介したどのログイン方法よりも簡単に実装できます。

firebaseAuthで匿名ログインを有効化

まず、firebaseAuthで匿名ログインを有効にしましょう。

firebaseAuthの導入などは、最初に紹介しているGoogleログインのところで説明しています。

匿名ログインの実装コード例

実装すると以下のようになります。

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

class AnonymousLoginScreen extends StatefulWidget {
  @override
  _AnonymousLoginScreenState createState() => _AnonymousLoginScreenState();
}

class _AnonymousLoginScreenState extends State<AnonymousLoginScreen> {
  final FirebaseAuth _auth = FirebaseAuth.instance;
  String userId;

  @override
  void initState() {
    super.initState();
    signInAnonymous();
  }

  //匿名ログイン
  void signInAnonymous() async {
    final AuthResult result = await _auth.signInAnonymously();
    setState(() {
      userId = result.user.uid;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('匿名ログイン'),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            Text('匿名ログインでユーザーID'),
            SizedBox(
              height: 20,
            ),
            if (userId != null) Text(userId)
          ],
        ),
      ),
    );
  }
}

initStateで自動で匿名ログインし、ユーザーIDを取得しています。

取得したユーザーIDが画面に表示されていますね。

ログイン部分は、

//匿名ログイン
  void signInAnonymous() async {
    final AuthResult result = await _auth.signInAnonymously();
    setState(() {
      userId = result.user.uid;
    });
  }

これだけです。

コード量的にも、実装は超簡単なので、ちょっとしたログイン機能をつけたい!って方には、超便利です。

まとめ

いかがでしたか?

flutterでfirebaseAuthを用いて、

・Googleログイン

・Appleログイン

・メールログイン

・匿名ログイン

の実装をまとめてみました。

きっとよく使うログイン方法だと思うので、役に立つと思います。

ちょっと大きな規模のアプリを作ろうと思うと、ログイン機能は高確率で実装すると思うので、実装のたびに見てもらえるような記事になっていれば嬉しいです。