📖

FlutterでFacebookログインを実装する

2022/12/31に公開

前提

  • Facebook DeveloperでアプリIDの作成が済んでる。
  • Firebaseプロジェクトの作成が済んでいる。

Facebook/iOS側/Android側の設定

以下のリンクの手順に沿って設定を進めます。
(項目が少し増えてたりしますが、概ね以下のサイトの方法で賄えます。)
https://blog.dalt.me/2200

iOSの最終設定

info.plist
	<key>CFBundleURLTypes</key>
	<array>
		<dict>
			<key>CFBundleTypeRole</key>
			<string>Editor</string>
			<key>CFBundleURLName</key>
			<string>Bundle ID</string>
			<key>CFBundleURLSchemes</key>
			<array>
				<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
			</array>
		</dict>
		// 以下追加
		<dict>
			<key>CFBundleURLSchemes</key>
			<array>
				<string>fb$(FACEBOOK_APP_ID)</string> // 頭にfbを忘れずに
			</array>
		</dict>
	</array>
	<key>FacebookAppID</key>
	<string>$(FACEBOOK_APP_ID)</string>
	<key>FacebookClientToken</key>
	<string>$(FACEBOOK_CLIENT_TOKEN)</string>
	<key>FacebookDisplayName</key>
	<string>$(APP_ID_SUFFIX)アプリの名前</string>
	<key>LSApplicationQueriesSchemes</key>
	<array>
		<string>fbapi</string>
		<string>fb-messenger-share-api</string>
	</array>

Androidの最終設定

デフォルトでstrings.xmlがなかったのでファイルを新規作成しました。

android/app/src/main/res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="facebook_app_id">1234567890123456</string>
    <string name="fb_login_protocol_scheme">fb1234567890123456</string>
    <string name="facebook_client_token">***********************</string>
</resources>
android/app/src/main/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.hogehoge">

    <uses-permission android:name="android.permission.INTERNET" />
    // queriesを追加
    <queries>
        <provider android:authorities="com.facebook.katana.provider.PlatformProvider" />
    </queries>
        // queriesを追加ここまで
    <application
        android:label="${appNamePrefix}アプリの名前"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
              />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>

            <meta-data
                android:name="flutter_deeplinking_enabled"
                android:value="true" />

            <intent-filter android:autoVerify="true">
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data
                    android:scheme="https"
                    android:host="hogehoge.page.link" />
            </intent-filter>
        </activity>

        <!-- image croper start -->
        <activity
            android:name="com.yalantis.ucrop.UCropActivity"
            android:screenOrientation="portrait"
            android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
        <!-- image croper end -->
        
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
	// 追加
        <!-- facebook login start -->
        <meta-data
            android:name="com.facebook.sdk.ApplicationId"
            android:value="@string/facebook_app_id" />
        <meta-data 
            android:name="com.facebook.sdk.ClientToken" 
            android:value="@string/facebook_client_token"/>        
        <!-- facebook login end -->
	// 追加ここまで
    </application>
</manifest>

Firebaseの設定

  1. AuthenticationのプロバイダでFacebookを有効にし、アプリIDとアプリシークレットをコピペします。
  2. OAuth リダイレクト URI(3つ目の黒塗り)はFacebook Developer側で設定します。

こちらも以下のサイトを参考にさせていただきました↓
https://blog.dalt.me/2197

Flutter側の実装

今回は以下のパッケージを使用します。
https://pub.dev/packages/flutter_facebook_auth

Firebaseとの連携は以下を参考にしています。
https://firebase.flutter.dev/docs/auth/social/#facebook

最終的な実装

facebook_auth.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hogehoge/log.dart';
import 'package:hogehoge/models/sns_login_result.dart';

class FacebookAuth {
  FacebookAuth() {
    facebookAuth = FacebookAuth.instance;
  }

  late FacebookAuth facebookAuth;

  Future<SNSLoginResult?> login() async {
    try {
      final loginResult = await facebookAuth.instance.login();

      if (loginResult.accessToken == null) {
        return null;
      }

      final userData = await facebookAuth.getUserData();

      final facebookAuthCredential =
          FacebookAuthProvider.credential(loginResult.accessToken!.token);

      return SNSLoginResult(
        name: userData['name'] as String,
        credential: facebookAuthCredential,
      );
    } on Exception catch (e) {
      Log.d(e);
      rethrow;
    }
  }
}

// DIできるようriver_podを使用
final facebookAuthProvider = Provider<FacebookAuth>((ref) {
  return FacebookAuth();
});
firebase_manager.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class FirebaseManager {
  FirebaseManager() {
    auth = FirebaseAuth.instance;
  }
  
  late FirebaseAuth auth;
  
  // Firebase関連のメソッドを書いてたりしますがここでは使わないので割愛
}

final firebaseManagerProvider = Provider<FirebaseManager>((ref) {
  return FirebaseManager();
});
login_repository.dart
class LoginRepository {
  LoginRepository(
    this._facebookAuth,
    this._firebaseManager,
  );

  final FacebookAuth _facebookAuth;
  final FirebaseManager _firebaseManager;

  // これをViewModelから呼び出す
  Future<Result<SNSLogin?>> facebookLogin() async {
    try {
      final facebookLoginResult = await _facebookAuth.login();

      if (facebookLoginResult == null) {
          // ログインをキャンセルした時など
        return const ApiResult.success(null);
      }

           // Firebaseにユーザを登録する
      await _firebaseManager.auth
          .signInWithCredential(facebookLoginResult.credential);

            // ViewModelにfacebookアカウントの名前を渡す
      return Result.success(
        SNSLogin(name: facebookLoginResult.name),
      );
    } on Exception catch (e) {
      return Result.failure(convertError(e));
    }
  }
}

final loginRepositoryProvider = Provider<LoginRepository>((ref) {
  final facebook = ref.read(facebookAuthProvider);
  final firebase = ref.read(firebaseManagerProvider);
  return LoginRepository(facebook, firebase);
});

あとはViewModel側でLoginRepository.を呼ぶだけです。
おわり。

Discussion