注意:查看本文章前請先查看更新日志,以至於該文章適合插件的最新版本
更新日志
| 詳情 | 日期 |
|---|---|
| 更新了插件的版本和file_selector的部分用法 | 2023-08-13 |
| 更新了插件的版本,用法不變 | 2022-12-10 |
| 更新了file_selector在0.8.4+3版本存儲文件的用法 | 2022-06-13 |
| 修正了file_picker存儲文件的用法 | 2022-05-24 |
上一節講了怎么拖動文件到應用中,從應用中點擊按鈕彈出選擇框上傳文件,也是必不可少的一個功能。
file_selector
安裝🛠
點擊file_selector獲取最新版本。以下是在編寫本文章時的最新版本:
file_selector: ^1.0.0
👻注意:在開發 macOS 端的程序時,還需要額外的操作,具體可以查看這里
了解🧩
在 file_selector 插件中,我們需要了解對象 XTypeGroup 和方法 openFiles。
XTypeGroup的作用是使用給定的標簽和文件擴展名創建一個新組,沒有提供任何類型選項的組表示允許任何類型。
XTypeGroup 可以傳入5個參數:
String? label:用來自己作區分List<String>? extensions:過濾非描述的后綴文件,默認加載全部類型的文件List<String>? mimeTypes:主要針對 Linux 系統下的文件類型List<String>? macUTIs:主要針對 mac 系統下的文件類型List<String>? webWildCards:主要針對網頁開發時的類型
openFile 方法返回一個 XFile 對象,可以傳入3個參數:
List acceptedTypeGroups = const []:接受的類型組,傳入一個XTypeGroup列表String? initialDirectory:初始化文件夾。打開文件對話框以加載文件並返回文件路徑String? confirmButtonText:彈窗“打開”按鈕的顯示文字
openFiles 方法返回 XFile 對象列表,傳入的參數和 openFile 一樣。
使用🥩
選擇的文件我們最后需要讀取出來,讀取需要接受一個 String 類型的路徑:
String path = '';
這里先以選擇圖片為例,我們需要先定義一個 XTypeGroup 對象:
final xType = XTypeGroup(label: '圖片', extensions: ['jpg', 'png']);
選擇單張圖片
打開彈窗選取單個文件,使用 openFile 方法:
final XFile? file = await openFile(acceptedTypeGroups: [xType]);
將獲取到的 XFile 對象的路徑值傳給 path。當然,並不是每次打開彈窗都會選擇圖片,所以需要判斷一下:
if (file != null) {
path = file.path;
setState((){});
} else {
BotToast.showText(text: '你不選擇圖片打開干啥😤');
}

openFile 方法中還有兩個屬性,我們修改試一下:
final XFile? file = await openFile(
acceptedTypeGroups: [xType],
initialDirectory: r'C:\Users\ilgnefz\Pictures',
confirmButtonText: '嘿嘿嘿',
);

initialDirectory 屬性貌似沒用😕去看了官方的例子,也沒用到過這個參數,以后就忽略它吧。
選擇多張圖片
選取多張圖片,我們就需要定義一個路徑的數組了:
final List<String> paths = [];
XTypeGroup 對象和剛才的一樣就行,重要的是使用 openFiles 方法:
final List<XFile> files = await openFiles(acceptedTypeGroups: [xType]);
將獲取到的文件路徑列表賦值給 paths:
if (file != null) {
paths.addAll(files.map((e) => e.path).toList());
setState((){});
} else {
BotToast.showText(text: '你不選擇圖片打開干啥😤');
}
好了,來看看效果如何

讀取文本文件
讀取文本文件,我們需要獲取文件的名稱和內容:
final String title = '';
final String content = '';
再更改一下 XTypeGroup 對象就行:
final XTypeGroup xType = XTypeGroup(label: '文本', extensions: ['txt']);
final XFile? file = await openFile(acceptedTypeGroups: [xType]);
將獲取到的 XFile 對象的屬性賦值給我們定義的對象:
if (file != null) {
title = file.name;
content = await file.readAsString();
setState((){});
} else {
BotToast.showText(text: '打開了個寂寞🙄');
}

存儲文本文件
存儲文本需要用到 XFile 對象中的 fromData 方法。讓我們來看看這個方法中需要傳入什么參數:
Uint8List bytes:存儲的主要內容String? mimeType:文件的 mine 類型String? name:文件名?測試了毫無用處😑int? length:不知道是什么的長度,反正無法截取內容😑DateTime? lastModified:最后修改文件的時間String? path:文件保存的路徑?測試了毫無效果😑CrossFileTestOverrides? overrides:覆蓋CrossFile的某些方法用來測試
(以上幾個參數要是有朋友測試出來了,可以告知一下😁)
在存儲文本文件前,我們需要先知道應該存儲在哪個文件夾:
final FileSaveLocation? path = await getSaveLocation();
getSavePath有以下幾個可選參數:
List acceptedTypeGroups:可以在對話框中選擇的文件類型組的列表。其顯示方式取決於 pltaform,例如:- 在 Windows 和 Linux 上,每個組將是過濾器選項列表中的一個條目
- 在 macOS 上,將允許所有組允許的所有類型的並集
String? initialDirectory:打開對話框時將顯示的目錄的完整路徑。如果未提供,平台將選擇一個初始位置String? suggestedName:文件名的初始值String? confirmButtonText:對話框的確認按鈕中的文本。如果未提供,則使用默認操作系統標簽(例如,“保存”)
以下是沒傳任何參數的效果:

以下是傳了參數的效果:
final FileSaveLocation? path = await getSaveLocation(
acceptedTypeGroups: [
XTypeGroup(label: '圖片', extensions: ['jpg', 'png']),
XTypeGroup(label: '視頻', extensions: ['mp4', 'avi'])
],
initialDirectory: r'C:\Users\ilgnefz\Pictures',
suggestedName: '新建文件',
confirmButtonText: '點我吧!',
);

getSaveLocation方法返回的FileSaveLocation對象有一個重要的屬性:path,記錄了文件的保存路徑。
XFile.fromData 雖然有很多參數可以使用,但是大部分沒有啥效果,通過上面的示例,我們可以直接使用getSaveLocation方法來是指必要的內容:
final String title = widget.provider.title;
final String content = widget.provider.txtContent;
final Uint8List fileData = const Utf8Encoder().convert(content);
// final Uint8List fileData = Uint8List.fromList(content.codeUnits);
final FileSaveLocation? saveLocation = await getSaveLocation(
acceptedTypeGroups: [
const XTypeGroup(label: '文本', extensions: ['txt']),
],
initialDirectory: r'C:\Users\ilgnefz\Pictures',
suggestedName: title,
);
debugPrint('存儲路徑:${saveLocation?.path}');
if (saveLocation != null) {
const String fileMimeType = 'text/plain';
final XFile xFile = XFile.fromData(
fileData,
mimeType: fileMimeType,
);
await xFile.saveTo(saveLocation.path);
} else {
BotToast.showText(text: '給你個眼神自己體會😑');
}

現在去選擇的文件夾就能找到我們存儲的文件了。initialDirectory是路徑的初始值,我打開文本改變了路徑,所以就沒生效。
選擇文件夾
選擇文件夾使用 getDirectoryPath 方法:
final String? path = await getDirectoryPath();
if (path != null) {
title = '目錄';
content = path;
setState((){});
}

選擇多文件夾
選擇多文件夾需要使用 getDirectoryPaths 方法:
final List<String?> paths = await getDirectoryPaths();
if (paths.isNotEmpty) {
widget.provider.setTitle('多目錄');
widget.provider.setTextContent(paths.join('\n'));
widget.provider.setFileType('text');
}

file_picker
安裝🛠
點擊file_picker獲取最新版本。以下是在編寫本文章時的最新版本:
file_picker: ^5.3.3
使用🥩
先定義一個默認的路徑:
String path = '';
選擇單個文件
選擇單個文件需要用到 pickFiles 方法,該方法可以傳入10個參數:
String? dialogTitle:彈窗的標題String? initialDirectory:初始化的文件夾FileType type = FileType.any:文件的類型List<String>? allowedExtensions:允許的文件后綴名稱,需配合FileType.custom使用dynamic Function(FilePickerStatus)? onFileLoading:監聽文件選擇的狀態bool allowCompression = true:是否允許壓縮bool allowMultiple = false:是否允許選擇多個文件bool withData = false:如果為true,選取的文件將在內存中立即以“Uint8List”的形式提供其字節數據,如果您選擇它進行服務器上傳或類似操作,這將很有用。但是,請記住,如果您允許多個選擇或選擇大文件,則在 IO(iOS 和 Android)上啟用此功能可能會導致內存不足問題。請改用 [withReadStream]。在 web 上默認為true,其他為falsebool withReadStream = false:拾取的文件將以 [Stream<List>] 的形式提供其字節數據,這對於上傳和處理大文件很有用 bool lockParentWindow = false:是否將子窗口(文件選擇器窗口)一直停留在 Flutter 窗口的前面,直到它關閉(如模態窗口)。此參數僅適用於 Windows
FilePickerResult? result = await FilePicker.platform.pickFiles();
if (result != null) {
File file = File(result.files.single.path!);
path = file.path;
setState((){});
}

我們試着添加一些參數:
FilePickerResult? result = await FilePicker.platform.pickFiles(
dialogTitle: '我的地盤我做主',
initialDirectory: r'C:\Users\ilgnefz\Pictures\Saved Pictures',
type: FileType.image,
);

initialDirectory 又沒起作用😑
選擇多個文件
定義一個接受所有路徑的數組:
final List<String> paths = [];
FilePickerResult? result = await FilePicker.platform.pickFiles(
allowMultiple: true,
);
if (result != null) {
paths = result.files.map((e) => e.path!).toList();
setState((){});
}
讀取文件信息
通過以上的方法,我們會得到一個 PlatformFile 對象:
FilePickerResult? result = await FilePicker.platform.pickFiles();
PlatformFile file = result.files.single;
該對象有以下幾個屬性:
name:文件名稱size:文件大小,以字節為單位bytes:此文件的字節數據。如果您想操作其數據或輕松上傳到其他地方,則特別有用。 在常見問題解答中查看此處 一個關於如何使用它在網絡上上傳的示例。extension:文件后綴path:文件路徑identifier:原始文件的平台標識符,是指 Android 上的 Uri 和 iOS 上的 NSURL。其他為nullreadStream:將文件內容轉換成流讀取

存儲文件
存儲文件需要使用 saveFile 方法,該方法有可以傳入6個參數:
String? dialogTitle:同 pickFiles 方法String? fileName:存儲文件的名字String? initialDirectory:同 pickFiles 方法FileType type = FileType.any:同 pickFiles 方法List ? allowedExtensions:同 pickFiles 方法bool lockParentWindow = false:同 pickFiles 方法
String? outputFile = await FilePicker.platform.saveFile(fileName: 'hello.txt');

這個時候我們去選擇的文件夾查看,並不能發現我們存儲的文件。這是因為這個方法並不能直接存儲文件,我們把outputFile打印一下會發現以下結果:
outputFile: C:\Users\ilgnefz\Pictures\hello.txt
誒😀雖然它不能幫我們直接存儲一個文件,但是會返回一個文件的路徑。我們可以使用File對象來將需要的內容存儲下來。
if (outputFile != null) {
File file = File(outputFile);
await file.writeAsString('Hello World');
await file.create();
BotToast.showText(text: '文件存儲成功');
}

現在我們打開文件存儲時選擇的文件夾查看

獲取文件夾路徑
獲取文件夾需要使用 getDirectoryPath 方法,可以傳入3個參數:
String? dialogTitle:同 pickFiles 方法bool lockParentWindow = false:同 pickFiles 方法String? initialDirectory:同 pickFiles 方法
final String title = '';
final String content = '';
String? dir = await FilePicker.platform.getDirectoryPath();
if (path != null) {
title = '目錄';
content = path;
setState((){});
}

🛫OK,以上就是這篇文章的全部內容,僅針對插件的當前版本,並不能保證適用於以后插件用法的更新迭代。
最后,感謝 flutter 團隊和 miguelpruivo 對以上插件的開發和維護😁。本應用代碼已上傳至 github 和 gitee,有需要的可以下載下來查看學習。
