Is there a way to implement Google places autocomplete in Flutter?

12,867

I have same issue with you. actually google autocomplete can be done by basic HTTP API request. I have create some article on it called "Your Own - Flutter Google Places Autocomplete.

I have done this by basic async HTTP Request. First you must set a controller and put listener at initialization for your TextField

TextField(
  controller: _controller,
  decoration: InputDecoration(
    hintText: "Seek your location here",
    focusColor: Colors.white,
    floatingLabelBehavior: FloatingLabelBehavior.never,
    prefixIcon: Icon(Icons.map),
    suffixIcon: IconButton(
      icon: Icon(Icons.cancel),
    ),
  ),
),

The _controller should be like this:

var _controller = TextEditingController();

And you must register a listener at your initState()

@override
void initState() {
  super.initState();
  _controller.addListener(() {
    _onChanged();
  });
}

_onChange function can you define as an asynchronous function to call the standard API request for Google Places Autocomplete that provided by Google

https://maps.googleapis.com/maps/api/place/autocomplete/json?

please refer to this link for better understanding about the Google Place Autocomplete.

Don't forget to add session token so your billing will secure :)

sessiontoken — A random string which identifies an autocomplete session for billing purposes. If this parameter is omitted from an autocomplete request, the request is billed independently. See the pricing sheet for details.

And for the last, here is the complete code for _onChange function:

_onChanged() {
  if (_sessionToken == null) {
    setState(() {
      _sessionToken = uuid.v4();
    });
  }
  getSuggestion(_controller.text);
}

void getSuggestion(String input) async {
  String kPLACES_API_KEY = "CHANGE THIS WITH YOUR GOOGLE API KEY";
  String type = '(regions)';
  String baseURL =
      'https://maps.googleapis.com/maps/api/place/autocomplete/json';
  String request =
      '$baseURL?input=$input&key=$kPLACES_API_KEY&sessiontoken=$_sessionToken';
  var response = await http.get(request);
  if (response.statusCode == 200) {
    setState(() {
      _placeList = json.decode(response.body)['predictions'];
    });
  } else {
    throw Exception('Failed to load predictions');
  }
}

your must declare List<dynamic> _placeList = []; at initial of Widget.

and the bonus, how we displaying the List of suggestion is like this:

ListView.builder(
  physics: NeverScrollableScrollPhysics(),
  shrinkWrap: true,
  itemCount: _placeList.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(_placeList[index]["description"]),
    );
  },
),

put this under your TextField widget.

for grab working code please refer this github link below (add the package by your own!):

https://github.com/cmailoa/google_places_autocomplete

for further explanation please check the link bellow.

https://link.medium.com/7fq2hr2WZ9

Share:
12,867
stefanodecillis
Author by

stefanodecillis

Updated on June 04, 2022

Comments

  • stefanodecillis
    stefanodecillis almost 2 years

    I'm using Flutter for the first time in order to build a mobile application and I found no way to implement autocomplete google places in a textEdit component. I aim to obtain a kind of list view while the user is typing the location, as we can do in Android/iOS native implementation and react/ionic.

    What I did obtain is a TextEdit component that searches possible locations and take the first one at the end. Here a snippet of my code

    class HomeFragmentState extends State<HomeFragment>{
    
      GoogleMapController mapController;
      String searchAddr;
      List<Marker> markersList = [];
      Marker finalMarker;
      LatLng _center = LatLng(45.4654219,9.1859243);  // default location
    
      @override
      void initState() {
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Stack(
            children: <Widget>[
              GoogleMap(
                  onMapCreated: onMapCreated,
                  initialCameraPosition: CameraPosition(
                  target: _center,
                  zoom: 11.0,
                ),
                markers: Set.from(markersList),
              ),
              Positioned(
                top: 10.0,
                right: 15.0,
                left: 15.0,
                child: Container(
                      height: 50.0,
                      width: double.infinity,
                      decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(10.0),
                          color: Colors.white
                      ),
                      child: Column(
                        children: <Widget>[
                          TextField(
                            decoration: InputDecoration(
                                hintText: 'Enter the place',
                                border: InputBorder.none,
                                contentPadding: EdgeInsets.only(left: 15.0,top:15.0),
                                suffixIcon: IconButton(
                                  icon: Icon(Icons.search),
                                  onPressed: searchAndNavigate,
                                  iconSize: 30.0,
                                )
                            ),
                            onChanged: (val) {
                              setState(() {
                                searchAddr = val;
                              });
                            },
                            onEditingComplete: searchAndNavigate,
                          ),
                        ],
                      )
                    ),
              ),
            ],
          ),
        );
      }
    
      void searchAndNavigate(){
        Geolocator().placemarkFromAddress(searchAddr).then((result) => {navigateTo(result:result)});
      }
    
    
      navigateTo({result, init: false}){
        var zoom = 11.0;
        var target;
        if(init){
          zoom = 12.0; //difference between searching and initializing
          target = LatLng(result.latitude, result.longitude);
        } else {
          target = LatLng(result[0].position.latitude,result[0].position.longitude);
        }
        mapController.animateCamera(CameraUpdate.newCameraPosition(CameraPosition(
            target:target,
            zoom: zoom
        )));
      }
    
      void onMapCreated(controller){
        setState(() {
          mapController = controller;
          Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high).then((position) => {
            navigateTo(result: position,init: true)
          });
        });
      }
    
    
    }
    

    As we can see from Geolocator we can retrieve some location based on the address we're typing (hence we can take the results from there) but I didn't find a smart way to implement the part of textedit suggestions

  • stefanodecillis
    stefanodecillis about 4 years
    I already found these packages but they create only a searchbar (as a new screen). I was expecting something like a listview below the textEdit component as we see in other framework but I think we don't have such a tool at the moment in flutter
  • CH Andre Mailoa
    CH Andre Mailoa over 3 years
    Thanks for notify me about this. I have modified my answers.. Just share what i know
  • chichi
    chichi over 3 years
    Thank you so much! Btw, how can I get the session token?!
  • CH Andre Mailoa
    CH Andre Mailoa over 3 years
    @GIHYUNNAM As i know you just create a random character that comply to UUID standard for that. In the answers was written _sessionToken = uuid.v4(); to generate uuid for your session.
  • tapizquent
    tapizquent over 3 years
    Since the API gets called on each letter typed, won't this eat up the autocomplete api request limit? Or rather get VERY expensive as the app scales?
  • CH Andre Mailoa
    CH Andre Mailoa about 3 years
    @JoseTapizquent yes you are right, so google provide a session. This will charged us for every ended sessions, not for each typed letter. Except you didn't create the sessions, then you will be charged very expensive :) As you noticed, at below query example from google docs, the end lines contain "sessiontoken" maps.googleapis.com/maps/api/place/autocomplete/…
  • abdul rehman
    abdul rehman over 2 years
    We can use stream builder and create bloc for places auto complete for easier use: arkapp.medium.com/…