g1455

Search bar

GlassSearchBar is the glass search field with a clear button and a Cancel that slides in on focus. One surface; the slide is its one cost, a capture a frame for 250 ms.

GlassSearchBar is UISearchBar with showsCancelButton, as an iPhone shows it: the glass search field of GlassTextField.search, a clear button inside it while there is text, and a Cancel beside it that slides in while the field has the focus.

The clear button, a filled circle with a cross at the field's end, empties the field and keeps the focus. Cancel empties it, lets go of the focus and calls onCancel. Cancel is plain text in cancelColor, beside the glass rather than on it, as iOS draws it.

When to use

  • The search at the top of a list or a screen, in a bar or floating on its own.
  • Not for a field that is not a search: use a GlassTextField, which has no Cancel.
  • Not in a row with other glass that would have to move as the field narrows. The bar takes the width it is given and narrows itself; give it a row of its own.

Usage

GlassSearchBar(
  onChanged: (String query) => setState(() => filter = query),
  onSubmitted: runSearch,
  onCancel: () => setState(() => filter = ''),
)

controller and focusNode are made and disposed of by the bar when you pass none; pass your own to read or set the query or the focus. placeholder is "Search" and cancelLabel "Cancel" by default.

Cost

One surface, the field's. Typing, the caret and the clear button are inside the glass and are no capture.

What does cost is Cancel's slide. The field's glass narrows to make room, and glass whose box changes is retaken: a capture a frame for cancelDuration, 16 over 250 ms at 60 Hz in the package's tests, once as the field takes the focus and once as it lets go, and none after. A GlassTravel around the row was tried and changed nothing (16 with it and without), because it is the field's resize that costs, not Cancel's paint. Under reduced motion the slide is one frame. showsCancelButton: false keeps the box still, and then focus costs nothing. The count under the demo shows both.

Layout

  • The field is kGlassFieldHeight (44) tall, and Cancel's tap target is at least 44 tall too.
  • The bar stretches to the width it is given, so the width must be bounded: an Expanded in a Row, a Positioned with a left and a right.

Accessibility

The field is an EditableText, read as a text field with its placeholder. The clear button and Cancel are buttons.

Code

import 'package:flutter/material.dart';
import 'package:g1455/g1455.dart';

/// A list of contacts with a search bar floating over its top.
/// Assumes a GlassHost above the navigator (MaterialApp.builder).
class Contacts extends StatefulWidget {
  const Contacts({super.key});

  @override
  State<Contacts> createState() => _ContactsState();
}

class _ContactsState extends State<Contacts> {
  static const List<String> _names = <String>['Anna', 'Ben', 'Chiara', 'Dmitri', 'Emma', 'Farid', 'Grace'];
  String _query = '';

  @override
  Widget build(BuildContext context) {
    final List<String> shown = <String>[
      for (final String n in _names)
        if (n.toLowerCase().contains(_query.toLowerCase())) n,
    ];
    final double top = MediaQuery.paddingOf(context).top;
    return Stack(
      children: <Widget>[
        ListView(
          padding: EdgeInsets.fromLTRB(16, top + 72, 16, 16),
          children: <Widget>[for (final String n in shown) ListTile(title: Text(n))],
        ),
        Positioned(
          top: top + 12,
          left: 16,
          right: 16, // a bounded width: the bar stretches, and narrows for Cancel
          child: GlassSearchBar(
            placeholder: 'Search contacts',
            onChanged: (String q) => setState(() => _query = q),
            onCancel: () => setState(() => _query = ''),
          ),
        ),
      ],
    );
  }
}

API

Parameter Type Default Description
controller TextEditingController? null The query. Null makes one the bar owns.
focusNode FocusNode? null The focus. Null makes one the bar owns.
placeholder String 'Search' Shown while the field is empty.
onChanged ValueChanged<String>? null Called on every change of the query, the clear button's included.
onSubmitted ValueChanged<String>? null Called on the keyboard's search action.
onCancel VoidCallback? null Called after Cancel has emptied the field and let go of the focus.
showsCancelButton bool true Whether Cancel slides in while the field has the focus. false keeps the field's box still.
cancelLabel String 'Cancel' Cancel's text.
cancelColor Color Color(0xFF007AFF) Cancel's colour.
cancelDuration Duration Duration(milliseconds: 250) How long Cancel takes to slide in or out: a capture a frame for as long.
autofocus bool false Focus the field as soon as it is built.
finish GlassFinish? null The glass. Null takes the theme's.
key Key? null