アプリケーションとして、データを作成して保持したり、読み出したりしたい場合があります
Flutter ではファイルを保存するために、プラグインの path_provider と dart:io ライブラリを使うことができます
Read and Write files
こちらの Read and write files このレシピがよくできているので、
これに沿って試してみたいと思います
ファイルをデバイスに保存して読み出す手順はこのようになります
- Find the correct local path.
- ディレクトリパスを取得
- Create a reference to the file location.
- ファイルの保存先を作成
- Write data to the file.
- ファイルにデータを書き込む
- Read data from the file.
- データを保存したファイルから、読み出しをかける
1. Find the correct local path
2. Create a reference to the file location
3. Write data to the file
4. Read data from the file
5. Complete example
6. 実行結果
Find the correct local path
このレシピではファイルを保存するために、プラグインの path_provider と dart:io ライブラリを使います
path_provider は以下のファイルシステムをサポートしています
- Temporary directory
- 一時的なディレクトリでシステムがいつでも必要に応じて自動的に削除される可能性がある
- getTemporaryDirectory を使用
- iOS: NSCachesDirectory
- Android: getCachDir( )
- Document directory
- アプリのドキュメントディレクトリに保存
アプリがアクセス可能で、ユーザーが削除したり編集したりしないファイルを保存するのに適していて、アプリの削除でこのディレクトリも同時にクリアされる - getApplicationDocumentsDirectory を使用
- iOS: NSDocumentDirectory
- Android: AppData
- アプリのドキュメントディレクトリに保存
尚、このDocument directory は、Android ではInternal Storageに該当します
AndroidのExternal Strageは
getExternalStorageDirectory
を使いますが、iOSなどでは使えない
path_providerを使い Document directory を取得するコードはこのようになります
1 2 3 4 5 6 7 |
import 'package:path_provider/path_provider.dart'; // ··· Future<String> get _localPath async { final directory = await getApplicationDocumentsDirectory(); return directory.path; } |
- get _localPath
- この get はDart では getter として扱われ、ファイルの_localPathを取得するメソッドのような働きとなる
- getApplicationDocumentsDirectory()
- アプリ専用のファイルを保管するパスを返す
Create a reference to the file location
ファイルを保存するパスに、Fileクラスを使って保存するファイルを指定
1 2 3 4 |
Future<File> get _localFile async { final path = await _localPath; return File('$path/counter.txt'); } |
- $path
- パラメータのpathをStringとして扱う
例えばpath = /data/user/0/xxx/app_flutterであれば
$path/counter.txt = /data/user/0/xxx/app_flutter/counter.txt となる
- パラメータのpathをStringとして扱う
Write data to the file
これで、fileAsStringを使ってカウンター回数をファイルに書き込みます
1 2 3 4 5 6 |
Future<File> writeCounter(int counter) async { final file = await _localFile; // Write the file return file.writeAsString('$counter'); } |
Read data from the file
保存されたファイルのデータをFileクラスを使って読み出し
読み出す時はTry-cathを使い例外を捕捉します
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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; } } |
Complete example
以上をまとめて実際のアプリを例として作成してみます
その前に事前準備として、
path_provider のパッケージをインストールします
1 |
$ flutter pub add path_provider |
最新バージョンの path_provider が pubspec.yaml に設定されています
1 2 3 4 |
dependencies: flutter: sdk: flutter path_provider: ^2.1.4 |
main.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
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), ), ); } } |
実行結果
iPhone:
フローティングボタンを4回タップしてみました
一旦終了して、エミュレータのみ起動させると
4回のカウントが残っています
読み出したPATHを表示させるために debugPrint を設定してみます
1 2 3 4 5 6 7 |
Future<String> get _localPath async { final directory = await getApplicationDocumentsDirectory(); debugPrint(directory.toString()); return directory.path; } |
iOSの実機のパスではないとは思いますが、シミュレータ上ではプライベートな場所に保存されているようです
1 |
/Users/[ユーザー名]/Library/Developer/CoreSimulator/Devices/FBF...A/data/Containers/Data/Application/56...2E/Documents |
Android:
フローティングボタンを4回タップしてみました
一旦終了して、エミュレータのみ起動させると
4回のカウントが残っています
1 |
/data/user/0/com.example.flutter_file_read_write/app_flutter |
Android Studio の Device Explorer でみてみると
app_flutter の下にcounter.txt があり内容は4となっています
References:
Read and write files
path_provider
getApplicationDocumentsDirectory