We are going to see one of the most useful use case that will be used in almost in all the mobile app, which is search and get results from api, For best user experience we generally like to see the autocomplete search result instantly when we start typing in the input using the event stream like keyup, keypress, input etc used in web development world, we are going to see some simple example how to do the same in the Flutter.
What is StreamBuilder in flutter?
In Flutter, a StreamBuilder
is a widget that rebuilds whenever a stream provided to it emits a new value. It takes a stream and a builder function as input and automatically rebuilds the widget tree when a new value is emitted from the stream. The builder function receives the latest value emitted by the stream and returns a widget tree based on that value. StreamBuilder
is often used to update a part of the UI in response to a stream of data.
Example search screen in flutter using StreamController and StreamBuilder
Here is an example of using a StreamBuilder in Flutter to build a search feature with an input text field and an API call.
First, you will need to create a Stream to hold the search query and bind it to the text field using a TextEditingController
.
<code class="">final TextEditingController _searchController = TextEditingController(); final StreamController<String> _searchStream = StreamController<String>(); @override void initState() { super.initState(); _searchController.addListener(() { _searchStream.add(_searchController.text); }); } </code>
Ok then we must prepare to get the data from api, so lets assume you already have an PersonService which handles the api request and response, we are just going to call that Api service from our search page, like below
<code class=""> PersonService personService = PersonService(); fetchData(query) async { return await personService.fetchPersons(query: query); }</code>
Next, you can use the StreamBuilder widget to build the search results based on the search query stream.
<code class="">StreamBuilder<Object>( initialData: "", stream: _searchStream.stream, builder: (context, snapshot) { if (snapshot.hasData) { String query = snapshot.data; return FutureBuilder( future: fetchData(query), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting || !snapshot.hasData) { return const Center( child: CircularProgressIndicator(), ); } else if ((snapshot.data! as dynamic).length <= 0) { return const Center(child: Text("No results")); } else { var results = snapshot.data; if (results != null) { return ListView.builder( itemCount: results.length, itemBuilder: (context, index) { Result result = results[index]; return ListTile( title: Text(result.title), ); }, ); } else{ return const Text("No results"); } } }, ); } else { return const Center( child: CircularProgressIndicator(), ); } }, )</code>
This example uses a FutureBuilder
to make an API call and build the search results based on the response. The api.search
function returns a Future
that resolves to a list of Result
objects.
Finally, don’t forget to dispose of the StreamController when the widget is disposed.
<code class="">@override void dispose() { _searchStream.close(); _searchController.dispose(); super.dispose(); }</code>
I hope this helps! Let me know if you have any questions.