Flutter音乐播放状态获取插件nowplaying的使用

Flutter音乐播放状态获取插件nowplaying的使用

nowplaying 是一个用于 iOS 和 Android 的 Flutter 插件,可以显示设备上当前正在播放的音频轨道的元数据。以下是如何在 Flutter 项目中使用 nowplaying 插件的详细步骤和示例代码。

安装

首先,在 pubspec.yaml 文件中添加 nowplaying 依赖:

dependencies:
    nowplaying: ^3.0.3

iOS 设置

ios/Runner/Info.plist 中添加以下权限描述:

<key>NSAppleMusicUsageDescription</key>
<string>We need this to show you what's currently playing</string>

Android 设置

android/app/src/main/AndroidManifest.xml 中添加通知监听服务配置:

<service android:name="com.gomes.nowplaying.NowPlayingListenerService"
    android:label="NowPlayingListenerService"
    android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
    <intent-filter>
        <action android:name="android.service.notification.NotificationListenerService" />
    </intent-filter>
</service>

对于 Android 11 或更高版本,可能需要添加以下内容:

<manifest ...>
    ...
    <queries>
        <package android:name="com.example.store" />
        <package android:name="com.example.services" />
    </queries>
    ...
    <application ...>
    ...
</manifest>

或者请求所有应用的访问权限:

<manifest ...>
    ...
    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
    ...
    <application ...>
    ...
</manifest>

使用

初始化

初始化 nowplaying 服务:

await NowPlaying.instance.start();

可以在任何地方执行此操作,包括在 runApp 命令之前。

权限

iOS 自动具有所需的权限,通过安装阶段添加的使用键获得。

Android 用户必须明确授予服务访问通知流的权限。可以通过实例的 isEnabled 方法测试是否已授予权限:

final bool isEnabled = await NowPlaying.instance.isEnabled();
if (!isEnabled) {
    // 请求权限
}

提供了一个方便的方法来打开设置页面:

final bool hasShownPermissions = await NowPlaying.instance.requestPermissions();

如果需要强制显示设置页面:

if (!hasShownPermissions) {
    final bool pleasePleasePlease = await Navigator.of(context).pushNamed('ExplainAgainReallyNicelyPage');
    if (pleasePleasePlease) NowPlaying.instance.requestPermissions(force: true);
}

Spotify 接入

访问 Spotify 需要客户端 ID 和客户端密钥,可以从 Spotify Developer Dashboard 获取。将这些凭据传递给 NowPlayingstart 函数:

await NowPlaying.instance.start(
    spotifyClientId: '<CLIENT ID>',
    spotifyClientSecret: '<CLIENT SECRET>',
);

连接到 Spotify:

if (NowPlaying.spotify.isEnabled && NowPlaying.spotify.isUnconnected) {
    Navigator.of(context).push(
        MaterialPageRoute(builder: (context) => NowPlaying.spotify.signInPage(context)),
    );
}

访问当前播放元数据

现在播放的元数据通过 NowPlayingTrack 对象的流传递,暴露为 NowPlaying.instance.stream。可以像通常消费流一样消费它:

StreamProvider.value(
    value: NowPlaying.instance.stream,
    child: MaterialApp(
        home: Scaffold(
            body: Consumer<NowPlayingTrack>(
                builder: (context, track, _) {
                    return Container(
                        // 显示轨道信息
                    );
                }
            )
        )
    )
)

NowPlayingTrack 对象包含以下字段:

String title;
String artist;
String album;
String genre;
Duration duration;
Duration progress; // 检查下面的说明
NowPlayingState state;
ImageProvider image;
ImageProvider icon;
String source;

其中 NowPlayingState 定义如下:

enum NowPlayingState {
  playing, paused, stopped
}

示例 Demo

以下是一个完整的示例应用程序,展示如何使用 nowplaying 插件:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:nowplaying/nowplaying.dart';
import 'package:provider/provider.dart';

void main() async {
  await NowPlaying.instance.start(
    resolveImages: true,
    spotifyClientId: 'xxxx',
    spotifyClientSecret: 'xxxx',
  );
  runApp(NowPlayingExample());
}

class NowPlayingExample extends StatelessWidget {
  const NowPlayingExample({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('NowPlaying example app')),
        body: Center(child: NowPlayingTrackWidget()),
      ),
    );
  }
}

class NowPlayingTrackWidget extends StatefulWidget {
  @override
  _NowPlayingTrackState createState() => _NowPlayingTrackState();
}

class _NowPlayingTrackState extends State<NowPlayingTrackWidget> {
  @override
  void initState() {
    super.initState();
    NowPlaying.instance.isEnabled().then((isEnabled) async {
      if (!isEnabled) {
        final shown = await NowPlaying.instance.requestPermissions();
        print('MANAGED TO SHOW PERMS PAGE: $shown');
      }

      if (NowPlaying.spotify.isEnabled && NowPlaying.spotify.isUnconnected) {
        NowPlaying.spotify.signIn(context);
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return StreamProvider<NowPlayingTrack>.value(
      initialData: NowPlayingTrack.loading,
      value: NowPlaying.instance.stream,
      child: Consumer<NowPlayingTrack>(
        builder: (context, track, _) {
          return Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              if (track.isStopped) Text('nothing playing'),
              if (!track.isStopped) ...[
                if (track.title != null) Text(track.title!.trim()),
                if (track.artist != null) Text(track.artist!.trim()),
                if (track.album != null) Text(track.album!.trim()),
                Text(track.duration.truncToSecond.toShortString()),
                TrackProgressIndicator(track),
                Text(track.state.toString()),
                Stack(
                  alignment: Alignment.center,
                  children: [
                    Container(
                      margin: const EdgeInsets.fromLTRB(8, 8, 8, 16),
                      width: 200,
                      height: 200,
                      alignment: Alignment.center,
                      color: Colors.grey,
                      child: AnimatedSwitcher(
                        duration: const Duration(milliseconds: 350),
                        child: _imageFrom(track),
                      ),
                    ),
                    Positioned(bottom: 0, right: 0, child: _iconFrom(track)),
                    Positioned(
                        bottom: 0, left: 8, child: Text(track.source!.trim())),
                  ],
                ),
              ]
            ],
          );
        },
      ),
    );
  }

  Widget _imageFrom(NowPlayingTrack track) {
    if (track.hasImage)
      return Image(
        key: Key(track.id),
        image: track.image!,
        width: 200,
        height: 200,
        fit: BoxFit.contain,
      );

    if (track.isResolvingImage) {
      return SizedBox(
        width: 50.0,
        height: 50.0,
        child: CircularProgressIndicator(
            valueColor: AlwaysStoppedAnimation<Color>(Colors.white)),
      );
    }

    return Text('NO\nARTWORK\nFOUND',
        textAlign: TextAlign.center,
        style: const TextStyle(fontSize: 24, color: Colors.white));
  }

  Widget _iconFrom(NowPlayingTrack track) {
    if (track.hasIcon)
      return Container(
        padding: const EdgeInsets.all(6),
        decoration: const BoxDecoration(
            color: Colors.white,
            boxShadow: [const BoxShadow(blurRadius: 5, color: Colors.black)],
            shape: BoxShape.circle),
        child: Image(
          image: track.icon!,
          width: 25,
          height: 25,
          fit: BoxFit.contain,
          color: _fgColorFor(track),
          colorBlendMode: BlendMode.srcIn,
        ),
      );
    return Container();
  }

  Color _fgColorFor(NowPlayingTrack track) {
    switch (track.source) {
      case "com.apple.music":
        return Colors.blue;
      case "com.hughesmedia.big_finish":
        return Colors.red;
      case "com.spotify.music":
        return Colors.green;
      default:
        return Colors.purpleAccent;
    }
  }
}

class TrackProgressIndicator extends StatefulWidget {
  final NowPlayingTrack track;

  TrackProgressIndicator(this.track);

  @override
  _TrackProgressIndicatorState createState() => _TrackProgressIndicatorState();
}

class _TrackProgressIndicatorState extends State<TrackProgressIndicator> {
  late Timer _timer;

  @override
  void initState() {
    _timer = Timer.periodic(const Duration(seconds: 1), (_) => setState(() {}));
    super.initState();
  }

  @override
  void dispose() {
    _timer.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final progress = widget.track.progress.truncToSecond;
    final countdown =
        widget.track.duration - progress + const Duration(seconds: 1);
    return Column(
      children: [
        Text(progress.toShortString()),
        Text(countdown.toShortString()),
      ],
    );
  }
}

extension DurationExtension on Duration {
  Duration get truncToSecond {
    final ms = this.inMilliseconds;
    return Duration(milliseconds: ms - ms % 1000);
  }

  String toShortString() => toString().split(".").first;
}

以上是 nowplaying 插件的基本使用方法和完整示例代码。通过这个插件,您可以轻松地在 Flutter 应用中获取和显示当前正在播放的音乐信息。


更多关于Flutter音乐播放状态获取插件nowplaying的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter音乐播放状态获取插件nowplaying的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用nowplaying插件来获取音乐播放状态的代码案例。这个插件通常用于与iOS和Android上的媒体会话(Media Session)进行交互,以获取当前播放的音乐信息。

首先,确保你已经在pubspec.yaml文件中添加了nowplaying依赖:

dependencies:
  flutter:
    sdk: flutter
  nowplaying: ^最新版本号  # 替换为实际最新版本号

然后,运行flutter pub get来安装依赖。

接下来,在你的Flutter项目中,你可以按照以下步骤使用nowplaying插件来获取音乐播放状态。

1. 导入插件

在你的Dart文件中导入nowplaying插件:

import 'package:nowplaying/nowplaying.dart';

2. 请求权限并监听播放状态

在你的主应用逻辑中,你可以请求必要的权限并开始监听媒体播放状态。以下是一个基本的示例:

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  NowPlaying? _nowPlaying;
  String _playbackState = 'Unknown';

  @override
  void initState() {
    super.initState();
    // 初始化NowPlaying实例
    _nowPlaying = NowPlaying();

    // 请求权限(仅iOS需要)
    _nowPlaying?.requestAuthorization().then((_) {
      // 开始监听播放状态变化
      _nowPlaying?.playerInfoStream.listen((playerInfo) {
        setState(() {
          // 更新播放状态
          if (playerInfo != null) {
            _playbackState = playerInfo.playbackState.toString();
          } else {
            _playbackState = 'No media info available';
          }
        });
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Music Playback State'),
        ),
        body: Center(
          child: Text('Playback State: $_playbackState'),
        ),
      ),
    );
  }

  @override
  void dispose() {
    // 停止监听
    _nowPlaying?.playerInfoStream.cancel();
    super.dispose();
  }
}

3. 注意事项

  • iOS权限:在iOS上,你需要在Info.plist中添加适当的权限请求,例如NSAppleMusicUsageDescription,如果你的应用需要访问Apple Music的信息。
  • Android权限:在Android上,通常不需要额外的权限,但你需要确保你的应用具有适当的上下文来处理媒体会话。
  • 错误处理:在实际应用中,添加适当的错误处理逻辑,以处理权限请求失败或流监听中的其他问题。

4. 运行应用

现在,你可以运行你的Flutter应用,并观察当媒体(如音乐)在设备上播放时,应用界面上显示的播放状态是如何变化的。

这个代码案例提供了一个基本的框架,展示了如何使用nowplaying插件来获取当前的媒体播放状态。你可以根据需要进一步扩展和自定义这个示例。

回到顶部