Search code examples
riverpod

no data from provider when using async OPEN


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!


Solution

  • 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