Flutter文件保存插件utopia_save_file的使用
Flutter文件保存插件utopia_save_file的使用
简介
utopia_save_file
是一个统一且正确的文件保存实现,适用于Flutter应用。它提供了多种方法来保存文件,包括从URL、字节流和文件对象中保存。该插件在不同平台(如Android、iOS和Web)上有不同的行为,但都尽量保持一致的API。
一般行为
- MIME类型和文件名推断:如果未提供MIME类型和文件名,插件会尝试推断。如果无法推断,则会抛出
SaveFileMetadataException
异常。 - 文件扩展名检查:检查文件名是否包含与MIME类型匹配的扩展名。如果不匹配,则根据
extensionBehavior
参数进行处理,默认情况下会替换原始扩展名。 - 触发保存操作:启动保存操作并等待完成,同时尽量减少Dart线程的工作量。
平台特定注意事项
Android
- 在Android上,插件会启动系统“文件”应用程序,允许用户选择保存的目标位置和文件名。
- 用户可以取消操作,此时
fromX
方法将返回SaveFileResultCancelled
。 - 使用
Intent.ACTION_CREATE_DOCUMENT
和ContentResolver.openInputStream
实现文件保存。 - 不需要
WRITE_EXTERNAL_STORAGE
权限。 name
参数只是一个建议,用户可以在保存时更改文件名。
iOS
- 在iOS上,文件保存到应用程序的文档目录,但文件会在系统“Files”应用程序中可见。
- 不需要用户交互,因此
fromX
方法总是返回true
。 - 需要在
Info.plist
中添加以下配置:<key>NSAllowsArbitraryLoads</key> <true/> <key>LSSupportsOpeningDocumentsInPlace</key> <true/> <key>UIFileSharingEnabled</key> <true/>
Web
- 在Web上,插件使用带有
download
属性的<a>
元素触发下载,下载完全在应用外部进行。 fromX
方法会立即返回true
,即使用户后来取消了下载。
完整示例代码
下面是一个完整的示例代码,展示了如何使用 utopia_save_file
插件来保存文件。这个示例包括从URL、字节流和文件对象中保存文件的功能。
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:utopia_save_file/utopia_save_file.dart';
// 根据平台导入不同的下载模块
import 'download_io.dart' if (dart.library.js) 'download_web.dart';
const _fileUrl = "https://upload.wikimedia.org/wikipedia/commons/8/80/Wikipedia-logo-v2.svg?download";
const _fileUrlName = "wiki_logo";
const _fileBytesName = "alamakota";
const _fileBytesMime = "text/plain";
// 构建字节流
Stream<List<int>> _buildFileBytes() =>
Stream.fromIterable(["ala", "ma", "kota"]).map((it) => Uint8ClampedList.fromList(it.codeUnits));
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
[@override](/user/override)
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _isInProgress = false;
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('UtopiaSaveFile example')),
body: Builder(
builder: (context) => Center(
child: _isInProgress ? _buildLoader() : _buildButtons(context),
),
),
),
);
}
// 构建加载指示器
Widget _buildLoader() => const CircularProgressIndicator();
// 构建按钮
Widget _buildButtons(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
onPressed: () => _saveUrl(context, useName: false),
child: const Text("Save without name"),
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: () => _saveUrl(context, useName: true),
child: const Text("Save with name"),
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: () => _saveBytes(context),
child: const Text("Save bytes"),
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: () => _saveFile(context),
child: const Text("Save file"),
),
],
);
}
// 从URL保存文件
Future<void> _saveUrl(BuildContext context, {required bool useName}) async =>
_runWithProgress(context, () => UtopiaSaveFile.fromUrl(_fileUrl, name: useName ? _fileUrlName : null));
// 从字节流保存文件
Future<void> _saveBytes(BuildContext context) async {
await _runWithProgress(
context,
() => UtopiaSaveFile.fromByteStream(_buildFileBytes(), name: _fileBytesName, mime: _fileBytesMime),
);
}
// 从文件对象保存文件
Future<void> _saveFile(BuildContext context) async =>
_runWithProgress(context, () async => UtopiaSaveFile.fromFile(await download(_fileUrl), name: _fileUrlName));
// 带进度显示的操作
Future<void> _runWithProgress(BuildContext context, Future<bool> Function() block) async {
late String message;
try {
setState(() => _isInProgress = true);
message = await block() ? "Success" : "Cancelled";
} catch (_) {
message = "Failed";
rethrow;
} finally {
setState(() => _isInProgress = false);
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
}
}
}
更多关于Flutter文件保存插件utopia_save_file的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter文件保存插件utopia_save_file的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用utopia_save_file
插件来保存文件的代码示例。请注意,实际使用时,你可能需要根据自己的项目结构和需求进行调整。
首先,确保你已经在pubspec.yaml
文件中添加了utopia_save_file
依赖:
dependencies:
flutter:
sdk: flutter
utopia_save_file: ^最新版本号 # 请替换为实际的最新版本号
然后,运行flutter pub get
来安装依赖。
接下来,在你的Dart文件中导入utopia_save_file
插件,并使用它来保存文件。以下是一个简单的示例:
import 'package:flutter/material.dart';
import 'package:utopia_save_file/utopia_save_file.dart';
import 'dart:io';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SaveFileDemo(),
);
}
}
class SaveFileDemo extends StatefulWidget {
@override
_SaveFileDemoState createState() => _SaveFileDemoState();
}
class _SaveFileDemoState extends State<SaveFileDemo> {
String _result = '';
void _saveFile() async {
// 要保存的文件内容
String content = "Hello, this is a test file content.";
// 获取外部存储目录(这里以Android为例,iOS类似但路径不同)
Directory externalDir = await getExternalStorageDirectory();
if (externalDir == null) {
setState(() {
_result = "Failed to get external storage directory.";
});
return;
}
// 构建文件路径和文件名
File file = File('${externalDir.path}/test_file.txt');
// 写入文件
try {
await file.writeAsString(content);
setState(() {
_result = "File saved successfully to ${file.path}.";
});
} catch (e) {
setState(() {
_result = "Failed to save file: $e.";
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Save File Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
_result,
style: TextStyle(fontSize: 18),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _saveFile,
child: Text('Save File'),
),
],
),
),
);
}
}
注意事项:
-
权限处理:在Android上,你需要在
AndroidManifest.xml
文件中请求存储权限。同时,在运行时检查并请求权限(Flutter有相关的权限请求插件,如permission_handler
)。 -
iOS路径:iOS上文件保存的路径和Android不同,通常你会保存到应用的沙盒目录中,如
Documents
目录。 -
错误处理:在实际应用中,你应该更细致地处理各种可能的错误情况,比如存储空间不足、权限被拒绝等。
-
插件版本:确保你使用的是最新版本的
utopia_save_file
插件,因为插件的API可能会随着版本更新而变化。
这个示例展示了如何使用utopia_save_file
(或类似的文件保存逻辑)来在Flutter应用中保存文本文件。根据你的需求,你可以扩展这个示例来保存其他类型的文件,比如图片、JSON数据等。