
Posted on • Edited on
A quick guide: obtaining current location on Google Maps using Riverpod in Flutter
Goal: Get the current location and set a marker
Hi everyone! This article will look at how we get the current location of the user and set a marker as being a specific location on Flutter. This app has several features as shown below.
- Show Google Map by google_maps_flutter
- Obtain the current location
- Set the marker and shift for it after pushing the bottom right button
Get API key
At first, let's obtain your API key for Google Maps.
Fix AndroidManifest.xml
- android/app/src/main/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.googlemap.testgooglemap"> // Add this <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <application android:label="testgooglemap" android:icon="@mipmap/ic_launcher"> // Add this <meta-data android:name="com.google.android.geo.API_KEY" android:value="<YOUR API KEY>" /> ... </application></manifest>
Modify AppDelegate.swift
- ios/Runner/AppDelegate.swift
importUIKitimportFlutterimportGoogleMaps@UIApplicationMain@objcclassAppDelegate:FlutterAppDelegate{overridefuncapplication(_application:UIApplication,didFinishLaunchingWithOptionslaunchOptions:[UIApplication.LaunchOptionsKey:Any]?)->Bool{GMSServices.provideAPIKey("<YOUR API KEY>")GeneratedPluginRegistrant.register(with:self)returnsuper.application(application,didFinishLaunchingWithOptions:launchOptions)}}
Add a key in Info.plist
To show a dialog to allow the user gives the current location to app, put a pair of key and description intoInfo.plist
.
- ios/Runner/Info.plist
<dict><key>NSLocationWhenInUseUsageDescription</key><string>Yourlocationisrequiredforthisapp</string>...</dict>
Upgrade minSdkVersion in build.gradle
You have to make minSdkVersion going up to 20 from 16 for working out well this app on google_maps_flutter package. In my case, I also had to fix compileSdkVersion to 31.
google_maps_flutter | Flutter Package
android{compileSdkVersion31// up to 31 from 30...defaultConfig{// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).applicationId"com.googlemap.testgooglemap"minSdkVersion20// up to 20 from 16targetSdkVersion30versionCodeflutterVersionCode.toInteger()versionNameflutterVersionName}
pubspec.yaml
dependencies:flutter:sdk:fluttercupertino_icons:^1.0.2google_maps_flutter:^2.1.1geolocator:^8.0.0riverpod:^1.0.0flutter_hooks:^0.18.0hooks_riverpod:^1.0.0http:^0.13.4freezed_annotation:^1.0.0json_annotation:^4.3.0flutter_secure_storage:^5.0.1geocoding:^2.0.1dev_dependencies:flutter_test:sdk:flutterfreezed:^1.0.0build_runner:^2.1.5json_serializable:^6.0.1
main.dart
Let's modify your code in main.dart. Some errors would be shown on your text editor when pasting it, but don't have to be careful about that for now. We'll fix it up by adding other code.
- lib/main.dart
import'package:flutter/material.dart';import'package:hooks_riverpod/hooks_riverpod.dart';import'/models/controllers/location/location_controller.dart';import'package:flutter_hooks/flutter_hooks.dart';import'package:google_maps_flutter/google_maps_flutter.dart';import'dart:async';voidmain(){runApp(ProviderScope(child:MyApp(),));}classMyAppextendsStatelessWidget{// This widget is the root of your application.@overrideWidgetbuild(BuildContextcontext){returnMaterialApp(title:'Flutter Demo',theme:ThemeData(primarySwatch:Colors.blue,),home:constHomepage(),);}}classHomepageextendsHookConsumerWidget{constHomepage({Key?key}):super(key:key);@overrideWidgetbuild(BuildContextcontext,WidgetRefref){finallocationState=ref.watch(locationNotifierProvider);finallocationNotifier=ref.watch(locationNotifierProvider.notifier);useEffect((){Future.microtask(()async{ref.watch(locationNotifierProvider.notifier).getCurrentLocation();});return;},const[]);returnScaffold(body:locationState.isBusy?constCenter(child:CircularProgressIndicator()):GoogleMap(mapType:MapType.normal,myLocationButtonEnabled:true,myLocationEnabled:true,zoomControlsEnabled:false,initialCameraPosition:CameraPosition(target:locationState.currentLocation,zoom:14.4746,),markers:locationState.markers,onMapCreated:locationNotifier.onMapCreated,),floatingActionButton:FloatingActionButton(onPressed:()async{locationNotifier.getNewLocation();},child:constIcon(Icons.location_searching)));}}
Let's take a look at that. We can handle the state on Flutter by Riverpod. In this sample app,locationState
for reading state andlocationNotifier
for changing state are defined.
@overrideWidgetbuild(BuildContextcontext,WidgetRefref){finallocationState=ref.watch(locationNotifierProvider);finallocationNotifier=ref.watch(locationNotifierProvider.notifier);
useEffect
is useful to initialize something on every build. In here's sample, we call thegetCurrentLocatoin()
function defined in location_controller.dart for state management.
useEffect((){Future.microtask(()async{locationNotifier.getCurrentLocation();});return;},const[]);
When user presses the bottom right button,FloatingActionButton()
callgetNewLocation()
function that shifts the position of camera and sets a pin.
floatingActionButton:FloatingActionButton(onPressed:()async{locationNotifier.getNewLocation();},child:constIcon(Icons.location_searching)));
LocationController
As you can see, LocationController extends StateNotifier class.
- lib/models/controllers/location/location_controller.dart
import'package:google_maps_flutter/google_maps_flutter.dart';import'package:riverpod/riverpod.dart';import'package:hooks_riverpod/hooks_riverpod.dart';import'package:flutter/material.dart';import'/models/repositories/location/location_repository.dart';import'/models/controllers/location/location_state.dart';import'dart:async';finallocationNotifierProvider=StateNotifierProvider<LocationController,LocationState>((ref)=>LocationController(),);classLocationControllerextendsStateNotifier<LocationState>{LocationController():super(constLocationState());finalrepository=LocationRepository();finalCompleter<GoogleMapController>_mapController=Completer();voidonMapCreated(GoogleMapControllercontroller){_mapController.complete(controller);}Future<void>getCurrentLocation()async{state=state.copyWith(isBusy:true);try{finaldata=awaitrepository.getCurrentPosition();state=state.copyWith(isBusy:false,currentLocation:LatLng(data.latitude,data.longitude));}onExceptioncatch(e,s){debugPrint('login error:$e - stack:$s');state=state.copyWith(isBusy:false,errorMessage:e.toString());}}Future<void>getNewLocation()async{await_setNewLocation();await_setMaker();}Future<void>_setNewLocation()async{state=state.copyWith(newLocation:constLatLng(35.658034,139.701636));}Future<void>_setMaker()async{// Set markersfinalSet<Marker>_markers={};_markers.add(Marker(markerId:MarkerId(state.newLocation.toString()),position:state.newLocation,infoWindow:constInfoWindow(title:'Remember Here',snippet:'good place'),icon:BitmapDescriptor.defaultMarker));state=state.copyWith(markers:_markers);// Shift camera positionCameraPosition_kLake=CameraPosition(target:state.newLocation,zoom:14.4746);finalGoogleMapControllercontroller=await_mapController.future;controller.animateCamera(CameraUpdate.newCameraPosition(_kLake));}}
LocationController defines three functions to call from main.dart.
voidonMapCreated(GoogleMapControllercontroller){}Future<void>getCurrentLocation()async{}Future<void>getNewLocation()async{}
Freezed for controller
This sample app uses Freezed package, making the data immutable easily. This is used for LocationController.
- lib/models/controllers/location/location_state.dart
import'package:freezed_annotation/freezed_annotation.dart';import'package:flutter/foundation.dart';import'package:google_maps_flutter/google_maps_flutter.dart';part'location_state.freezed.dart';@freezedclassLocationStatewith_$LocationState{constfactoryLocationState({@Default(false)boolisBusy,@Default(LatLng(35.658034,139.701636))LatLngcurrentLocation,@Default(LatLng(35.658034,139.701636))LatLngnewLocation,@Default({})Set<Marker>markers,String?errorMessage,})=_LocationState;}
Run the following command with build_runner.
$flutter pub run build_runner build--delete-conflicting-outputs
Repository class for the error handling
We'll also set upgetCurrentPosition()
in LocationRepository class, the function to handle errors on the permission regarding the location.
- lib/models/repositories/location/location_repository.dart
import'package:geolocator/geolocator.dart';
classLocationRepository{
Future<Position>getCurrentPosition()async{
boolserviceEnabled;
LocationPermissionpermission;
<span>serviceEnabled</span> <span>=</span> <span>await</span> <span>Geolocator</span><span>.</span><span>isLocationServiceEnabled</span><span>();</span><span>if</span> <span>(</span><span>!</span><span>serviceEnabled</span><span>)</span> <span>{</span> <span>return</span> <span>Future</span><span>.</span><span>error</span><span>(</span><span>'Location services are disabled.'</span><span>);</span><span>}</span><span>permission</span> <span>=</span> <span>await</span> <span>Geolocator</span><span>.</span><span>checkPermission</span><span>();</span><span>if</span> <span>(</span><span>permission</span> <span>==</span> <span>LocationPermission</span><span>.</span><span>denied</span><span>)</span> <span>{</span> <span>permission</span> <span>=</span> <span>await</span> <span>Geolocator</span><span>.</span><span>requestPermission</span><span>();</span> <span>if</span> <span>(</span><span>permission</span> <span>==</span> <span>LocationPermission</span><span>.</span><span>denied</span><span>)</span> <span>{</span> <span>return</span> <span>Future</span><span>.</span><span>error</span><span>(</span><span>'Location permissions are denied'</span><span>);</span> <span>}</span><span>}</span><span>if</span> <span>(</span><span>permission</span> <span>==</span> <span>LocationPermission</span><span>.</span><span>deniedForever</span><span>)</span> <span>{</span> <span>return</span> <span>Future</span><span>.</span><span>error</span><span>(</span> <span>'Location permissions are permanently denied, we cannot request permissions.'</span><span>);</span><span>}</span><span>return</span> <span>await</span> <span>Geolocator</span><span>.</span><span>getCurrentPosition</span><span>(</span> <span>desiredAccuracy:</span> <span>LocationAccuracy</span><span>.</span><span>high</span><span>);</span>
}
}
References
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse