flutter_google_places
flutter_google_places copied to clipboard
Flutter Chrome Web
When I click on Search places in the default project from here - I'm using flutter_google_places: ^0.2.3 in pubspec ════════ Exception caught by widgets library ═══════════════════════════════════════════════════════ The following UnsupportedError was thrown building PlacesAutocompleteWidget(dirty, dependencies: [_InheritedTheme, _LocalizationsScope-[GlobalKey#c60ed]], state: _PlacesAutocompleteOverlayState#914fb): Unsupported operation: Platform._operatingSystem
When the exception was thrown, this was the stack: package:build_web_compilers/src/dev_compiler/C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/internal/js_dev_runtime/private/ddc_runtime/errors.dart 196:49 throw package:build_web_compilers/src/dev_compiler/C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/patch/io_patch.dart 241:5 _operatingSystem package:build_web_compilers/src/dev_compiler/C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/io/platform_impl.dart 62:40 get operatingSystem package:build_web_compilers/src/dev_compiler/C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/io/platform.dart 73:45 get _operatingSystem package:build_web_compilers/src/dev_compiler/C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/utils.dart 77:6 get ... ════════════════════════════════════════════════════════════════════════════════════════════════════
════════ (2) Exception caught by widgets library ═══════════════════════════════════════════════════ Failed assertion: boolean expression must not be null The relevant error-causing widget was: PlacesAutocompleteWidget org-dartlang-app:///packages/flutter_google_places/src/flutter_google_places.dart:447:43 ════════════════════════════════════════════════════════════════════════════════════════════════════
Can you try again with the 0.2.4 version?
@KevMorelli does it intended to work on web? Requests are failing because blocked by CORS. As I know google forces to use its native SDKs and doesn't allow api requests from frontend. Tried flutter_google_places: ^0.2.4
I will take a look at this, for the moment you can install the CORS plugin for Chrome or add this parameter to disable it. https://alfilatov.com/posts/run-chrome-without-cors/
Any updates on this? Also experiencing blocked requests when not using CORS plugin.
@KevMorelli Thanks for this great lib. But I am facing CORS problem for web version, can you please solve the CORS problem for web implementation or suggest any tweak to resolve the issue.
me too - wish i could use this with Flutter Web
Any updates on this?, I'm also experiencing the same issue...
Hey guys, for the moment this is not possible. see this Explanation
The main issue is that we are using the Google API for server-side use, they "fixed" this issue with a client-side JS library to use their API.
I just found a way to fix it...
you can use a proxy in order to avoid the CORS problem
example...
Prediction p = await PlacesAutocomplete.show(
context: context,
apiKey: kGoogleApiKey,
mode: Mode.overlay, // Mode.fullscreen
language: "es",
proxyBaseUrl: "https://cors-anywhere.herokuapp.com/https://maps.googleapis.com/maps/api",
components: [new Component(Component.country, country)]);
You can easily run your own proxy using code from https://github.com/Rob--W/cors-anywhere/. You can also easily deploy your own proxy to Heroku in literally just 2-3 minutes, with 5 commands:
git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master
After running those commands, you’ll end up with your own CORS Anywhere server running at, e.g., https://mmucito-3434343.herokuapp.com/. So then rather than prefixing your request URL with https://cors-anywhere.herokuapp.com, prefix it instead with the URL for your own instance; e.g., https://mmucito-3434343.herokuapp.com/https://maps.googleapis.com/maps/api.
I think it is better to setup your own proxy for security reason.
Also fix the issue by installing https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf?hl=es
thx to @KevMorelli
Thank you @mmucito for the solution! Now it is showing the search results but now I am getting XMLHttpRequest error after tapping on any result. this too has to do something with CORS right? How do I fix this?
Thank you @mmucito for the solution! Now it is showing the search results but now I am getting XMLHttpRequest error after tapping on any result. this too has to do something with CORS right? How do I fix this?
Fixed it!
I was calling GoogleMapsPlaces's getDetailsByPlaceId(prediction.placeId) method after getting the result from PlacesAutocomplete.show()
The getDetailsByPlaceId() method uses "https://maps.googleapis.com/maps/api" URL by default which was causing the XMLHttpRequest error. I fixed by setting base url to the one you suggested while initializing GoogleMapsPlaces object like this
GoogleMapsPlaces _places = GoogleMapsPlaces(apiKey: Constants.MAPS_API, baseUrl:"https://cors-anywhere.herokuapp.com/https://maps.googleapis.com/maps/api" );
Thank you @mmucito for the solution! Now it is showing the search results but now I am getting XMLHttpRequest error after tapping on any result. this too has to do something with CORS right? How do I fix this?
Fixed it! I was calling GoogleMapsPlaces's getDetailsByPlaceId(prediction.placeId) method after getting the result from PlacesAutocomplete.show() The getDetailsByPlaceId() method uses "https://maps.googleapis.com/maps/api" URL by default which was causing the XMLHttpRequest error. I fixed by setting base url to the one you suggested while initializing GoogleMapsPlaces object like this
GoogleMapsPlaces _places = GoogleMapsPlaces(apiKey: Constants.MAPS_API, baseUrl:"https://cors-anywhere.herokuapp.com/https://maps.googleapis.com/maps/api" );
Note this is a fix only for testing. Their servers are NOT for production and will limit the number of calls.
I created a utility class using Google place rest APIs - https://arkapp.medium.com/flutter-and-google-map-for-beginners-part-1-fdba4ab22148
I created a utility class using Google place rest APIs - https://arkapp.medium.com/flutter-and-google-map-for-beginners-part-1-fdba4ab22148
Hey @abdulrehmank7 I tried this implementation but still received the same "XMLHttpRequest error."
What is the solution for production ? or permanent solution?
Any solution?
Any solution?
I was able to get it working without using a proxy. I have a javascript file (app.js) in my root flutter web folder that my application makes calls to from Dart. Hopefully this will help give some direction...
To use I put the following in the GooglePlacesAutocompleteWeb.dart inside the doSearch function:
/// Make call to javascript web API
var res = await placesAutocompleteQuery(value); // json result from app.js
PlacesAutocompleteResponse? placesAutocompleteResponse = PlacesAutocompleteResponse.fromJson(json.decode(res));
js_placesAutocopmleteQuery.dart
/// HELPER FOR FLUTTER WEB
///
/// Allows us to setup a promise / future between javascript and flutter
@JS()
library app.js;
import 'package:js/js.dart';
import 'dart:js_util';
@JS()
external dynamic getPlaces(String query);
// Google places autocomplete
Future<dynamic> placesAutocompleteQuery(query) async {
var promise = getPlaces(query);
var result = await promiseToFuture(promise);
return result;
}
js_placesGeocodeQuery.dart
/// HELPER FOR FLUTTER WEB
///
/// Allows us to setup a promise / future between javascript and flutter
@JS()
library app.js;
import 'package:js/js.dart';
import 'dart:js_util';
@JS()
external dynamic getPlaces(String query);
// Google places autocomplete
Future<dynamic> placesAutocompleteQuery(query) async {
var promise = getPlaces(query);
var result = await promiseToFuture(promise);
return result;
}
App.js
var map;
var service;
var infowindow;
var placeResults;
const autoCompleteService = new google.maps.places.AutocompleteService();
var autocompleteResponse;
// GOOGLE PLACES API CALL
// https://developers.google.com/maps/documentation/javascript/reference/places-autocomplete-service?hl=en#AutocompleteService.getPlacePredictions
async function getPlaces(query) {
autocompleteResponse = await autoCompleteService.getPlacePredictions({
input: query,
componentRestrictions: { country: "us" },
fields: ["address_components", "geometry", "name"],
}, verifyStatusCallback);
autocompleteResponse = JSON.stringify(autocompleteResponse);
return autocompleteResponse;
}
// Check that we have a good connection
function verifyStatusCallback(result, status) {
if (status != google.maps.places.PlacesServiceStatus.OK) {
alert(status);
return;
}
}
// Lookup geocode data for the selected location
async function geocodePlace (placeId, status) {
const geocoder = new google.maps.Geocoder();
var geocodeResult; // map of data we want to return
// API CALL
await geocoder.geocode({ placeId: placeId }).then(({ results }) => {
console.log(results[0].formatted_address);
geocodeResult = {
address: results[0].formatted_address,
latitude: results[0].geometry.location.lat(),
longitude: results[0].geometry.location.lng(),
address_components: results[0].address_components,
};
})
.catch((e) => window.alert("Geocoder failed due to: " + e));
return JSON.stringify(geocodeResult);
};
function alertMessage(text) {
alert(text);
}
window.logger = (flutter_value) => {
console.log({ js_context: this, flutter_value });
}
googlePlacesSearchWeb.dart
import 'dart:convert';
import 'package:eagerbeaverpro/helper/js_placesGeocodeQuery.dart';
import 'package:eagerbeaverpro/widget/GooglePlacesAutocompleteWeb.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_maps_webservice/places.dart';
/// do the geocode lookup
Future<dynamic> geocodePlace(placeId) async {
var result = await placesGeocodePlace(placeId);
GeocodePlace data = GeocodePlace.fromJson(json.decode(result));
String selectedLat = data.latitude;
String selectedLng = data.longitude;
/// Assign the address
/// This is a shorter version from the p.description
/// Substring to remove ", USA"
String selectedAddress = data.address.substring(0, data.address.length - 5);
var addressComponents = data.addressComponents;
var formatted = {
'selectedAddress': selectedAddress,
'selectedLng': selectedLng,
'selectedLat': selectedLat,
'addressComponents': addressComponents,
};
return formatted;
}
Future<Prediction?> placesAutocompleteShow() async {
Prediction? p = await GooglePlacesAutocompleteWeb.show(
context: Get.context!,
// apiKey: Strings.GOOGLE_PLACES_API_KEY,
mode: ModeWeb.overlay, // Mode.fullscreen
language: "en",
offset: 0,
radius: 1000,
types: [],
decoration: InputDecoration(),
strictbounds: false,
// components: [Component(Component.country, "uk")]
components: [Component(Component.country, "us")]);
return p;
}