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?

Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
Topazoo
  • 113
  • 1
  • 1
  • 5

2 Answers2

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
    });
});
Topazoo
  • 113
  • 1
  • 1
  • 5
Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • "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. – Topazoo Dec 05 '18 at 12:56
  • No they don't. You really have to use `await` – Rémi Rousselet Dec 05 '18 at 12:58
  • From the Dart documentation (https://www.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. – Topazoo Dec 05 '18 at 13:00
  • Once is not enough. you need more then just one `await` in your example – Rémi Rousselet Dec 05 '18 at 13:12
  • 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() – Topazoo Dec 05 '18 at 13:34
  • 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' I/SimplePermission(18817): Requesting permission : android.permission.READ_EXTERNAL_STORAGE` – Topazoo Dec 05 '18 at 13:48
  • NEVERMIND!! Declaring the first then() function as asynchronous (since the second is nested) did the job. Thanks for all your help Rémi. – Topazoo Dec 05 '18 at 14:01
  • 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/await – Rémi Rousselet Dec 05 '18 at 14:11
  • @RémiRousselet can you help me with this question. I am also facing similar issue. https://stackoverflow.com/questions/62502334/flutter-run-function-after-gps-is-enabled – Roxx Jun 21 '20 at 18:40
  • The use of await is to discard asynchronous programming way of adding a ".then". using await and ".then" together dissolves the purpose – Saurabh Kumar Sep 15 '22 at 17:04
-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;
  }

}
Topazoo
  • 113
  • 1
  • 1
  • 5
  • 1
    that makes your `async` flag on `get_permissions` useless. – Rémi Rousselet Dec 05 '18 at 13:07
  • 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. – Topazoo Dec 05 '18 at 13:14
  • I'm not saying the code doesn't work, but that the syntax is not the right one. – Rémi Rousselet Dec 05 '18 at 13:15
  • The docs for reference (https://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? – Topazoo Dec 05 '18 at 13:17