Flutter-文件操作

265 阅读4分钟

Dart的IO库包含了文件读写的相关类,它属于Dart语法标准的一部分,所以通过Dart IO库,无论是Dart VM下的脚本还是Flutter,都是通过Dart IO库来操作文件的,不过和Dart VM相比,Flutter有一个重要差异是文件系统路径不同,因为Dart VM是运行在PC或者服务器操作系统下,而Flutter是运行在移动操作系统中,他们的文件系统会有一些差异。

APP目录
Android和iOS的应用存储目录不同,PathProvider插件提供了一种平台透明的方式来访问设备文件系统的常用位置,该类当前支持访问两个文件系统位置:

  • 临时目录:可以使用getTemporaryDirectory()来获取临时目录;系统可随时清除临时目录的文件。在iOS上对应NSTemporaryDirectory()的返回值。在Android上对应getCacheDir()返回值。
  • 文档目录:可以使用getApplicationDocumentsDirectory()来获取应用程序的文档目录,该目录用于存储只有自己可以访问的文件。只有当应用程序被卸载时,系统才会清除该目录。在iOS上对应NSDocumentDirectory()。Android上对应AppData目录。
  • 外部存储目录:可以使用getExternalStorageDirectory()来获取外部存储目录,如SD卡;由于iOS不支持外部目录,所以iOS下调用该方法会抛出UnsupportError异常,而在Android下时Android SDK中getExternalStorageDirectory返回值。

一旦Flutter应用程序有一个文件位置的引用,可以使用dart:io API来执行对文件系统的读写操作。
示例:

  1. 引入PathPrivider插件,在pubspec.yaml文件中添加声明:
path_provider: ^2.0.14

然后执行flutter packages get 获取一下,版本号会随着时间推移发生变化。
示例:

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';

class SSLFileManager extends StatefulWidget{
  const SSLFileManager({Key? key}):super(key: key);

  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return SSLFileManagerState();
  }
}

class SSLFileManagerState extends State<SSLFileManager>{
  int counter = 0;
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    readCounter().then((int value){
      setState(() {
        debugPrint("init counter $counter\n");
        counter = value;
      });
    });
  }

  incrementCounter() async{
    setState(() {
      counter ++;
    });
    await (await getLocalFile()).writeAsString('$counter');
  }

  Future<File> getLocalFile() async{
    String dir = (await getApplicationDocumentsDirectory()).path;
    return File("$dir/counter.txt");
  }

  Future<int> readCounter() async{
    try{
      File file = await getLocalFile();
      String contents = await file.readAsString();
      return int.parse(contents);
    } on FileSystemException{
      return 0;
    }
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: const Text("Flie IO"),
      ),
      body: Center(
        child: Text("Current num $counter"),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: incrementCounter,
        tooltip: "Increment",
        child: const Icon(Icons.add),
      ),
    );
  }
}

需要注意的是: 如果是刚引入path_provider库,可能会出现问题。
问题1:

  • Unhandled Exception: MissingPluginException(No implementation found for method on channel

类似这种报错,不要慌,先百度查询一下资料,发现这个问题回复的答案各不相同,有的说是库的问题,有的说是安卓和iOS的通道。但是我们实际上啥都没有操作。所以怀疑不是以上说的问题。
处理方案:

  1. 关闭工程,然后打开终端,输入
flutter clean
flutter packages get
flutter run
选择设备

通常到这里,是可以正常运行设备的。但是也有可能出幺蛾子。

截屏2023-04-15 17.07.55.png
大致就是什么CocoaPods的问题。
那么是不是CocoaPods的问题?我们来看一下,找到iOS对应的工程,然后执行

pod install --no-repo-update

可以看到是有些库在更新。 这个时候去运行工程,工程是可以正常起来的。但是还是会报错。最后神奇的是使用Android Studio Run 会经常报错,但是debug运行是正常的,难道是因为我用的模拟器?Fuck!!!!!!,更神奇的是这个时候如果再去Run,又好了!!!
个人推测应该是缓存或者是热加载惹的祸,大家在遇到类似的问题时也可以用类似的方式去校验处理。。。。
最后验证了,如果出现类似使用了跨平台的组件,最好先检查一下自己的Flutter是否为最新版,如果是,再检查一下使用的Android Studio是否为最新的,如果不是,请升级!!!!!!!

问题记录1:当真机上无法运行时,先检查开发者账号是否有问题,若无问题,将Flutter安装目录下的bin/cache目录删除,然后执行flutter doctor。
问题记录2: flutter 运行在iOS上的app,当停止运行后,app不能打开。Fuck!!!
原因似乎是由于Flutter的debug模式使用的编译方式和iOS系统有冲突。具体的解决方案就是,要么Flutter直接运行release模式,要么改Xcode的编译模式为Release模式。