Implementing Place Autocomplete and Map Marking in Flutter with BLoC and Cubit
Greetings!🌟
In this article, I will try to explain how to implement Place AutoComplete and mark the selected place on the map using BLoC and Cubit.
First, let’s look at the AutoComplete structure. I will use the Google Places API for Place Autocomplete. Before we start, I should mention that we need an API key to use Google APIs. In this article, you need to enable Google Places API for places Autocomplete and create an API Key, as a prerequisite. If you need more detailed information, you can get it from here.
For the Place AutoComplete request, in its simplest form, we will send a request to the following URL by giving the value entered by the user to the “input” parameter . Of course, we can add many more parameters such as language, country and radius, but for now, this request is enough to proceed.
https://maps.googleapis.com/maps/api/place/autocomplete/json
?input=Paris
&key=YOUR_API_KEY
Yes, we have made the necessary preparations to use the Google Place API. Let’s examine the response that will be returned from this service and start creating our model classes. I will use JsonSerializable
for the model classes.
Place Auto Complete Model
You can visit this link for an example response. After the service request, we will obtain an array of “predictions.” This array contains various values that you can customize to use in your application. In this application, using the “place_id” and “structured_formatting” values will be sufficient for our needs.
- Place ID: We will use the ID of the selected place to get the details in the next request.
- Structured Formatting: With the “main_text” and “secondary_text” values inside it, we will have information about the name and location of the place.
We prepared our models for the AutoComplete structure. Let’s create g.dart files using the dart run build_runner build
command.
For now, our work with the AutoComplete structure is done. Next, we need to learn the details of the selected place.🤓
Place Details Model
To learn the details of the selected place from the Prediction list obtained with the AutoComplete service, the “place_id” value is sufficient.
We will send a request to the service below using the “place_id” value for the details of the selected place.
https://maps.googleapis.com/maps/api/place/details/json
?place_id=ChIJN1t_tDeuEmsRUsoyG83frY4
&key=YOUR_API_KEY
This service returns a lot of data to us, and you can access this data from here. Since we will mark the selected place on the map, the coordinates will be sufficient for us.
Let’s create g.dart files again using the dart run build_runner build
command.
Yes, now that we have prepared our models, we can start creating our class in which we will send service requests.
Place Search Service
I generally use Dio for service requests and I make sure that the class in which I manage service requests is testable. Therefore, I define them as follows.
PlaceSearchService({Dio? dio}) : _dio = dio ?? Dio();
final Dio _dio;
Yes, now I have defined the constants as follows. Since this is an open-source project, I used an environment variables to hide my private key. I will talk about using the Environment variables in another article.
static const _baseUrl = 'https://maps.googleapis.com/maps/api/place';
static const _autoCompletePath = '/autocomplete/json?';
static const _placeSearchPath = '/details/json?';
static const _key = Env.token;// Or "YOUR_API_KEY"
Now that I’ve created my constants, I can create the methods. I will create two methods: `getPlace()` and `placesAutoCompleteSearch()`, and I have already explained what these methods will do above. The final version of the `PlaceSearchService` class looks like the following:
We have prepared our methods for sending requests to services. Now it’s Bloc’s turn.🖖
The purpose before starting this article was to explain the AutoComplete structure using Bloc. While coding the application, I wanted to use both Bloc and Cubit in the same application to better understand the differences between them.
Autocomplete Bloc
Now it’s time to create our Bloc structure. For the AutoComplete structure, I created the States based on the changes we will make on our screen, as shown below. I added the values necessary for the States.
- AutoCompleteEmpty : State where the result list is empty and represents the initial states.
- AutoCompleteLoading: When a request is send to the service, the loading indicator will be displayed on the screen.
- AutoCompleteSuccess: State where we will display search results on the screen after the service request. This state contains the List<Prediction> received from the service.
- AutoCompleteSelectedPlace: State indicating that a place is selected from the list. This state contains the information of the selected place.
- AutoCompleteError: This state will be used if the response from the service is null.
I created the TextChanged event for the values written by the user. This event will notify Bloc when the user types anything in the TextField.
We are now at the final stage for AutoComplete. Now we will create the AutoCompleteBloc. But before we start, we do not want the user to send a request after each letter selection. I recommend that you take precautions in this regard. If you are not careful, you may exceed free tier limits, and you may come up with an expensive invoice. 💸
What we need is that when the user writes something to the TextField, the relevant event for each letter does not run continuously.
For this, we will use a “transformer”, which is one of the most important features that distinguish Bloc from Cubit . We created the Transformer as follows. Now, every time the event is triggered, we have established a structure that waits for 300 milliseconds and then creates a service request.
const _duration = Duration(milliseconds: 300);EventTransformer<Event> debounce<Event>(Duration duration) {
return (events, mapper) => events.debounce(duration).switchMap(mapper);}
Next, we created AutoCompleteBloc as follows. In the _onTextChanged method, we manage the states as we wish.
Marker Cubit
In this part of the article, we will prepare the selected place to be marked on the map using Cubit. For Cubit, 2 states will be sufficient.
- MarkerInitial: This state represents the initial state. In this case, there is no marker on the map.
- MarkerComplete: This state indicates the situation where the user selects a place from the list. This state has a Place.
We created Cubit as follows. After the selectPlace method completes marking the selected place on the map, it updates the state to MarkerAutoComplete.
mapMarkers: We created the markers on the map here. When a new location is selected, the existing location will be removed from the map.
controller: We used it to update the camera position.
Now that we have AutoComplete and MarkerCubit created, we can create our homepage.
MapView
While creating MapView, I used a TextField on the map. I created the page as follows. The important point here is that since the application is single-page, we created the existing Blocs at the beginning of the page. BlocProvider needs to be called where the relevant Bloc will be used.
We defined the Map widget as follows. I wrapped the GoogleMap widget in BlocBuilder.
The Prediction List widget also includes a TextFormField for searching and a Search Result widget that lists the results.
We trigger the TextChanged event using the onChanged property in TextFormField.
We created our SearchResult widget as follows. This widget lists search results. In this widget, we have specified how to treat all states one by one. While creating the state before, we also used the values of the state.
As seen below, we used the selectPlace method to mark a selected place on the map from the result list.
Now that I’ve explained the last widget, we’ve reached the end of the article. ✨
In this article, we comprehensively covered how to implement Place Autocomplete and mark the selected place on the map using BLoC and Cubit. We examined the Autocomplete structure using the Google Places API and attempted to explain how to manage this information with a BLoC architecture.
Later on, we established the focal point of the article, which is the BLoC structure. We created a BLoC for AutoComplete and dynamically sent API requests for every letter the user typed using this BLoC.
Finally, we used a Cubit to mark the selected place on the map. This way, we were able to mark the place selected by the user on the map.
I hope this article helps developers who want to add a Place Search Autocomplete feature using Flutter and BLoC. If you have any questions, please feel free to ask. Happy coding!🚀💙