Guides / Building Search UI / Ecommerce ui template / Components / Search autocomplete
On this page

The Autocomplete mobile component

Code summary

The Autocomplete component is used on the Ecommerce UI demo app search page when you tap the search bar in the main page. It consists of two sections: one presenting the search history and the second one presenting the query suggestions.

The AppBar includes SearchHeaderView` widget representing the search box on the autocomplete screen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class SearchHeaderView extends StatelessWidget {
  const SearchHeaderView(
      {Key? key, required this.controller, this.onSubmitted, this.onChanged})
      : super(key: key);

  final TextEditingController controller;
  final ValueChanged<String>? onChanged;
  final ValueChanged<String>? onSubmitted;

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Expanded(
          child: TextField(
            autofocus: true,
            controller: controller,
            onSubmitted: onSubmitted,
            onChanged: onChanged,
            decoration: const InputDecoration(
                border: InputBorder.none,
                hintText: "Search products, articles, faq, ..."),
          ),
        ),
        if (controller.text.isNotEmpty)
          IconButton(
              iconSize: 34,
              onPressed: controller.clear,
              icon: const Icon(Icons.clear),
              color: AppTheme.darkBlue),
        const SizedBox(width: 8)
      ],
    );
  }
}

Each section consists of couple of SliverAppBar and SliverList components representing the section header and section body respectively.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
Widget _sectionHeader<Item>(Stream<List<Item>> itemsStream, Widget title) =>
    StreamBuilder<List<Item>>(
        stream: itemsStream,
        builder: (context, snapshot) {
          final suggestions = snapshot.data ?? [];
          return SliverSafeArea(
              top: false,
              bottom: false,
              sliver: SliverPadding(
                  padding: const EdgeInsets.only(left: 15),
                  sliver: SliverToBoxAdapter(
                    child:
                        suggestions.isEmpty ? const SizedBox.shrink() : title,
                  )));
        });

Widget _sectionBody<Item>(
  BuildContext context,
  Stream<List<Item>> itemsStream,
  Function(Item) rowBuilder,
) =>
    StreamBuilder<List<Item>>(
        stream: itemsStream,
        builder: (context, snapshot) {
          final suggestions = snapshot.data ?? [];
          if (suggestions.isEmpty) {
            return const SliverToBoxAdapter(child: SizedBox.shrink());
          }
          return SliverSafeArea(
              top: false,
              sliver: SliverPadding(
                  padding: const EdgeInsets.only(left: 15),
                  sliver: SliverFixedExtentList(
                      itemExtent: 44,
                      delegate: SliverChildBuilderDelegate(
                        (BuildContext context, int index) {
                          final item = suggestions[index];
                          return InkWell(
                              onTap: () {
                                final query = item.toString();
                                _onSubmitSearch(query, context);
                              },
                              child: rowBuilder(item));
                        },
                        childCount: suggestions.length,
                      ))));
        });

The first section represents the search history. All the submitted search queries are stored in the Autocomplete screen state. Users can delete each history item or remove them all by tapping the corresponding button in the section header.

The search history section

Each history row is represented by a HistoryRowView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class HistoryRowView extends StatelessWidget {
  const HistoryRowView({Key? key, required this.suggestion, this.onRemove})
      : super(key: key);

  final String suggestion;
  final Function(String)? onRemove;

  @override
  Widget build(BuildContext context) {
    return Row(children: [
      const Icon(Icons.refresh),
      const SizedBox(
        width: 10,
      ),
      Text(suggestion, style: const TextStyle(fontSize: 16)),
      const Spacer(),
      IconButton(
          onPressed: () => onRemove?.call(suggestion),
          icon: const Icon(Icons.close)),
    ]);
  }
}

The second section represents the query suggestions. By default it shows the list of popular searches. When user starts typing a search query the list is refreshing automatically and presents the highlighted search completions.

The popular searches section

Each suggestion row is represented by a SuggestionRowView:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class SuggestionRowView extends StatelessWidget {
  const SuggestionRowView({Key? key, required this.suggestion, this.onComplete})
      : super(key: key);

  final QuerySuggestion suggestion;
  final Function(String)? onComplete;

  @override
  Widget build(BuildContext context) {
    return Row(children: [
      const Icon(Icons.search),
      const SizedBox(
        width: 10,
      ),
      RichText(
          text: TextSpan(
              style: const TextStyle(color: Colors.black),
              children: suggestion.highlighted!.toInlineSpans())),
      const Spacer(),
      IconButton(
        onPressed: () => onComplete?.call(suggestion.query),
        icon: const Icon(Icons.north_west),
      )
    ]);
  }
}

The highlighting of the suggestion row is possible through HighlightedString.toInlineSpans() from Flutter helpers.

The entire autocomplete logic can be found in the autocomplete_screen file.

You can customize Autocomplete in:

Did you find this page helpful?
Algolia for Flutter v1