Reputation: 549
I am working with the Location 3.0.1 package in Flutter. I want to listen for location changes. My code (which is taken pretty much entirely from the github example for this package) is as follows:
class _HomePageState extends State<HomePage> {
StreamSubscription<LocationData> locationSubscription;
final Location location = new Location();
LocationData _locationData;
String _error;
var popupData;
PermissionStatus _permissionGranted;
_checkPermissions() async {
PermissionStatus permissionGrantedResult = await location.hasPermission();
setState(() {
_permissionGranted = permissionGrantedResult;
});
}
_requestPermission() async {
if (_permissionGranted != PermissionStatus.GRANTED) {
PermissionStatus permissionRequestedResult =
await location.requestPermission();
print(permissionRequestedResult);
setState(() {
_permissionGranted = permissionRequestedResult;
});
if (permissionRequestedResult != PermissionStatus.GRANTED) {
return;
} else{
_loadLocation();
}
}
else{
_loadLocation();
}
}
Future<void> _listenLocation() async {
locationSubscription =
location.onLocationChanged().handleError((dynamic err) {
setState(() {
_error = err.code;
});
locationSubscription.cancel();
}).listen((LocationData currentLocation) {
setState(() {
_error = null;
_locationData = currentLocation;
});
});
}
void _loadLocation() async {
_locationData = await location.getLocation();
_listenLocation();
}
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
if(_locationData == null){
_checkPermissions();
_requestPermission();
}
. . .
However, the stream subscription keeps getting triggered quite frequently (about once ever 2-3 seconds) (which causes setState
to be called, and the whole screen to be rebuilt) even when the location is not changing (I am running this code on my emulator, so location should be static). Does anyone know why this is happening and how to fix it?
Output of flutter doctor:
[✓] Flutter (Channel stable, v1.12.13+hotfix.5, on Mac OS X 10.15.2 19C57, locale en-US)
• Flutter version 1.12.13+hotfix.5 at /Users/foo/development/flutter
• Framework revision 27321ebbad (4 months ago), 2019-12-10 18:15:01 -0800
• Engine revision 2994f7e1e6
• Dart version 2.7.0
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
• Android SDK at /Users/foo/Library/Android/sdk
• Android NDK location not configured (optional; useful for native profiling support)
• Platform android-29, build-tools 28.0.3
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b49-5587405)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 11.3)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Xcode 11.3, Build version 11C29
• CocoaPods version 1.8.4
[✓] Android Studio (version 3.5)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin version 39.0.3
• Dart plugin version 191.8423
• Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b49-5587405)
[✓] IntelliJ IDEA Ultimate Edition (version 2019.1.4)
• IntelliJ at /Applications/IntelliJ IDEA.app
• Flutter plugin version 38.1.3
• Dart plugin version 191.8593
[✓] Connected device (2 available)
• Android SDK built for x86 • emulator-5554 • android-x86 • Android 9 (API 28) (emulator)
• iPhone 11 Pro Max • B3536B50-C435-4442-9CF4-69D470B979CA • ios •
com.apple.CoreSimulator.SimRuntime.iOS-13-3 (simulator)
• No issues found!
If someone could just give me a snippet of code showing how to properly listen for updates, that would also be helpful (i.e. I'm not set on my code looking exactly like this).
Upvotes: 1
Views: 9762
Reputation: 54367
You can copy paste run full code below
Because you put code in Widget build
cause this issue
You can reference this https://medium.com/flutter-community/build-a-location-service-in-flutter-367a1b212f7a to build a Location Service
I made few change, you can directly see full code
You can use StreamProvider
instead of await location.getLocation
in Widget build
Youn can directly use location.requestPermission
instead of Permission Handler
code snippt
return StreamProvider<UserLocation>(
create: (context) => LocationService().locationStream,
...
LocationService() {
// Request permission to use location
location.requestPermission().then((permissionStatus) {
if (permissionStatus == PermissionStatus.granted) {
// If granted listen to the onLocationChanged stream and emit over our controller
location.onLocationChanged.listen((locationData) {
working demo
full code
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:location/location.dart';
import 'dart:async';
class LocationService {
UserLocation _currentLocation;
var location = Location();
StreamController<UserLocation> _locationController =
StreamController<UserLocation>();
Stream<UserLocation> get locationStream => _locationController.stream;
LocationService() {
// Request permission to use location
location.requestPermission().then((permissionStatus) {
if (permissionStatus == PermissionStatus.granted) {
// If granted listen to the onLocationChanged stream and emit over our controller
location.onLocationChanged.listen((locationData) {
if (locationData != null) {
_locationController.add(UserLocation(
latitude: locationData.latitude,
longitude: locationData.longitude,
));
}
});
}
});
}
Future<UserLocation> getLocation() async {
try {
var userLocation = await location.getLocation();
_currentLocation = UserLocation(
latitude: userLocation.latitude,
longitude: userLocation.longitude,
);
} on Exception catch (e) {
print('Could not get location: ${e.toString()}');
}
return _currentLocation;
}
}
void main() {
runApp(MyApp());
}
class UserLocation {
final double latitude;
final double longitude;
UserLocation({this.latitude, this.longitude});
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return StreamProvider<UserLocation>(
create: (context) => LocationService().locationStream,
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: HomeView(),
)),
);
}
}
class HomeView extends StatelessWidget {
const HomeView({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
var userLocation = Provider.of<UserLocation>(context);
return Center(
child: Text(
'Location: Lat${userLocation?.latitude}, Long: ${userLocation?.longitude}'),
);
}
}
Upvotes: 3