I'm trying to get data from a database into a FeatureBuilder using Riverpod. This is the first time using Riverpod and I'm struggeling.
You can find the code of the two short files here, including the generated file by the riverpod generator: https://pastebin.com/ewFHBfnZ
The problem is that when I call any async function, no results are returned.
I think the problem is in this method: Future<List<dynamic>> getEvents(int event_id) async{...}
If I mock the data, the data will show in the widget, but the moment I call something async, the results stay empty, even no error message.
For example if I just un-comment this line: //Connection conn = await openDatabaseConnection();
The mocked data will not show anymore.
(btw, I know the database call will return data, I have tested it in a unit test.
Could somebody help me and point me in the right direction?
Thanks!
First, it seems you're nesting providers, EventsNotifier
is of type AutoDisposeAsyncNotifier
but you're creating another one called EventsNotifierRepositoryProvider
where you return an EventsNotifier
and that provider works as a regular provider
EventsNotifierProvider -> of type AsyncNotifier
EventsNotifierRepositoryProvider -> of type EventsNotifier
So you should just keep one, EventsNotifierRepositoryProvider
should be enough, and that should be just a Provider with no AsyncNotifier:
@riverpod
EventsRepository eventsRepository(EventsRepositoryRef ref) => const EventsRepository(); /// Maybe the DB should be a provider too?
class EventsRepository {
const EventsRepository();
Future<List<dynamic>> getEvents(int event_id) async {
// This does not work: the moment I execute an 'await' I get no results
//Connection conn = await openDatabaseConnection();
//this.events = await conn.execute('SELECT id, name, description, place, is_active FROM events WHERE type = ${event_id}');
// mocking the results, this works:
final events = [[1,'EventA','','', true], [2,'EventB','','', false]];
return events;
}
Future<void> updateEvents(int i, bool val) async {
/// Connection conn = await openDatabaseConnection();
/// update DB
}
}
@riverpod
class EventsController extends _$EventsController {
late final id;
@override
FutureOr<List<dynamic>> build({int id}) {
this.id = id;
return ref.read(eventsRepositoryProvider).getEvents(id);
}
Future<void> updateEvents({
required int i, // the index in the events list
required bool val,
}) async {
final repo = ref.read(eventsRepositoryProvider);
state = const AsyncLoading();
// perform the mutation and update the state
state = await AsyncValue.guard(() async {
await repo.updateEvents(i, val);
return ref.read(eventsRepositoryProvider).getEvents(id);
});
}
}
And finally no need to use a FutureBuilder because the build method already initializes calling for events
class EventsForm extends ConsumerStatefulWidget {
const EventsForm({Key? key}) : super(key: key);
@override
ConsumerState<EventsForm> createState() => _EventsForm();
}
class _EventsForm extends ConsumerState<EventsForm> {
GlobalKey<FormState> _key = new GlobalKey();
int event_id = 17;
@override
Widget build(BuildContext context) {
/// check when there is an error maybe?
final events = ref.watch(eventsNotifierRepositoryProvider.select((s) => s.maybeWhen(data: (v) => v, orElse() => []);
final itemCount = events.length;
return Scaffold(
appBar: AppBar(
title: Text('Events'),
),
body: ListView.builder(
itemCount: itemCount,
itemBuilder: (BuildContext context, int i) {
return CheckboxListTile(
value:events[i][4] ,
onChanged: (bool? val) {
ref.read(eventsControllerProvider.notifier).updateEvents(i: i, val: val!);
},
title: Text(events[i][1]),
),
},
);
} // widget
} //class