skip to Main Content

Im making a Flutter app that shows a list of items, and I would like to filter this list based on a String in a search bar. I have Riverpod to provide a String that will be used in the filter. Base on this String I would like to change the suffixIcon when the String is not empty.

I only able to update only one or the other (text in search bar or the icon) together they don’t work.

What is going on?

final searchProvider = StateProvider<String>((ref) => '');

class OverviewScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final houseDataValue = ref.watch(listHousesProvider);
    final searchController = TextEditingController();

    return Column(
      children: [
        Padding(
          padding: EdgeInsets.only(top: 0.75.h, bottom: 1.5.h, right: 4.2.w, left: 4.2.w),
          child: Container(
            decoration: BoxDecoration(
              color: AppColors.darkGray,
              borderRadius: BorderRadius.circular(8.0),
            ),
            child: Padding(
              padding: EdgeInsets.only(left: 4.w, top: 0.4.h),
              child: TextField(
                controller: searchController,
                cursorColor: AppColors.medium,
                style: AppTypography.input,
                decoration: InputDecoration(
                  hintText: 'Search for a home',
                  hintStyle: AppTypography.hint,
                  suffixIcon: IconButton(
                    icon: ref.watch(searchProvider).isNotEmpty // ICON DOES NOT UPDATE
                        ? Icon(Icons.ac_unit_outlined)
                        : Icon(Icons.access_alarm_outlined),
                    onPressed: () {},
                  ),
                  border: InputBorder.none,
                ),
                onChanged: (value) {
                  // OR THE TEXT INSIDE THE SEARCH BAR
                  ref.read(searchProvider.notifier).update((state) => state = value);
                },
              ),
            ),
          ),
        ),
      ],
    );
  }
}

2

Answers


  1. You need to declare TextEditingController outside the build method. It’s getting reassigned empty string on input changed.

    class OverviewScreen extends ConsumerWidget {
      final searchController = TextEditingController();
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        print('Build');
    
        return Column(
          children: [
            TextField(
              controller: searchController,
              decoration: InputDecoration(
                hintText: 'Search for a home',
                suffixIcon: IconButton(
                  icon: ref.watch(searchProvider).isNotEmpty 
                      ? Icon(Icons.ac_unit_outlined)
                      : Icon(Icons.access_alarm_outlined),
                  onPressed: () {},
                ),
                border: InputBorder.none,
              ),
              onChanged: (value) {
                print(value);
                // OR THE TEXT INSIDE THE SEARCH BAR
                ref.read(searchProvider.notifier).update((state) => state = value);
              },
            ),
          ],
        );
      }
    }
    

    I would suggest to check ConsumerStatefulWidget.

    Login or Signup to reply.
  2. In fact, you need to make another provider that would only control the empty string. Try this:

    final searchProvider = StateProvider<String>((_) => '');
    
    final searchIsEmptyProvider = Provider<bool>(
        (ref) => ref.watch(searchProvider).isEmpty
    );
    

    Now your ui looks like this:

    class OverviewScreen extends ConsumerWidget {
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        print('$OverviewScreen been builded');
    
        return Column(
          children: [
            TextField(
              decoration: InputDecoration(
                hintText: 'Search for a home',
                suffixIcon: Consumer(
                  builder: (context, ref, _) => IconButton(
                    icon: ref.watch(searchIsEmptyProvider) 
                        ? Icon(Icons.access_alarm_outlined)
                        : Icon(Icons.ac_unit_outlined),
                    onPressed: () {},
                  ),
                ),
                border: InputBorder.none,
              ),
              onChanged: (value) {
                ref.read(searchProvider.notifier).update((_) => value);
              },
            ),
          ],
        );
      }
    }
    

    I specifically used Consumer to show that only the icon will be rebuilt, and not the entire OverviewScreen widget.


    Also note that you should create ANY controllers, text, focus, scroll, etc., only inside initState, and dispose of them inside dispose. This will help prevent memory leaks.

    Happy coding!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search