Flutter获取Android应用目录MP3文件并展示至ListView问题求助
问题分析与解决方案
核心问题
- 路径硬编码错误:代码中硬编码的目录是
/storage/emulated/0/Android/data/com.example.cast/files,但你的实际应用包名是com.example.MyApp,路径不匹配导致读取到错误目录的文件。 - 文件名显示错误:ListView的
title直接输出了整个allSongs列表,无法正确展示单个文件名。 - 同步目录遍历阻塞UI:使用
listSync()同步遍历目录,可能导致UI卡顿。
修正步骤
1. 用官方API获取正确的应用专属目录
避免硬编码路径,使用path_provider插件获取系统推荐的应用专属外部存储目录,适配所有Android版本和设备。
- 先在
pubspec.yaml添加依赖:
dependencies: path_provider: ^2.0.15
- 修改
getFiles()方法,替换目录获取逻辑:
import 'package:path_provider/path_provider.dart'; // 导入插件 void getFiles() async { // 获取应用专属外部文件目录 Directory? dir = await getExternalStorageDirectory(); if (dir == null) { print("无法获取应用目录"); return; } String mp3Path = dir.path; print(mp3Path); List<FileSystemEntity> _songs = []; try { // 异步遍历目录,避免阻塞UI await for (var entity in dir.list(recursive: true, followLinks: false)) { String path = entity.path; if (path.endsWith('.mp3')) { _songs.add(entity); } } } catch (e) { print("读取目录出错: $e"); } print("找到MP3文件数量: ${_songs.length}"); setState(() { files = _songs; allSongs = _songs; }); }
2. 修正ListView的文件名显示
使用path包的basename()方法从文件路径提取纯文件名,替换原有的列表输出:
- 先导入
path包:
import 'package:path/path.dart' as path;
- 修改ListTile的title部分:
title: Text( path.basename(allSongs![index].path), // 提取单个文件名 maxLines: 1, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w400, ), ),
3. 权限适配(可选)
- Android 9及以下版本:需在
AndroidManifest.xml添加存储权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- Android 10及以上:应用专属目录无需额外权限,系统自动授权。
完整修正代码
import 'dart:io'; import 'package:cast/utilities/bottom_sheet_widget.dart'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; import 'package:path/path.dart' as path; class MyAudioList extends StatefulWidget { @override State<StatefulWidget> createState() { return _MyAudioList(); } } class _MyAudioList extends State<MyAudioList> { var files; List<FileSystemEntity>? allSongs; void getFiles() async { Directory? dir = await getExternalStorageDirectory(); if (dir == null) { print("无法获取应用专属目录"); return; } String mp3Path = dir.path; print(mp3Path); List<FileSystemEntity> _songs = []; try { await for (var entity in dir.list(recursive: true, followLinks: false)) { String filePath = entity.path; if (filePath.endsWith('.mp3')) { _songs.add(entity); } } } catch (e) { print("读取目录错误: $e"); } print("找到MP3文件数量: ${_songs.length}"); setState(() { allSongs = _songs; files = _songs; }); } @override void initState() { getFiles(); super.initState(); } @override Widget build(BuildContext context) { return RefreshIndicator( onRefresh: () async { getFiles(); }, child: Scaffold( body: allSongs == null ? const Center( child: Text( '搜索中..', style: TextStyle(color: Colors.white), ), ) : allSongs!.isEmpty ? const Center( child: Text( '未找到MP3文件', style: TextStyle(color: Colors.white), ), ) : ListView.builder( itemCount: allSongs!.length, itemBuilder: (context, index) { final song = allSongs![index]; return ListTile( key: UniqueKey(), leading: ClipRRect( borderRadius: BorderRadius.circular(8), child: Image.network( "https://images.unsplash.com/photo-1541963463532-d68292c34b19?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8Ym9va3xlbnwwfHwwfHw%3D&w=1000&q=80", width: 50, height: 50, fit: BoxFit.cover, ), ), title: Text( path.basename(song.path), maxLines: 1, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w400, ), ), subtitle: Text( "publisher", style: TextStyle( color: Colors.grey[400]!, fontSize: 13, ), ), trailing: IconButton( onPressed: () {}, icon: const Icon(Icons.more_vert, color: Colors.grey), ), ); }, ), ), ); } }
内容的提问来源于stack exchange,提问作者Digamber negi
相关产品推荐
相关产品推荐

