Files
2026-09-04 15:22:20 +08:00

81 lines
3.8 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)),
),
],
),
),
);
}
}