Flutter 中的 io 文件读写及实现持久化数据计数
一、path_provider
path_provider
是 flutter 提供的用于进行文件存储的 package
本质上是封装了 Android 和 iOS Native 方法去进行读写磁盘,读写分为两种场景:
- 临时文件夹
- 应用的Documents 目录
临时文件夹在执行系统的清空缓存时会清空掉该文件夹,documents 目录则是随着应用创建而创建,只有在删除应用时,才会清空这个目录。
临时文件夹在 iOS 上对用的是 NSCachesDirectory
在 Android 对用的是 getCacheDir()
而 Documents 目录在 iOS 对应的是 NSDocumentDirectory
,在 Android 上对应的是 AppData
目录
本身 package_provider
提供了三个方法分别获取不同的文件路径
1、getTemporaryDirectory
获取设备上的临时目录,也就是上面提到的 临时文件夹
2、getApplicationDocumentsDirectory
获取应用的 document 目录
3、getExternalStorageDirectory
这个方法可以修改顶级存储也就是系统级别的存储路径,但是只有在 android 上生效,因为 iOS 的 sandbox Application 执行特性,不允许修改顶级存储。
因此如果在 iOS 上使用会抛出异常
使用这个方法需要额外注意
二、使用 path_provider 的 getApplicationDocumentsDirectory
无论是获取临时 cache 路径还是 documents 文件夹,都是异步的方法,并且返回的都是 Directory
类型
Directory 是一个抽象类,其中实现了 get 属性 path
:
@pragma("vm:entry-point")
abstract class Directory implements FileSystemEntity {
/**
* Gets the path of this directory.
*/
String get path;
因此返回的 Directory
需要通过 Directory.path
拿到具体的路径
1、获取文件夹路径
最终我们要在 document 中访问一个文件,首先需要知道路径是什么,因此每次我们都需要调用 await getApplicationDocumentsDirectory()
拿到文件夹的路径。
这里通过 一个 get 属性 _localPath
获取,本质上还是每次调用方法获取数据,当然可以通过属性判断是否存在值,还是异步方法返回
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
print(directory.path);
return directory.path;
}
2、获取文件实例
一个文件总是通过 File()
创建出实例来,File 的工厂方法如下:
@pragma("vm:entry-point")
factory File(String path) {
final IOOverrides overrides = IOOverrides.current;
if (overrides == null) {
return new _File(path);
}
return overrides.createFile(path);
}
但我们拿到文件路径之后,就可以通过 File 拿到文件实例,然后通过文件实例做其他事情。
_file
存储数据,如果 _file
并不是 File 类型,在通过 File('$path/counter.txt')
实例化文件实例
Future<File> get _localFile async {
if (_file is File) {
return _file;
} else {
final String path = await _localPath;
_file = File('$path/counter.txt');
return _file;
}
}
3、读取文件内容
读取文件的内容。首先需要拿到文件实例,然后 File
提供了两种读取形式,分别是 readAsBytes
和 readAsString
,表示字节读取和字符串读取。
每种读取形式包含了异步和同步方法,分别是:
- readAsBytes
- readAsBytesSync
- readAsString
- readAsStringSync
下面的方法中,读取内容作为字符串,并且将其转成 int 类型:
_initCounter() async {
final File file = await _localFile;
final String res = await file.readAsString();
setState(() {
_counter = int.parse(res ?? 0);
});
}
4、写入文件内容
同样的写入文件内容也提供了 字节和字符串两种形式,并且提供了同步和异步
- writeAsBytes
- writeAsBytesSync
- writeAsString
- writeAsStringSync
下面方法中,讲一个 int 类型数据,转成 String 然后写入到文件中
_saveCounter() async {
final File file = await _localFile;
file.writeAsString(_counter.toString());
}
三、文件读写实践
基于文件存储,创建一个计数应用
1、首页进行路由导航
如果没有任何持久化数据的策略,每次进去 counter 都是置空,这并不是我们期望的结果,这里刻意做了个首页的导航
class HomeDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: OutlineButton(
child: Text('go to counter'),
onPressed: () => {
Navigator.of(context).push(MaterialPageRoute(builder:(context) => CounterDemo()))
},
),
);
}
}
2、一个页面承载 counter 计数示例
class CounterDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('counter')
),
body: FileIODemo()
);
}
}
3、最终持久化计数示例的实现
class FileIODemo extends StatefulWidget {
FileIODemo({Key key}) : super(key: key);
_FileIODemoState createState() => _FileIODemoState();
}
class _FileIODemoState extends State<FileIODemo> {
int _counter = 0;
File _file;
_FileIODemoState() {
_initFile();
}
@override
void initState() {
super.initState();
_initCounter();
}
_initCounter() async {
final File file = await _localFile;
final String res = await file.readAsString();
setState(() {
_counter = int.parse(res ?? 0);
});
}
_initFile() async {
_file = await _localFile;
}
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
print(directory.path);
return directory.path;
}
Future<File> get _localFile async {
if (_file is File) {
return _file;
} else {
final String path = await _localPath;
_file = File('$path/counter.txt');
return _file;
}
}
_saveCounter() async {
final File file = await _localFile;
file.writeAsString(_counter.toString());
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_counter.toString()),
],
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
RaisedButton(
child: Text('-'),
onPressed: () {
setState(() {
_counter -= 1;
_saveCounter();
});
},
),
RaisedButton(
child: Text('+'),
onPressed: () {
setState(() {
_counter += 1;
_saveCounter();
});
},
),
],
)
],
);
}
}
四、效果
五、在线代码
文章版权:Postbird-There I am , in the world more exciting!
本文链接:http://www.ptbird.cn/flutter-file-io.html
转载请注明文章原始出处 !
文章不错支持一下吧