注: 已实现登录Flutter端全部功能 基于
google_sign_in: ^7.1.1
#可选【如果找不到包的话 可以导入】
# google_sign_in_web: ^1.0.0
# google_sign_in_platform_interface: ^3.0.0
代码实现为
class AuthService extends GetxService{
//从 Google Cloud Console 获取的后端 Web Client ID
final String? _serverClientId = kIsWeb? null:'******.apps.googleusercontent.com';
//Flutter Web Client ID,用于 Web 端的初始化
final String? _webClientId = '******.apps.googleusercontent.com';
String _errorMessage = '';
// 根据平台选择不同的后端 URL
final String AuthUrl = kIsWeb
? "https://localhost/api/auth/google/"
: "https://localhost/api/auth/google/";
final _storage = GsUtil.to;
final GoogleSignIn _googleSignIn = GoogleSignIn.instance;
GoogleSignIn get googleSignIn => _googleSignIn;
@override
void onInit() async{
await initialize();
super.onInit();
unawaited(_googleSignIn
.initialize(clientId: _webClientId, serverClientId: _serverClientId)
.then((_) {
_googleSignIn.authenticationEvents
.listen(_handleAuthenticationEvent)
.onError(_handleAuthenticationEvent);
/// This example always uses the stream-based approach to determining
/// which UI state to show, rather than using the future returned here,
/// if any, to conditionally skip directly to the signed-in state.
_googleSignIn.attemptLightweightAuthentication();
}));
}
@override
void onClose(){
super.onClose();
}
var isLoading = true.obs;
// 初始化 GoogleSignIn 实例
Future<void> initialize() async {
try {
await _googleSignIn.initialize(
clientId: kIsWeb ? _webClientId : null,
serverClientId: _serverClientId,
);
if(kIsWeb){
final web.GoogleSignInPlugin _webPlugin = web.GoogleSignInPlugin();
await _webPlugin.init(InitParameters(
clientId: _webClientId,
serverClientId: _serverClientId,
),);
}
} catch (error) {
debugPrint("Google Sign-In 初始化失败: $error");
} finally {
//无论成功或失败,最后都必须将 loading 状态设为 false**
isLoading.value = false;
}
}
// 触发登录流程
Future<void> signIn() async {
try {
await _googleSignIn.authenticate();
} catch (e) {
debugPrint("authenticate error: $e");
}
}
final List<String> scopes = <String>[
'email',
'profile', //可选 获取基础信息 头像 名称 等
'https://www.googleapis.com/auth/user.phonenumbers.read', //可选 获取绑定手机号
// 'https://www.googleapis.com/auth/contacts.readonly',
];
// 在获取到 user 对象后,调用此方法与后端通信
/**
* nickName 用户昵称
* areaCode 电话区号
* phone 手机号
* vCode 邀请码
*/
String nickName="";
String areaCode = "91";
String phone = "";
String vCode = "";
Future<String?> exchangeCodeForToken(GoogleSignInAccount user) async {
final LoadingController _loadingController = Get.find<LoadingController>();
try {
String serverAuthCode = "";
_loadingController.show();
// if(context.mounted) {
// DialogUtil.showLoadingDialog(context);
// }
// 关键步骤:请求服务器授权码
try {
final GoogleSignInServerAuthorization? serverAuth = await user.authorizationClient.authorizeServer(scopes);
serverAuthCode = serverAuth == null ? '' : serverAuth.serverAuthCode;
if (TextUtil.isEmpty(serverAuthCode)) {
throw Exception("Failed to obtain Server Auth Code!");
}
} on GoogleSignInException catch (e) {
_errorMessage = _errorMessageFromSignInException(e);
}
debugPrint("获取到 Server Auth Code,正在发送到后端...");
final di = SpUtil.to?.readString(Config.deviceID);
// 将 code 发送到 后端
final response = await http.post(
Uri.parse(AuthUrl),
headers: {'Content-Type': 'application/json'},
// Body 的 key 必须是 "code"
body: jsonEncode({'code': serverAuthCode)}),
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
final String jwtToken = data['access_token']; // 后端 返回的 key
await _storage?.writeString("jwtToken", jwtToken);
debugPrint("后端验证成功,已登录!");
return jwtToken;
} else {
throw Exception("后端处理 code 失败: ${response.body}");
}
} catch (e) {
debugPrint("exchangeCodeForToken error: $e");
return null;
}finally{
_loadingController.hide();
}
}
// 登出
Future<void> signOut() async {
await googleSignIn.disconnect();
}
//回调
Future<void> _handleAuthenticationEvent(GoogleSignInAuthenticationEvent event) async {
final GoogleSignInAccount? user = switch (event) {
GoogleSignInAuthenticationEventSignIn() => event.user,
GoogleSignInAuthenticationEventSignOut() => null,
};
final GoogleSignInClientAuthorization? authorization = await user?.authorizationClient.authorizationForScopes(scopes);
if (user != null && authorization != null) {
unawaited(exchangeCodeForToken(user));
}
}
String _errorMessageFromSignInException(GoogleSignInException e) {
return switch (e.code) {
GoogleSignInExceptionCode.canceled => 'Sign in canceled',
_ => 'GoogleSignInException ${e.code}: ${e.description}',
};
}
//按钮逻辑
Widget buildGoogleSignInButton() {
if (kIsWeb) {
return web_only.renderButton(
configuration: web.GSIButtonConfiguration(
type: web.GSIButtonType.icon,
theme: web.GSIButtonTheme.outline,
size: web.GSIButtonSize.large,
text: web.GSIButtonText.continueWith,
shape: web.GSIButtonShape.pill,
// locale: ,
minimumWidth: 45.0,
// logoAlignment: web.GSIButtonLogoAlignment.left
),
);
} else {
// 移动端就用普通按钮
if (GoogleSignIn.instance.supportsAuthenticate()){
return GoogleSignInButton(onPressed: () async {
if (Click.intervalClick(needTime: 2)) {
await signIn();
}},);
}
}
return SizedBox(); // const Text('This platform does not have a known authentication method')
}
}
以上就是核心工具类 踩了不少坑 自 github-google_sign 中抽取而出,都是精华
列位共勉