Stream and Future in Dart

2,045

Stream.forEach() returns a Future. Your last return statement runs immediately after the for-each call, but should await it.

await fileList.forEach((x) {
  ...

https://api.dartlang.org/stable/2.2.0/dart-async/Stream/forEach.html

Share:
2,045
vale
Author by

vale

Updated on December 10, 2022

Comments

  • vale
    vale over 1 year

    I've been using basic async/await for some time without many problems and I thought I understood how it worked. Can't say I'm an expert in it, but I understadn the gist of it. I just can't get my head around Streams though. Before today I thought I understood how they worked (basically ala Reactive Programming), but I can't get them to work in Dart.

    I'm working on a persistance layer with the possibility of saving and retrieving (json) files. I've been using the fileManager example as a guideline.

    import 'dart:io';
    import 'dart:async';
    import 'package:intl/intl.dart'; //date
    import 'package:markdowneditor/model/note.dart';//Model
    import 'package:path_provider/path_provider.dart';
    import 'package:path/path.dart' as p;
    import 'package:flutter/foundation.dart'; //log
    import 'package:simple_permissions/simple_permissions.dart';//OS permissions
    
    class FileManager {
      static final FileManager _singleton = new FileManager._internal();
    
      factory FileManager() {
        return _singleton;
      }
    
      FileManager._internal();
    
      Future<String> get _localPath async {
        final directory = (await getApplicationDocumentsDirectory()).toString();
        return p.join(directory, "notes"); //path takes strings and not Path objects
      }
    
      Future<File> writeNote(Note note) async {
        var file = await _localPath;
        file = p.join(
            file,
            DateFormat('kk:mm:ssEEEMMd').format(DateTime.now()) +
                " " +
                note.title); //add timestamp to title
        // Write the file
    
        SimplePermissions.requestPermission(Permission.WriteExternalStorage)
            .then((value) {
          if (value == PermissionStatus.authorized) {
            return File(file).writeAsString('$note');
          } else {
            SimplePermissions.openSettings();
            return null;
          }
        });
    
      }
    
      Future<List<Note>> getNotes() async {
        //need file access permission on android. use https://pub.dartlang.org/packages/simple_permissions#-example-tab-
        final file = await _localPath;
    
        SimplePermissions.requestPermission(Permission.ReadExternalStorage)
            .then((value) {
          if (value == PermissionStatus.authorized) {
            try {
              Stream<FileSystemEntity> fileList =
                  Directory(file).list(recursive: false, followLinks: false);
    
              // await for(FileSystemEntity s in fileList) { print(s); }
              List<Note> array = [];
              fileList.forEach((x) {
    
                if (x is File) {
                  var res1 = ((x as File).readAsString()).then((value2) {
                    Note note = Note.fromJsonResponse(value2);
                    return note;
                  }).catchError((error) {
                    debugPrint('is not file content futurestring getNoteError: $x');
                    return null;
                  });
                  var array2 = res1.then((value3) {
                    array.add(value3);
                    return array;
                  });
                //?
                } else {
                  debugPrint('is not file getNoteError: $x');
                }
              });
    
    
              // Add the file to the files array
              //Return the Future<List<Note>>
              return array;
    
            } catch (e) {
              debugPrint('getNoteError: $e');
              // If encountering an error, return 0
              return null;
            }
          } else {
            SimplePermissions.openSettings();
            return null;
          }
        });
      }
    }
    

    Obviously as it is it won't work, but even trying to await the loop using the commented out parts raises an error.

    In "getNotes", after checking the permissions I want to get an array of all the files in the directory, parse them as Note objects and return the resulting array.

    I get the list of files:

    Stream<FileSystemEntity> fileList =
              Directory(file).list(recursive: false, followLinks: false);
    

    And for each one of them in the stream I want to parse the file into an object and append it to an array to return at the end.

           List<Note> array = [];
          fileList.forEach((x) {
    
            if (x is File) {
              var res1 = ((x as File).readAsString()).then((value2) {
                Note note = Note.fromJsonResponse(value2);
                return note;
              }).catchError((error) {
                debugPrint('is not file content futurestring getNoteError: $x');
                return null;
              });
              var array2 = res1.then((value3) {
                array.add(value3);
                return array;
              });
            //?
            } else {
              debugPrint('is not file getNoteError: $x');
            }
          });
    
    
          // Add the file to the files array
          //Return the Future<List<Note>>
          return array;
    
    • vale
      vale about 5 years
      Edited the question to make it clearer. Let me know if you have any other question.
    • vale
      vale about 5 years
      Allright got it. You can convert a stream to a Future if you need all elements and don't want them one by one by using .toList(); at the end and return that future in the end. Thanks.
    • pskink
      pskink about 5 years
      sure, your welcome, Stream#asyncMap is very handy if you have to perform some Future actions on the elements of your Stream
    • Jacob Phillips
      Jacob Phillips about 5 years
      What is the error? Include it in the post please.