Flutter中使用on_audio_query查询应用目录音频文件失败
解决on_audio_query指定应用私有目录查询不到歌曲的问题
我开发了一款Flutter应用,允许用户下载歌曲并存入应用私有目录/storage/emulated/0/Android/data/com.zylae/files/downloads,但调用on_audio_query的querySongs方法并添加path参数后,完全查询不到任何歌曲,尝试移除路径开头的斜杠也无效。以下是修改自官方示例的代码:
import 'dart:io'; import 'package:flutter/material.dart'; import 'package:on_audio_query/on_audio_query.dart'; class AudioQ extends StatefulWidget { const AudioQ({Key? key}) : super(key: key); @override _AudioQState createState() => _AudioQState(); } class _AudioQState extends State<AudioQ> { // Main method. final OnAudioQuery _audioQuery = OnAudioQuery(); // Indicate if application has permission to the library. bool _hasPermission = false; @override void initState() { super.initState(); // (Optinal) Set logging level. By default will be set to 'WARN'. // // Log will appear on: // * XCode: Debug Console // * VsCode: Debug Console // * Android Studio: Debug and Logcat Console LogConfig logConfig = LogConfig(logType: LogType.DEBUG); _audioQuery.setLogConfig(logConfig); // Check and request for permission. checkAndRequestPermissions(); } checkAndRequestPermissions({bool retry = false}) async { // The param 'retryRequest' is false, by default. _hasPermission = await _audioQuery.checkAndRequest( retryRequest: retry, ); // Only call update the UI if application has all required permissions. _hasPermission ? setState(() {}) : null; } @override Widget build(BuildContext context) { // _audioQuery.querySongs(); return Scaffold( appBar: AppBar( title: const Text("OnAudioQueryExample"), elevation: 2, ), body: Center( child: !_hasPermission ? noAccessToLibraryWidget() : FutureBuilder<List<SongModel>>( // Default values: future: _audioQuery.querySongs( sortType: null, orderType: OrderType.ASC_OR_SMALLER, uriType: UriType.EXTERNAL, ignoreCase: true, path: '/storage/emulated/0/Android/data/com.zylae/files/downloads', ), builder: (context, item) { // Display error, if any. if (item.hasError) { return Text(item.error.toString()); } // Waiting content. if (item.data == null) { return const CircularProgressIndicator(); } // 'Library' is empty. if (item.data!.isEmpty) return const Text("Nothing found!"); // You can use [item.data!] direct or you can create a: // List<SongModel> songs = item.data!; return ListView.builder( itemCount: item.data!.length, itemBuilder: (context, index) { return ListTile( title: Text(item.data![index].title), subtitle: Text(item.data![index].artist ?? "No Artist"), trailing: const Icon(Icons.arrow_forward_rounded), // This Widget will query/load image. // You can use/create your own widget/method using [queryArtwork]. leading: QueryArtworkWidget( controller: _audioQuery, id: item.data![index].id, type: ArtworkType.AUDIO, ), ); }, ); }, ), ), ); } Widget noAccessToLibraryWidget() { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: Colors.redAccent.withOpacity(0.5), ), padding: const EdgeInsets.all(20), child: Column( mainAxisSize: MainAxisSize.min, children: [ const Text("Application doesn't have access to the library"), const SizedBox(height: 10), ElevatedButton( onPressed: () => checkAndRequestPermissions(retry: true), child: const Text("Allow"), ), ], ), ); } }
问题原因及解决办法
系统媒体库未索引私有目录文件:Android的应用私有目录(
Android/data/包名/)下的文件,默认不会被系统媒体扫描器收录,而on_audio_query是基于系统媒体数据库进行查询的,所以指定该路径无法获取到文件。手动触发媒体扫描:在歌曲下载完成后,调用媒体扫描接口让系统识别新文件。可以使用
flutter_media_scanner插件实现,示例代码:import 'package:flutter_media_scanner/flutter_media_scanner.dart'; // 下载完成后调用此方法扫描文件 Future<void> scanDownloadedSong(String filePath) async { await FlutterMediaScanner.scanFile(filePath); }扫描完成后,
on_audio_query就能查询到该文件。权限适配:确保应用已申请对应权限:
- Android 12及以下:申请
READ_EXTERNAL_STORAGE权限 - Android 13+:申请
READ_MEDIA_AUDIO权限
- Android 12及以下:申请
直接读取目录(替代方案):如果不想依赖系统媒体库,可以直接通过Dart的
dart:io遍历目标目录,结合audio_metadata插件解析音频信息:import 'dart:io'; import 'package:audio_metadata/audio_metadata.dart'; Future<List<Map<String, dynamic>>> getLocalSongs() async { final songDir = Directory('/storage/emulated/0/Android/data/com.zylae/files/downloads'); if (!await songDir.exists()) return []; final songFiles = songDir.listSync().where((entity) => entity is File && entity.path.toLowerCase().endsWith('.mp3') ).cast<File>(); final songs = <Map<String, dynamic>>[]; for (var file in songFiles) { final metadata = await MetadataRetriever.fromFile(file); songs.add({ 'title': metadata.title ?? file.path.split('/').last, 'artist': metadata.artist ?? 'Unknown', 'file': file, }); } return songs; }
内容的提问来源于stack exchange,提问作者Kenneth Ben-Boulo
相关产品推荐
相关产品推荐

