Customize search bar in Flutter

What’s effect we can realise?

Custom search bar

The search bar can be used in any listview page,when user texts something in different page should list the correct outcome.

Customise text input


✨ Encapsulate TextFormField


TextFormField widget is used to take input from the user in flutter. This is a simple and easy user input widget in flutter.

We can perform any operation on that user input data using TextFormField. You can use that user input, can send and show that input. You can store it into a TextEditingController type object.

In TextFormField, controller is important to monitor the text user input. focusNode is to monitor user wheater click the text bar.

In this search bar, when the text is empty, the suffix icon is “search”, whereas should be “close” icon.


 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
48
49
50
51
52
53
class CustomInputSearch extends ConsumerWidget {
  final TextEditingController controller = TextEditingController();
  final FocusNode? focusNode;

  CustomInputSearch({key,  this.focusNode });

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final size = Applayout.getSize(context);
    final searchNotifier = ref.watch(searchProvider);
    print("CustomInputSearch ${controller.text}");
    return Container(
      height: Applayout.getHeight(50),
      width: Applayout.getHeight(size.width * .8),
      margin: EdgeInsets.symmetric(
        horizontal: size.width * 0.1, vertical: Applayout.getHeight(10),
      ),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(Applayout.getHeight(10)),
        color: Styles.bgColor2,
      ),
      child: TextFormField(
        focusNode:  focusNode?? FocusNode(),
        onChanged: (value){
          searchNotifier.setSearchValue(value);
        },
        controller: controller,
        autofocus: false,
        decoration: InputDecoration(
          contentPadding: EdgeInsets.symmetric(
            horizontal: Applayout.getWidth(20),
            vertical: Applayout.getHeight(14),
          ),
          alignLabelWithHint: true,
          floatingLabelAlignment: FloatingLabelAlignment.start,
          suffixIcon: controller.text.trim().isNotEmpty
              ? IconButton(
            icon: Icon(Icons.clear_rounded, color: Styles.orangeColor),
            onPressed: () {
              controller.clear();
              FocusManager.instance.primaryFocus?.unfocus();
              searchNotifier.clearSearch();
            },) : IconButton(
            icon: Icon(Icons.search, color: Styles.whiteColor),
            onPressed: null,),
          border: InputBorder.none,
          hintText: 'Search ...',
          hintStyle: Styles.textStyle,
        ),
      ),
    );
  }
}

📍 Use it in anywhere


It’s very simple to use it. beacuse we use riverpod to monitor the textinput, we don’t need to pass textcontroller to watch value changes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  final FocusNode _searchFocusNode = FocusNode();
    .....

// general use

 CustomInputSearch( focusNode: _searchFocusNode, )

// using in riverpod.

ProviderScope(child: CustomInputSearch(
    focusNode: _searchFocusNode,
),),

👉 Monitor text changes and filter the target data by using Riverpod


Riverpod is a state management library for Flutter developed by the same team behind the Provider library. Riverpod is designed to provide an improved API for managing state in Flutter apps, with a focus on performance, simplicity, and flexibility.

Riverpod uses a concept called “Scoped Providers” to manage state within an app. A Scoped Provider is a function that takes a context and returns an object. The Scoped Provider is responsible for creating and managing the state for that object within the scope of the widget tree.


Import riverpod plugin


Here is riverpod offical website.


1
2
3
4
5
6
7
8
environment:
  sdk: ">=2.17.0 <3.0.0"
  flutter: ">=3.0.0"

dependencies:
  flutter:
    sdk: flutter
  flutter_riverpod: ^2.3.6

watch(provider) & read(provider)

In Riverpod, watch and read are two ways to access the state of a provider. Here’s what they do:


watch(provider)


Returns a value that will rebuild the widget tree whenever the state of the provider changes. Should be used inside a widget's build method to subscribe to changes in state. Returns a T object that can be accessed using the .state property. For example: watch(counterProvider).state Here's an example of how to use watch to subscribe to changes in a counter:
1
2
3
4
5
6
7
class MyCounterWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = context.watch(counterProvider).state;
    return Text('Counter: $counter');
  }
}

read(provider):


Returns the current value of the provider without subscribing to future changes. Should be used outside of a widget’s build method, such as in a button press or event handler. Returns a T object that can be accessed using the .state property. For example: final counter = context.read(counterProvider).state Here’s an example of how to use read to get the current value of a counter and update it:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class MyButtonWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Text('Increment Counter'),
      onPressed: () {
        final counter = context.read(counterProvider);
        counter.state++;
      },
    );
  }
}

It’s important to note that you should use watch inside a widget’s build method and read outside of it. Using read inside a build method can lead to unnecessary widget rebuilds and performance issues. Additionally, it’s generally recommended to use watch over read whenever possible, as watch provides a more reactive and efficient approach to managing state in Riverpod.


Create a SearchNotifier.dart file


 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
import 'package:flutter/cupertino.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';


class SearchNotifier extends ChangeNotifier {
  Ref ref;
  String state = "";

  SearchNotifier(this.ref) {
    state = "";
  }

  void setSearchValue(String value) {
    state = value;
    notifyListeners();
  }

  String get searchValue => state;

  set searchValue(String value) {
    setSearchValue(value);
    notifyListeners();
  }

  void clearSearch() {
    state = "";
    notifyListeners();
  }
}

final searchProvider = ChangeNotifierProvider.autoDispose<SearchNotifier>((ref) {
  return SearchNotifier(ref);
});

In search bar, “watch” means when the value changed, the lasted value can be catched.


1
final searchNotifier = ref.watch(searchProvider);

Filter data based on text value


In the listview page, “watch” the value in the real time. “cartListProvider” is another part to access data using RESTful api. We can define a function call “filterOrders” to filter the data.


1
2
3
final value = ref.watch(searchProvider).state;

filteredData =  ref.read(cartListProvider(supplierId)).filterOrders(value);

In cartListProvider.dart.
1
2
3
4
5
6
7
 List<SupplierProductModel> filterOrders(String _searchString) {
    String lowerValue = _searchString.toLowerCase();
    final filteredData = list.where((item) =>
        item.productName!.toLowerCase().contains(lowerValue)
    ).toList();
    return filteredData;
  }

Thanks.