Building Search UIs with Flutter Searchbox 4.0

CEO @reactivesearch, search engine dx
Search for a command to run...

CEO @reactivesearch, search engine dx
No comments yet. Be the first to comment.
Supercharged search for Elasticsearch and OpenSearch

Build Semantic Search UIs with Faceting and Production-Ready Performance in One Unified API

Build Hybrid Search with ReactiveSearch UI Kit and Elasticsearch 8.17

We will start by creating vector embeddings and indexing data, then create a search endpoint, and finally build a search UI.

We will start by creating vector embeddings and indexing data, then create a search endpoint, and finally build a search UI.

Search functionality is a vital component of modern applications, significantly enhancing user experience by providing quick access to relevant information. As Flutter continues to gain popularity for building cross-platform apps, integrating a robust search interface has become increasingly important. Enter Flutter Searchbox 4.0, a package that offers a declarative API to seamlessly connect your Flutter app with Elasticsearch or OpenSearch, enabling the creation of powerful search UIs.
In this post, we’ll guide you through setting up a basic search interface using Flutter Searchbox 4.0, highlighting its key features and providing a practical example.
Flutter Searchbox is designed to integrate seamlessly with ReactiveSearch, making it easier to query Elasticsearch and OpenSearch. It allows developers to build responsive and dynamic search UIs with minimal code, leveraging pre-built widgets for autosuggestions, results rendering, facets and maps.
Before diving into coding, let’s set up the necessary dependencies.
Depend on it
Add the following dependencies to your pubspec.yaml file:
codedependencies:
flutter_searchbox: ^4.0.0
searchbase: ^4.0.0
Install it
Run the following command to install the packages:
flutter pub get
Now that we have the dependencies installed, let’s build a simple search UI.
The following example demonstrates how to create a search interface with an autosuggestion search widget and a custom result widget.
import 'package:flutter/material.dart';
import 'package:searchbase/searchbase.dart';
import 'package:flutter_searchbox/flutter_searchbox.dart';
void main() {
runApp(FlutterSearchBoxApp());
}
class FlutterSearchBoxApp extends StatelessWidget {
final searchbaseInstance = SearchBase(
'good-books-ds',
'https://appbase-demo-ansible-abxiydt-arc.searchbase.io',
'a03a1cb71321:75b6603d-9456-4a5a-af6b-a487b309eb61',
appbaseConfig: AppbaseSettings(
recordAnalytics: true,
userId: 'test@dev',
),
);
@override
Widget build(BuildContext context) {
return SearchBaseProvider(
searchbase: searchbaseInstance,
child: MaterialApp(
title: "SearchBox Demo",
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomePage(),
),
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('SearchBox Demo'),
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {
showSearch(
context: context,
delegate: SearchBox(
id: 'search-widget',
enableRecentSearches: true,
enablePopularSuggestions: true,
showAutoFill: true,
maxPopularSuggestions: 3,
size: 10,
dataField: [
{'field': 'original_title', 'weight': 1},
{'field': 'original_title.search', 'weight': 3},
],
),
);
},
),
],
),
body: Center(
child: SearchWidgetConnector(
id: 'result-widget',
dataField: 'original_title',
react: {
'and': ['search-widget'],
},
size: 10,
triggerQueryOnInit: true,
preserveResults: true,
builder: (context, searchController) => ResultsWidget(searchController),
),
),
);
}
}
class ResultsWidget extends StatelessWidget {
final SearchController searchController;
ResultsWidget(this.searchController);
@override
Widget build(BuildContext context) {
return Column(
children: [
Card(
child: Align(
alignment: Alignment.centerLeft,
child: Container(
color: Colors.white,
height: 20,
child: Text(
'${searchController.results.numberOfResults} results found in ${searchController.results.time.toString()} ms',
),
),
),
),
Expanded(
child: ListView.builder(
itemCount: searchController.results.data.length + 1,
itemBuilder: (context, index) {
WidgetsBinding.instance.addPostFrameCallback((_) {
var offset = (searchController.from ?? 0) + searchController.size;
if (index == offset - 1 && searchController.results.numberOfResults > offset) {
searchController.setFrom(offset, options: Options(triggerDefaultQuery: true));
}
});
return index < searchController.results.data.length
? Container(
margin: const EdgeInsets.all(0.5),
padding: const EdgeInsets.fromLTRB(0, 15, 0, 0),
decoration: BoxDecoration(border: Border.all(color: Colors.black26)),
height: 200,
child: Row(
children: [
Expanded(
flex: 3,
child: Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Image.network(
searchController.results.data[index]["image_medium"],
fit: BoxFit.fill,
),
elevation: 5,
margin: EdgeInsets.all(10),
),
),
Expanded(
flex: 7,
child: Column(
children: [
ListTile(
title: Text(
searchController.results.data[index]["original_title"],
style: TextStyle(fontSize: 20.0),
),
subtitle: Text(
'By: ${searchController.results.data[index]["authors"]}',
style: TextStyle(fontSize: 15.0),
),
),
Row(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(10, 5, 0, 0),
child: Text(
'(${searchController.results.data[index]["average_rating"]} avg)',
style: TextStyle(fontSize: 12.0),
),
),
],
),
Row(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(27, 10, 0, 0),
child: Text(
'Pub: ${searchController.results.data[index]["original_publication_year"]}',
style: TextStyle(fontSize: 12.0),
),
)
],
),
],
),
),
],
),
)
: searchController.requestPending
? Center(child: CircularProgressIndicator())
: ListTile(
title: Center(
child: RichText(
text: TextSpan(
text: searchController.results.data.length > 0
? "No more results"
: 'No results found',
style: TextStyle(color: Colors.black54, fontSize: 20, fontWeight: FontWeight.bold),
),
),
),
);
},
),
),
],
);
}
}
This code sets up a basic search interface where users can search for books, get autosuggestions, and view detailed results, including images, titles, authors, and publication years.

Flutter Searchbox offers flexibility in customizing search experiences. You can tailor search widgets, adjust search parameters, and integrate additional features like filters and facets to refine results based on user preferences.
Optimizing your search interface is crucial for delivering a fast and responsive user experience. Consider implementing caching strategies, managing large datasets efficiently, and ensuring the UI remains smooth even under heavy search loads.
Building search UIs with Flutter Searchbox 4.0 is efficient and customizable, offering a rich set of features to create a seamless search experience. By following the steps in this guide, you can add powerful search capabilities into your Flutter app.
For more examples with facets, custom Search UI, and maps, explore the Flutter Searchbox documentation and start building search-powered apps today.