Flutter showSearch async operation when building suggestions

2,320

Return a FutureBuilder:

Future<Widget> buildSuggestions(BuildContext context) {
  return FutureBuilder(
    future: Geocoder.local.findAddressesFromQuery(query),
    builder: (BuildContext context, AsyncSnapshot snapshot) {
      // check if snapshot.hasData
      var addresses = snapshot.data;

      final Iterable<Address> suggestions = query.isEmpty
          ? _history
          : addresses;

      return _SuggestionList(
        query: query,
        suggestions: suggestions.map<String>((String i) => '$i').toList(),
        onSelected: (String suggestion) {
          query = suggestion;
          this.close(context, query);
        },
      );
   )
}

FutureBuilder is built on top of StatefulWidget. Attempting to solve this problem with a StatefulWidget is not wrong but simply lower-level and more tedious.

This article explains it in detail: https://flutterigniter.com/build-widget-with-async-method-call/

Share:
2,320
Jakub
Author by

Jakub

Passionate about developing in JavaScript

Updated on December 16, 2022

Comments

  • Jakub
    Jakub over 1 year

    I am using the flutter showSearch function to search for addresses. Everything works fine so far. Now I want to implement suggestions that will be rebuild each time the user changes the search input. To do this I am using the flutter geocode library.

    The problem is that I don't know how I can execute an async operation when building suggestions using the buildSuggestions function.

    Obviously I cannot simple make the buildSuggestions function async, but if I could my code would look like this:

    @override
    Future<Widget> buildSuggestions(BuildContext context) async {
    
      var addresses = await Geocoder.local.findAddressesFromQuery(query);
    
      final Iterable<Address> suggestions = query.isEmpty
          ? _history
          : addresses;
    
      return _SuggestionList(
        query: query,
        suggestions: suggestions.map<String>((String i) => '$i').toList(),
        onSelected: (String suggestion) {
          query = suggestion;
          this.close(context, query);
        },
      );
    }
    

    There must be ways of achieving what I want to do. Any suggestions?

    UPDATE:

    With the help of the post from frank06 I wrote the following code:

    
       @override
      Widget buildSuggestions(BuildContext context) {
        return FutureBuilder(
            future: Geocoder.local.findAddressesFromQuery(query),
            builder: (BuildContext context, AsyncSnapshot<List<Address>> snapshot) {
              // check if snapshot.hasData
    
              if (snapshot.connectionState == ConnectionState.done) {
                List<Address> addresses;
                if (snapshot.hasData) {
                  addresses = snapshot.data;
                } else {
                  addresses = List<Address>();
                }
    
                List<String> addressNames = addresses.map((address) {
                  return address.addressLine;
                }).toList();
    
                final Iterable<String> suggestions =
                    query.isEmpty ? _history : addressNames;
    
                return _SuggestionList(
                  query: query,
                  suggestions: suggestions.map<String>((String i) => '$i').toList(),
                  onSelected: (String suggestion) {
                    query = suggestion;
                    this.close(context, query);
                  },
                );
              } else {
                return _SuggestionList(
                    query: query,
                    suggestions: List<String>(),
                    onSelected: (String suggestion) {
                      query = suggestion;
                      this.close(context, query);
                    });
              }
            });
      }
    

    I feel like I have gotten a lot closer to the solution, but currently I have the problem that snapshot.hasData always returns false.

    Any further suggestions?

    FINAL UPDATE:

    Finally it did work. My future was not returning data because I hadn't made sure that my query was actually of datatype String. The code below is the final functioning version. (Thanks again to frank06!).

    @override
     Widget buildSuggestions(BuildContext context) {
       String queryString = query;
    
       return FutureBuilder(
           future: queryString.length > 0 ? Geocoder.local.findAddressesFromQuery(queryString) : Future.value(List<Address>()),
           builder: (BuildContext context, AsyncSnapshot<List<Address>> snapshot) {
    
             if (snapshot.connectionState == ConnectionState.done) {
               List<Address> addresses;
               if (snapshot.hasData) {
                 addresses = snapshot.data;
               } else {
                 addresses = List<Address>();
               }
    
               List<String> addressNames = addresses.map((address) {
                 return address.addressLine;
               }).toList();
    
               final Iterable<String> suggestions =
                   query.isEmpty ? _history : addressNames;
    
               return _SuggestionList(
                 query: query,
                 suggestions: suggestions.map<String>((String i) => '$i').toList(),
                 onSelected: (String suggestion) {
                   query = suggestion;
                   this.close(context, query);
                 },
               );
             } else {
               return _SuggestionList(
                   query: query,
                   suggestions: List<String>(),
                   onSelected: (String suggestion) {
                     query = suggestion;
                     this.close(context, query);
                   });
             }
           });
     } 
    
    
  • Jakub
    Jakub over 4 years
    Hey frank, thank you for your post! I see what you are suggesting. I think it might work, atm I am just having the problem that snapshot.hasData always returns false.
  • Frank Treacy
    Frank Treacy over 4 years
    You're welcome Jakub, did you check hasError? is your future actually working?
  • Frank Treacy
    Frank Treacy over 4 years
    @Jakub try future: Future.value([Address(), Address()]) (e.g. two dummy addresses) and if that does work with snapshot.data then the problem is with your future
  • Jakub
    Jakub over 4 years
    Thank you again, you have been very helpful! The problem was, that my future did not actually return any data, because the "query" was not of type String. After I had casted the query to String everything worked perfectly (: