Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XMLHttpRequest error with Flutter-Web using Google Places API (firebase hosting)

Inside my Flutter-Web application I'm trying to get address using flutter_google_places package. I'm trying simple code to get autocomplete address field (MyTextField is just a customized Text Field):

final addressField = MyTextField(
  controller: _addressController,
  labelText: 'Indirizzo',
  readOnly: true,
  onTap: () async {
    await PlacesAutocomplete.show(
      context: context,
      apiKey: kGoogleApiKey,
      mode: Mode.overlay,
      onError: (error){print('ERROR: $error');},
    );
  },
);

When I run the app and insert something to the field I don't get any result. But I get this error (captured from inspect console on hosting, and I get the same error locally also):

Access to XMLHttpRequest at 'https://maps.googleapis.com/maps/api/place/autocomplete/json?input=h&key=**MY-API-KEY**' from origin 'https://**MY-HOSTING**.firebaseapp.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

I read that it's a server-side issue and I tried to modify firebase.json like this:

{
  "hosting": {
    "public": "build/web",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ],
    "headers": [ {
      "source" : "**",
      "headers" : [ {
        "key" : "Access-Control-Allow-Origin",
        "value" : "*"
      } ]
    }]
  }
}

Deployed that but also got the same error.

Any tips about solving that (locally and on hosting) are appreciated.

like image 594
Feras Senjab Avatar asked Apr 12 '20 11:04

Feras Senjab


2 Answers

I found a solution that lets you keep using PlacesAutocomplete in this github comment: https://github.com/lejard-h/google_maps_webservice/issues/70#issuecomment-636919093

GoogleMapsPlaces(
  apiKey: 'YOUR_API_KEY',
  baseUrl: kIsWeb
      ? 'https://cors-anywhere.herokuapp.com/https://maps.googleapis.com/maps/api'
      : null,
);

This other StackOverflow answer talks about how the solution works a little more, as well as give more info about cors-anywhere (including how to host your own): https://stackoverflow.com/a/43881141/3001277

like image 80
mrmax99 Avatar answered Oct 22 '22 17:10

mrmax99


So as there was no answers I'm sharing what solved my situation, hope getting better answers from experts soon.

The Problem:

Google places API prevents CORS. So we can't make a request from client-side. And As the PlacesAutocomplete widget makes http request to the Google places API like this:

https://maps.googleapis.com/maps/api/place/autocomplete/json?input={queryString}&key={API-Key}

This client-side request will be prevented.

My Solution:

First I'm not using PlacesAutocomplete widget anymore.

I wrote a simple cloud function that takes Url as parameter then makes http request for the same Url and returns data. But this time the request will be from server-side so I can bypass cors. So Inside index.ts I wrote the following function:

const functions = require('firebase-functions');
const axios = require('axios');

exports.getDataFromUrl = functions.https.onCall(async (data, context) => {
  const url = data.url;
  try {
    const info = await axios.get(url);
    return info.data;
  } catch (error) {
    return (error);
  }
});

Then from client-side I wrote this function (in dart) which calls the cloud function for any url:

  Future httpRequestViaServer({url}) async {
    HttpsCallable callable = CloudFunctions.instance.getHttpsCallable(
      functionName: 'getDataFromUrl',
    );
    try {
      final HttpsCallableResult result = await callable.call(
        <String, dynamic>{
          'url': url,
        },
      );
      return (result.data);
    } on CloudFunctionsException catch (e) {
        print('caught firebase functions exception');
        print(e.code);
        print(e.message);
        print(e.details);
        return null;
    } catch (e) {
        print('caught generic exception');
        print(e);
        return null;
    }

Now I can call this function to get data for the API url (or any url) from anywhere in my project.

I ended building my own autoComplete widget which calls the API using this function.

Hope this answer helps someone facing similar problem, and looking forward for better answers from experts.

like image 39
Feras Senjab Avatar answered Oct 22 '22 17:10

Feras Senjab