Flutter/Dart Async Not Waiting
Asked Answered
C

2

10

I'm building my first Flutter application and I've run into a bit of an async issue.

When my application executes I'd like it to ask for permissions and wait until they are granted. My main() function looks like this:

import 'permission_manager.dart' as Perm_Manager;

void main() async
{
  //Ensure valid permissions
  Perm_Manager.Permission_Manager pm = Perm_Manager.Permission_Manager();
  var res = await pm.get_permissions();
  print(res);

  return runApp(MyApp());
} 

The Permission Manager class' get_permissions() function uses the Flutter Simple Permissions package to check and ask for permissions.

import 'package:simple_permissions/simple_permissions.dart';
import 'dart:io' as IO;
import 'dart:async';

class Permission_Manager {
  /* Get user permissions */

  Future<bool> get_permissions() async
  {
    //Android handler
    if (IO.Platform.isAndroid)
    {
        //Check for read permissions
        SimplePermissions.checkPermission(Permission.ReadExternalStorage).then((result)
        {
          //If granted
          if (result)
            return true;

          //Otherwise request them
          else
          {
            SimplePermissions.requestPermission(Permission.ReadExternalStorage)
            .then((result)
            {
              // Determine if they were granted
              if (result == PermissionStatus.authorized)
                return true;
              else
                IO.exit(0); //TODO - display a message
            });
          }
        });
    }

    else
      return true;
  }
}

When I run the application it does not wait for the function to complete as intended and prints the value of "res" before the Future is updated.

Launching lib\main.dart on Android SDK built for x86 in debug mode...
Built build\app\outputs\apk\debug\app-debug.apk.
I/SimplePermission(15066): Checking permission : android.permission.READ_EXTERNAL_STORAGE
I/flutter (15066): null
I/SimplePermission(15066): Requesting permission : android.permission.READ_EXTERNAL_STORAGE

The Future returns a value midway through the function! Does anyone know what I'm doing wrong?

Cushy answered 5/12, 2018 at 12:15 Comment(0)
O
15

To await something you have to call the await keyword on a future instead of .then

final result = await future;
// do something

instead of

future.then((result) {
  // do something
});

If you really want to use .then then you can await the generated future:

await future.then((result) {
  // do something
});

Just ensure that when using nested asynchronous calls that the async keyword is used on each:

await future.then((result) async{
    // do something
    await future.then((result_2) {
       // do something else
    });
});
Odontograph answered 5/12, 2018 at 12:50 Comment(10)
"await" is used in the main() function which calls the asynchronous function. Notwithstanding I was under the impression that "then" and "await" while syntactically different (with "then" running a function after completion) they operated the same way.Cushy
No they don't. You really have to use awaitAshtray
From the Dart documentation (dartlang.org/tutorials/language/futures): "To suspend execution until a future completes, use await in an async function (or use then())." But despite this I did use await in my main() function to no avail.Cushy
Once is not enough. you need more then just one await in your exampleAshtray
I don't think you can await the generated future. The generated future is local to the callback function run by then() and doing something like "await SimplePermissions.checkPermission(Permission.ReadExternalStorage).then((result) { ..." generates an error. It's true that you could return the generated future from the callback, but since the plugin doesn't return boolean values you'd need to rewrite most of the function in main()Cushy
Ah you are correct! My apologies. You can indeed await the result of then. Unfortunately in this case I am returning values and it still doesn't work :(. I/SimplePermission(18817): Checking permission : android.permission.READ_EXTERNAL_STORAGE I/flutter (18817): Instance of 'Future<bool>' I/SimplePermission(18817): Requesting permission : android.permission.READ_EXTERNAL_STORAGECushy
NEVERMIND!! Declaring the first then() function as asynchronous (since the second is nested) did the job. Thanks for all your help Rémi.Cushy
Perfect. I'd still recommend you to not use then though. It's harder to read and doesn't bring anything compared to the flattened async/awaitAshtray
@RémiRousselet can you help me with this question. I am also facing similar issue. #62502834Towline
The use of await is to discard asynchronous programming way of adding a ".then". using await and ".then" together dissolves the purposeMckenzie
C
-1

Got it working. The issue seems to be resolved using a Completer:

import 'package:simple_permissions/simple_permissions.dart';
import 'dart:io' as IO;
import 'dart:async';

class Permission_Manager {
  /* Get user permissions */

  final Completer c = new Completer();

  Future get_permissions() async
  {

    //Android handler
    if (IO.Platform.isAndroid)
    {
        //Check for read permissions
        SimplePermissions.checkPermission(Permission.ReadExternalStorage).then((result)
        {
          //If granted
          if (result)
          {
            c.complete(true);
          }
          //Otherwise request them
          else
          {
            SimplePermissions.requestPermission(Permission.ReadExternalStorage).then((result)
            {
              // Determine if they were granted
              if (result == PermissionStatus.authorized)
              {
                c.complete(true);
              }
              else
              {
                IO.exit(0); //TODO - display a message
              }
            });
          }
        });
    }

    else
    {
      c.complete(true);
    }

    return c.future;
  }

}
Cushy answered 5/12, 2018 at 12:59 Comment(4)
that makes your async flag on get_permissions useless.Ashtray
The application requires read permissions for any functionality but the Simple Permissions plugin operates asynchronously. Specifically, when opening the application reads all files in the Downloads directory. This solution stops the application from reading the directory until permissions are granted. If there's a way to use the plugin synchronously that would be ideal, but I don't think it's possible.Cushy
I'm not saying the code doesn't work, but that the syntax is not the right one.Ashtray
The docs for reference (pub.dartlang.org/packages/simple_permissions#-readme-tab-). Running the code without the async flag and returning a non-Future bool produces the same results as seen in the initial question (due to the plugin code running asynchronously). Is there another way to do it?Cushy

© 2022 - 2024 — McMap. All rights reserved.