Flutter Connectivity Package: Android Permissions

1,102

I created a sample app that demonstrated the solution but apparently the Stack Overflow folks want me to work even harder to share this information with you. They deleted my original answer (even though it solved the problem for everyone who had the issue). And want me to pull out parts of the answer and post them here. I did a lot of work to solve this issue and publish it as a complete app sample, but apparently that's not enough. I hate this. I'm trying to help people but they don't like external references to the solution.

I created a complete solution that demonstrates how to solve this problem in https://github.com/johnwargo/flutter-android-connectivity-permissions.

Flutter doesn't seem to have a way to query an app user for permissions, so someone created the Flutter permissions_handler package. Adding that to my app, and a variant of the code shown in the original issue, solved my issue.

I quickly realized that the answer on the Flutter repo was incomplete and anyone trying to implement this solution would need some help. To make it easier for these developers, I created a complete sample application here in this repo.

Here's what I did

Starting with the project's pubspec.yaml file, I added the connectivity and permissions_handler packages:

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.3
  connectivity: ^0.4.8+6  
  permission_handler: ^5.0.1

Next, I added the imports to the project's main.dart file:

import 'package:connectivity/connectivity.dart';
import 'package:permission_handler/permission_handler.dart';

Then I added some code to the initConnectivity function:

Future<void> initConnectivity() async {
  ConnectivityResult result;
  // Platform messages may fail, so we use a try/catch PlatformException.
  try {
    result = await _connectivity.checkConnectivity();
  } on PlatformException catch (e) {
    print(e.toString());
  }

  // If the widget was removed from the tree while the asynchronous platform
  // message was in flight, we want to discard the reply rather than calling
  // setState to update our non-existent appearance.
  if (!mounted) {
    return Future.value(null);
  }

  // Check to see if Android Location permissions are enabled
  // Described in https://github.com/flutter/flutter/issues/51529
  if (Platform.isAndroid) {
    print('Checking Android permissions');
    var status = await Permission.location.status;
    // Blocked?
    if (status.isUndetermined || status.isDenied || status.isRestricted) {
      // Ask the user to unblock
      if (await Permission.location.request().isGranted) {
        // Either the permission was already granted before or the user just granted it.
        print('Location permission granted');
      } else {
        print('Location permission not granted');
      }
    } else {
      print('Permission already granted (previous execution?)');
    }
  }

  return _updateConnectionStatus(result);
}

The code I added is:

// Check to see if Android Location permissions are enabled
// Described in https://github.com/flutter/flutter/issues/51529
if (Platform.isAndroid) {
  print('Checking Android permissions');
  var status = await Permission.location.status;
  // Blocked?
  if (status.isUndetermined || status.isDenied || status.isRestricted) {
    // Ask the user to unblock
    if (await Permission.location.request().isGranted) {
      // Either the permission was already granted before or the user just granted it.
      print('Location permission granted');
    } else {
      print('Location permission not granted');
    }
  } else {
    print('Permission already granted (previous execution?)');
  }
}

This code executes once at startup and checks permissions before retrieving connection status from the device.

If you run the code at this point, everything will seem to work, but the values for Wi-Fi Name, BSSID and IP address will all report null. When you look at the console, you'll see:

I/flutter ( 6506): Checking Android permissions
I/flutter ( 6506): Result: ConnectivityResult.wifi
D/permissions_handler( 6506): No permissions found in manifest for: 3
I/flutter ( 6506): Wi-Fi Name: null
D/permissions_handler( 6506): No permissions found in manifest for: 3
I/flutter ( 6506): Location permission not granted
I/flutter ( 6506): Result: ConnectivityResult.wifi
I/flutter ( 6506): BSSID: 02:00:00:00:00:00
I/flutter ( 6506): Wi-Fi Name: null
I/flutter ( 6506): BSSID: 02:00:00:00:00:00

That's because without the right permissions defined within the app, the status value is null and none of the other permissions checking stuff happens. The user is never even asked for permission as expected.

To solve this, open the project's android/app/src/main/AndroidManifest.xml file and add the location permission to the app's config:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

I used the course setting, you can use the fine too I think.

So many times writers tell you what permission change to make, but don't tell you where it should be made in the file. I don't do a lot of native Android development, so I'm never sure where to put that setting, so here's the complete file listing so you can see its proper place:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.johnwargo.connectivitypermissions">
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <application
        android:name="io.flutter.app.FlutterApplication"
        android:label="connectivitypermissions"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
              />
            <!-- Displays an Android View that continues showing the launch screen
                 Drawable until Flutter paints its first frame, then this splash
                 screen fades out. A splash screen is useful to avoid any visual
                 gap between the end of Android's launch screen and the painting of
                 Flutter's first frame. -->
            <meta-data
              android:name="io.flutter.embedding.android.SplashScreenDrawable"
              android:resource="@drawable/launch_background"
              />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
</manifest>

When you launch the app on Android API 29, it looks like this:

For my particular app I don't care about getting settings from the device when the app isn't running, so I selected 'Allow only when using the app`. If you look in the console, you'll see the following:

I/flutter ( 7670): Checking Android permissions
I/flutter ( 7670): Result: ConnectivityResult.wifi
I/flutter ( 7670): Wi-Fi Name: null
I/flutter ( 7670): BSSID: 02:00:00:00:00:00
I/flutter ( 7670): Location permission granted
I/flutter ( 7670): Result: ConnectivityResult.wifi
I/flutter ( 7670): Wi-Fi Name: AndroidWifi
I/flutter ( 7670): BSSID: 02:15:b2:00:01:00

Notice the app goes ahead and checks Wi-Fi settings while it waits for the user to grant permission; that's because of the way the code is written, you could easily tweak this so it waits for permission before trying, but it runs the init twice, so you'll get the right data the second time around (no, I don't know why it runs twice).

Share:
1,102
johnwargo
Author by

johnwargo

Updated on December 21, 2022

Comments

  • johnwargo
    johnwargo over 1 year

    I'm using the sample code from https://pub.dev/packages/connectivity#-example-tab- in a Flutter application and when running the application on an Android emulator, the app sees the debug wi-fi network. However, when I run the app on my Pixel 4XL, I get null for wi-fi name and BSSID.

    I'm assuming I have to enable permissions to access Wi-Fi settings to use this package, but I can't find any reference anywhere that describes what's required for this package on Flutter.

    I updated the Android app appmanifest file with the wi-fi permissions from the wi-fi package example, but nothing changed in my app.

    • johnwargo
      johnwargo almost 4 years
      Actually, I just tested on the Emulator and its not working there as well. Next, I created a new project then replaced the new project code with the sample from that link and it doesn't work either. I also downgraded the package to the original version I used a few weeks ago (from 4.8+6 down to 4.8.2) with the same results. This is just weird. I'm going to create a new emulator image and try again.
    • Daniel
      Daniel over 3 years
      When I try to get the wifi name via Connectivity().getWifiName()) I always get null both in emulator and on Android phone. Did you find any permissions that could fix this?
    • Daniel
      Daniel over 3 years
      Great work, you should update this with an answer based on the fixes you have done in your sample app: github.com/johnwargo/flutter-android-connectivity-permission‌​s It solved the problem for me.