Flutter with Google Maps and Google Place

Rajesh muthyala
16 min readJun 24, 2019

Get an API Key to use Google features

To use the Maps SDK for Android you must have an API key. The API key is a unique identifier that is used to authenticate requests associated with your project for usage and billing purposes.

Get the API key

You must have at least one API key associated with your project.

To get an API key:

  1. Go to the Google Cloud Platform Console.
fig1.1 API screen for google
  1. From the Project drop-down menu, select or create the project for which you want to add an API key.
  2. From the
Navigation menu, select APIs & Services > Credentials.

4. On the Credentials page, click Create credentials > API key.
The API key created dialog displays your newly created API key (an encrypted string).

4. Close.
The new API key is listed on the Credentials page under API keys.
(Remember to restrict the API key before using it in production.

On the Restrict and rename API key page, set the restrictions:

  • Application restrictions
  • Select Android apps and follow the instructions.
  • Click + Add package name and fingerprint.
  • Enter your package name and SHA-1 certificate fingerprint. For example:
  • com.example.android.mapexample
  • BB:0D:AC:74:D3:21:E1:43:67:71:9B:62:91:AF:A1:66:6E:44:5D:75

For more information, see Where to get your app’s SHA-1 fingerprint).

  • API restrictions
  • Select Restrict key.
  • Click Select APIs and select Maps SDK for Android.
    (If there are other enabled APIs you want to use with this key, select them as well.)
  • Click SAVE.

please refer the link given below for complete google API documentation

Adding the API key to Flutter App

The first step is to add the Google Maps Flutter plugin as a dependency in the pubspec.yaml file. The package is available as google_maps_flutter on pub.dartlang.org.

dependencies:

google_maps_flutter: ^0.4.0

Once you do that, you need to run flutter packages get.

The next step is getting an API key for both Android and iOS. For Android, follow the instructions at Maps SDK for Android — Get API Key. Once you have your API key, add it to your Flutter app in the application manifest (android/app/src/main/AndroidManifest.xml), as follows:

<manifest … <application … <meta-data android:name=”com.google.android.geo.API_KEY” android:value=” YOUR ANDROID API KEY HERE”/>

For iOS, follow the instructions at Maps SDK for iOS — Get API Key. Once you have this key, add it to your Flutter app in the application delegate (ios/Runner/AppDelegate.m):

#include “AppDelegate.h”
#include “GeneratedPluginRegistrant.h”
// Add the GoogleMaps import.
#import “GoogleMaps/GoogleMaps.h”

@implementation AppDelegate

  • (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Add the following line with your API key.
    [GMSServices provideAPIKey:@”YOUR IOS API KEY HERE”];
    [GeneratedPluginRegistrant registerWithRegistry:self];
    return [super application:application didFinishLaunchingWithOptions:launchOptions];
    }
    @end
  • On iOS, you also need to add a setting to the app’s Info.plist file (ios/Runner/Info.plist).
    These entry forces Flutter on iOS into a single threaded mode,
    which is required for the platform view embedding to work.
    This technical restriction is being worked on and will be lifted before Google Maps moves out of Developer Preview.
  • Add a boolean property with the key io.flutter.embedded_views_preview and the value true:
  • <key>io.flutter.embedded_views_preview</key><true/>

Top Google API’s for Location:-

1. google_maps_flutter 0.5.19

2. geolocator 5.0.1

3. flutter_google_places 0.2.3

Google_maps_flutter 0.5.19

Setup

The first step is to add the Google Maps Flutter plugin as a dependency in the pubspec.yaml file. The package is available as google_maps_flutter on pub.dartlang.org.

Use this package as a library

1. Depend on it

Add this to your package’s pubspec.yaml file:

dependencies:

google_maps_flutter: ^0.5.19

  • README.md
  • CHANGELOG.md
  • Example
  • Installing
  • Versions
  • 99

Use this package as a library

1. Depend on it

Add this to your package’s pubspec.yaml file:

dependencies:
google_maps_flutter: ^0.5.19

2. Install it

You can install packages from the command line:

with Flutter:

flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:

import ‘package:google_maps_flutter/google_maps_flutter.dart’;

Adding a Google map widget

Now you are ready to add a google map widget! Run flutter clean to make sure the API key changes are picked up on the next build. Then, add a google map widget that covers the entire screen:

import ‘dart:async’;

import ‘package:flutter/material.dart’;
import ‘package:google_maps_flutter/google_maps_flutter.dart’;

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
Completer<GoogleMapController> _controller = Completer();

static const LatLng _center = const LatLng(45.521563, -122.677433);

void _onMapCreated(GoogleMapController controller) {
_controller.complete(controller);
}

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(‘Maps Sample App’),
backgroundColor: Colors.green[700],
),
body: GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: CameraPosition(
target: _center,
zoom: 11.0,
),
),
),
);
}
}

Important bits:

  • onMapCreated: method that is called on map creation and takes a MapController as a parameter.
  • initialCameraPosition: required parameter that sets the starting camera position. Camera position describes which part of the world you want the map to point at.
  • mapController: manages camera function (position, animation, zoom). This pattern is similar to other controllers available in Flutter, for example TextEditingController.

If you run your app at this point, it should look like this:-

Fig1.1 integrated map inside flutter

What can you do with a Google Map?

So now you have Google Maps in your app, but you probably want to do something more interesting. What about putting Flutter widgets on top of the map, changing the map’s appearance, or adding place markers to the map? You can do it all!

Add a widget on top of the map

It’s important to remember that the GoogleMap widget is just a Flutter widget, meaning you can treat it like any other widget. This includes placing another widget on top of it. By placing the GoogleMap widget inside of a Stack widget, you can layer other Flutter widgets on top of the map widget:

body: Stack(
children: <Widget>[
GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: CameraPosition(
target: _center,
zoom: 11.0,
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Align(
alignment: Alignment.topRight,
child: FloatingActionButton(
onPressed: () => print(‘button pressed’),
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Colors.green,
child: const Icon(Icons.map, size: 36.0),
),
),
),
],
),

Now, add a method that will modify the value of _currentMapType inside of a setState() call. This will update the appearance of the map to match the new value of _currentMapType.

void _onMapTypeButtonPressed() {
setState(() {
_currentMapType = _currentMapType == MapType.normal
? MapType.satellite
: MapType.normal;
});
}

Add a marker

How about creating another button that, when pressed, adds place markers to the map. Following the same pattern as before, add a button to the stack. Place another FloatingActionButton inside the Align widget from the build method. You’ll need to wrap both FloatingActionButtons in a Column widget:

FloatingActionButton(
onPressed: _onAddMarkerButtonPressed,
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Colors.green,
child: const Icon(Icons.add_location, size: 36.0),
),

To implement the _onAddMarkerButtonPressed method, we need to do a couple things. First, create a variable called _markers to store the map’s markers. Set this as the markers property of the GoogleMap widget.

final Set<Marker> _markers = {};

@override

Widget build(BuildContext context) {

return MaterialApp(

… GoogleMap(

markers: _markers,

)

);

}

To add a marker in the center of the map, we need to track the map’s current camera position. Add the following code to do that:

LatLng _lastMapPosition = _center;

void _onCameraMove(CameraPosition position) {
_lastMapPosition = position.target;
}

@override
Widget build(BuildContext context) {
return MaterialApp(

GoogleMap(

onCameraMove: _onCameraMove,
),
);
}

Now, we can add a marker to the map by modifying the content of _markersinside of a setState() call.

void _onAddMarkerButtonPressed() {
setState(() {
_markers.add(Marker(
// This marker id can be anything that uniquely identifies each marker.
markerId: MarkerId(_lastMapPosition.toString()),
position: _lastMapPosition,
infoWindow: InfoWindow(
title: ‘Really cool place’,
snippet: ‘5 Star Rating’,
),
icon: BitmapDescriptor.defaultMarker,
));
});
}

fig 1.2 with markers

2. geolocator 5.0.1

Features #

  • Get the current location of the device;
  • Get the last known location;
  • Get continuous location updates;
  • Check if location services are enabled on the device;
  • Translate an address to geocoordinates and vice verse (a.k.a. Geocoding);
  • Calculate the distance (in meters) between two geocoordinates;
  • Check the availability of Google Play services (on Android only).

Usage #

To use this plugin, add geolocator as a dependency in your pubspec.yaml file. For example:

dependencies:
geolocator: '^5.0.0'

API #

Geolocation #

To query the current location of the device simply make a call to the getCurrentPositionmethod:

import 'package:geolocator/geolocator.dart';Position position = await Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high);

To query the last known location retrieved stored on the device you can use them getLastKnownPosition method (note that this can result in a null value when no location details are available):

import 'package:geolocator/geolocator.dart';Position position = await Geolocator().getLastKnownPosition(desiredAccuracy: LocationAccuracy.high);

To listen for location changes you can subscribe to the onPositionChanged stream. Supply an instance of the LocationOptions class to configure the desired accuracy and the minimum distance change (in meters) before updates are sent to the application.

import 'package:geolocator/geolocator.dart';var geolocator = Geolocator();
var locationOptions = LocationOptions(accuracy: LocationAccuracy.high, distanceFilter: 10);
StreamSubscription<Position> positionStream = geolocator.getPositionStream(locationOptions).listen(
(Position position) {
print(position == null ? 'Unknown' : position.latitude.toString() + ', ' + position.longitude.toString());
});

To check if location services are enabled you can call the checkGeolocationPermissionStatusmethod. This method returns a value of the GeolocationStatus enum indicating the availability of the location services on the device. Optionally you can specify if you want to test for GeolocationPermission.locationAlways or GeolocationPermission.locationWhenInUse (by default GeolocationPermission.location is used, which checks for either one of the previously mentioned permissions). Example usage:

import 'package:geolocator/geolocator.dart';GeolocationStatus geolocationStatus  = await Geolocator().checkGeolocationPermissionStatus();

By default Geolocator will use FusedLocationProviderClient on Android when Google Play Services are available. It will fall back to LocationManager when it is not available. You can override the behavior by setting forceAndroidLocationManager.

import 'package:geolocator/geolocator.dart';Geolocator geolocator = Geolocator()..forceAndroidLocationManager = true;
GeolocationStatus geolocationStatus = await geolocator.checkGeolocationPermissionStatus();

Geocoding #

To translate an address into latitude and longitude coordinates you can use them placemarkFromAddress method:

import 'package:geolocator/geolocator.dart';List<Placemark> placemark = await Geolocator().placemarkFromAddress("Gronausestraat 710, Enschede");

If you want to translate latitude and longitude coordinates into an address you can use them placemarkFromCoordinates method:

import 'package:geolocator/geolocator.dart';List<Placemark> placemark = await Geolocator().placemarkFromCoordinates(52.2165157, 6.9437819);

Both the placemarkFromAddress and placemarkFromCoordinates accept an optional localeIdentifier parameter. This parameter can be used to enforce the resulting placemark to be formatted (and translated) according to the specified locale. The localeIdentifiershould be formatted using the syntax: [languageCode]_[countryCode]. Use the ISO 639-1 or ISO 639-2 standard for the language code and the 2 letter ISO 3166-1 standard for the country code. Some examples are:

Calculate distance #

To calculate the distance (in meters) between two geocoordinates you can use them distanceBetween method. The distanceBetween the method takes four parameters:

Parameter Type Description:-
startLatitude double Latitude of the start position
startLongitude double Longitude of the start position
endLatitude double Latitude of the destination position
endLongitude double Longitude of the destination position

import ‘package:geolocator/geolocator.dart’;

double distanceInMeters = await geolocator().distance between(52.2165157, 6.9437819, 52.3546274, 4.8285838);

Setup

The setup for this plugin is more or less the same as the location. If you plan to use coarse location as well, add

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

to AndroidManifest.xml .

First, we will do the same two tasks with this plugin, which is more or less the same:

Get location once

Use the Geolocator object instead of Location and instead of a Map<String,double> , we get back a Position object.

Geolocator geolocator = Geolocator();Position userLocation;

And we do this to take location:

currentLocation = await geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.best);

You can get all the details about the place as:

print(placemark[0].country);
print(placemark[0].position);
print(placemark[0].locality);
print(placemark[0].administrativeArea);
print(placemark[0].postalCode);
print(placemark[0].name);
print(placemark[0].subAdministratieArea);
print(placemark[0].isoCountryCode);
print(placemark[0].subLocality);
print(placemark[0].subThoroughfare);
print(placemark[0].thoroughfare);

We can also get the details of a place from the coordinates:

List<Placemark> placemark = await Geolocator().placemarkFromCoordinates(52.2165157, 6.9437819);

Getting the distance between coordinates

This plugin also allows us to calculate the distance between two coordinates as:

double distanceInMeters = await Geolocator().distanceBetween(52.2165157, 6.9437819, 52.3546274, 4.8285838);

flutter_google_places 0.2.3

Getting Started #

# pubspec.yamldependencies:
flutter:
sdk: flutter
flutter_google_places: <last-version>

Flutter has just achieved its latest milestone with the release of stable 1.0 version. It has many amazing features that provide a foundation for an amazing future ahead as it matures. Here are several of the great features:

  • PlatformView. Widget to render native platform views such as iOS UIView and Android View
  • MapView plugin. Widget to display Google Map View built on top of PlatformView API
  • Add to App: Tools to convert existing iOS and Android apps to Flutter in stages.
  • Flutter Desktop Embedding & Hummingbird: An initiative to bring Flutter to Web & Desktop.

To celebrate the release of Flutter 1.0, we are going to build an app to display and search nearby places inside Map View using Google Map and Google Places API.

Project Setup & Import Dependencies

First make sure you have upgraded your Flutter SDK to 1.0.0 using flutter upgrade command from your shell.

Create a new project using flutter create create command, open the project using Visual Studio Code or Android Studio.

Next, we will add dependencies inside the pubspec.yaml file. There are 3 dependencies we will use:

  • google_maps_flutter: Official Google Map View plugin from the Flutter team that uses PlatformView under the hood. It is still in Developer Preview status, so breaking changes and bugs can be expected. Link
  • flutter_google_places: Plugin from Hadrien Lejard that provides an interface to us to communicate with the Google Places Autocomplete API. Link
  • location: Plugin from Guillaume Bernos that uses Platform Channel API to access GPS on Android & iOS devices used to get user current location. Link
  • name: Google Places App
    description: Display & Search your nearby places
  • version: 1.0.0+1
  • environment:
    SDK: “>=2.0.0-dev.68.0 ❤.0.0”
  • dependencies:
    flutter:
    SDK: flutter
  • google_maps_flutter: ^0.0.3
    flutter_google_places: ^0.1.4
    location: ^1.4.1
  • dev_dependencies:
    flutter_test:
    SDK: flutter
  • flutter:
    uses-material-design: true
fig 1.3 after implementing Google places

In our main screen, we will ask user to grant GPS access and access their location, then we will ask the Google Places API to get nearby places to visit around. Make sure to paste your Google API key in the YOUR_API_KEYplaceholder.

  1. This widget consists of Scaffold with AppBar containing 2 actions item, refresh and search . The body uses Column with MapView as the first item, the second item wraps the ListView in an Expanded widget so it can flex all the remaining vertical space of the screen.
  2. When the MapView has been created, a method is invoked to assign the MapControlller as the widget property, then it calls the refresh method that will asks user their current location using location plugin, then using the location it will invoke getNearbyPlaces to retrieve the nearby places using flutter_google_places plugin, then assigned the places as the state of the widget.
  3. The places will be displayed in a Card inside ListView, each row shows the place name, address, and type. The inline MapView on the top displays all the pin marker of the places. When user taps on the row, the app will navigate to the place detail screen passing the placeId .

import ‘dart:async’;
import ‘package:google_maps_webservice/places.dart’;
import ‘package:flutter_google_places/flutter_google_places.dart’;
import ‘package:flutter/material.dart’;
import ‘package:google_maps_flutter/google_maps_flutter.dart’;
import ‘package:location/location.dart’ as LocationManager;
import ‘place_detail.dart’;

const kGoogleApiKey = “TOUR_API_KEY”;
GoogleMapsPlaces _places = GoogleMapsPlaces(apiKey: kGoogleApiKey);

void main() {
runApp(MaterialApp(
title: “PlaceZ”,
home: Home(),
debugShowCheckedModeBanner: false,
));
}

class Home extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return HomeState();
}
}

class HomeState extends State<Home> {
final homeScaffoldKey = GlobalKey<ScaffoldState>();
GoogleMapController mapController;
List<PlacesSearchResult> places = [];
bool isLoading = false;
String errorMessage;

@override
Widget build(BuildContext context) {
Widget expandedChild;
if (isLoading) {
expandedChild = Center(child: CircularProgressIndicator(value: null));
} else if (errorMessage != null) {
expandedChild = Center(
child: Text(errorMessage),
);
} else {
expandedChild = buildPlacesList();
}

return Scaffold(
key: homeScaffoldKey,
appBar: AppBar(
title: const Text(“PlaceZ”),
actions: <Widget>[
isLoading
? IconButton(
icon: Icon(Icons.timer),
onPressed: () {},
)
: IconButton(
icon: Icon(Icons.refresh),
onPressed: () {
refresh();
},
),
IconButton(
icon: Icon(Icons.search),
onPressed: () {
_handlePressButton();
},
),
],
),
body: Column(
children: <Widget>[
Container(
child: SizedBox(
height: 200.0,
child: GoogleMap(
onMapCreated: _onMapCreated,
options: GoogleMapOptions(
myLocationEnabled: true,
cameraPosition:
const CameraPosition(target: LatLng(0.0, 0.0))))),
),
Expanded(child: expandedChild)
],
));
}

void refresh() async {
final center = await getUserLocation();

mapController.animateCamera(CameraUpdate.newCameraPosition(CameraPosition(
target: center == null ? LatLng(0, 0) : center, zoom: 15.0)));
getNearbyPlaces(center);
}

void _onMapCreated(GoogleMapController controller) async {
mapController = controller;
refresh();
}

Future<LatLng> getUserLocation() async {
var currentLocation = <String, double>{};
final location = LocationManager.Location();
try {
currentLocation = await location.getLocation();
final lat = currentLocation[“latitude”];
final lng = currentLocation[“longitude”];
final center = LatLng(lat, lng);
return center;
} on Exception {
currentLocation = null;
return null;
}
}

void getNearbyPlaces(LatLng center) async {
setState(() {
this.isLoading = true;
this.errorMessage = null;
});

final location = Location(center.latitude, center.longitude);
final result = await _places.searchNearbyWithRadius(location, 2500);
setState(() {
this.isLoading = false;
if (result.status == “OK”) {
this.places = result.results;
result.results.forEach((f) {
final markerOptions = MarkerOptions(
position:
LatLng(f.geometry.location.lat, f.geometry.location.lng),
infoWindowText: InfoWindowText(“${f.name}”, “${f.types?.first}”));
mapController.addMarker(markerOptions);
});
} else {
this.errorMessage = result.errorMessage;
}
});
}

void onError(PlacesAutocompleteResponse response) {
homeScaffoldKey.currentState.showSnackBar(
SnackBar(content: Text(response.errorMessage)),
);
}

Future<void> _handlePressButton() async {
try {
final center = await getUserLocation();
Prediction p = await PlacesAutocomplete.show(
context: context,
strictbounds: center == null ? false : true,
apiKey: kGoogleApiKey,
onError: onError,
mode: Mode.fullscreen,
language: “en”,
location: center == null
? null
: Location(center.latitude, center.longitude),
radius: center == null ? null : 10000);

showDetailPlace(p.placeId);
} catch (e) {
return;
}
}

Future<Null> showDetailPlace(String placeId) async {
if (placeId != null) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PlaceDetailWidget(placeId)),
);
}
}

ListView buildPlacesList() {
final placesWidget = places.map((f) {
List<Widget> list = [
Padding(
padding: EdgeInsets.only(bottom: 4.0),
child: Text(
f.name,
style: Theme.of(context).textTheme.subtitle,
),
)
];
if (f.formattedAddress != null) {
list.add(Padding(
padding: EdgeInsets.only(bottom: 2.0),
child: Text(
f.formattedAddress,
style: Theme.of(context).textTheme.subtitle,
),
));
}

if (f.vicinity != null) {
list.add(Padding(
padding: EdgeInsets.only(bottom: 2.0),
child: Text(
f.vicinity,
style: Theme.of(context).textTheme.body1,
),
));
}

if (f.types?.first != null) {
list.add(Padding(
padding: EdgeInsets.only(bottom: 2.0),
child: Text(
f.types.first,
style: Theme.of(context).textTheme.caption,
),
));
}

return Padding(
padding: EdgeInsets.only(top: 4.0, bottom: 4.0, left: 8.0, right: 8.0),
child: Card(
child: InkWell(
onTap: () {
showDetailPlace(f.placeId);
},
highlightColor: Colors.lightBlueAccent,
splashColor: Colors.red,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: list,
),
),
),
),
);
}).toList();

return ListView(shrinkWrap: true, children: placesWidget);
}
}

fig1.5 place details screen

This screen displays the detail of the Places when user taps on the row of the main list screen with a MapView and the pin marker. Make sure to paste your Google API key in the YOUR_API_KEY placeholder

  1. The widget consists of Scaffold , with AppBar that displays the name of the place inside the Title . The main body used the approach from the main widget that uses Column to stack widget vertically, MapView and ListView .
  2. ListView displays the places information such as the name, address, phone number for each of its item . It also displays the photos of the place in a horizontal ListView that acts like a carousel in the first item.
  3. When the widget initializes, the fetchPlaceDetail method is invoked to retrieve the detail of the place using the placeId from the Google Places API. It will then assigned the details as the state of the widget.

import ‘package:google_maps_webservice/places.dart’;
import ‘package:flutter/material.dart’;
import ‘package:google_maps_flutter/google_maps_flutter.dart’;

const kGoogleApiKey = “YOUR_API_KEY”;
GoogleMapsPlaces _places = GoogleMapsPlaces(apiKey: kGoogleApiKey);

class PlaceDetailWidget extends StatefulWidget {
String placeId;

PlaceDetailWidget(String placeId) {
this.placeId = placeId;
}

@override
State<StatefulWidget> createState() {
return PlaceDetailState();
}
}

class PlaceDetailState extends State<PlaceDetailWidget> {
GoogleMapController mapController;
PlacesDetailsResponse place;
bool isLoading = false;
String errorLoading;

@override
void initState() {
fetchPlaceDetail();
super.initState();
}

@override
Widget build(BuildContext context) {
Widget bodyChild;
String title;
if (isLoading) {
title = “Loading”;
bodyChild = Center(
child: CircularProgressIndicator(
value: null,
),
);
} else if (errorLoading != null) {
title = “”;
bodyChild = Center(
child: Text(errorLoading),
);
} else {
final placeDetail = place.result;
final location = place.result.geometry.location;
final lat = location.lat;
final lng = location.lng;
final center = LatLng(lat, lng);

title = placeDetail.name;
bodyChild = Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
child: SizedBox(
height: 200.0,
child: GoogleMap(
onMapCreated: _onMapCreated,
options: GoogleMapOptions(
myLocationEnabled: true,
cameraPosition: CameraPosition(target: center, zoom: 15.0)),
),
)),
Expanded(
child: buildPlaceDetailList(placeDetail),
)
],
);
}

return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: bodyChild);
}

void fetchPlaceDetail() async {
setState(() {
this.isLoading = true;
this.errorLoading = null;
});

PlacesDetailsResponse place =
await _places.getDetailsByPlaceId(widget.placeId);

if (mounted) {
setState(() {
this.isLoading = false;
if (place.status == “OK”) {
this.place = place;
} else {
this.errorLoading = place.errorMessage;
}
});
}
}

void _onMapCreated(GoogleMapController controller) {
mapController = controller;
final placeDetail = place.result;
final location = place.result.geometry.location;
final lat = location.lat;
final lng = location.lng;
final center = LatLng(lat, lng);
var markerOptions = MarkerOptions(
position: center,
infoWindowText: InfoWindowText(
“${placeDetail.name}”, “${placeDetail.formattedAddress}”));
mapController.addMarker(markerOptions);
mapController.animateCamera(CameraUpdate.newCameraPosition(
CameraPosition(target: center, zoom: 15.0)));
}

String buildPhotoURL(String photoReference) {
return “https://maps.googleapis.com/maps/api/place/photo?maxwidth=400&photoreference=${photoReference}&key=${kGoogleApiKey}";
}

ListView buildPlaceDetailList(PlaceDetails placeDetail) {
List<Widget> list = [];
if (placeDetail.photos != null) {
final photos = placeDetail.photos;
list.add(SizedBox(
height: 100.0,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: photos.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(right: 1.0),
child: SizedBox(
height: 100,
child: Image.network(
buildPhotoURL(photos[index].photoReference)),
));
})));
}

list.add(
Padding(
padding:
EdgeInsets.only(top: 4.0, left: 8.0, right: 8.0, bottom: 4.0),
child: Text(
placeDetail.name,
style: Theme.of(context).textTheme.subtitle,
)),
);

if (placeDetail.formattedAddress != null) {
list.add(
Padding(
padding:
EdgeInsets.only(top: 4.0, left: 8.0, right: 8.0, bottom: 4.0),
child: Text(
placeDetail.formattedAddress,
style: Theme.of(context).textTheme.body1,
)),
);
}

if (placeDetail.types?.first != null) {
list.add(
Padding(
padding:
EdgeInsets.only(top: 4.0, left: 8.0, right: 8.0, bottom: 0.0),
child: Text(
placeDetail.types.first.toUpperCase(),
style: Theme.of(context).textTheme.caption,
)),
);
}

if (placeDetail.formattedPhoneNumber != null) {
list.add(
Padding(
padding:
EdgeInsets.only(top: 4.0, left: 8.0, right: 8.0, bottom: 4.0),
child: Text(
placeDetail.formattedPhoneNumber,
style: Theme.of(context).textTheme.button,
)),
);
}

if (placeDetail.openingHours != null) {
final openingHour = placeDetail.openingHours;
var text = ‘’;
if (openingHour.openNow) {
text = ‘Opening Now’;
} else {
text = ‘Closed’;
}
list.add(
Padding(
padding:
EdgeInsets.only(top: 0.0, left: 8.0, right: 8.0, bottom: 4.0),
child: Text(
text,
style: Theme.of(context).textTheme.caption,
)),
);
}

if (placeDetail.website != null) {
list.add(
Padding(
padding:
EdgeInsets.only(top: 0.0, left: 8.0, right: 8.0, bottom: 4.0),
child: Text(
placeDetail.website,
style: Theme.of(context).textTheme.caption,
)),
);
}

if (placeDetail.rating != null) {
list.add(
Padding(
padding:
EdgeInsets.only(top: 0.0, left: 8.0, right: 8.0, bottom: 4.0),
child: Text(
“Rating: ${placeDetail.rating}”,
style: Theme.of(context).textTheme.caption,
)),
);
}

return ListView(
shrinkWrap: true,
children: list,
);
}
}

above code for search places implementation

fig1.5 implementation for search place implementation

When user taps on the search ActionBar un the main list AppBar. We will display a search bar where user can enter the name of the place they want to search. Then it will provide Autocomplete suggestions using Google Places autocomplete API for user to choose using a dropdown.

When they tap on the place, we just navigate to the place detail widget passing the placeId just like the nearby places item in the main widgetListView.

--

--