You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

157 lines
5.0 KiB

import 'dart:ffi';
import 'package:flutter/services.dart';
class MusicService {
static final MusicService _instance = MusicService._internal();
/// 获取单例
factory MusicService() => _instance;
// 方法通道
final MethodChannel _methodChannel = const MethodChannel(
'com.yunqiinnovation.music_service',
);
// 事件通道 - 简化为只使用数据和状态通道
late final EventChannel _dataEventChannel;
late final EventChannel _statusEventChannel;
// 事件流
late final Stream<dynamic> _dataStream;
late final Stream<dynamic> _statusStream;
// 获取事件流
Stream<dynamic> get onDataReceived => _dataStream;
// 状态流直接传递原始数据
Stream<dynamic> get onStatusUpdated => _statusStream;
// 内部构造函数
MusicService._internal() {
_dataEventChannel = const EventChannel(
'com.yunqiinnovation.music_service/data_events',
);
_statusEventChannel = const EventChannel(
'com.yunqiinnovation.music_service/status_events',
);
// 初始化事件流
_dataStream = _dataEventChannel.receiveBroadcastStream();
_statusStream = _statusEventChannel.receiveBroadcastStream();
}
Future<void> playMusic(Map<String, dynamic> song) async {
await _methodChannel.invokeMethod('play', {'song': song});
}
Future<void> pauseMusic() async => _methodChannel.invokeMethod('pause');
Future<void> resumeMusic() async => _methodChannel.invokeMethod('resume');
Future<void> nextTrack() async => _methodChannel.invokeMethod('next');
Future<void> previousTrack() async => _methodChannel.invokeMethod('previous');
Future<void> stopMusic() async => _methodChannel.invokeMethod('stop');
Future<void> setVolume(double volume) async => _methodChannel.invokeMethod(
'setVolume',
{'volume': volume},
); // volume: 0.0~1.0
Future<Map<String, dynamic>> getStatus() async {
final result = await _methodChannel.invokeMethod<Map>('getStatus');
return {
'isPlaying': result?['isPlaying'] ?? false,
'id': result?['id'] ?? 0,
};
}
/// 获取播放进度信息
Future<Map<String, dynamic>> getProgress() async {
final result = await _methodChannel.invokeMethod<Map>('getProgress');
return {
'currentPosition': result?['currentPosition'] ?? 0, // 当前播放位置(毫秒)
'duration': result?['duration'] ?? 0, // 总时长(毫秒)
'progress': result?['progress'] ?? 0.0, // 播放进度(0.0-1.0)
};
}
/// 跳转到指定位置
Future<void> seekTo(int positionMs) async {
await _methodChannel.invokeMethod('seekTo', {'position': positionMs});
}
/// 播放音乐列表
Future<void> playMusicPlaylist(List<Map<String, dynamic>> songs) async {
await _methodChannel.invokeMethod('playlist', {'songs': songs});
}
/// 更新媒体会话进度信息
Future<void> updateMediaSessionProgress(
int currentPosition,
int duration,
double progress,
) async {
await _methodChannel.invokeMethod('updateMediaSessionProgress', {
'currentPosition': currentPosition,
'duration': duration,
'progress': progress,
});
}
/// 设置媒体会话回调
Future<void> setMediaSessionCallbacks({
required Function() onPlay,
required Function() onPause,
required Function() onNext,
required Function() onPrevious,
required Function(int) onSeekTo,
required Function() onStop,
required Function(int, int, int, double) onProgress,
}) async {
// 设置方法调用处理器来处理来自原生平台的回调
_methodChannel.setMethodCallHandler((call) async {
switch (call.method) {
case 'onMediaSessionPlay':
onPlay();
break;
case 'onMediaSessionPause':
onPause();
break;
case 'onMediaSessionNext':
onNext();
break;
case 'onMediaSessionPrevious':
onPrevious();
break;
case 'onMediaSessionSeekTo':
final position = call.arguments['position'] as int;
onSeekTo(position);
break;
case 'onMediaSessionStop':
onStop();
break;
case 'onMediaSessionProgress':
final currentIdInternal = call.arguments['currentIdInternal'] as int;
final currentPosition = call.arguments['currentPosition'] as int;
final duration = call.arguments['duration'] as int;
final progress = call.arguments['progress'] as double;
onProgress(currentIdInternal, currentPosition, duration, progress);
break;
}
});
// 通知原生平台启用媒体会话回调
await _methodChannel.invokeMethod('enableMediaSessionCallbacks');
}
/// 更新媒体会话元数据
Future<void> updateMediaSessionMetadata(Map<String, dynamic> song) async {
await _methodChannel.invokeMethod('updateMediaSessionMetadata', {
'song': song,
});
}
/// 更新媒体会话播放状态
Future<void> updateMediaSessionPlaybackState(bool isPlaying) async {
await _methodChannel.invokeMethod('updateMediaSessionPlaybackState', {
'isPlaying': isPlaying,
});
}
}