Flutter实现音频ZIP下载与应用内播放(文件不暴露至系统存储)
Flutter 音频ZIP包下载、提取与播放实现
需求说明
我是Flutter开发新手,当前项目中服务器托管了多份按分类存储音频的ZIP文件。现需实现:用户下载指定ZIP包并提取音频后,可在应用内播放该音频,且音频文件不会显示在手机系统存储中。
基于图片示例修改后的音频处理代码
首先在pubspec.yaml中添加依赖:
dependencies: flutter: sdk: flutter archive: ^3.3.7 http: ^1.1.0 path_provider: ^2.1.1 audioplayers: ^5.2.1 # 新增音频播放依赖
完整实现代码:
import 'package:flutter/material.dart'; import 'dart:io'; import 'package:archive/archive.dart'; import 'package:http/http.dart' as http; import 'package:path_provider/path_provider.dart'; import 'package:audioplayers/audioplayers.dart'; // 引入音频播放库 class AudioZipDemo extends StatefulWidget { const AudioZipDemo({super.key}); final String title = "音频ZIP下载与播放"; @override AudioZipDemoState createState() => AudioZipDemoState(); } class AudioZipDemoState extends State<AudioZipDemo> { bool _downloading = false; String? _appDir; List<String> _audioPaths = []; final AudioPlayer _audioPlayer = AudioPlayer(); // 音频播放器实例 // 替换为你的目标音频ZIP地址 final String _zipUrl = 'https://example.com/audio_category.zip'; final String _localZipName = 'audio.zip'; @override void initState() { super.initState(); _initAppDir(); } // 初始化应用私有存储目录 Future<void> _initAppDir() async { _appDir ??= (await getApplicationDocumentsDirectory()).path; } // 下载ZIP文件到应用私有目录 Future<File> _downloadZipFile(String url, String fileName) async { final response = await http.Client().get(Uri.parse(url)); final file = File('$_appDir/$fileName'); return file.writeAsBytes(response.bodyBytes); } // 下载并提取ZIP中的音频文件 Future<void> _downloadAndExtractAudio() async { setState(() { _downloading = true; _audioPaths.clear(); }); final zippedFile = await _downloadZipFile(_zipUrl, _localZipName); await _extractAudioFromZip(zippedFile); setState(() { _downloading = false; }); } // 解压ZIP并保存音频文件到应用私有目录 Future<void> _extractAudioFromZip(File zippedFile) async { final bytes = zippedFile.readAsBytesSync(); final archive = ZipDecoder().decodeBytes(bytes); for (final file in archive) { if (file.isFile && _isAudioFile(file.name)) { final filePath = '$_appDir/${file.name}'; final outFile = File(filePath); await outFile.create(recursive: true); await outFile.writeAsBytes(file.content); _audioPaths.add(filePath); } } } // 判断是否为音频文件(可按需扩展格式) bool _isAudioFile(String fileName) { final audioExtensions = ['.mp3', '.wav', '.aac', '.m4a']; return audioExtensions.any(ext => fileName.toLowerCase().endsWith(ext)); } // 播放指定路径的音频 Future<void> _playAudio(String audioPath) async { await _audioPlayer.stop(); await _audioPlayer.play(DeviceFileSource(audioPath)); } // 构建音频列表UI Widget _buildAudioList() { return Expanded( child: _audioPaths.isEmpty ? const Center(child: Text('暂无音频文件')) : ListView.builder( itemCount: _audioPaths.length, itemBuilder: (context, index) { final audioName = File(_audioPaths[index]).path.split('/').last; return ListTile( title: Text(audioName), trailing: IconButton( icon: const Icon(Icons.play_arrow), onPressed: () => _playAudio(_audioPaths[index]), ), ); }, ), ); } // 下载进度指示器 Widget _buildProgressIndicator() { return Container( width: 25, height: 25, padding: const EdgeInsets.fromLTRB(0, 20, 10, 20), child: const CircularProgressIndicator( strokeWidth: 3, valueColor: AlwaysStoppedAnimation<Color>(Colors.white), ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), actions: [ if (_downloading) _buildProgressIndicator(), IconButton( icon: const Icon(Icons.file_download), onPressed: _downloadAndExtractAudio, ), ], ), body: Column( children: [ _buildAudioList(), ], ), ); } @override void dispose() { _audioPlayer.dispose(); // 释放播放器资源 super.dispose(); } }
关键改动说明
- 私有存储保障:使用
getApplicationDocumentsDirectory()获取应用私有目录,该目录下的文件不会被系统媒体库扫描,满足"音频不显示在系统存储"的要求。 - 音频播放支持:引入
audioplayers库实现本地音频播放,包含播放控制和资源释放逻辑。 - 文件过滤处理:添加
_isAudioFile方法仅提取ZIP中的音频文件,过滤无关文件。 - UI适配调整:将图片列表替换为音频列表项,显示文件名并添加播放按钮。
内容的提问来源于stack exchange,提问作者joshua
相关产品推荐
相关产品推荐

