檔案讀寫
磁碟檔案的讀寫操作可能會相對方便地實現某些業務場景。它常見於應用啟動期間產生的持久化資料,或者從網路下載資料供離線使用。
為了將檔案儲存到磁碟,你需要結合使用 dart:io
和 path_provider
這個 package。
參考以下步驟:
-
找到正確的本地路徑
-
建立一個指向檔案位置的引用
-
將資料寫入檔案
-
從檔案讀取資料
你可以觀看每週 package 影片來了解更多關於 path_provider
的內容:
1. 找到正確的本地路徑
這個例子裡,我們將會顯示一個計數器,當計數器發生變化時,你將在磁碟中寫入資料,以便在應用載入時重新讀取這些資料。因此,你一定想知道:我應該將這些資料儲存在哪裡?
path_provider
package 提供一種平台無關的方式以一致的方式訪問裝置的檔案位置系統。該 plugin 當前支援訪問兩種檔案位置系統:
臨時資料夾:
這是一個系統可以隨時清空的臨時(快取)資料夾。在 iOS 上對應 NSCachesDirectory
的回傳值;在 Android 上對應 getCacheDir()
的回傳值。
Documents 目錄:
供應用使用,用於儲存只能由該應用訪問的檔案。只有在刪除應用時,系統才會清除這個目錄。在 iOS 上,這個目錄對應於 NSDocumentDirectory
。在 Android 上,則是 AppData
目錄。
在本範例中,你需要將訊息儲存在 Documents 目錄中。可以按如下所示,找到 Documents 目錄路徑:
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
2. 建立一個指向檔案位置的引用
確定檔案的儲存位置後,需要建立對檔案完整位置的引用。為此,你可以使用 dart:io
函式庫的 File
類來實現。
Future<File> get _localFile async {
final path = await _localPath;
return File('$path/counter.txt');
}
3. 將資料寫入檔案
現在你已經有了可以使用的 File
,接下來使用這個檔案來讀寫資料。首先,將一些資料寫入該檔案。由於使用了計數器,因此只需將整數儲存為字串格式,使用 '$counter'
即可呼叫。
Future<File> writeCounter(int counter) async {
final file = await _localFile;
// Write the file
return file.writeAsString('$counter');
}
4. 從檔案讀取資料
現在,你的磁碟上已經有了一些資料可供讀取。此時同樣需要使用 File
類。
Future<int> readCounter() async {
try {
final file = await _localFile;
// Read the file
final contents = await file.readAsString();
return int.parse(contents);
} catch (e) {
// If encountering an error, return 0
return 0;
}
}
完整範例
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
void main() {
runApp(
MaterialApp(
title: 'Reading and Writing Files',
home: FlutterDemo(storage: CounterStorage()),
),
);
}
class CounterStorage {
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> get _localFile async {
final path = await _localPath;
return File('$path/counter.txt');
}
Future<int> readCounter() async {
try {
final file = await _localFile;
// Read the file
final contents = await file.readAsString();
return int.parse(contents);
} catch (e) {
// If encountering an error, return 0
return 0;
}
}
Future<File> writeCounter(int counter) async {
final file = await _localFile;
// Write the file
return file.writeAsString('$counter');
}
}
class FlutterDemo extends StatefulWidget {
const FlutterDemo({super.key, required this.storage});
final CounterStorage storage;
@override
State<FlutterDemo> createState() => _FlutterDemoState();
}
class _FlutterDemoState extends State<FlutterDemo> {
int _counter = 0;
@override
void initState() {
super.initState();
widget.storage.readCounter().then((value) {
setState(() {
_counter = value;
});
});
}
Future<File> _incrementCounter() {
setState(() {
_counter++;
});
// Write the variable as a string to the file.
return widget.storage.writeCounter(_counter);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Reading and Writing Files'),
),
body: Center(
child: Text(
'Button tapped $_counter time${_counter == 1 ? '' : 's'}.',
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}