Flutter 在颤振应用程序中从文件加载模型的位置?

Flutter 在颤振应用程序中从文件加载模型的位置?,flutter,asynchronous,dart,async-await,initialization,Flutter,Asynchronous,Dart,Async Await,Initialization,假设我将数据存储在一个专用的repo类中,如下所示: class UrlEntry { final String url; final String title; UrlEntry({@required this.url, this.title}); } class UrlRepository with ChangeNotifier { List<UrlEntry> urlEntries = new List<UrlEntry>(); //

假设我将数据存储在一个专用的repo类中,如下所示:


class UrlEntry {
  final String url;

  final String title;

  UrlEntry({@required this.url, this.title});
}

class UrlRepository with ChangeNotifier {

  List<UrlEntry> urlEntries = new List<UrlEntry>();

  // Returns the urls as a separate list. Modifyable, but doesnt change state.
  List<UrlEntry> getUrls() => new List<UrlEntry>.from(urlEntries);

  add(UrlEntry url) {
      this.urlEntries.add(url);
      print(
          "url entry ${url.url} added. Now having ${urlEntries.length} entries ");
      notifyListeners();
  }

  removeByUrl(String url) {
      var beforeCount = this.urlEntries.length;
      this.urlEntries.removeWhere((entry) => entry.url == url);
      var afterCount = this.urlEntries.length;
      if (beforeCount != afterCount) notifyListeners();
      print("removed: ${beforeCount != afterCount}");
  }

  save() async {
      final storageFile = await composeStorageFile();
      print("storage file is '${storageFile.path}");
      if (await storageFile.exists()) {
        print("deleting existing file");
        await storageFile.delete();
      }
      if (urlEntries == null || urlEntries.length < 1) {
        print("no entries to save");
        return false;
      }
      print(
          "saving ${urlEntries.length} url entries to file $storageFile} ...");

      for (var entry in urlEntries) {
        await storageFile.writeAsString('${entry.url} ${entry.title}',
            mode: FileMode.append);
      }
  }

  Future<File> composeStorageFile() async {
    Directory storageDir = await getApplicationDocumentsDirectory();
    return File('${storageDir.path}/url_collection.lst');
  }

  void dispose() async {
    super.dispose();
    print("disposing ...");
    urlEntries.clear();
    this.urlEntries = null;
  }

  load() async {
      final storageFile = await composeStorageFile();
      if (!await storageFile.exists()) {
        print("storage file  ${storageFile.path}  not existing - not loading");
        return false;
      }
      print("loading file ${storageFile.path}");
      urlEntries = List <UrlEntry> () ;
      final fileLines = storageFile.readAsLinesSync() ;

      for (var line in fileLines) {
        var separatorIndex = line.indexOf(' ') ;
        final url = line.substring(0, separatorIndex) ;
        var title = line.substring(separatorIndex+1) ;
        if (title == 'null') title = null ;
        urlEntries.add(new UrlEntry(url: url, title: title)) ;
      }
      notifyListeners() ;
  }
}


类URL条目{
最终字符串url;
最后的字符串标题;
UrlEntry({@required this.url,this.title});
}
使用ChangeNotifier类UrlRepository{
List urlEntries=新列表();
//将URL作为单独的列表返回。可修改,但不更改状态。
List getUrls()=>新列表.from(urlEntries);
添加(url条目url){
this.urlcentries.add(url);
印刷品(
“添加了url项${url.url}。现在有${urlEntries.length}项”);
notifyListeners();
}
removeByUrl(字符串url){
var beforeCount=this.urlEntries.length;
this.urlEntries.removeWhere((entry)=>entry.url==url);
var afterCount=this.urlEntries.length;
if(beforeCount!=afterCount)notifyListeners();
打印(“删除:${beforeCount!=afterCount}”);
}
save()异步{
final storageFile=wait composeStorageFile();
打印(“存储文件为“${storageFile.path}”);
如果(等待storageFile.exists()){
打印(“删除现有文件”);
等待storageFile.delete();
}
if(urlEntries==null | | urlEntries.length<1){
打印(“无需保存的条目”);
返回false;
}
印刷品(
“正在将${urlEntries.length}url条目保存到文件$storageFile}…”);
for(URLCentries中的var条目){
等待storageFile.writeAsString(“${entry.url}${entry.title}”,
模式:FileMode.append);
}
}
Future ComposeStorage File()异步{
目录storageDir=await getApplicationDocumentsDirectory();
返回文件(“${storageDir.path}/url_collection.lst”);
}
void dispose()异步{
super.dispose();
打印(“处理…”);
url.clear();
this.urlEntries=null;
}
load()异步{
final storageFile=wait composeStorageFile();
如果(!wait storageFile.exists()){
打印(“存储文件${storageFile.path}不存在-未加载”);
返回false;
}
打印(“正在加载文件${storageFile.path}”);
URLCentries=List();
final fileLines=storageFile.readAsLinesSync();
for(文件行中的var行){
var separatorIndex=行indexOf(“”);
最终url=line.substring(0,分隔索引);
变量标题=行子字符串(分隔索引+1);
如果(title='null')title=null;
添加(新的url条目(url:url,title:title));
}
notifyListeners();
}
}
不幸的是,上述代码有几个问题我不知道如何规避:

  • UrlRepository的大多数方法都是异步的。这是因为getApplicationDocumentsDirectory()是异步的。我认为前者是一个绝对的缺陷,但在这里引入信号量来创建一个人工瓶颈会污染代码,所以我仍然坚持异步;但是你可以说我过时了——我不喜欢保存和加载操作在理论上能够相互重叠的想法。我的意思是,使用getApplicationDocumentsDirectory,我们讨论的是一个简单的配置细节,它不需要太多的计算能力来计算,也不需要存储,也不会经常更改,它会用其他不必要的东西污染代码。那么,有没有其他方法可以在不等待/async/then的情况下获得getApplicationDocumentsDirectory()的结果

  • 如果不是这种情况,我应该在哪里调用save()?我的第一个想法是不是在每次模型更改时都保存数据,而是在最新可能的执行位置保存数据,这是与dispose相关的方法之一,如下所示:

  • 类MyAppState扩展了状态{
    UrlRepository UrlRepository;
    ...
    @凌驾
    void deactivate()异步{
    等待urlRepository.save();
    super.deactivate();
    }
    
    不幸的是,这导致了URLReavyStudio。SAVER()只执行了一半,无论是在单元测试中调用它,还是在AVD或实际设备上。在中间它被终止了——我用打印输出来检查。我想这是因为,被迫再次创建一个完全不相关的方法异步(这里是DeAc激活())。,我必须接受,执行没有被授予在返回命令时终止的权限,而是更早(?)。我尝试调用MyState.dispose()以及urlRepository.dispose(),结果相同,只是我无法使dispose方法异步,因此只需调用save()async,并希望在super.dispose()之前保存所有内容开始

  • 我认为在initState()中加载Repository状态是很自然的,但我想确保要么在创建小部件之前加载完成(即调用构建器),要么在所有小部件都已就位之后加载,这样模型更改将触发重建由于已知的原因,必须是异步的,而initState不是,我甚至不能保证上述任何一种情况,并坚持使用urlRepository.load(),希望是最好的。那么,将对urlRepository.load()的调用放在哪里呢

  • 首先:您必须使用异步/等待方法,因为您不知道用户的设备在运行应用程序时可能在做什么,因此即使设备可能正在执行“不需要太多计算能力”的过程,该过程也可能比预期的要长一点

    第二:不要信任
    deactivate()
    dispose()
    函数,用户可能会杀死你的应用程序,它永远不会执行保存过程。我不确定如何自动执行此过程。我建议你在代码中的某个地方手动执行此操作

    第三:不要使用initState()加载数据,应该使用FutureBuilder,其中future参数是您的
    urlRepository.load()
    函数,在加载时,您可以显示
    CircularProgressIndicator()
    ,如果它有数据,则显示您的小部件

    例如:

    @覆盖
    小部件构建(){
    回归未来建设者(
    future:urlRepository.load(
    
    class MyAppState extends State<MyApp> {
    
      UrlRepository urlRepository;
    
    ...
    
    
      @override
      void deactivate() async {
        await urlRepository.save() ;
        super.deactivate();
      }