Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for A quick guide: obtaining current location on Google Maps using Riverpod in Flutter
uuta
uuta

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

Google Maps on Flutter

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.

  1. Show Google Map by google_maps_flutter
  2. Obtain the current location
  3. 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>
Enter fullscreen modeExit fullscreen mode

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)}}
Enter fullscreen modeExit fullscreen mode

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>
Enter fullscreen modeExit fullscreen mode

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}
Enter fullscreen modeExit fullscreen mode

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
Enter fullscreen modeExit fullscreen mode

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)));}}
Enter fullscreen modeExit fullscreen mode

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);
Enter fullscreen modeExit fullscreen mode

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[]);
Enter fullscreen modeExit fullscreen mode

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)));
Enter fullscreen modeExit fullscreen mode

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));}}
Enter fullscreen modeExit fullscreen mode

LocationController defines three functions to call from main.dart.

voidonMapCreated(GoogleMapControllercontroller){}Future<void>getCurrentLocation()async{}Future<void>getNewLocation()async{}
Enter fullscreen modeExit fullscreen mode

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;}
Enter fullscreen modeExit fullscreen mode

Run the following command with build_runner.

$flutter pub run build_runner build--delete-conflicting-outputs

Enter fullscreen modeExit fullscreen mode




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>
Enter fullscreen modeExit fullscreen mode

}
}

Enter fullscreen modeExit fullscreen mode




References

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

  • Joined

More fromuuta

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp