Search code examples
flutterdartgoogle-cloud-firestore

How can I listen to document changes inside referenced documents in firestore?


Is it possible to listen to changes inside referenced documents within an array 'events' in my collection 'calendar'. Within this array I save references to another collection where I store my calendar item information that I then read out per day in my calendar?

With my current code it only listens to changes in the calendar documents. But not in the referenced documents.

calendar document structure:

array : events
   0: assignments/reference(docid)
   1: assignments/reference(docid)

assignments document structure :

String date : dd/MM/yyyy
String eventType : Public opening
String time : HH:mm-HH:mm
String responsible : username
  void listenToDocumentChanges(DateTime selectedDay) {
    final formattedDate = DateFormat('yyyy-MM-dd').format(selectedDay);

    FirebaseFirestore.instance
        .collection('calendar')
        .doc(formattedDate)
        .snapshots()
        .listen((calendarDocSnapshot) async {
      try {
        state = state.copyWith(isLoading: true, hasError: false);

        if (calendarDocSnapshot.exists) {
          final data = calendarDocSnapshot.data() ?? {};
          final events = (data['events'] as List?)?.cast<dynamic>() ?? [];

          final resolvedEvents = await resolveEvents(events);

          // Update the cache with events for the selected day
          state = state.copyWith(
            eventsCache: {
              ...state.eventsCache,
              formattedDate: resolvedEvents,
            },
            isLoading: false,
          );

          print(
              'Updated cache with ${resolvedEvents.length} events for       $formattedDate');
        } else {
          state = state.copyWith(
            eventsCache: {
              ...state.eventsCache,
              formattedDate: [],
            },
            isLoading: false,
          );

          print('No document exists for date: $formattedDate');
        }
      } catch (e) {
        logError('Error in listenToDocumentChanges', e);

        state = state.copyWith(
          eventsCache: {
            ...state.eventsCache,
            formattedDate: [],
          },
          isLoading: false,
          hasError: true,
        );
      }
    }, onError: (error) {
      logError('Snapshot listener error', error);

      state = state.copyWith(
        eventsCache: {
          ...state.eventsCache,
          formattedDate: [],
        },
        isLoading: false,
        hasError: true,
      );
    });
  }

    Future<List<Map<String, dynamic>>> resolveEvents(List<dynamic> events) async {
    try {
      final List<Map<String, dynamic>> resolvedEvents = [];
      final List<DocumentReference> references = [];

      for (var event in events) {
        if (event is DocumentReference) {
          references.add(event);
        } else if (event is Map<String, dynamic>) {
          resolvedEvents.add(event);
        }
      }

      if (references.isNotEmpty) {
        final snapshots = await Future.wait(references.map((ref) => ref.get()));
        for (var snapshot in snapshots) {
          if (snapshot.exists) {
            final data = snapshot.data() as Map<String, dynamic>?;
            if (data != null) {
              resolvedEvents.add({
                ...data,
                'reference': snapshot.reference.path,
              });
            }
          }
        }
      }

      return resolvedEvents;
    } catch (e) {
      logError('Error resolving events', e);
      return [];
    }
  }

    void logError(String message, dynamic error) {
    print('$message: $error');
  }

Solution

  • If you want to also listen for changes in linked/references documents, you'll need to set up separate listeners for that. Firestore does not have a built-in feature for that.

    So:

    1. Read the events as you currently do.
    2. Set up separate listeners for the event documents.
    3. Track your listeners, so you can detach then when they're no longer needed.