Search code examples
flutterdartsearchriverpodflutter-riverpod

How to create a search field in Flutter?


I am using the flutter_riverpod package. I'm using it for a search field and it's showing an error when searching for something. I asked a question about the error but couldn't fix it. Is there any other way to create a search field in Flutter using the flutter_riverpod package?

Here is some code:

User interface code:

TextFormField(
  …
  onChanged: (search) => controller.updateSearch(search),
  onSaved: (search) {
    search == null ? null : controller.updateSearch(search);
  },
),

itemBuilder: (context, index) {
 if (mounted) {
    return name.contains(state.search) ? ListTile(title: Text(name)) : Container();
  }

  return Container();
},

Controller code:

class Controller extends StateNotifier<State> {
Controller() : super(State());

void updateSearch(String search) => state = state.copyWith(search: search);

}

final controllerProvider = StateNotifierProvider.autoDispose< Controller, State>((ref) {
  return Controller();
});

State code:

class State {
  State({this.search = "", this.value = const AsyncValue.data(null)});

  final String search;
  final AsyncValue<void> value;
  bool get isLoading => value.isLoading;

  State copyWith({String? search, AsyncValue<void>? value}) {
    return State(search: search ?? this.search, value: value ?? this.value);
  }
}

Is there something wrong with the code above? If yes, what is the way to create the seach field using the flutter_riverpod package? If not, why am I getting the error message (go to this question to see the error message)?

If I can't create a search field using the flutter_riverpod package in Flutter, how can I create a search field (I hope maybe without using any package and without using setState function)?

Feel free to comment if you need more information!

How to fix this error? I would appreciate any help. Thank you in advance!

Update:

We can discuss in this room.


Solution

  • Try the following code:

    class FooSearchRiverpod extends ConsumerWidget {
      final controller = StreamController<String>();
      late final provider = obtainProvider(controller);
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        AsyncValue<List<String>> list = ref.watch(provider);
        return Column(
          children: [
            TextField(
              onChanged: controller.add,
              decoration: const InputDecoration(
                  prefixIcon: Icon(Icons.search), hintText: 'search...'),
            ),
            Expanded(
              child: list.when(
                loading: () => const Center(child: CircularProgressIndicator()),
                error: (err, stack) => Center(
                  child: Padding(
                    padding: const EdgeInsets.all(32.0),
                    child: Card(elevation: 4, child: Text(err.toString())),
                  ),
                ),
                data: (list) => AnimatedSwitcher(
                  duration: const Duration(milliseconds: 400),
                  child: ListView.builder(
                    itemCount: list.length,
                    itemBuilder: (ctx, i) => ListTile(
                      key: UniqueKey(),
                      title: Text(list[i]),
                      onTap: () => debugPrint('onTtap: ${list[i]}'),
                    ),
                  ),
                ),
              ),
            ),
          ],
        );
      }
    
      obtainProvider(StreamController<String> controller) {
        return StreamProvider.autoDispose<List<String>>((ref) => controller.stream
               .debounce(const Duration(milliseconds: 1000))
               .asyncMap(_getAPI));
      }
    
      final list = ['foo', 'foo 2', 'bar', 'bar 2', 'spam'];
      Future<List<String>> _getAPI(String query) async {
        debugPrint('_getAPI: |$query|');
        if (query.isEmpty) return [];
        return list.where((s) => s.contains(query)).toList();
      }
    }