skip to Main Content

I need to show a disclosure window before asking the location permissions to the users to make the app google play conformed for my flutter app.
I made the following changes but after that neither the location permission window nor the disclosure message window with the text "We are collecting location for suggesting you interesting places." are shown. I am using latest flutter version. Here are the relevant classes:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:location/location.dart';

import '../../Datamodel/user_location.dart';

class LocationService {
  static final LocationService _instance = LocationService._internal();

  factory LocationService(BuildContext context) {
    return _instance;
  }
  LocationService._internal({BuildContext? context}) {
    if (context != null) {
    getLocationOnchange(context);
  }
  }

  Location location = Location();
  final StreamController<UserLocation> _locationController =
  StreamController<UserLocation>.broadcast();
  StreamSubscription<LocationData>? listener;

  Stream<UserLocation> get locationStream => _locationController.stream;

  void getLocationOnchange(BuildContext context) async {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('Location Tracking'),
          content: const Text('We are collecting location for suggesting you interesting places.'),
          actions: [
            TextButton(
              onPressed: () {
                Navigator.pop(context);
                requestLocationPermission();
              },
              child: Text('OK'),
            ),
          ],
        );
      },
    );
  }

  void requestLocationPermission() {
    location.requestService().then((value) {
      location.requestPermission().then((permissionStatus) {
        if (permissionStatus == PermissionStatus.granted) {
          location.enableBackgroundMode(enable: true);
          location.changeSettings(
            interval: 60000,
            accuracy: LocationAccuracy.high,
          );
          listener = initListener();
        }
      });
    });
  }

  StreamSubscription<LocationData>? getListener() {
    return listener;
  }

  StreamSubscription<LocationData> initListener() {
    return location.onLocationChanged.listen((locationData) {
      _locationController.add(UserLocation(
        locationData.latitude,
        locationData.longitude,
        locationData.altitude,
        locationData.time,
      ));
    });
  }

  void dispose() {
    listener?.cancel();
    listener = null;
  }

}

startLocation.dart:

import 'package:flutter/cupertino.dart';
import '../services/location/LocationService.dart';
import '../services/location/post_location.dart';

Future<void> startLocation(BuildContext context) async {
  var listener = LocationService(context).getListener();
  listener ??= LocationService(context).initListener();
  LocationService(context).locationStream;
  await startLocationStream(context);
}

main.dart:

import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:sdx_patient_portal/backend/services/location/LocationService.dart';
import 'package:sdx_patient_portal/backend/services/sharedPref.dart';
import 'package:sdx_patient_portal/firebase_options.dart';

import 'MyApp.dart';

Future<void> backgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
}

late AndroidNotificationChannel channel;

late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  var messaging = FirebaseMessaging.instance;
  await EasyLocalization.ensureInitialized();
  String routeFromMessage = '';

  BuildContext? appContext;

  runApp(
    Builder(
      builder: (BuildContext context) {
        appContext = context;
        return Container();
      },
    ),
  );


  messaging.getInitialMessage().then((message) {
    if (message != null) {
      routeFromMessage = "/" + message.data["route"];
    }
  });
  await messaging.requestPermission(
    alert: true,
    announcement: false,
    badge: true,
    carPlay: false,
    criticalAlert: false,
    provisional: false,
    sound: true,
  );

  LocationService(appContext!).getListener()?.resume();
  FirebaseMessaging.onBackgroundMessage(backgroundHandler);

  if (!kIsWeb) {
    channel = const AndroidNotificationChannel(
        'high_importance_channel', 'High Importance Notifications',
        importance: Importance.high);

    flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

    await flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            AndroidFlutterLocalNotificationsPlugin>()
        ?.createNotificationChannel(channel);

    await messaging.setForegroundNotificationPresentationOptions(
      alert: true,
      badge: true,
      sound: true,
    );
  }

  if (defaultTargetPlatform == TargetPlatform.android) {
    messaging.getToken().then((newToken) {
      SharedPref.saveFCMToken(newToken);
    });
  } else if (defaultTargetPlatform == TargetPlatform.iOS) {
    messaging.requestPermission(alert: true, sound: true);
    await messaging.setForegroundNotificationPresentationOptions(
      alert: true,
      badge: true,
      sound: true,
    );
    messaging.getToken().then((newToken) {
      SharedPref.saveFCMToken(newToken);
    });
  }

  bool? isLoggedIn = await SharedPref.getUserLoginSharedPrefernces();
  isLoggedIn ??= false;
  String route = '';
  if (isLoggedIn && routeFromMessage != '') {
    route = routeFromMessage;
  } else {
    route = isLoggedIn ? '/' : '/loginScreen';
  }

  runApp(EasyLocalization(
    supportedLocales: const [Locale('en', 'US'), Locale('es', 'ES'), Locale('nl', 'NL'), Locale('de', 'DE')],
    path: 'i18n',
    fallbackLocale: const Locale('en', 'US'),
    child: MyApp(
          homepage: route,
          isLoggedIn: isLoggedIn,
          isLightTheme: true
        ),
  ));
}

and init:

    class PushMessaging extends StatefulWidget {
  final String title;
  final String homepage;
  final bool? isLoggedIn;

  const PushMessaging(
      {Key? key, required this.homepage, required this.title, this.isLoggedIn})
      : super(key: key);

  @override
  State<PushMessaging> createState() => _PushMessagingState();
}

class _PushMessagingState extends State<PushMessaging> {
  FirebaseMessaging messaging = FirebaseMessaging.instance;

  bool isLoading = false;
  bool isLightTheme = true;    
  int steps = 0;
  String version = '';

  void initPermission() async {
    NotificationSettings settings = await messaging.requestPermission(
      alert: true,
      announcement: false,
      badge: true,
      carPlay: false,
      criticalAlert: false,
      provisional: true,
      sound: true,
    );

    if (settings.authorizationStatus == AuthorizationStatus.authorized) {
      if (kDebugMode) {
        print('User granted permission');
      }
    } else if (settings.authorizationStatus ==
        AuthorizationStatus.provisional) {
      if (kDebugMode) {
        print('User granted provisional permission');
      }
    } else {
      if (kDebugMode) {
        print('User declined or has not accepted permission');
      }
    }
  }

  void toggleTheme() {
    setState(() => isLightTheme = !isLightTheme);
  }

  @override
  void initState() {
    super.initState();
    getNewVersion();
    getAmountOfQuest();
    PedometerService.instance.init();
    startLocation(context);
    initPermission();
    ...rest of code

2

Answers


  1. Chosen as BEST ANSWER

    here are the current changes that made it happen:

        class MyApp extends StatefulWidget {
      static LocationService? locationService = LocationService();
      const MyApp({
        Key? key,
        required this.homepage,
        required this.isLoggedIn,
        required this.isLightTheme,
      }) : super(key: key);
      final bool? isLoggedIn;
      final String homepage;
      final bool isLightTheme;
    
      @override
      _MyAppState createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      //LocationService? locationService;
      @override
      void initState() {
        super.initState();
        PedometerService.instance.init();
        //locationService = LocationService();
        startLocation();
      }
    
      Future<void> startLocation() async {
        await MyApp.locationService?.getLocationOnchange(context);
        MyApp.locationService?.getListener();
        startLocationStream();
      }
     
    
     @override
      Widget build(BuildContext context) {
        if (widget.isLoggedIn ?? false) {
          MyApp.locationService ??= LocationService();
          startLocation();
        }
    
    return MaterialApp(
      title: 'Semdatex Patient Portal',
      debugShowCheckedModeBanner: false,
      theme: widget.isLightTheme ? lightMode : darkMode,
      localizationsDelegates: context.localizationDelegates,
      supportedLocales: context.supportedLocales,
      locale: context.locale,
      initialRoute: widget.homepage,
      home: PushMessaging(
        isLoggedIn: widget.isLoggedIn,
        homepage: widget.homepage,
        title: "Semdatex Patient Portal",
      ),
      routes: { ...rest
    

    I removed it from main:

    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      await Firebase.initializeApp();
      await EasyLocalization.ensureInitialized();
    
      final messaging = FirebaseMessaging.instance;
      final routeFromMessageCompleter = Completer<String>();
    
      // Request permission for receiving notifications
      await messaging.requestPermission(
        alert: true,
        announcement: false,
        badge: true,
        carPlay: false,
        criticalAlert: false,
        provisional: false,
        sound: true,
      );
    
      // Get the initial message if available
      messaging.getInitialMessage().then((message) {
        if (message != null) {
          routeFromMessageCompleter.complete("/${message.data['route']}");
        } else {
          routeFromMessageCompleter.complete("");
        }
      });
    
      // Background message handling
      FirebaseMessaging.onBackgroundMessage(backgroundHandler);
    
      // Initialize FlutterLocalNotificationsPlugin
      if (!kIsWeb) {
        channel = const AndroidNotificationChannel(
          'high_importance_channel',
          'High Importance Notifications',
          importance: Importance.high,
        );
        flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
        await flutterLocalNotificationsPlugin
            .resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
            ?.createNotificationChannel(channel);
      }
    
      // Get FCM token and save it
      final fcmToken = await messaging.getToken();
      SharedPref.saveFCMToken(fcmToken);
    
      // Check if the user is logged in
      final bool isLoggedIn = await SharedPref.getUserLoginSharedPrefernces() ?? false;
      final String routeFromMessage = await routeFromMessageCompleter.future;
      String route = isLoggedIn && routeFromMessage != '' ? routeFromMessage : (isLoggedIn ? '/' : '/loginScreen');
      runApp(
          MaterialApp(
            home: Builder(
          builder: (context) {
            return EasyLocalization(
              supportedLocales: const [
                Locale('en', 'US'),
                Locale('es', 'ES'),
                Locale('nl', 'NL'),
                Locale('de', 'DE'),
              ],
              path: 'i18n',
              fallbackLocale: const Locale('en', 'US'),
              child: MyApp(
                homepage: route,
                isLoggedIn: isLoggedIn,
                isLightTheme: true,
              ),
            );
          },
        ),
      ),
      );
    }
    

    and here is the location service itself:

    import 'dart:async';
    
    import 'package:flutter/material.dart';
    import 'package:location/location.dart';
    import 'package:permission_handler/permission_handler.dart' as perm;
    
    import '../../Datamodel/user_location.dart';
    
    class LocationService {
      static final LocationService _instance = LocationService._internal();
      bool _isStreamInitialized = false;
    
      factory LocationService() {
        return _instance;
      }
    
      LocationService._internal({BuildContext? context}) {
        if (context != null) {
          getLocationOnchange(context);
        }
      }
    
      Location location = Location();
      final StreamController<UserLocation> _locationController =
      StreamController<UserLocation>.broadcast();
      StreamSubscription<LocationData>? listener;
    
      Stream<UserLocation> get locationStream => _locationController.stream;
    
      Future<void> getLocationOnchange(BuildContext context) async {
        await showPermissionDialogAndAskPermission(context, perm.Permission.location);
        listener ??= initListener();
      }
    
      Future<void> showPermissionDialogAndAskPermission(
          BuildContext context, perm.Permission permission) async {
        final perm.PermissionStatus status = await permission.status;
        if (status.isGranted) {
          location.enableBackgroundMode(enable: true);
          location.changeSettings(
            interval: 10000,
            accuracy: LocationAccuracy.high,
          );
          listener = initListener();
        } else if (status.isDenied || status.isPermanentlyDenied) {
          showDialog(
            context: context,
            builder: (BuildContext dialogContext) {
              return AlertDialog(
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(2.5),
                ),
                title: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    const Text("Permission required"),
                    GestureDetector(
                      onTap: () {
                        Navigator.pop(dialogContext);
                      },
                      child: const Icon(
                        Icons.close,
                        size: 6.0,
                      ),
                    ),
                  ],
                ),
                content: const Text(
                  "This app collects location data to enable suggesting you to do activities which are beneficiary for your health"
                      "even when the app is closed or not in use."
                ),
                actionsPadding: const EdgeInsets.fromLTRB(5, 0, 5, 5.0),
                actions: [
                  Row(
                    children: [
                      Expanded(
                        child: GestureDetector(
                          onTap: () {
                            Navigator.of(context).pop();
                          },
                          child: const Center(
                            child: Text("Cancel"),
                          ),
                        ),
                      ),
                      Expanded(
                        child: GestureDetector(
                          onTap: () async {
                            Navigator.of(context).pop();
                            final perm.PermissionStatus newStatus = await permission.request();
                            if (newStatus.isGranted) {
                              location.enableBackgroundMode(enable: true);
                              location.changeSettings(
                                interval: 10000,//
                                accuracy: LocationAccuracy.high,
                              );
                              initListenerIfNeeded();
                              //listener = initListener();
                            } else if (newStatus.isDenied) {
                              // Permission denied
                            } else if (newStatus.isPermanentlyDenied) {
                              // Permission permanently denied
                            }
                          },
                          child: const Center(
                            child: Text("Grant"),
                          ),
                        ),
                      ),
                    ],
                  ),
                ],
                actionsAlignment: MainAxisAlignment.spaceAround,
              );
            },
          );
        } else {
          // Handle other permission statuses if needed
        }
      }
    
      StreamSubscription<LocationData>? getListener() {
        return listener;
      }
    
      void initListenerIfNeeded() {
        if (!_isStreamInitialized) {
          listener = initListener();
          _isStreamInitialized = true;
        }
      }
      StreamSubscription<LocationData> initListener() {
        return location.onLocationChanged.listen((locationData) {
          if (!_locationController.isClosed) {
            _locationController.add(UserLocation(
              locationData.latitude,
              locationData.longitude,
              locationData.altitude,
              locationData.time,
            ));
          }
        });
      }
    
      void dispose() {
        _locationController.close();
        listener?.cancel();
        listener = null;
    
      }
    }
    

  2. Try this :

        static showPermissionDialogAndAskPermission(
          BuildContext context,
          Permission permission) {
        return showDialog(
            context: context,
            builder: (BuildContext dialogContext) {
              return AlertDialog(
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(2.5.w),
                ),
                title: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    const Text(
                      "Permission required",
                    ),
                    GestureDetector(
                        onTap: () {
                          Navigator.pop(dialogContext);
                        },
                        child: Icon(
                          Icons.close,
                          size: 6.w,
                        )),
                  ],
                ),
                content: const Text("We are collecting location for suggesting you interesting places."),
                actionsPadding: EdgeInsets.fromLTRB(5.w, 0, 5.w, 5.w),
                actions: [
                  Row(
                    children: [
                      GestureDetector(
                        onTap: () {
                          Navigator.of(context).pop();
                        },
                        child: const Center(
                          child: Text(
                            "Cancel",
                          ),
                        ),
                      ).expand,
                      GestureDetector(
                        onTap: () async {
                          Navigator.of(context).pop();
                          var status = await permission.request();
                          if (status.isGranted) {
                            // Do whatever you want
                          }
                          else if(status.isDenied){
                            // Do whatever you want
                          }
                          else if(status.isPermanentlyDenied){
                            // Do whatever you want
                          }
                        },
                        child: const Center(
                          child: Text(
                            "Grant",
                          ),
                        ),
                      ).expand
                    ],
                  )
                ],
                actionsAlignment: MainAxisAlignment.spaceAround,
              );
            });
      }
    

    You should give text styles as per your needs.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search