在Flutter中,如果你想将文件保存到手机的公有目录(如音乐、图片、下载等),可以使用path_provider
插件来获取路径,但需要注意Android 10及以上版本的存储访问框架(Storage Access Framework, SAF)和分区存储(Scoped Storage)的限制。
解决方案
使用path_provider
插件:
path_provider
插件可以获取应用的私有目录(如getApplicationDocumentsDirectory
),但不能直接获取公有目录。
使用permission_handler
插件:
在Android 10及以上版本中,需要动态申请存储权限。permission_handler
插件可以帮助你请求权限。
使用flutter_file_manager
插件:
这个插件可以帮助你管理文件和目录,但同样需要注意权限问题。
使用flutter_downloader
插件:
这个插件可以帮助你下载文件到公有目录,但需要处理权限和存储访问框架。
具体步骤
1. 添加依赖
在你的pubspec.yaml
文件中添加以下依赖:
dependencies:
flutter:
sdk: flutter
path_provider: ^2.0.11
permission_handler: ^10.0.0
flutter_downloader: ^1.7.1
2. 请求权限
在Android 10及以上版本中,需要动态请求存储权限。使用permission_handler
插件来请求权限:
import 'package:permission_handler/permission_handler.dart';
void requestStoragePermission() async {
var status = await Permission.storage.status;
if (!status.isGranted) {
await Permission.storage.request();
}
}
3. 获取公有目录路径
使用path_provider
插件获取公有目录路径:
import 'package:path_provider/path_provider.dart';
import 'dart:io';
Future<String> getPublicDirectoryPath(String type) async {
Directory directory;
switch (type) {
case 'music':
directory = Directory('/storage/emulated/0/Music');
break;
case 'alarms':
directory = Directory('/storage/emulated/0/Alarms');
break;
case 'podcasts':
directory = Directory('/storage/emulated/0/Podcasts');
break;
case 'notifications':
directory = Directory('/storage/emulated/0/Notifications');
break;
case 'pictures':
directory = Directory('/storage/emulated/0/Pictures');
break;
case 'movies':
directory = Directory('/storage/emulated/0/Movies');
break;
case 'downloads':
directory = Directory('/storage/emulated/0/Download');
break;
case 'media':
directory = Directory('/storage/emulated/0/DCIM');
break;
case 'documents':
directory = Directory('/storage/emulated/0/Documents');
break;
default:
throw Exception('Unsupported directory type');
}
return directory.path;
}
4. 写入文件
使用dart:io
库将文件写入公有目录:
import 'dart:io';
Future<void> writeFileToPublicDirectory(String filePath, String content) async {
final file = File(filePath);
await file.writeAsString(content);
}
5. 示例代码
以下是一个完整的示例代码,展示如何将文件写入公有目录:
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'dart:io';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
Future<void> writeToPublicDirectory() async {
await requestStoragePermission();
final path = await getPublicDirectoryPath('music');
await writeFileToPublicDirectory('$path/test.txt', 'Hello, World!');
print('File written to $path');
}
Future<String> getPublicDirectoryPath(String type) async {
Directory directory;
switch (type) {
case 'music':
directory = Directory('/storage/emulated/0/Music');
break;
case 'alarms':
directory = Directory('/storage/emulated/0/Alarms');
break;
case 'podcasts':
directory = Directory('/storage/emulated/0/Podcasts');
break;
case 'notifications':
directory = Directory('/storage/emulated/0/Notifications');
break;
case 'pictures':
directory = Directory('/storage/emulated/0/Pictures');
break;
case 'movies':
directory = Directory('/storage/emulated/0/Movies');
break;
case 'downloads':
directory = Directory('/storage/emulated/0/Download');
break;
case 'media':
directory = Directory('/storage/emulated/0/DCIM');
break;
case 'documents':
directory = Directory('/storage/emulated/0/Documents');
break;
default:
throw Exception('Unsupported directory type');
}
return directory.path;
}
Future<void> writeFileToPublicDirectory(String filePath, String content) async {
final file = File(filePath);
await file.writeAsString(content);
}
void requestStoragePermission() async {
var status = await Permission.storage.status;
if (!status.isGranted) {
await Permission.storage.request();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Write to Public Directory'),
),
body: Center(
child: ElevatedButton(
onPressed: writeToPublicDirectory,
child: Text('Write File'),
),
),
);
}
}
注意事项
分区存储(Scoped Storage):
Android 10及以上版本引入了分区存储(Scoped Storage),限制了应用对公有目录的直接访问。你需要使用存储访问框架(SAF)来访问这些目录。
权限管理:
确保你已经请求了必要的权限,并且在AndroidManifest.xml中声明了存储权限。
兼容性:
对于Android 10及以上版本,建议使用flutter_downloader
插件来处理文件下载和存储,因为它已经处理了分区存储的兼容性问题。
通过以上步骤,你应该能够在Flutter中将文件写入手机的公有目录。