使用FlutterWebAuth2实现Flutter应用Spotify认证时触发PlatformException(CANCELED)异常求助
使用FlutterWebAuth2实现Flutter应用Spotify认证时触发PlatformException(CANCELED)异常求助
大家好,我正在尝试在Flutter应用中集成Spotify API,但是在使用FlutterWebAuth2做用户认证时遇到了问题:点击登录按钮后,Spotify的登录页面能正常加载,我也可以输入邮箱和密码,但点击最终的登录按钮后,FlutterWebAuth2抛出了这个异常:
PlatformException(CANCELED, User canceled login, null, null)
之后用户会被重定向回我的应用。有没有人遇到过类似的问题,或者知道可能是什么原因导致的?
以下是我的相关代码:
认证逻辑代码
import 'dart:convert'; import 'dart:math'; import 'package:crypto/crypto.dart'; import 'package:http/http.dart' as http; import 'package:flutter_web_auth_2/flutter_web_auth_2.dart'; class SpotifyAuth { final String clientId; final String redirectUri; final List<String> scopes; SpotifyAuth({ required this.clientId, required this.redirectUri, this.scopes = const [ 'user-read-playback-state', 'user-modify-playback-state', 'user-read-currently-playing', 'user-read-private', 'user-read-email', 'playlist-read-private', 'playlist-read-collaborative', 'playlist-modify-private', 'playlist-modify-public', 'user-library-read', 'user-library-modify', 'user-follow-read', 'user-follow-modify', 'user-read-recently-played', 'user-top-read', 'app-remote-control', 'streaming', 'ugc-image-upload', 'user-read-playback-position', ], }); Future<String> authenticate() async { final codeVerifier = _generateCodeVerifier(); final codeChallenge = _codeChallenge(codeVerifier); final authUri = Uri.https('accounts.spotify.com', '/authorize', { 'client_id': clientId, 'response_type': 'code', 'redirect_uri': redirectUri, 'code_challenge_method': 'S256', 'code_challenge': codeChallenge, 'scope': scopes.join(' '), }); print('Starting redirect URI: $redirectUri'); print('Launching auth URL: $authUri'); // This is where the exception is thrown final result = await FlutterWebAuth2.authenticate( url: authUri.toString(), callbackUrlScheme: Uri.parse(redirectUri).scheme, ); print('>>> Redirect result 2: $result'); final redirectedUri = Uri.parse(result); final code = redirectedUri.queryParameters['code']; if (code == null) { throw Exception("Authorization code not found in redirect URI"); } final tokenResponse = await http.post( Uri.parse('https://accounts.spotify.com/api/token'), headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: { 'client_id': clientId, 'grant_type': 'authorization_code', 'code': code, 'redirect_uri': redirectUri, 'code_verifier': codeVerifier, }, ); if (tokenResponse.statusCode != 200) { throw Exception("Token exchange failed: ${tokenResponse.body}"); } final Map<String, dynamic> jsonBody = jsonDecode(tokenResponse.body); final accessToken = jsonBody['access_token']; if (accessToken == null) { throw Exception("No access token in response"); } return accessToken; } String _generateCodeVerifier() { final rand = Random.secure(); final values = List<int>.generate(64, (_) => rand.nextInt(256)); return base64UrlEncode(values).replaceAll('=', ''); } String _codeChallenge(String codeVerifier) { final bytes = utf8.encode(codeVerifier); final digest = sha256.convert(bytes); return base64UrlEncode(digest.bytes).replaceAll('=', ''); } }
应用启动与认证触发代码
import 'package:flutter/material.dart'; import 'package:music_android_app/api/spotify_api.dart'; import 'package:music_android_app/api/spotify_auth.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { final SpotifyAuth auth = SpotifyAuth( clientId: '****YOUR_CLIENT_ID_HERE****', redirectUri: 'mymusicmobileapp://callback', ); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: Text('Spotify Test')), body: Center( child: ElevatedButton( onPressed: () async { final token = await auth.authenticate(); final api = SpotifyApi(); api.setToken(token); final playlists = await api.getUserPlaylists(); print('Playlists:'); for (var p in playlists) { print('${p.name}'); } }, child: Text('Login to Spotify and Fetch Playlists'), ), ), ), ); } }
AndroidManifest.xml 配置片段
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application android:label="music_android_app" android:name="${applicationName}" android:icon="@mipmap/ic_launcher"> <activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop" android:taskAffinity="" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> <meta-data android:name="io.flutter.embedding.android.NormalTheme" android:resource="@styl
内容来源于stack exchange
相关产品推荐
相关产品推荐

