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.
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();
}
}