Flutter集成Passport.js Google OAuth2.0登录遇空值异常求解决
问题:Flutter集成Passport.js Google OAuth2登录的调用错误
问题背景
已有可正常运行的登录功能,需转为Flutter应用中基于Passport.js和Google OAuth2.0的signInWithGoogle功能,但不清楚如何调用/google回调URL及处理响应,点击按钮时出现Unexpected null value错误。
现有代码
后端路由(routes.ts)
router.post('/login', AuthController.login); router.get('/google', passport.authenticate('google', { scope: 'profile email' })); router.get('/google/callback', AuthController.googleCallback);
后端回调处理(auth.ts)
googleCallback: async (req: Request, res: Response, next: NextFunction): Promise<any> => { passport.authenticate( 'google', { failureRedirect: `${process.env.FRONTEND_URL}?auth=social&responseType=error`, session: false }, async (err, user) => { req.login(user, { session: false }, async (error) => { if (error) return next(error); const body = { _id: user._id, email: user.email }; const token = jwt.sign({ user: body }, process.env.SECRET); const payload = { auth: 'social', responseType: 'success', provider: SocialProvider.Google, token, }; const queryString = new URLSearchParams(payload).toString(); return res.cookie('token', token).redirect(`${process.env.FRONTEND_URL}/?${queryString}`); }); } )(req, res, next); },
Flutter端API调用(SignInWithGoogleApi)
import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:flutter/cupertino.dart'; import 'package:ltp/utils/navigateFromResponse.dart'; import '../API/apiconstants.dart'; NavigateFromResponse navigateFromResponse = NavigateFromResponse(); ApiConstants apiConstants = ApiConstants(); Dio dio = Dio(); class SignInWithGoogleApi { Future siginwithgoogle() async { try { Response response = await dio.post(ApiConstants().baseURL + "google", options: Options( followRedirects: true, validateStatus: (status) { return status! <= 500; } ), ); return response; } on DioError catch (dioError) { if (dioError.response!.statusCode == 401) { debugPrint(dioError.message); } } } }
Flutter按钮点击逻辑
ElevatedButton( onPressed: () => { setState((){ isLoading = true; }), googleApi.siginwithgoogle().then((value) async => { if( value.statusCode == 200){ profileInfoProvider.profileInfoProfileID(value.data["user"]["_id"]), profileInfoProvider.profileInfoToken(value.data["token"]), profileInfoProvider.profileInfoName(value.data["user"]["fullName"]), profileInfoProvider.profileInfoObject(value.data["user"]), profileInfoProvider.profileInitialAssign(value.data["user"]["fullName"]), profileInfoProvider.profileColorSet(randomNumber.nextInt(7)), channelApi.getAllChannels().then((value) async => { channelProvider.channelListRefreshed(value.data), setState((){ isLoading = false; }), Get.offAllNamed('/wrapper') }), } else{ setState((){ isLoading = false; }), ScaffoldMessenger.of(context) .showSnackBar(const SnackBar( behavior: SnackBarBehavior.floating, duration: Duration(seconds: 2), content: Text( 'Invalid/Enter Credentials'))) } } ) },
错误信息
[GETX] NEW ROUTE null Error: Unexpected null value. dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 266:49 throw_ dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 528:63 nullCheck packages/ltp/API/sigininwithgoogle.dart 27:41 siginwithgoogle ...
错误原因分析
- 请求方法不匹配:后端
/google路由是GET请求,但Flutter端误用POST调用,导致后端返回无效响应,触发null值错误。 - OAuth2流程理解偏差:Google OAuth2需要用户在浏览器中完成授权登录,直接用Dio发起后台请求无法触发用户交互,会得到无效的重定向响应,无法获取预期的用户数据和token。
解决方案
步骤1:引入浏览器授权跳转依赖
使用flutter_web_auth包实现浏览器授权跳转(支持移动端和Web),先在pubspec.yaml添加依赖:
dependencies: flutter_web_auth: ^0.5.0
步骤2:重构Flutter端API调用类
import 'package:flutter_web_auth/flutter_web_auth.dart'; import 'package:ltp/API/apiconstants.dart'; import 'package:flutter/cupertino.dart'; ApiConstants apiConstants = ApiConstants(); class SignInWithGoogleApi { Future<Map<String, String>?> siginwithgoogle() async { try { // 构造后端授权跳转URL final authorizationUrl = "${apiConstants.baseURL}google"; // 回调URL需与Google Cloud控制台、后端FRONTEND_URL配置一致 final callbackUrlScheme = "your-app-scheme"; // 示例:ltp://callback // 打开Google授权页面 final result = await FlutterWebAuth.authenticate( url: authorizationUrl, callbackUrlScheme: callbackUrlScheme, ); // 解析回调返回的查询参数 final queryParams = Uri.parse(result).queryParameters; return queryParams; } catch (e) { debugPrint("Google登录错误: $e"); return null; } } }
步骤3:修改按钮点击逻辑
ElevatedButton( onPressed: () async { setState(() { isLoading = true; }); final queryParams = await googleApi.siginwithgoogle(); setState(() { isLoading = false; }); if (queryParams != null && queryParams['responseType'] == 'success') { final token = queryParams['token']; // 存储token到状态管理 profileInfoProvider.profileInfoToken(token!); // 若需用户详细信息,可携带token调用后端用户信息接口,或解析JWT payload // 示例:解析JWT获取用户ID // final payload = JwtDecoder.decode(token); // final userId = payload['user']['_id']; // profileInfoProvider.profileInfoProfileID(userId); // 执行后续页面跳转和数据加载 final channelResponse = await channelApi.getAllChannels(); channelProvider.channelListRefreshed(channelResponse.data); Get.offAllNamed('/wrapper'); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( behavior: SnackBarBehavior.floating, duration: Duration(seconds: 2), content: Text('Google登录失败,请重试'), ), ); } }, ),
步骤4:配置回调URL
- 在Google Cloud控制台的OAuth2客户端配置中,添加Flutter应用的回调URL(如
ltp://callback或Web域名)。 - 确保后端
FRONTEND_URL与回调URL完全一致。
关键说明
- OAuth2授权必须通过用户交互完成,不能用后台HTTP请求直接调用授权接口。
- 后端回调会重定向到前端并携带查询参数,Flutter端需解析这些参数获取token和登录状态。
- 若需用户详细信息,可携带token调用后端用户信息接口,或在客户端安全解析JWT payload(注意校验签名)。
内容的提问来源于stack exchange,提问作者jayapriya durairajan
相关产品推荐
相关产品推荐

