Flutter对接Google登录流程

157 阅读2分钟

Flutter对接Google登录流程

1. firebase平台配置(console.firebase.google.com/)

1.1 直接创建新的Firebase项目,然后添加Android应用

image-20251016163918624.png 包名填写正确,其它不用管,无脑下一步

1.2 在身份验证中添加登录方法

image-20251016164221475.png 直接勾选启用,添加支持账号就行 image-20251016164510324.png

1.3 配置SHA-1指纹证书

image-20251016164752866.png 配置完成下载最新的json文件

2. 项目内配置

2.1 下载的文件到android\app目录下

image-20251016165406253.png

2.1 配置文件修改

android\build.gradle.kts
allprojects {
    repositories {
        google()
        mavenCentral()
    }
}
plugins {
  // ...
  // Add the dependency for the Google services Gradle plugin
  id("com.google.gms.google-services") version "4.4.3" apply false
  id("com.google.cloud.artifactregistry.gradle-plugin") version "2.2.1"
}
val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
rootProject.layout.buildDirectory.value(newBuildDir)

subprojects {
    val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
    project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
    project.evaluationDependsOn(":app")
}

tasks.register<Delete>("clean") {
    delete(rootProject.layout.buildDirectory)
}

增加这一块

plugins {
  // ...
  // Add the dependency for the Google services Gradle plugin
  id("com.google.gms.google-services") version "4.4.3" apply false
  id("com.google.cloud.artifactregistry.gradle-plugin") version "2.2.1"
}
android\settings.gradle.kts
pluginManagement {
    val flutterSdkPath = run {
        val properties = java.util.Properties()
        file("local.properties").inputStream().use { properties.load(it) }
        val flutterSdkPath = properties.getProperty("flutter.sdk")
        require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
        flutterSdkPath
    }

    includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")

    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}

plugins {
    id("dev.flutter.flutter-plugin-loader") version "1.0.0"
    id("com.android.application") version "8.7.3" apply false
    id("org.jetbrains.kotlin.android") version "2.1.0" apply false
    // Google services 插件
    id("com.google.gms.google-services") version "4.4.3" apply false
}

include(":app")

plugins增加这一块

// Google services 插件
id("com.google.gms.google-services") version "4.4.3" apply false
android\app\build.gradle.kts
plugins {
    id("com.android.application")
    id("kotlin-android")
    // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
    id("dev.flutter.flutter-gradle-plugin")
    // Add the Google services Gradle plugin
    id("com.google.gms.google-services")
}
dependencies {
  // Import the Firebase BoM
  implementation(platform("com.google.firebase:firebase-bom:34.1.0"))
  implementation("com.google.android.gms:play-services-auth")
  // TODO: Add the dependencies for Firebase products you want to use
  // When using the BoM, don't specify versions in Firebase dependencies
  implementation("com.google.firebase:firebase-analytics")
  // Add the dependencies for any other desired Firebase products
  // https://firebase.google.com/docs/android/setup#available-libraries
}
android {
    namespace = "com.your.project"
    compileSdk = flutter.compileSdkVersion
    ndkVersion = "27.0.12077973"

    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }

    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_11.toString()
    }

    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId = "com.your.project"
        // You can update the following values to match your application needs.
        // For more information, see: https://flutter.dev/to/review-gradle-config.
        minSdk = flutter.minSdkVersion
        targetSdk = flutter.targetSdkVersion
        versionCode = flutter.versionCode
        versionName = flutter.versionName
    }

    buildTypes {
        release {
            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig = signingConfigs.getByName("debug")
        }
    }
}

flutter {
    source = "../.."
}

plugins增加这一块

// Add the Google services Gradle plugin
id("com.google.gms.google-services")

增加这一块

dependencies {
  // Import the Firebase BoM
  implementation(platform("com.google.firebase:firebase-bom:34.1.0"))
  implementation("com.google.android.gms:play-services-auth")
  // TODO: Add the dependencies for Firebase products you want to use
  // When using the BoM, don't specify versions in Firebase dependencies
  implementation("com.google.firebase:firebase-analytics")
  // Add the dependencies for any other desired Firebase products
  // https://firebase.google.com/docs/android/setup#available-libraries
}

3.实际应用

封装的服务工具

import 'dart:async';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:dio/dio.dart';
import 'package:yourapp/app/utils/logger/logger.dart';

/// Google 登录全局服务(单例)
/// 支持:
///  - 全局唯一实例
///  - 登录/登出状态监听
///  - 获取服务端授权码
///  - 与后端 quickLogin 接口对接
class GoogleSignInService {
  // ------------------- 单例实现 -------------------
  static final GoogleSignInService _instance = GoogleSignInService._internal();
  factory GoogleSignInService() => _instance;
  GoogleSignInService._internal();

  // ------------------- 内部属性 -------------------
  final GoogleSignIn _googleSignIn = GoogleSignIn.instance;
  // 当前登录的 Google 用户对象
  GoogleSignInAccount? currentUser;
  // 服务端授权码,用于后端 quickLogin
  String serverAuthCode = '';
  // 错误提示信息
  String errorMessage = '';

  // 登录状态广播(支持多处监听)
  final StreamController<GoogleSignInAccount?> _loginStateController =
      StreamController<GoogleSignInAccount?>.broadcast();

  /// 登录状态变化流(可用 StreamBuilder 监听)
  Stream<GoogleSignInAccount?> get onLoginStateChanged =>
      _loginStateController.stream;

  /// 快捷判断当前是否已登录
  bool get isLoggedIn => currentUser != null;

  // ------------------- 初始化 -------------------
  /// 初始化 GoogleSignIn 实例,注册登录事件监听
  Future<void> initialize({String? clientId, String? serverClientId}) async {
    await _googleSignIn.initialize(
      clientId: clientId,
      serverClientId: serverClientId,
    );

    // 监听登录事件(SignIn/SignOut)
    _googleSignIn.authenticationEvents
        .listen(_handleAuthenticationEvent)
        .onError(_handleAuthenticationError);

    logger.i('✅ GoogleSignIn 初始化完成');
    // 尝试自动登录(静默登录)
    //await _googleSignIn.attemptLightweightAuthentication();
  }

  // ------------------- 登录 -------------------
  /// 发起 Google 登录流程
  Future<GoogleSignInAccount?> signIn() async {
    try {
      currentUser = await _googleSignIn.authenticate(); // 新 API
      _loginStateController.add(currentUser);
      logger.i('✅ 登录成功: ${currentUser?.displayName}, ${currentUser?.email}');
      return currentUser;
    } catch (e) {
      errorMessage = 'Google 登录失败: $e';
      _loginStateController.add(null);
      logger.e(errorMessage);
      return null;
    }
  }

  // ------------------- 登出 -------------------
  /// 退出登录,并清空缓存的用户信息
  Future<void> signOut() async {
    await _googleSignIn.disconnect();
    currentUser = null;
    serverAuthCode = '';
    errorMessage = '';
    _loginStateController.add(null);
    logger.i('🚪 已退出登录');
  }

  // ------------------- 获取服务端授权码 -------------------
  /// 获取 Google serverAuthCode,用于后端验证登录
  Future<String?> getServerAuthCode() async {
    if (currentUser == null) {
      logger.w('⚠️ 获取授权码失败:当前用户为空');
      return null;
    }
    // currentUser!
    // 是当前已登录的 Google 用户对象(类型为 GoogleSignInAccount)。
    // .authorizationClient
    // 表示该用户的授权客户端对象,可以用来发起 OAuth2 授权。
    // .authorizeServer([...])
    // 发起服务端授权请求,让你的 app 能访问用户的某些数据(Scopes)。
    try {
      final GoogleSignInServerAuthorization? serverAuth = await currentUser!
          .authorizationClient
          .authorizeServer(['email', 'profile']);
      serverAuthCode = serverAuth?.serverAuthCode ?? '';
      logger.i('🔑 服务端授权码获取成功: $serverAuthCode');
      return serverAuthCode;
    } on GoogleSignInException catch (e) {
      errorMessage = 'Google 授权异常 ${e.code}: ${e.description}';
      logger.e(errorMessage);
      return null;
    } catch (e) {
      errorMessage = '未知错误(授权阶段): $e';
      logger.e(errorMessage);
      return null;
    }
  }

  // ------------------- quickLogin -------------------
  /// 调用后端 quickLogin 接口,用 Google 信息登录/注册
  Future<Map<String, dynamic>?> quickLogin() async {
    if (currentUser == null) {
      logger.w('⚠️ quickLogin 调用失败:用户未登录');
      return null;
    }

    try {
      final dio = Dio();
      final data = FormData.fromMap({
        'type': 'google',
        'nickname': currentUser!.displayName ?? '',
        'openid': currentUser!.id,
        'avatar': currentUser!.photoUrl ?? '',
      });

      final response = await dio.post(
        '',//自己的API接口地址
        data: data,
        options: Options(headers: {'server': 'true', 'think-lang': 'zh-tw'}),
      );

      logger.i('🧩 用户ID: ${currentUser!.id}');
      logger.i('✅ 登录返回结果: ${response.data}');
      return response.data;
    } catch (e) {
      logger.e('❌ quickLogin 请求出错: $e');
      errorMessage = e.toString();
      return null;
    }
  }

  // ------------------- 内部事件处理 -------------------
  // 当 Google 登录状态改变时触发
  // event: GoogleSignInAuthenticationEvent 包含 SignIn 或 SignOut 事件
  void _handleAuthenticationEvent(GoogleSignInAuthenticationEvent event) {
    currentUser = switch (event) {
      GoogleSignInAuthenticationEventSignIn() => event.user,
      GoogleSignInAuthenticationEventSignOut() => null,
    };
    _loginStateController.add(currentUser);
    logger.i(
      currentUser == null ? '👋 检测到登出事件' : '🙌 检测到登录事件: ${currentUser?.email}',
    );
  }

  // 处理 Google 登录异常
  // 当登录过程中出现异常时调用
  void _handleAuthenticationError(Object e) {
    currentUser = null;
    _loginStateController.add(null);
    errorMessage = e is GoogleSignInException
        ? 'Google 登录异常 ${e.code}: ${e.description}'
        : '未知错误: $e';
    logger.e(errorMessage);
  }

  // ------------------- 销毁 -------------------
  /// 释放资源(一般不需要调用)
  void dispose() {
    _loginStateController.close();
  }
}

调用

一、初始化(App 启动时调用)

在你的 main.dart 或者 App 初始化逻辑中(比如 main() 或 initState()),先初始化服务:

import 'package:yourapp/app/service/google_signin_service.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 初始化 Google 登录服务
  await GoogleSignInService().initialize(
    clientId: null,
    serverClientId: null,
  );

  runApp(MyApp());
}

二、在登录页面使用(发起登录)

在 LoginView 或者登录按钮点击事件中:

import 'package:yourapp/app/service/google_signin_service.dart';
import 'package:flutter/material.dart';

class LoginView extends StatefulWidget {
  const LoginView({super.key});

  @override
  State<LoginView> createState() => _LoginViewState();
}

class _LoginViewState extends State<LoginView> {
  final _googleService = GoogleSignInService();

  Future<void> _handleGoogleLogin() async {
    final user = await _googleService.signIn();
    if (user == null) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('登录失败,请重试')),
      );
      return;
    }

    // 拿到 serverAuthCode(可选)
    final authCode = await _googleService.getServerAuthCode();
    print('🔑 服务端授权码:$authCode');

    // 调后端 quickLogin
    final result = await _googleService.quickLogin();
    print('✅ 后端返回:$result');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton.icon(
          icon: const Icon(Icons.login),
          label: const Text('使用 Google 登录'),
          onPressed: _handleGoogleLogin,
        ),
      ),
    );
  }
}

三、登出
await GoogleSignInService().signOut();

可以放在 “退出登录” 按钮 或 “个人中心” 里。

四、实时监听登录状态(推荐做法)

在任意页面,你可以用 StreamBuilder 监听登录状态:
StreamBuilder(
  stream: GoogleSignInService().onLoginStateChanged,
  builder: (context, snapshot) {
    final user = snapshot.data;
    if (user == null) {
      return const Text('未登录');
    } else {
      return Column(
        children: [
          CircleAvatar(backgroundImage: NetworkImage(user.photoUrl ?? '')),
          Text(user.displayName ?? ''),
          Text(user.email),
        ],
      );
    }
  },
)

这样当用户登录或登出时,界面会自动刷新,无需手动 setState。

五、判断是否已登录(快速判断)
final loggedIn = GoogleSignInService().isLoggedIn;
if (loggedIn) {
  print('当前已登录:${GoogleSignInService().currentUser?.email}');
}