flutter_google_places icon indicating copy to clipboard operation
flutter_google_places copied to clipboard

Flutter Chrome Web

Open emailsubjekt opened this issue 5 years ago • 19 comments

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 ════════════════════════════════════════════════════════════════════════════════════════════════════

emailsubjekt avatar Dec 20 '19 15:12 emailsubjekt

Can you try again with the 0.2.4 version?

KevMorelli avatar Feb 14 '20 23:02 KevMorelli

@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

sushchevskiy avatar Feb 26 '20 18:02 sushchevskiy

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/

KevMorelli avatar Mar 05 '20 23:03 KevMorelli

Any updates on this? Also experiencing blocked requests when not using CORS plugin.

justinenerio avatar Apr 02 '20 13:04 justinenerio

@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.

amansinghal avatar Apr 02 '20 17:04 amansinghal

me too - wish i could use this with Flutter Web

joe-getcouragenow avatar Apr 28 '20 14:04 joe-getcouragenow

Any updates on this?, I'm also experiencing the same issue...

mmucito avatar May 13 '20 22:05 mmucito

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.

KevMorelli avatar May 13 '20 23:05 KevMorelli

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.

mmucito avatar May 14 '20 03:05 mmucito

I think it is better to setup your own proxy for security reason.

jackyq2015 avatar May 14 '20 15:05 jackyq2015

Also fix the issue by installing https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf?hl=es

thx to @KevMorelli

kiiiiwiiii avatar Jun 09 '20 00:06 kiiiiwiiii

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?

mustafabhatkar avatar Jun 30 '20 18:06 mustafabhatkar

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" );

mustafabhatkar avatar Jul 01 '20 08:07 mustafabhatkar

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.

MattyBoy4444 avatar Oct 13 '20 23:10 MattyBoy4444

I created a utility class using Google place rest APIs - https://arkapp.medium.com/flutter-and-google-map-for-beginners-part-1-fdba4ab22148

abdulrehmank7 avatar Aug 26 '21 07:08 abdulrehmank7

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."

suhmeanhuck avatar Jun 08 '22 00:06 suhmeanhuck

What is the solution for production ? or permanent solution?

urvashi-k-7span avatar Jun 27 '22 06:06 urvashi-k-7span

Any solution?

Sunsiha avatar Sep 16 '22 11:09 Sunsiha

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;
}

sleewok avatar Jan 19 '23 17:01 sleewok