Flutter: Searchbox like Gmail with Overlay widget

Vinay Shankri
Flutter Community
Published in
5 min readDec 27, 2020

tl;dr

I am working on enabling Elasticsearch for an enterprise app. On the server, it is a Java-based microservice with multiple search endpoints (communicating with Elasticsearch) for various modules. On the UI, I wanted a simple search widget (interacting with REST endpoints) that can be used across screens with different search contexts. I am sharing requirements and implementation details of developing Gmail like search widget with Flutter’s Overlay widget. It was more challenging for the web as I needed basic keyboard support.

For a quick overview just refer to Screen recording, See it in action, and GitHub sections below.

Note: This is primarily for devices with good real estate like tablets or web browsers on desktops. I will share more info on small devices like phones when I get to it (thinking on the lines of search delegate).

See it in action

Click here to see it in action

Screen Recording

Github

Click here for source code

You can find details of the source code in the “Files” section below.

For this sample project

  1. I branched out my code to create a simple project to specifically focus on the searchbox widget and avoid my app-specific nuances.
  2. I am just using stateful widgets to keep it simple (unlike my actual app where I use a combination of stateful widgets and “provider” for state management).
  3. For search results, I am using “english words” packages (in my actual app they are REST calls)
  4. Excluding theming and translations (l10n)

Anatomy of the Searchbox

The searchbox is very much like Gmail + an additional feature of “filter options”.

  1. ”Suggest list” appears as an overlay when a user starts typing in the textbox.
  2. Click on “Search Icon” or hit the Enter key to search.
  3. “Clear Search” clears the current search text.
  4. “Search Options” displays multiple search options (form like) in the overlay (optional parameter for the widget).
  5. “Filter Options” display a list of filters in the overlay (optional parameter for the widget).

Note: All overlays (Suggest List, Search Options & Filter Options)are mutually exclusive.

Nested Overlays

Supporting nested overlays is a key aspect. “From” is an editable search dropdown that displays a “search list” in an overlay (this is a nested overlay ).

Note: I could not use DropdownButton within a overlay as it appears behind the overlay.

Closing or Hiding Overlays

Overlays should be closed or hidden (in reverse order) when the user clicks or taps outside the boundary of the current overlay widget. Flutter does not directly support event bubbling (like HTML). However, we can use routes or a combination of NotificationListener and ValueListenableBuilder to achieve this. Depending on your app, this may get complicated and may proliferate to other screens or widgets.

To control this centrally, I have used a Stack within the overlay implementation to mask the screen with a transparent glass pane (on all sides) that handles tap or click event to close the current overlay

Keyboard Support

Keyboard support for the web:

  1. The “Escape” key should close or collapse the last opened overlay.
  2. “Enter” key is used to select a value or submit (depending on the state and context)
  3. “Arrow Up” & “Arrow Down” key to navigate through the list (circular)
  4. “Tab” and “Shift + Tab” to navigate through form elements of the “Search Options” form (circular).

Note: FocusableActionDetector is used for keyboard and mouse (hovering) support.

For a little more infotmation, please refer this medium story (Flutter: Keyboard & Mouse Support for Web)

Files

Note: “xwidgets” contains re-usable widgets.

WIDGETS

hover_extension.dart: To change mouse pointer (web) on hover. This is an extension for the Widget.

x_fad: Common widget to handle required keys (keyboard) and mouse hover through FocusableActionDetector.

x_overlay: This is the main widget handling overlays. XOverlayController & XOverlayStack class provide required overlay handle. This widget is not directly used to build screens. x_search_textbox.dart and x_search_dropdown.dart widgets use x_overlay.dart to provide required functionality.

x_search_textbox.dart: Searchbox widget used in main.dart

x_search_dropdown.dart: Editable & searchable dropdown widget used in search_options_screen.dart (“from” field)

SCREENS

main.dart: A simple screen with XSearchTextbox and its supporting functionality.

filter_options_screen.dart: List of filter options that appear on click or tap of “filter options” icon.

search_options_screen.dart: Multiple search options form that appears on click or tap of “search options” icon.

random_list_screen.dart: Common list implementation that appears as “search suggest” and also the list of “from” field (of type XSearchDropdown)

XOverlay: x_overlay.dart

Just going through some code excerpts here. Please refer Github for the complete source code.

Constructor

child: Overlay widgets appear right below the child widget. The width of the overlay widget is derived from the runtime width of the ‘child’ widget.

insideOverlayWidgetMap: A map of widgets that can appear in the overlay as specified by or the state of the parent widget. Just one widget is displayed in the overlay at any given instant of time.

xOverlayController: An instance of an overlay controller that gives the parent a handle on the overlay including disposing of resources.

onHideOverlayFunc: A callback function to notify the parent when an overlay is hidden.

XOverlayStack

A singleton that provides direct access to one or more stacked overlays. This can be directly used to handle common overlay functions when we do not have access to XOverlayController.

Core of XOverlay

  1. Use of CompositedTransformTarget & CompositedTransformFollower for the overlay to follow the widget it is attached to.
  2. Line #19 — Line #32: The stack widget is at the root. Glass panel covers the entire screen except for the widget and its overlay in context. This provides the ability to close the overlay no matter where the user clicks.
  3. Line 13 & Line 36: Width and position are calculated with the help of RenderBox.
  4. Line #47: A specific widget is picked from the map based on the widgetId (map and widgetId are both passed by the parent widget).

CONCLUSION

Since supporting web is part of MVP (minimal viable product). I had to quickly come up with these widgets to start testing Elasticsearch integration. The idea here is to have a custom overlay widget (XOverlay) to develop other widgets (like XSearchTextbox & XSearchDropdown) that can readily use overlays. I plan to further extend this to other overlay uses cases of the app.

Thank you.

Vinay

Follow Flutter Community on Twitter: https://www.twitter.com/FlutterComm

--

--

Vinay Shankri
Flutter Community

With 21+ years of experience. I see myself as a craftsman who takes a keen interest in every aspect of building a great quality product.