Baseflow / flutter-geolocator

Android and iOS Geolocation plugin for Flutter
https://baseflow.com/
MIT License
1.25k stars 662 forks source link

PositionStream does not work properly if location service is initially disabled then enabled #568

Open ordinatorix opened 4 years ago

ordinatorix commented 4 years ago

🐛 Bug Report

I actually have two bugs that are somewhat similar. So instead of creating to reports I have them listed as 1) & 2).

1) Location stream throw a LocationServicesDisabledException when app is launched with location service disabled, but does not continue the steam when location service is then enabled by user using the notification drawer.

2) On new app install, after location permission is granted, I get an unhandled exception: [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: The location service on the device is disabled. if location service is initially disabled. Stream does not continue if location the then enabled.

I get the expected behavior when location service is initially enabled then disabled and the re-enabled after the #548 fix.

Expected behavior

1) Location stream should continue after exception is thrown and location service is enabled.

2) a) Exception should be caught/handled by Geolocator.getPositionStream( desiredAccuracy: LocationAccuracy.medium, distanceFilter: 0) .handleError((onError) { log.e('handled position error: $onError'); }).listen((Position position) { log.d('$position');}); b) location stream should continue after location service is enabled.

Reproduction steps

1) a) Build basic app containing a PositionStream using a StreamSubscription or StreamProvider or a Streambuilder. b) run the app while Location services are off and permission granted. c) Turn on Location via Android Notification Drawer after exception is thrown.

2) a) Build basic app containing a PositionStream using a StreamSubscription or StreamProvider or a Streambuilder. b) Install & run the app while Location services are off and grant permissions when prompted. c) Turn on Location via Android Notification Drawer after exception is thrown.

Configuration

Version: 6.1.2

Platform:

mvanbeusekom commented 4 years ago

@seb3n,

Thank you for submitting this report. My compliments on all the details, I wish everyone submitted issues like this ;).

I do have some reservations on point 2.a you describe in the "Expected behavior". Here you mention the Geolocator should handle the LocationServicesDisabledException. But this means you (as app developer) will not get notified something is wrong and you will not be able to alert the user of the App to enable the location services again. On Android with Google Play Services installed we might be able to do an automatic resolution but on Android without Google Play Service and iOS installations this will not work, so it will help developers to be alerted that the location services were disabled. So I am curious how you think about this.

I will however have a look if it is possible to automatically resume the stream when the location services are enabled again.

Protofall commented 4 years ago

I'd like to add sometimes I get "The location service on the device is disabled." exception even when the services haven't been disabled. It tends to happen when I'm on the App Info page looking around, but not actually changing stuff (The App Info page was opened with Geolocator.openAppSettings()). Let me know if there's anything I can do to get more debug info.

ordinatorix commented 4 years ago

@mvanbeusekom , I understand you confusion.

What I mean is that the package does not throw any error for me to catch. Notice in the expected behavior, I give a snippet of the code I have and how I catch any errors thrown by the package .handleError((onError) { log.e('handled position error: $onError');

The issue encountered is that .handleError() does not work in the 2nd situation, but works in the 1st situation.

Hopefully that clears things up.

ordinatorix commented 4 years ago

@mvanbeusekom

Forget the second bug report (2) I just realized I was not catching the exception further down in my code. 🤦
Sorry about that!

I am leaving the issue open, as the first bug (1) is still an issue.

I created this repo with my code if you want to reproduce the issue (1).

Just launch with locationsServiceStatus disabled; grant permission when prompted; error will be thrown; enable location service. => stream will not continue.

I am assuming this happens because the stream is never started when an error is thrown first thing when getPositionStream is executed(?).

Let me know if there is anything else I can do to help or if you have any suggestions on how to bypass that issue and still get the expected result.

Roms1383 commented 2 years ago

Yes, same issue here (the first one, when app is launched with location service disabled).

innovatoraakash commented 1 year ago

How to automatically resume the stream when the location services are enabled again. Due to this issue Stream does not work if location is initially turned off

Letiste commented 1 year ago

As I was having the same problem, I found a workaround that did the job for me.

The idea is:

Example code:

class PositionService {
  late final BehaviorSubject<LatLng> _controller; // using BehaviorSubject to get last emitted value on relisten
  StreamSubscription<Position>? _streamSubscription;

  void listenToPosition() {
    if (_streamSubscription != null) return;
    _streamSubscription = Geolocator.getPositionStream()
        .handleError(handleError) // handle error if you need to
        .listen((position) => _controller.add(position));
  }

  void unlistenPosition() {
    _streamSubscription?.cancel();
    _streamSubscription = null;
  }

  void _initializeStreamPosition() async {
    if (await Geolocator.isLocationServiceEnabled()) listenToPosition();
  }

  PositionService() {
    _controller = BehaviorSubject();
    _initializeStreamPosition();
    Geolocator.getServiceStatusStream().listen((event) {
      if (event == ServiceStatus.enabled) {
        listenToPosition();
      } else {
        unlistenPosition();
      }
    });
  }

  // custom position stream
  Stream<Position> watchCurrentPosition() {
    return _controller.stream;
  }
}

You may also do it without making the stream subscription nullable. Just start listening to the position stream on initialization, without checking the service location status.