现阶段通知中心播放暂停切换功能已实现,但是上下一曲功能暂时消失,需要后续优化,这个commit是snapshot

This commit is contained in:
2026-08-21 22:58:44 +08:00
parent 94a4906671
commit 89c4ba8191
5 changed files with 201 additions and 156 deletions
+85 -148
View File
@@ -1,78 +1,53 @@
import 'package:flutter/material.dart';
import 'package:audio_service/audio_service.dart';
import 'playback_service.dart';
import 'audio_service.dart' as local_audio;
// lib/services/audio_player_handler.dart
import 'package:flutter/foundation.dart';
import 'package:audio_service/audio_service.dart' as audio_service;
import '../audio/player_controller.dart';
import '../audio/playback_state_manager.dart';
import '../services/audio_service.dart'; // 你的业务 AudioService
class AudioPlayerHandler extends BaseAudioHandler {
final PlaybackService _playback = PlaybackService();
late final local_audio.AudioService _localAudio;
// ⭐ 切歌锁,防止重复命令
bool _switchingTrack = false;
class AudioPlayerHandler extends audio_service.BaseAudioHandler {
final PlayerController _player = PlayerController();
final PlaybackStateManager _state = PlaybackStateManager();
AudioPlayerHandler() {
_localAudio = local_audio.AudioService();
// ⭐ 唯一状态来源:media_kit 的 playing 流
_playback.player.stream.playing.listen((playing) {
debugPrint('🎵 [stream] playing changed: $playing');
_updatePlaybackState(playing: playing);
_player.playingStream.listen((playing) {
_state.updatePlaying(playing);
_publishState();
});
// ⭐ 播放完成 → 触发下一首(不直接广播状态)
_playback.player.stream.completed.listen((_) {
debugPrint('🎵 [stream] completed, auto next');
_localAudio.next(); // 触发切歌,playing 流会自然变化
// 不调用 _updatePlaybackState()
});
// ⭐ 歌曲变化 → 更新媒体信息(独立于播放状态)
// 这里用 duration 变化作为“歌曲已切换”的信号
_playback.player.stream.duration.listen((duration) {
if (duration.inMilliseconds > 0) {
_syncMediaItem();
}
});
}
// ============================================================
// 状态同步链(只能由 playing 流触发)
// ============================================================
void _updatePlaybackState({bool? playing}) {
final isPlaying = playing ?? _playback.player.state.playing;
debugPrint('🎵 _updatePlaybackState: isPlaying=$isPlaying');
final controls = [
MediaControl.skipToPrevious,
if (isPlaying) MediaControl.pause else MediaControl.play,
MediaControl.skipToNext,
];
debugPrint('🎵 controls: ${controls.map((c) => c.action).join(', ')}');
playbackState.add(PlaybackState(
controls: controls,
processingState: AudioProcessingState.ready,
playing: isPlaying,
androidCompactActionIndices: const [0, 1, 2],
updateTime: DateTime.now(),
));
}
// ============================================================
// 媒体信息同步链(独立于播放状态)
// ============================================================
void _syncMediaItem() {
final song = _localAudio.currentSong;
if (song != null) {
debugPrint('🎵 [sync] media item: ${song.title}');
mediaItem.add(MediaItem(
id: song.id,
title: song.title,
artist: song.artist,
duration: _playback.player.state.duration,
_player.positionStream.listen((position) {
_state.updatePosition(position);
final current = playbackState.value;
playbackState.add(audio_service.PlaybackState(
controls: current.controls,
processingState: current.processingState,
playing: current.playing,
androidCompactActionIndices: current.androidCompactActionIndices,
updateTime: DateTime.now(),
));
}
});
_player.durationStream.listen((duration) {
_state.updateDuration(duration);
_publishState();
});
_player.completedStream.listen((_) {
debugPrint('⏭️ [handler] completed, auto next');
AudioService().next();
});
}
void _publishState() {
final state = _state.playbackState;
debugPrint(
'📡 [publish] '
'playing=${state.playing}, '
'controls=${state.controls.map((e) => e.action).toList()}',
);
playbackState.add(state);
}
void updateNotification({
@@ -80,115 +55,77 @@ class AudioPlayerHandler extends BaseAudioHandler {
required String title,
required String artist,
}) {
mediaItem.add(MediaItem(
debugPrint('📢 [handler] updateNotification: $title');
mediaItem.add(audio_service.MediaItem(
id: id,
title: title,
artist: artist,
duration: _playback.player.state.duration,
duration: _player.duration,
));
_updatePlaybackState();
}
// ============================================================
// 控制命令(只发命令,不宣布状态)
// ============================================================
// ---- 控制命令(系统回调) ----
@override
Future<void> play() async {
debugPrint('🎵 [audio_service] play() called');
await _playback.resume();
// ⭐ 不调用 _updatePlaybackState()
// 等待 playing 流触发
debugPrint('▶️ [handler] play() called');
await _player.play();
}
@override
Future<void> pause() async {
debugPrint('🎵 [audio_service] pause() called');
await _playback.pause();
// ⭐ 不调用 _updatePlaybackState()
debugPrint('⏸️ [handler] pause() called');
await _player.pause();
}
@override
Future<void> stop() async {
debugPrint('🎵 [audio_service] stop() called');
await _playback.stop();
// ⭐ 不调用 _updatePlaybackState()
debugPrint('⏹️ [handler] stop() called');
_player.stop();
}
@override
Future<void> seek(Duration position) async {
debugPrint('🎵 [audio_service] seek() called: $position');
await _playback.seek(position);
debugPrint(' [handler] seek() called: $position');
_player.seek(position);
}
@override
Future<void> skipToNext() async {
if (_switchingTrack) {
debugPrint('🎵 skipToNext ignored: already switching');
return;
}
_switchingTrack = true;
try {
debugPrint('🎵 [audio_service] skipToNext() called');
await _localAudio.next();
// ⭐ 不调用 _updatePlaybackState()
// playing 流和 duration 流会自然触发状态更新
} finally {
// ⭐ 用真实状态释放锁,不用时间
await Future.delayed(const Duration(milliseconds: 50));
_switchingTrack = false;
}
debugPrint('⏭️ [handler] skipToNext() called');
AudioService().next();
}
@override
Future<void> skipToPrevious() async {
if (_switchingTrack) {
debugPrint('🎵 skipToPrevious ignored: already switching');
return;
}
_switchingTrack = true;
try {
debugPrint('🎵 [audio_service] skipToPrevious() called');
await _localAudio.previous();
} finally {
await Future.delayed(const Duration(milliseconds: 50));
_switchingTrack = false;
}
debugPrint('⏮️ [handler] skipToPrevious() called');
AudioService().previous();
}
// ============================================================
// click():薄路由层,不操作播放器
// ============================================================
// ⭐ 统一点击处理(仅使用 MediaButton 存在的枚举)
@override
Future<void> click([MediaButton button = MediaButton.media]) async {
debugPrint('🎵 [audio_service] click(): ${button.name} (index: ${button.index})');
// ⭐ 用 button.name 判断,不用 index
final name = button.name.toLowerCase();
// 路由到标准方法
if (name.contains('next') || name == 'next') {
await skipToNext();
return;
}
if (name.contains('previous') || name == 'previous') {
await skipToPrevious();
return;
}
if (name.contains('pause')) {
await pause();
return;
}
if (name.contains('play') || name.contains('media')) {
await play();
return;
}
// fallback: 根据当前播放状态 toggle
debugPrint('🎵 → fallback toggle');
if (_playback.player.state.playing) {
await pause();
} else {
await play();
Future<void> click(
[audio_service.MediaButton button =
audio_service.MediaButton.media]) async {
debugPrint('🎯 [handler] click() called with button: ${button.name}');
switch (button) {
case audio_service.MediaButton.next:
await skipToNext();
break;
case audio_service.MediaButton.previous:
await skipToPrevious();
break;
case audio_service.MediaButton.media:
// 播放/暂停切换
if (_player.isPlaying) {
await pause();
} else {
await play();
}
break;
default:
// 其他按钮(fastForward, rewind等)忽略或fallback
debugPrint('⚠️ [handler] unhandled MediaButton: $button');
break;
}
}
}
}
+8 -8
View File
@@ -44,27 +44,27 @@ class PlaybackService {
}
// ---------- 控制 ----------
void pause() {
Future<void> pause() async {
if (_initialized && _player != null) {
_player!.pause();
await _player!.pause();
}
}
void resume() {
Future<void> resume() async {
if (_initialized && _player != null) {
_player!.play();
await _player!.play();
}
}
void stop() {
Future<void> stop() async {
if (_initialized && _player != null) {
_player!.stop();
await _player!.stop();
}
}
void seek(Duration position) {
Future<void> seek(Duration position) async {
if (_initialized && _player != null) {
_player!.seek(position);
await _player!.seek(position);
}
}