如果只是想寫讀取檔案,在〈Assets 管理〉中有提及,可以透過 AssetBundle
來的 loadString
等方法,從 assets 資料夾中讀取,那麼寫入呢?
AssetBundle
只提供了一些讀取的方法,若要寫入,基本上可以透過 Dart 提供的標準 io
程式庫,只不過,必須解決路徑的問題,Android、iOS 等彼此間會有路徑不同的問題,雖然試著指定特定路徑也可以,然而 Flutter 本身的目標是跨裝置,最好是有一致的方式來指定路徑。
Flutter 官方推薦使用 path_provider,撰寫文件的這個時間點,其版本是 1.6.11,要使用這個程式庫,可以在 pubspec.yaml 中的 dependencies
區段加入:
dependencies:
path_provider: ^1.6.11
接著在終端機中執行以下指令取得程式庫:
flutter packages get
為了使用 Dart 標準 io
與 path_provider
,先如下 import
:
import 'package:path_provider/path_provider.dart';
import 'package:flutter/material.dart';
path_provider
提供了一些方法,可以取得路徑,來看看其中的幾個方法:
getApplicationSupportDirectory
:可存放應用程式資料之路徑,這類資料由應用程式自行產生,不曝露給使用者,例如應用程式自行產生的專屬格式檔案,這個資料夾會在應用程式被移除後自動刪除。getApplicationDocumentsDirectory
:通常可用來存放應用程式專屬格式,然而是使用者操作而產生的檔案,例如特定格式的應用程式專案封裝檔案,要存放通用格式的使用者資料也可以,不過這個這個資料夾會在應用程式被移除後自動刪除。getExternalStorageDirectory
:取得外部儲存裝置的路徑,例如 SD 卡,應用程式被移除後不會影響這個路徑,可用來儲存使用者操作而產生的檔案,然而非應用程式專屬格式的檔案,例如 JPG 格式的照片,若不支援或沒有外部儲存裝置,呼叫此方法會拋出錯誤。getTemporaryDirectory
:取得暫存資料夾,這個資料夾隨時可能被系統或使用者操作清除。
來寫個函式取得 getApplicationDocumentsDirectory
的路徑:
Future<String> get _appDocPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
有了路徑之後,可以進一步建立 File
,例如,建立一個代表 message.txt 的 File
:
Future<File> get _messageFile async {
final path = await _appDocPath;
return File('$path/message.txt');
}
有了 File
後,做點基本的讀寫:
Future<String> readMessage() async {
final file = await _messageFile;
String contents = await file.readAsString();
return contents;
}
Future<File> writeMessage(String message) async {
final file = await _messageFile;
return file.writeAsString('$message');
}
底下是個應用範例,應用程式會有個輸入欄位,載入 message.txt 的內容,如果一開始沒有這個檔案就建立一個,你在輸入欄位鍵入內容後按下 Enter,會將欄位內容寫入 message.txt:
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:flutter/material.dart';
Future<String> get _appDocPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> get _messageFile async {
final path = await _appDocPath;
return File('$path/message.txt');
}
Future<String> readMessage() async {
final file = await _messageFile;
String contents = await file.readAsString();
return contents;
}
Future<File> writeMessage(String message) async {
final file = await _messageFile;
return file.writeAsString('$message');
}
void main() => runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Openhome.cc'),
),
body: Message()
)
)
);
class Message extends StatefulWidget {
@override
State<StatefulWidget> createState() => _Message();
}
class _Message extends State<Message> {
var messageController = TextEditingController();
File messageFile;
@override
void initState() {
super.initState();
_messageFile
.then((file) => file.exists())
.then((exists) async {
if(exists) {
messageController.text = await readMessage();
print(messageController.text);
}
else {
//messageController.text = '';
messageFile.create();
}
});
}
@override
Widget build(BuildContext context) {
return TextField(
controller: messageController,
decoration: InputDecoration(
labelText: '訊息',
hintText: '無訊息',
prefixIcon: Icon(Icons.message),
),
textInputAction: TextInputAction.done,
onSubmitted: (value) => writeMessage(value),
);
}
}