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

如何在Flutter中使用audio_service调整音频音量

如何在audio_service中调整音频音量

我想知道怎么用audio_service包调整音频音量,在AudioHandler类里没找到对应的方法。比如怎么通过Flutter UI的按钮调整音量,或者用5秒左右的时间逐步改变音量实现淡入淡出?用just_audio的setVolume很容易做到,但不知道怎么和audio_service集成。


解决方案

你的MyAudioHandler已经持有了AudioPlayer实例,只需要在MyAudioHandler中封装音量控制方法,再在UI层调用这些方法即可。

步骤1:在MyAudioHandler中添加音量控制逻辑

修改你的MyAudioHandler类,添加设置音量、获取当前音量的方法,同时封装淡入淡出的实现:

import 'package:rxdart/rxdart.dart';

class MyAudioHandler extends BaseAudioHandler {
  final _player = AudioPlayer();
  // 用Stream同步音量变化,方便UI实时更新
  final _volumeSubject = BehaviorSubject<double>.seeded(1.0);
  Stream<double> get volumeStream => _volumeSubject.stream;

  MyAudioHandler() {
    _player.playbackEventStream.map(updatePlaybackState).pipe(playbackState);
    _volumeSubject.add(_player.volume);
  }

  // 设置音量(范围0.0-1.0,自动限制边界)
  Future<void> setVolume(double volume) async {
    final clampedVolume = volume.clamp(0.0, 1.0);
    await _player.setVolume(clampedVolume);
    _volumeSubject.add(clampedVolume);
  }

  // 获取当前音量
  double get currentVolume => _player.volume;

  // 淡入淡出效果:duration为总时长,targetVolume为目标音量
  Future<void> fadeVolume(double targetVolume, Duration duration) async {
    const steps = 20; // 分多步调整避免卡顿
    final stepDuration = duration ~/ steps;
    final volumeStep = (targetVolume - currentVolume) / steps;

    for (int i = 0; i < steps; i++) {
      await Future.delayed(stepDuration);
      await setVolume(currentVolume + volumeStep * (i + 1));
    }
    // 确保最终精确设置到目标值
    await setVolume(targetVolume);
  }

  // 原有方法保持不变
  @override
  Future<void> play() async {
    await _player.play();
    return super.play();
  }

  @override
  Future<void> playMediaItem(MediaItem item) async {
    mediaItem.add(item);
    _player.setAudioSource(AudioSource.asset(item.id));
    await _player.play();
    return super.playMediaItem(item);
  }

  @override
  Future<void> stop() async {
    await _player.stop();
  }

  PlaybackState updatePlaybackState(PlaybackEvent event) {
    return PlaybackState(
      processingState: const {
        ProcessingState.idle: AudioProcessingState.idle,
        ProcessingState.loading: AudioProcessingState.loading,
        ProcessingState.buffering: AudioProcessingState.buffering,
        ProcessingState.ready: AudioProcessingState.ready,
        ProcessingState.completed: AudioProcessingState.completed,
      }[_player.processingState]!,
      playing: _player.playing,
      updatePosition: _player.position,
      repeatMode: AudioServiceRepeatMode.all
    );
  }

  // 释放资源
  @override
  Future<void> dispose() async {
    await _volumeSubject.close();
    await _player.dispose();
    super.dispose();
  }
}

步骤2:在UI中调用音量控制方法

修改UI代码,给加减按钮添加点击事件,还可以增加淡入淡出的控制按钮:

return Scaffold(
  body: StreamBuilder<PlaybackState>(
      stream: _audioHandler.playbackState,
      builder: (context, snapshot) {
        bool isPlaying = snapshot.data?.playing ?? false;

        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            if (isPlaying)
              ElevatedButton(
                onPressed: () {
                  _audioHandler.stop();
                },
                child: const Text('Stop'),
              )
            else ...[
              ElevatedButton(
                onPressed: () async {
                  const item = MediaItem(
                    title: 'Sea',
                    id: 'assets/audio/seaside.mp3',
                  );
                  // 播放前先设音量为0,然后淡入到1.0(5秒)
                  await _audioHandler.setVolume(0.0);
                  await _audioHandler.playMediaItem(item);
                  await _audioHandler.fadeVolume(1.0, const Duration(seconds: 5));
                },
                child: const Text('Play Seaside (淡入)'),
              ),
            ],
            const SizedBox(height: 20),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                IconButton(
                  onPressed: () async {
                    // 每次增加0.1音量
                    await _audioHandler.setVolume(_audioHandler.currentVolume + 0.1);
                  },
                  icon: const Icon(Icons.add),
                ),
                // 实时显示当前音量百分比
                StreamBuilder<double>(
                  stream: _audioHandler.volumeStream,
                  initialData: 1.0,
                  builder: (context, snapshot) {
                    return Text('${(snapshot.data! * 100).round()}%');
                  },
                ),
                IconButton(
                  onPressed: () async {
                    // 每次减少0.1音量
                    await _audioHandler.setVolume(_audioHandler.currentVolume - 0.1);
                  },
                  icon: const Icon(Icons.remove),
                ),
              ],
            ),
            const SizedBox(height: 20),
            // 淡出后停止的按钮
            if (isPlaying)
              ElevatedButton(
                onPressed: () async {
                  await _audioHandler.fadeVolume(0.0, const Duration(seconds: 5));
                  await _audioHandler.stop();
                },
                child: const Text('淡出停止'),
              ),
          ],
        );
      }),
);

核心说明

  • 利用MyAudioHandler内部的AudioPlayer实例直接调用setVolume,封装成对外方法后,UI层可以通过AudioHandler直接控制音量。
  • 淡入淡出通过分步骤调整音量+延迟实现,避免音量突变带来的突兀感。
  • 用BehaviorSubject维护音量流,让UI可以实时同步显示当前音量值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 01:25:11