【Flutter/FirebaseAuth】Google認証でサインイン|Riverpodプロバイダを使用

今回はFirebase AuthenticationとRiverpodを使用してGoogle認証によるサインイン機能をFlutterアプリに実装します。Riverpodで使用するプロバイダはChangeNotifierProviderです。

Firebaseプロジェクトの新規作成方法から順に解説していきます。コピペ用コードだけ欲しい方は記事下をご覧ください。

\ 世界最大級のオンライン学習サービス /

目次

Firebaseプロジェクトを作成

まずは「Firebase Console」でFirebaseプロジェクトを作成します。

iOS/AndroidアプリにFirebaseを追加

Firebaseプロジェクトを作成したら、Flutterで開発中のiOSまたはAndroidアプリにFirebaseを追加します。

FirebaseAuthでプロバイダを追加

Firebaseプロジェクトの管理画面を開き「Authentication」>「Sign-in method」から「新しいプロバイダを追加」をクリック。

「Google」を選択。

Googleプロバイダを「有効にする」をオンにし、「プロジェクトの公開名」と「プロジェクトのサポートメール」を設定したら保存します。

AndroidアプリにはSHA証明書が必要

AndroidアプリでFirebaseのGoogle認証を実装するには「SHA証明書」が必要となります。

Flutter側の事前準備

パッケージの導入

$ flutter pub add firebase_core
$ flutter pub add firebase_auth
$ flutter pub add google_sign_in
$ flutter pub add flutter_riverpod
$ flutter pub add flutter_signin_button
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_signin_button/flutter_signin_button.dart';

firebase_corefirebase_authがFirebaseのパッケージ、google_sign_inがGoogle認証を行うためのパッケージ、flutter_riverpodがRiverpodのパッケージです。

flutter_signin_buttonは下画像のようなソーシャルメディアのログインボタンが簡単に作れるパッケージです。

google_sign_in導入の追加ステップ

google_sign_inを導入する際、Flutterプロジェクトで./ios/Runner/info.plistInfo.plistに下記コードを追加する必要があります。(pub.devのReadmeに指示あり)

 <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>

上記コードをコピーしたらInfo.plistを開きます。

Info.plistを開いたら上画像の位置にコードをペーストします。

Info.plistにペーストしたコードの赤枠部分をGoogleService-Info.plistにある「REVERSED_CLIENT_ID」のコード(下画像の部分)と入れ替えます。

赤枠部分を入れ替えたらInfo.plistのコメントは消去して大丈夫です。

初期コード

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

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  runApp(ProviderScope(child: MyApp()));
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/',
      routes: {
        '/': (context) => LoginScreen(),
        '/home': (context) => HomeScreen(),
      },
    );
  }
}

FirebaseとRiverpodをFlutterアプリで実装するためにFirebase.initializeAppProviderScopemainに追加します。

また今回アプリで使用するログイン画面とホーム画面(ログアウト画面)をMaterialApproutesに追加しておき、Navigatorを使用して画面遷移させます。

これで事前準備は完了です。

これから作成するFlutterアプリ

ログイン画面

ホーム画面(ログアウト画面)

これから作るアプリの「ログイン画面(LoginScreen)」と「ホーム画面(HomeScreen)」の画像がこちらです。

ホーム画面ではGoogle認証でログインしたユーザーの名前とメールアドレスが表示されます。

Google認証画面

Google認証画面はこんな感じです。

開発手順&ステップ別コード(コピペOK)

STEP
Riverpodでプロバイダを作成
STEP
ログイン画面の実装
STEP
ホーム画面(ログアウト画面)の実装

今回のステップは上記の3つです。

ステップ1:プロバイダの作成

RiverpodのChangeNotifierProviderを使用してGoogle認証で必要となるプロパティ、メソッドをプロバイダで供給します。

//ChangeNotifierProvider
final googlSignInProvider = ChangeNotifierProvider((ref) {
  return googlSignInNotifier();
});
//ChangeNotifier
class googlSignInNotifier extends ChangeNotifier {
  final _googleSignIn = GoogleSignIn();
  final _auth = FirebaseAuth.instance;

  GoogleSignInAccount? _user;
  GoogleSignInAccount get user => _user!;
  FirebaseAuth get auth => _auth;

  Future googleLogin() async {
    try {
      final googleUser = await _googleSignIn.signIn();

      if (googleUser == null) return;
      _user = googleUser;

      final GoogleSignInAuthentication googlAuth =
          await googleUser.authentication;

      final credential = GoogleAuthProvider.credential(
        accessToken: googlAuth.accessToken,
        idToken: googlAuth.idToken,
      );

      await _auth.signInWithCredential(credential);
      notifyListeners();
    } catch (e) {
      print('Error: $e');
    }
  }

  Future logout() async {
    await _googleSignIn.signOut();
    FirebaseAuth.instance.signOut();
  }
}

googleLoginはログイン画面のログインボタン、logoutはホーム画面のログアウトボタンで使用します。

ステップ2:ログイン画面のコード

class LoginScreen extends ConsumerWidget {
  const LoginScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final googleProvider = ref.watch(googlSignInProvider);

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('FlutterZero')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              SignInButton(
                Buttons.Google,
                onPressed: () async {
                  await googleProvider.googleLogin();
                  if (googleProvider.auth.currentUser != null) {
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                        builder: (context) => const HomeScreen(),
                      ),
                    );
                  }
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

ステップ3:ホーム画面(ログアウト画面)のコード

class HomeScreen extends ConsumerWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final googleProvider = ref.watch(googlSignInProvider);

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Home')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('Name: ' + googleProvider._user!.displayName.toString()),
              Text('Email: ' + googleProvider._user!.email.toString()),
              SizedBox(height: 20),
              SignInButtonBuilder(
                icon: Icons.logout,
                text: 'Log Out',
                backgroundColor: Colors.grey,
                onPressed: () {
                  googleProvider.logout();
                  Navigator.pop(context);
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

サンプルコード

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

class googlSignInNotifier extends ChangeNotifier {
  final _googleSignIn = GoogleSignIn();
  final _auth = FirebaseAuth.instance;

  GoogleSignInAccount? _user;
  GoogleSignInAccount get user => _user!;
  FirebaseAuth get auth => _auth;

  Future googleLogin() async {
    try {
      final googleUser = await _googleSignIn.signIn();

      if (googleUser == null) return;
      _user = googleUser;

      final GoogleSignInAuthentication googlAuth =
          await googleUser.authentication;

      final credential = GoogleAuthProvider.credential(
        accessToken: googlAuth.accessToken,
        idToken: googlAuth.idToken,
      );

      await _auth.signInWithCredential(credential);
      notifyListeners();
    } catch (e) {
      print('Error: $e');
    }
  }

  Future logout() async {
    await _googleSignIn.signOut();
    FirebaseAuth.instance.signOut();
  }
}

final googlSignInProvider = ChangeNotifierProvider((ref) {
  return googlSignInNotifier();
});

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  runApp(ProviderScope(child: MyApp()));
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/',
      routes: {
        '/': (context) => LoginScreen(),
        '/home': (context) => HomeScreen(),
      },
    );
  }
}

class LoginScreen extends ConsumerWidget {
  const LoginScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final googleProvider = ref.watch(googlSignInProvider);

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('FlutterZero')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              SignInButton(
                Buttons.Google,
                onPressed: () async {
                  await googleProvider.googleLogin();
                  if (googleProvider.auth.currentUser != null) {
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                        builder: (context) => const HomeScreen(),
                      ),
                    );
                  }
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class HomeScreen extends ConsumerWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final googleProvider = ref.watch(googlSignInProvider);

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Home')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('Name: ' + googleProvider._user!.displayName.toString()),
              Text('Email: ' + googleProvider._user!.email.toString()),
              SizedBox(height: 20),
              SignInButtonBuilder(
                icon: Icons.logout,
                text: 'Log Out',
                backgroundColor: Colors.grey,
                onPressed: () {
                  googleProvider.logout();
                  Navigator.pop(context);
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

一緒に読みたい

参考

  • URLをコピーしました!
目次