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

Flutter音频播放器:如何实现单个按钮控制播放/暂停状态

实现可控制音频播放/暂停按钮的具体方案

1. 添加音频播放依赖

先在项目的pubspec.yaml中引入音频处理库,这里使用audioplayers:

dependencies:
  flutter:
    sdk: flutter
  audioplayers: ^4.0.1 # 建议使用最新版本

执行flutter pub get完成依赖安装。

2. 改造代码实现状态控制

因为需要跟踪每个列表项的播放状态,要把原Widget改为StatefulWidget,同时维护播放状态和音频播放器实例:

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

class RadioListScreen extends StatefulWidget {
  final List<RadioDetail> radioDetails; // 你的电台数据模型类

  const RadioListScreen({super.key, required this.radioDetails});

  @override
  State<RadioListScreen> createState() => _RadioListScreenState();
}

class _RadioListScreenState extends State<RadioListScreen> {
  // 记录每个列表项的播放状态,初始全为未播放
  late List<bool> _isPlayingList;
  // 音频播放器实例,若要支持多音频同时播放,可改为Map<int, AudioPlayer>
  final AudioPlayer _audioPlayer = AudioPlayer();
  // 记录当前正在播放的列表项索引,避免多音频同时播放
  int? _currentPlayingIndex;

  @override
  void initState() {
    super.initState();
    // 初始化播放状态列表,长度与电台数据一致
    _isPlayingList = List.filled(widget.radioDetails.length, false);
  }

  @override
  void dispose() {
    // 页面销毁时释放音频资源,防止内存泄漏
    _audioPlayer.dispose();
    super.dispose();
  }

  // 播放/暂停切换逻辑
  Future<void> _togglePlayPause(int index) async {
    final targetRadio = widget.radioDetails[index];
    
    setState(() {
      if (_currentPlayingIndex == index) {
        // 点击的是正在播放的项,切换暂停/播放状态
        _isPlayingList[index] = !_isPlayingList[index];
      } else {
        // 点击的是未播放的项,先停止之前的播放项
        if (_currentPlayingIndex != null) {
          _isPlayingList[_currentPlayingIndex!] = false;
        }
        _isPlayingList[index] = true;
        _currentPlayingIndex = index;
      }
    });

    if (_isPlayingList[index]) {
      // 开始播放,假设你的RadioDetail类有audioUrl字段存储音频地址
      await _audioPlayer.play(UrlSource(targetRadio.audioUrl));
    } else {
      // 暂停播放
      await _audioPlayer.pause();
      _currentPlayingIndex = null;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: const AppBar(title: Text("ListView.builder")),
      body: ListView.builder(
        itemCount: widget.radioDetails.length,
        itemBuilder: (BuildContext context, int index) {
          return Card(
            clipBehavior: Clip.antiAlias,
            child: ListTile(
              title: Text(widget.radioDetails[index].name),
              trailing: IconButton(
                // 根据播放状态切换图标
                icon: Icon(
                  _isPlayingList[index] ? Icons.pause : Icons.play_arrow,
                  color: Colors.blue,
                ),
                onPressed: () => _togglePlayPause(index),
              ),
            ),
          );
        },
      ),
    );
  }
}

// 假设你的电台数据模型类
class RadioDetail {
  final String name;
  final String audioUrl;

  RadioDetail({required this.name, required this.audioUrl});
}

核心逻辑说明

  • 状态跟踪:用_isPlayingList数组记录每个列表项的播放状态,_currentPlayingIndex标记当前播放项,避免多音频同时播放。
  • 切换逻辑:点击按钮时,若当前项未播放则启动播放并停止之前的播放项;若当前项正在播放则暂停。
  • 资源管理:在页面销毁时释放音频播放器资源,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 20:57:18