Flutter with Google Maps and Google Place
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:
- Go to the Google Cloud Platform Console.
- From the Project drop-down menu, select or create the project for which you want to add an API key.
- From the
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:-
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 _markers
inside 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,
));
});
}
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 getCurrentPosition
method:
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 checkGeolocationPermissionStatus
method. 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 localeIdentifier
should 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 usesPlatformView
under the hood. It is still in Developer Preview status, so breaking changes and bugs can be expected. Linkflutter_google_places
: Plugin from Hadrien Lejard that provides an interface to us to communicate with the Google Places Autocomplete API. Linklocation
: 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
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_KEY
placeholder.
- This widget consists of
Scaffold
withAppBar
containing 2 actions item,refresh
andsearch
. Thebody
usesColumn
withMapView
as the first item, the second item wraps theListView
in anExpanded
widget so it can flex all the remaining vertical space of the screen. - When the
MapView
has been created, a method is invoked to assign theMapControlller
as the widget property, then it calls therefresh
method that will asks user their current location usinglocation
plugin, then using the location it will invokegetNearbyPlaces
to retrieve the nearby places usingflutter_google_places
plugin, then assigned the places as thestate
of the widget. - The places will be displayed in a
Card
insideListView
, each row shows the place name, address, and type. The inlineMapView
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 theplaceId
.
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);
}
}
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
- The widget consists of
Scaffold
, withAppBar
that displays the name of the place inside theTitle
. The mainbody
used the approach from the main widget that usesColumn
to stack widget vertically,MapView
andListView
. ListView
displays the places information such as the name, address, phone number for each of itsitem
. It also displays the photos of the place in a horizontalListView
that acts like a carousel in the first item.- When the widget initializes, the
fetchPlaceDetail
method is invoked to retrieve the detail of the place using theplaceId
from the Google Places API. It will then assigned the details as thestate
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
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.