How to implement Android runtime permission flow in Flutter

Many Android apps require what Android deems a dangerous permission. For example using the camera, adding an event to the user calendar, or reading the user contacts. Previously, Android used to ask for those permissions at install time, but since Marshmallow, it does it at runtime. So, how do you implement the Android runtime permission flow in Flutter?

A search of Flutter plugins currently shows no results, so I had to figure it out myself for my app Preset SMSs.

In this code tutorial, we will create an app that shows a list of all contacts with a mobile phone number. The app displays a loading screen while it obtains the contacts and an error Snackbar when the app has no permission. The Android code itself is based on the official Requesting Permissions at Run Time guide. For communicating between Flutter and Android, we will use Method Channels.

Setting up the app

The app has one screen, which has a list, as well as a CircularProgressIndicator. It handles errors with snackbars. We will use a basic MVP structure for this screen.

To follow the code tutorial, create a new app as follows.

If you’re unsure how to set up a Flutter app, check out Getting started with Flutter official tutorial.

Firstly, we create a Material app in main.dart, which will launch the HomePage widget.

 

Secondly, we create home_page.dart. This displays a list or an in progress indicator, depending on its state.

 

Thirdly, we define a simple Contact, with a display name and a mobile phone number, in contact.dart.

 

Setting up the MVP structure

I like to use “contracts” when using MVP. Contracts are interfaces (in Java)  or abstract classes (in Dart) that define the methods for each component of the feature, ie View, Model, and Presenter.

Note: You do not need to set up contracts for applying MVP, but I find it very helpful to have them. I define them all in one file, and this makes it easy to understand what a feature does.  When I worked as an Android DPE at Google, I worked on the first release of the Android Architecture Blueprints project and we decided to use contracts. I have used contracts on all my professional projects since, and I haven’t looked back: this really helps make a complex app maintainable!

So let’s set up our contract for this feature, by creating home_contract.dart.

 

The only user action per se is the user starting the app to show the screen, ie view displayed. This is async, because it will update the view based on async data responses from the Model.

 

Getting the contacts is 2 separate data actions: check if we can get contacts, and get contacts. The former takes care of requesting permission if the app hasn’t got it.

 

Finally, the view has 4 possible states: loading in progress, contacts list, display error message when permission is denied, and display permission rationale.

 

Now, we create Model and Presenter classes that implement the abstract classes. The View abstract class is implemented in HomePage.

Firstly, let’s start with home_presenter.dart. The Presenter has a reference to the View and the Model, as it acts as a “coordinator” between View and Model.

Note: in case of permission denied, we add a 1 second wait. This is because the Android SDK somehow still holds the UI (due to the permission dialog). If we don’t add this delay, the error snackbar isn’t shown. You may want to play with the duration value, but I found that 1 second worked on all the devices I tested it on. UPDATE: There is apparently a way around this, refer to comment by Christian. I’ll update the tutorial accordingly soon.

Secondly, let’s set up home_model.dart.

 

And, lastly, HomePage itself will implement the View, and will create the Presenter when initialised.

Checking and requesting permission

Firstly, we add the permission to the Android app manifest.

 

Secondly, to check and request a permission, we need to use the Android SDK. Therefore, we use a MethodChannel to communicate between Flutter and the Android SDK.

On the Flutter side, it is pretty simple. We set up the channel in home_model.dart, as per below.

Note: for the permission status, the channel returns an int, which corresponds to the index of the PermissionState value.

On the Android side, we need to set it up in MainActivity.java as below. When requesting a permission, the process is async, relying on a system callback in the Activity. So we define our own Callback, to be able to send the result back to the Channel. The permission code itself is copied from the official Android documentation – check it out if you need more explanations.

Note: the Android SDK has no method to indicate that the rationale has been shown. When the user has just seen the rationale, the app is expected to request the permission directly, without checking if we should show the rationale (as this will return true and we will be stuck in a loop). To handle this, we are using a boolean rationaleJustShown. We handle this in the Android code and not the Flutter code because it is an Android SDK detail, and the implementation on iOS may well be different.

Lastly, don’t forget to add the support library to build.gradle.

Getting contacts with a mobile phone number

The process to get the contacts is similar. We need to use the Android SDK to query the contacts, so we set up a Method Channel.

In Flutter, we do this in home_model.dart.

 

In Android, we set it up in MainActivity.java. To avoid blocking the UI thread, we use an AsyncTask to get the contacts; to send the results back to the channel,  we use a callback setup similar to permission. If you didn’t use an AsyncTask to query the contacts, the circular progress bar would freeze.

 

What next?

This code tutorial includes an introduction to MVP. App architecture is probably the most important factor determining app longevity. With good architecture, it is easy to add or change features, and therefore, the app is a pleasure to maintain. So it’s well worth keeping an eye on Flutter Architecture Samples,  a project inspired, in part, by Android Architecture Blueprints.

 

 

Author: Natalie Masse Hooper

Mobile app developer with 14 years experience. Started with the Android SDK in 2010 and switched to Flutter in 2017. Past apps range from start ups to large tech (Google) and non tech (British Airways) companies, in various sectors (transport, commercial operations, e-commerce).

11 thoughts on “How to implement Android runtime permission flow in Flutter”

    1. I plan to do it, but it will take me a few weeks: I need to sort out access to a Mac & iPhone, which I currently don’t have (I’m an Android/Linux user).

  1. Hi Natalie,

    Thank you very much for your write up.

    I’ve had a similar need to ask for permissions, and used the (defunct) permit plugin (https://github.com/goposse/permit) to get me started.

    I spent more than an hour trying to figure out why my snackbar isn’t showing, until I read about your one second delay. It was even worse for me, no snackbars would show anymore at all until I restarted the app.

      1. Great, thanks very much for looking into it, that’s very much appreciated! I’ll update the code-tutorial.

    1. Not yet on github (it will be at some point, I haven’t got around to it yet), but all the code is in the tutorial, there is no extra code required.

      1. Thanks a lot. The code worked beautifully. I am novice Android programmer and I learnt a lot by following your tutorial.

        1. I need to change the line in the function getContactsWithMobilePhoneNumber(home_model.dart) from

          final List<Map> result = await _methodChannel.invokeMethod(‘getContacts’);

          to
          var result = await _methodChannel.invokeMethod(‘getContacts’);

          After the latest upgrade, it was producing a run time error

          type ‘List’ is not a subtype of type ‘List<Map>’ where

          Hope this is useful to someone

          Thanks
          Jay

  2. Thanks so much for this. I’ve been trying to figure out how to get user permissions and wait for a result for a while, and I couldn’t figure out how to make the call synchronous with the async callback within the native code.

    I’ve been in C# for so long these days that it’s taking a while to get back to how Java does stuff.

    Thanks again!

  3. Thanks for your post, I would like to know if it would be the same to request permission to write in “/storage/emulated/0/Download”, I have tried many plugins and I have not been able to make it work

Leave a Reply

Your email address will not be published.