
我正在用 Flutter 做一款 Spotify 综合播放器。我的需求包括一个精简的播放器,但 Spotify SDK 无法获取当前的播放队列(queue)。因此,我选择自己使用 AppAuth 和 http 实现。
在 iOS 平台打包时发现一个重要问题:网页回调(web callback)无法正常工作。即使修改 URL Scheme,授权页面依然无法正确打开应用。这是因为 iOS 上的 Spotify 授权必须通过其官方应用完成。
解决方案
0.5. 原来的使用 AppAuth 实现认证
class SpotifyAuthService {
final FlutterAppAuth _appAuth;
final FlutterSecureStorage _secureStorage;
final String clientId;
final String clientSecret;
final String redirectUrl;
SpotifyAuthService({
required this.clientId,
required this.clientSecret,
required this.redirectUrl,
}) : _appAuth = const FlutterAppAuth(),
_secureStorage = const FlutterSecureStorage();
Future<SpotifyAuthResponse> login({List<String>? scopes}) async {
try {
final authResult = await _appAuth.authorize(
AuthorizationRequest(
clientId,
redirectUrl,
serviceConfiguration: _serviceConfiguration,
scopes: scopes ?? defaultScopes,
),
);
final tokenResult = await _appAuth.token(
TokenRequest(
clientId,
redirectUrl,
authorizationCode: authResult.authorizationCode,
codeVerifier: authResult.codeVerifier,
serviceConfiguration: _serviceConfiguration,
clientSecret: clientSecret,
),
);
return SpotifyAuthResponse(
accessToken: tokenResult.accessToken!,
refreshToken: tokenResult.refreshToken,
expirationDateTime: tokenResult.accessTokenExpirationDateTime!,
tokenType: tokenResult.tokenType ?? 'Bearer',
);
} catch (e) {
rethrow;
}
}
}
1 使用 Spotify SDK 以适配 iOS
class SpotifyService {
final String clientId;
final String redirectUrl;
final FlutterSecureStorage _secureStorage;
SpotifyService({
required this.clientId,
required this.redirectUrl,
}) : _secureStorage = const FlutterSecureStorage();
Future<bool> connect() async {
try {
final connected = await SpotifySdk.connectToSpotifyRemote(
clientId: clientId,
redirectUrl: redirectUrl,
);
if (connected) {
final token = await getAccessToken();
await _saveAccessToken(token);
}
return connected;
} catch (e) {
throw SpotifyServiceException('Failed to connect to Spotify: $e');
}
}
Future<String> getAccessToken() async {
try {
return await SpotifySdk.getAccessToken(
clientId: clientId,
redirectUrl: redirectUrl,
scope: [
'app-remote-control',
'playlist-read-private',
'user-library-read',
].join(', '),
);
} catch (e) {
throw SpotifyServiceException('Failed to get access token: $e');
}
}
Future<bool> isAuthenticated() async {
try {
final token = await _secureStorage.read(key: _accessTokenKey);
final expirationStr = await _secureStorage.read(key: _tokenExpirationKey);
if (token == null || expirationStr == null) return false;
final expiration = DateTime.parse(expirationStr);
if (expiration.isBefore(DateTime.now())) {
final newToken = await getAccessToken();
await _saveAccessToken(newToken);
}
return true;
} catch (e) {
return false;
}
}
}
2. 使用 http 调用 Spotify API,而不是 Spotify SDK
下面是两个核心 API 调用的示例:
// 需要导入 http 包
// 控制随机播放状态
Future<void> setShuffle(bool state) async {
final headers = await getAuthenticatedHeaders();
final response = await http.put(
Uri.parse('https://api.spotify.com/v1/me/player/shuffle?state=$state'),
headers: headers,
);
if (response.statusCode != 200) {
throw SpotifyAuthException('设置随机播放失败');
}
}
// 获取当前播放队列
Future<Map<String, dynamic>> getPlaybackQueue() async {
final headers = await getAuthenticatedHeaders();
final response = await http.get(
Uri.parse('https://api.spotify.com/v1/me/player/queue'),
headers: headers,
);
if (response.statusCode != 200) {
throw SpotifyAuthException('获取播放队列失败');
}
return json.decode(response.body);
}
建议
在 Flutter 中开发 Spotify 相关功能时,我建议:使用 Spotify SDK(一个 Flutter Package)处理认证流程,获取和保存访问令牌。直接调用 Spotify API 实现具体功能,而不是依赖官方 SDK。(根据实际需求选择必要的 API 端点)
RELATED POSTS
View all