81 lines
3.8 KiB
Dart
81 lines
3.8 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||
import 'package:new_learn/core/components/audio_player_card.dart';
|
||
|
||
/// ============================================================
|
||
/// 【AudioPlayerDemoPage】音频播放 Demo(just_audio 控制条组件)
|
||
///
|
||
/// 一、本页解决什么问题:
|
||
/// 展示 AudioPlayerCard 的用法:传 asset 即可得到一个
|
||
/// 完整的音频控制条(播放/暂停、进度拖动、倍速、循环)。
|
||
///
|
||
/// 二、音频源说明:
|
||
/// 用【本地 assets 音频】做 demo:本地文件 seek 跳播无需重新缓冲,
|
||
/// 进度条拖动秒响应(在线流 seek 会重新缓冲,见组件文档"高频坑")。
|
||
/// 以后接真实课程音频时,把 asset 换成 url 传后端在线地址即可。
|
||
///
|
||
/// 三、高频坑:
|
||
/// 1. AudioPlayerCard 只是"控制条",不是整页 —— 它设计成可嵌进
|
||
/// 任意列表/详情页(如课程详情里的音频小节);
|
||
/// 2. 切后台会自动暂停(组件内部处理),无需业务页操心;
|
||
/// 3. 若要"列表连续播放",后面在 AudioPlayerCard 上加
|
||
/// setAudioSources + 上一首/下一首即可(just_audio 原生支持);
|
||
/// 4. 网络流 seek 后会有一次"缓冲中 X%"(进度条浅色区域 = 已缓冲),
|
||
/// 这是 ExoPlayer 的正常机制,不是卡死。
|
||
/// ============================================================
|
||
class AudioPlayerDemoPage extends StatelessWidget {
|
||
const AudioPlayerDemoPage({super.key});
|
||
|
||
/// 本地测试音频(30 秒,来自 assets/audio/sample-30s.mp3)
|
||
// static const String _audioAsset = 'assets/audio/sample-30s.mp3';
|
||
|
||
/// 【临时调试】网络源:观察 seek 后进度条的"缓冲区域 + 缓冲中 X%"。
|
||
/// ⚠ 踩坑:之前用 samplelib.com —— 实测它的服务器【不支持 HTTP Range】
|
||
/// (Range 请求返回 200 而非 206),导致:第一次点进度条中间段,
|
||
/// ExoPlayer 无法字节定位 → 位置被重置回起点;第二次打开因为有系统
|
||
/// 缓存 → 看似正常(会误导排查方向!)。
|
||
/// 现用 SoundHelix(curl 实测 206 Partial Content + Accept-Ranges: bytes),
|
||
/// 服务器在境外,网络慢 = 缓冲条涨得慢看得清,正好演示缓冲机制。
|
||
/// 以后接自己的课程音频,务必先验收服务器支持 Range:
|
||
/// curl -s -D - -o NUL -H "Range: bytes=0-1023" <音频url>
|
||
/// 看到 HTTP/1.1 206 Partial Content 才算支持 seek 跳播。
|
||
static const String _audioUrl =
|
||
'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3';
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
appBar: AppBar(
|
||
title: const Text('音频播放'),
|
||
centerTitle: true,
|
||
),
|
||
body: Padding(
|
||
padding: EdgeInsets.all(16.w),
|
||
child: Column(
|
||
children: [
|
||
// 音频控制条
|
||
// 【临时调试】切网络源看缓冲条;要回本地源时取消下面注释
|
||
// 并把上面的 url 改回 asset。
|
||
// const AudioPlayerCard(
|
||
// asset: _audioAsset,
|
||
// title: '本地测试音频(30 秒)',
|
||
// autoPlay: true,
|
||
// ),
|
||
const AudioPlayerCard(
|
||
url: _audioUrl,
|
||
title: '网络测试音频(约 6 分钟)',
|
||
autoPlay: true,
|
||
),
|
||
SizedBox(height: 20.h),
|
||
const Text(
|
||
'支持:播放/暂停、进度拖动(本地秒响应)、倍速切换、循环;\n切后台自动暂停;全部通过 props 配置。',
|
||
textAlign: TextAlign.center,
|
||
style: TextStyle(fontSize: 13, color: Color(0xFF999999)),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|