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

7

12

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.

Tacy answered 12/4, 2020 at 11:42 Comment(0)
T
6

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.

Tacy answered 16/4, 2020 at 11:17 Comment(3)
Im still getting this error, can you help? Access to fetch at 'us-central1-otostopbydyog.cloudfunctions.net/getDataFromUrl' from origin 'localhost:62259' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.Cyte
The Method has sligttly changed by now i added my answer belowVladamar
hi could help me with elaboration of above code and how to integrate cloud function within flutter web and where exactly to put index.ts file so it can be called from somewhere?Guideline
S
4

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://mcmap.net/q/18437/-no-39-access-control-allow-origin-39-header-is-present-on-the-requested-resource-when-trying-to-get-data-from-a-rest-api

Staal answered 18/9, 2020 at 16:6 Comment(3)
OMG thank you. I didn't knew about cors-anywhere or about herokuapp in any way. It's increadible that i can host node projects 24/7 for free, i could even make a free api there :oMishandle
tried this, stuck in this issue now: github.com/lejard-h/google_maps_webservice/issues/92 any solution?Carycaryatid
this seams to be dangerous, they you have your keyPlot
C
4

Call Google Places API from Flutter Web.

Add Headers as below, to your api-client.

{
 "x-requested-with" : "XMLHttpRequest"
}

Now, append below URL with your Google Places API.

https://proxy.cors.sh/

Thus, call your API as

https://proxy.cors.sh/<Google Places API>
Churchwell answered 29/12, 2022 at 14:12 Comment(0)
P
1

This API is not meant for the web. To take advantage of Place Autocomplete on Flutter web, it is necessary to use a library that implements the interop with javascript like this: https://pub.dev/packages/flutter_google_places_sdk

Pignus answered 13/4, 2022 at 14:18 Comment(0)
V
1

I Reference to the answer of Feras Senjab:

The MEthods should have slightly changed by now in order to still work .

For the index.js no changes

// Forwards a request call to get Google Places AUtocomplete Running
exports.getGooglePlacesAutocomplete = functions.https.onCall(async (data, context) => {
  const url = data.url;
  try {
    console.log(url);
    const info = await axios.get(url);
    console.log(info);
    return info.data;
  } catch (error) {
    return (error);
  }
});

For the CLient side, this is the update

import 'package:cloud_functions/cloud_functions.dart';

Future googlePlacesAutocompleteRequestViaServer({url}) async {
  HttpsCallable callable =
      FirebaseFunctions.instance.httpsCallable('getGooglePlacesAutocomplete');
  try {
    final HttpsCallableResult result = await callable.call(
      <String, dynamic>{
        'url': url,
      },
    );
    return (result.data);
  } on FirebaseFunctionsException 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;
  }
}
Vladamar answered 26/6, 2022 at 22:3 Comment(0)
F
0

if you want to test this locally you can use the "Allow CORS: Access-Control-Allow-Origin" for Chrome to disable it

Function answered 15/4, 2020 at 11:15 Comment(1)
Thanks. but it's important to have the same behavior of users while testing.Tacy
C
0

Based on this answer,

Use https://app.cors.bridged.cc/ instead of https://cors-anywhere.herokuapp.com/

Follow this article for more details: https://blog.grida.co/cors-anywhere-for-everyone-free-reliable-cors-proxy-service-73507192714e

Chimere answered 19/9, 2021 at 7:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.