You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 11:14:42