How can i use Streambuilder with firestore?

125 Views Asked by At

How can I update my data with recent data from firestore ? this is my code so far


class Captain extends StatefulWidget {
  const Captain({
    Key? key,
  }) : super(key: key);
  @override
  _CaptainState createState() => _CaptainState();
}

class _CaptainState extends State<Captain> {
  PlutoGridStateManager? _stateManager; // Manages the state of the PlutoGrid.
  List<PlutoColumn>? columns;
  List<PlutoRow>? plutoRows;
  List<Map<String, dynamic>>? captainData;
  @override
  void initState() {
    super.initState();
  }

  void _updateGrid() async {
    // final captainData =
    //     Provider.of<FirebaseProvider>(context, listen: false).captainSnapShot;
    captainData = await FirebaseServices.getCaptainData();
    final keys = [
      'S/N',
      'NAME',
      'CODE',
      '42 CLEARANCE',
      'Surkhet/ATR42/72',
      'Tumlingtar/ATR42',
      'Pokhara/72',
      'Simara/72',
    ];
    columns = keys.map((key) {
      return PlutoColumn(
        title: key.toUpperCase(),
        field: key,
        type: PlutoColumnType.text(),
        //width: 200,
      );
      // }
    }).toList();
    captainData!.sort((a, b) => int.parse(a['S/N']).compareTo(int.parse(
        b['S/N']))); // Sorts the _captainData list by the 'S/N' column.
    final plutoRows = captainData!.map((captain) {
      final cells = captain.map((key, value) {
        if (key == '42 CLEARANCE' ||
            key == 'Surkhet/ATR42/72' ||
            key == 'Tumlingtar/ATR42' ||
            key == 'Pokhara/72' ||
            key == 'Simara/72') {
          // If the column name is one of the five columns that require a dropdown list, a PlutoCell object is created with the value set to 'CLEARED'.
          return MapEntry(
              key,
              PlutoCell(
                  value:
                      value)); // Sets the initial value of the cell to 'CLEARED'.
        } else {
          // If the column name is not one of the five columns that require a dropdown list, a PlutoCell object is created with the value set to the corresponding value in the _captainData list.
          return MapEntry(
              key,
              PlutoCell(
                  value: value ??
                      '')); // Sets the initial value of the cell to the corresponding value in the _captainData list.
        }
      });
      // A PlutoRow object is created with the cells list.
      return PlutoRow(cells: cells);
    }).toList();
    PlutoGridStateManager.initializeRowsAsync(
      columns!,
      plutoRows,
    ).then((value) {
      // _stateManager!.refRows.addAll(FilteredList(initialList: value));
      _stateManager!.insertColumns(0, columns!);
      _stateManager!.appendRows(plutoRows);
      _stateManager!.notifyListeners();
    });
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: MediaQuery.of(context).size.width,
      height: MediaQuery.of(context).size.height,
      child: PlutoGrid(
        configuration: const PlutoGridConfiguration(
          columnSize: PlutoGridColumnSizeConfig(
              resizeMode: PlutoResizeMode.pushAndPull),
        ),
        columns: [], // Empty list of columns.
        rows: [], // Empty list of rows.
        onSelected: (PlutoGridOnSelectedEvent event) {
          final PlutoRow row = _stateManager!.rows.elementAt(event.rowIdx!);
          final String userId = row.cells['NAME']!.value.toString();
          if (event.cell!.column.field == 'S/N' ||
              event.cell!.column.field == 'NAME' ||
              event.cell!.column.field == 'CODE') {
          } else {
            openDetail(event.cell, event.cell!.column.field, userId);
          }
        },
        mode: PlutoGridMode.selectWithOneTap,
        onChanged: (PlutoGridOnChangedEvent event) {},
        onLoaded: (PlutoGridOnLoadedEvent event) {
          _stateManager = event
              .stateManager; // Updates the _stateManager object with the new state of the PlutoGrid.
          _updateGrid();
        },
      ),
    );
  }

I tried something like this but i don't receive any realtime updates ?


  @override
  Widget build(BuildContext context) {
    final CollectionReference captainCollection =
        FirebaseFirestore.instance.collection('CAPTAIN_DATA');
    return SizedBox(
      width: MediaQuery.of(context).size.width,
      height: MediaQuery.of(context).size.height,
      child: StreamBuilder<QuerySnapshot>(
          stream: captainCollection.snapshots(),
          builder: (context, snapshot) {
            if (snapshot.hasError) {
              return const Text('Something went wrong');
            }

            if (snapshot.connectionState == ConnectionState.waiting) {
              return const Text('Loading...');
            }
            if (snapshot.hasData) {
              //print all the data received
              snapshot.data!.docs.forEach((element) {
                print(element.data());
              });
              _updateGrid();
            }
            return PlutoGrid(
              configuration: const PlutoGridConfiguration(
                columnSize: PlutoGridColumnSizeConfig(
                    resizeMode: PlutoResizeMode.pushAndPull),
              ),
              columns: [], // Empty list of columns.
              rows: [], // Empty list of rows.
              onSelected: (PlutoGridOnSelectedEvent event) {
                final PlutoRow row =
                    _stateManager!.rows.elementAt(event.rowIdx!);
                final String userId = row.cells['NAME']!.value.toString();
                if (event.cell!.column.field == 'S/N' ||
                    event.cell!.column.field == 'NAME' ||
                    event.cell!.column.field == 'CODE') {
                } else {
                  openDetail(event.cell, event.cell!.column.field, userId);
                }
              },
              mode: PlutoGridMode.selectWithOneTap,
              onChanged: (PlutoGridOnChangedEvent event) {},
              onLoaded: (PlutoGridOnLoadedEvent event) {
                _stateManager = event
                    .stateManager; // Updates the _stateManager object with the new state of the PlutoGrid.
                _updateGrid();
              },
            );
          }),
    );
  }

Any help is appriciated. one more question , do i need to get all the data if just one column is updated? how to handle minor changes by getting the value of only something that has changed not the whole database ?

1

There are 1 best solutions below

1
Adarsh Sharma On
The code you tried to use will not receive any real-time updates because you are not listening to the snapshot.documentChanges stream. This stream will emit an event for every document that is added, updated, or deleted in the collection. You can use this stream to update your UI with the latest data from Firestore.

Here is the code that you can use to listen to the snapshot.documentChanges stream:

final CollectionReference captainCollection =
    FirebaseFirestore.instance.collection('CAPTAIN_DATA');

return StreamBuilder<QuerySnapshot>(
  stream: captainCollection.snapshots(),
  builder: (context, snapshot) {
    if (snapshot.hasError) {
      return const Text('Something went wrong');
    }

    if (snapshot.connectionState == ConnectionState.waiting) {
      return const Text('Loading...');
    }

    if (snapshot.hasData) {
      // Listen to document changes
      snapshot.docChanges.forEach((change) {
        if (change.type == DocumentChangeType.added) {
          // A new document was added
          print('A new document was added: ${change.doc.id}');
        } else if (change.type == DocumentChangeType.modified) {
          // An existing document was modified
          print('An existing document was modified: ${change.doc.id}');
        } else if (change.type == DocumentChangeType.removed) {
          // An existing document was removed
          print('An existing document was removed: ${change.doc.id}');
        }
      });

      // Update the UI with the latest data
      _updateGrid();
    }

    return PlutoGrid(
      configuration: const PlutoGridConfiguration(
        columnSize: PlutoGridColumnSizeConfig(
            resizeMode: PlutoResizeMode.pushAndPull),
      ),
      columns: [], // Empty list of columns.
      rows: [], // Empty list of rows.
      onSelected: (PlutoGridOnSelectedEvent event) {
        final PlutoRow row =
            _stateManager!.rows.elementAt(event.rowIdx!);
        final String userId = row.cells['NAME']!.value.toString();
        if (event.cell!.column.field == 'S/N' ||
            event.cell!.column.field == 'NAME' ||
            event.cell!.column.field == 'CODE') {
        } else {
          openDetail(event.cell, event.cell!.column.field, userId);
        }
      },
      mode: PlutoGridMode.selectWithOneTap,
      onChanged: (PlutoGridOnChangedEvent event) {},
      onLoaded: (PlutoGridOnLoadedEvent event) {
        _stateManager = event
            .stateManager; // Updates the _stateManager object with the new state of the PlutoGrid.
        _updateGrid();
      },
    );
  },
);

& for the 2nd part.To handle minor changes by getting the value of only something that has changed, you can use the snapshot.docChanges.where() method.

you could use the following code to filter the stream to only include document changes where the NAME field has been modified:

snapshot.docChanges.where((change) => change.doc.data['NAME'] != null);

This code will only emit events for document changes where the NAME field is not null. You can then use this filtered stream to update your UI with the latest data for the NAME field.