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

Flutter实现Spotify当前播放歌曲展示应用:认证问题求助

Flutter实现Spotify认证及获取当前播放歌曲

一、添加依赖

使用spotify包处理Spotify API交互,webview_flutter处理OAuth网页授权。在pubspec.yaml中添加:

dependencies:
  flutter:
    sdk: flutter
  spotify: ^0.9.0
  webview_flutter: ^4.4.0

执行flutter pub get安装依赖。

二、配置Spotify开发者平台回调URL

在Spotify开发者后台找到你的应用,设置Redirect URI为自定义scheme,比如myapp://spotify-auth,后续代码和平台配置要保持一致。

三、实现OAuth认证与核心功能

1. 授权WebView页面

用于加载Spotify授权页并捕获回调中的授权码:

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class SpotifyAuthWebView extends StatefulWidget {
  final String authUrl;
  final Function(String) onAuthCodeReceived;

  const SpotifyAuthWebView({
    super.key,
    required this.authUrl,
    required this.onAuthCodeReceived,
  });

  @override
  State<SpotifyAuthWebView> createState() => _SpotifyAuthWebViewState();
}

class _SpotifyAuthWebViewState extends State<SpotifyAuthWebView> {
  late final WebViewController controller;

  @override
  void initState() {
    super.initState();
    controller = WebViewController()
      ..loadRequest(Uri.parse(widget.authUrl))
      ..setNavigationDelegate(
        NavigationDelegate(
          onNavigationRequest: (request) {
            if (request.url.startsWith('myapp://spotify-auth')) {
              final uri = Uri.parse(request.url);
              final code = uri.queryParameters['code'];
              if (code != null) {
                widget.onAuthCodeReceived(code);
                Navigator.pop(context);
              }
              return NavigationDecision.prevent;
            }
            return NavigationDecision.navigate;
          },
        ),
      );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Spotify授权')),
      body: WebViewWidget(controller: controller),
    );
  }
}

2. 主页面实现认证与播放状态获取

替换代码中的YOUR_CLIENT_ID和YOUR_CLIENT_SECRET为你在Spotify平台获取的真实值:

import 'package:flutter/material.dart';
import 'package:spotify/spotify.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Spotify当前播放',
      home: const CurrentTrackPage(),
    );
  }
}

class CurrentTrackPage extends StatefulWidget {
  const CurrentTrackPage({super.key});

  @override
  State<CurrentTrackPage> createState() => _CurrentTrackPageState();
}

class _CurrentTrackPageState extends State<CurrentTrackPage> {
  SpotifyApi? spotifyApi;
  Track? currentTrack;
  bool isLoading = false;

  final String clientId = 'YOUR_CLIENT_ID';
  final String clientSecret = 'YOUR_CLIENT_SECRET';
  final String redirectUri = 'myapp://spotify-auth';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('当前播放歌曲')),
      body: Center(
        child: isLoading
            ? const CircularProgressIndicator()
            : spotifyApi == null
                ? ElevatedButton(
                    onPressed: _initAuth,
                    child: const Text('登录Spotify'),
                  )
                : currentTrack != null
                    ? Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          Text(
                            currentTrack!.name,
                            style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
                          ),
                          const SizedBox(height: 8),
                          Text('歌手: ${currentTrack!.artists!.first.name}'),
                          const SizedBox(height: 16),
                          ElevatedButton(
                            onPressed: _fetchCurrentTrack,
                            child: const Text('刷新当前播放'),
                          ),
                        ],
                      )
                    : const Text('暂无播放歌曲'),
      ),
    );
  }

  void _initAuth() async {
    final credentials = SpotifyApiCredentials(clientId, clientSecret);
    final grant = SpotifyApi.authorizationCodeGrant(credentials);

    // 请求获取当前播放状态的权限
    final authUrl = grant.getAuthorizationUrl(
      Uri.parse(redirectUri),
      scopes: [Scopes.userReadCurrentlyPlaying],
    );

    await Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => SpotifyAuthWebView(
          authUrl: authUrl.toString(),
          onAuthCodeReceived: (code) async {
            setState(() => isLoading = true);
            final token = await grant.handleAuthorizationResponse(
              Uri.parse('$redirectUri?code=$code'),
            );
            spotifyApi = SpotifyApi.fromToken(token);
            await _fetchCurrentTrack();
            setState(() => isLoading = false);
          },
        ),
      ),
    );
  }

  Future<void> _fetchCurrentTrack() async {
    try {
      final currentPlayback = await spotifyApi!.me.currentlyPlaying();
      setState(() {
        currentTrack = currentPlayback?.item as Track?;
      });
    } catch (e) {
      setState(() => currentTrack = null);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('获取失败: $e')),
      );
    }
  }
}

四、配置平台Scheme

Android

在android/app/src/main/AndroidManifest.xml的<activity>标签内添加:

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="myapp" />
</intent-filter>

iOS

在ios/Runner/Info.plist中添加:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>myapp</string>
        </array>
    </dict>
</array>

五、注意事项

  • 确保授权时请求了user-read-currently-playing权限,否则无法获取当前播放状态
  • 测试时需使用正在播放歌曲的Spotify账号登录
  • 不要将Client Secret暴露在客户端代码中,正式环境建议通过后端代理处理认证流程

内容的提问来源于stack exchange,提问作者Ajay Kumar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 18:22:53