How to get the device's location in Flutter
Thinking about adding location features to your Flutter application? If you are building a navigation application, a running tracker, or anything that needs to use location data, the location package can make things much simpler. In this post, we will explore how to use this package to retrieve the device's location, handle permissions, and even keep track of ongoing location updates.
Installing
To get started, we need to install the location package into our project. The installation process is simple. Just execute the following command: flutter pub add location
.
Once the command is executed, make sure to check your pubspec.yaml
file for the added dependencies. You should see the location package included in the dependencies section, like this:
dependencies:
flutter:
sdk: flutter
location: ^5.0.2+1
Android
In Android, to make this package work, we need to add two permission entries in the AndroidManifest.xml
file:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
iOS
In iOS, you need to add the location permission to the ios/Runner/info.plist
file:
<plist version="1.0">
<dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access when in use</string>
...
</dict>
</plist>
Ensure that the key between the <key>
tags remains exactly the same. However, feel free to modify the description between the <string>
tags. This description will be displayed to the user when requesting location access.
Unfortunately, I do not have access to an iOS device for testing, so I recommend checking the package's documentation if you run into issues on iOS devices.
Getting the device's location
With the package installed, we can go straight to the implementation, see the code below:
import 'package:flutter/material.dart';
import 'package:location/location.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: LocationRetriever(),
);
}
}
class LocationRetriever extends StatefulWidget {
@override
_LocationRetrieverState createState() => _LocationRetrieverState();
}
class _LocationRetrieverState extends State<LocationRetriever> {
LocationData? _currentLocation;
Location _location = Location();
@override
void initState() {
super.initState();
_getLocation();
}
Future<void> _getLocation() async {
await _location.serviceEnabled().then((bool enabled) async {
if (!enabled) {
await _location.requestService().then((bool enabled) {
if (!enabled) return;
});
}
});
await _location.hasPermission().then((PermissionStatus status) async {
if (status == PermissionStatus.denied) {
await _location.requestPermission().then((PermissionStatus status) {
if (status != PermissionStatus.granted) return;
});
}
});
try {
await _location.getLocation().then((LocationData locationData) =>
setState(() => _currentLocation = locationData));
} catch (error) {
print('Error getting location: $error');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Location Demo'),
),
body: Center(
child: _currentLocation != null
? Text(
'Accuracy: ${_currentLocation!.accuracy}\n'
'Altitude: ${_currentLocation!.altitude}\n'
'longitude: ${_currentLocation!.longitude}\n'
'provider: ${_currentLocation!.provider}\n'
'speed: ${_currentLocation!.speed}\n'
'speedAccuracy: ${_currentLocation!.speedAccuracy}\n'
'time: ${DateTime.fromMillisecondsSinceEpoch(
_currentLocation!.time!.toInt()
)}\n'
'verticalAccuracy: ${_currentLocation!.verticalAccuracy}',
style: TextStyle(fontSize: 20))
: Text(
'Getting location...',
style: TextStyle(fontSize: 20),
),
),
);
}
}
In this code snippet, we created two widgets, the MyApp
widget which is the root widget of the application, and the LocationRetriever
widget. Which is responsible for showing the current location.
The LocationRetriever
widget is a stateful widget. Inside its state, we defined two private variables _currentLocation
and _location
. The _currentLocation
variable is of type LocationData?
and will be used to save the data of the retrieved location. The _location
variable is of type Location
and will be set to an instance of the Location()
class coming from the location package. To make this work, we imported the package using import 'package:location/location.dart';
.
The initState
function, which we override, calls our _getLocation
function. This function performs three essential tasks:
- Checks if location services are enabled and request them if needed using the package's
serviceEnabled
andrequestService
functions. - Checks for location permission and requests them if necessary using the package's
hasPermission
andrequestPermission
functions. - Retrieves the current location and assigns it to the
_currentLocation
variable using the package'sgetLocation
function.
Finally, in the build
function, the application's user interface is constructed. If _currentLocation
is null, a Text
widget displays "Getting location..." indicating that the data is still loading. If _currentLocation
holds a value, then we display another Text
widget with location data.

Continuous listening
If you are creating an application that should always be aware of the user's location, such as a run tracker, you can use the location data in a way that it is constantly updated. This way, the application consistently keeps an eye on where the user is and updates the location. With the location package, this can be achieved in the following way:
@override
void initState() {
super.initState();
_location.onLocationChanged.listen((LocationData currentLocation) =>
setState(() => _currentLocation = currentLocation));
_getLocation();
}
In this code snippet, we updated the initState
function to also call the onLocationChanged.listen
function which takes a callback. In this callback, we update the _currentLocation
variable with the newly retrieved location.
As you can see in the GIF below the values keep changing, meaning the location is continuously retrieved.

Receiving location in the background
When the application is in the background some applications still require location data, here is how you can do it using the location package:
@override
void initState() {
_location.enableBackgroundMode(enable: true);
super.initState();
_getLocation();
}
In this code snippet, we changed the initState
function to also call the enableBackgroundMode
function on the _location
variable. By setting the enable
parameter to true
we ensure that the location is also tracked when the application is in the background.
In the GIF below you can see that I modified the code to print out the location. As you can see when the application is closed it keeps on printing the location.

Conclusion
By using the location package, you can easily add location features to your Flutter application. Whether you want to check the location just once or keep it updated all the time, this package makes it simple. Also, it assists in managing location permissions and even provides functions to ask for them when needed.