Load more data from Firebase Firestore

Hi friends,

I want to load data from my Firebase Cloud Firestore database in loadMoreViewBuilder, I'm getting my data from Firestore and passing the new list in the SfDataGrid source like this:

  void _getMoreData() async {
    customCompsListData.clear();

    QuerySnapshot queryDocs = await FirebaseFirestore.instance
        .collection('companies')
        .where('id', isEqualTo: _filterInputCtrl.text)
        .orderBy('dateOfCreation', descending: true)
        .limit(30)
        .get();

    //print('GETT DOCS: ${queryDocs.docs.length}');

    if (queryDocs.docs.isNotEmpty) {
      // customCompsListData = queryDocs.docs;
      for (var d in queryDocs.docs) {
        customCompsListData.add(ComppGlobalModel.fromDocument(d));
        //print('COMP DATA: ${d['name']}  --- SICS: ${d['sic']}');
      }
    }
  }


How can I adapt this to the handleLoadMoreRows ​:

  //! LOAD MORE IN LISTS-------------------------------------------------------/
  @override
  Future<void> handleLoadMoreRows() async {
    await Future.delayed(const Duration(milliseconds: 1000));
    print('LOAD MOREEEEEE!');

    // Here we are getting the last dataGridRow's companyID

    // It's unique ID and we are using it to get the last documet

    // from the FireStore collection.

    var lastRowCompanyID = dataGridRows.last.getCells()[1].value;
    print('LAST DOC ID: $lastRowCompanyID');

    // Here we are getting the last document from the FireStore collection

    var lastDoument = await FirebaseFirestore.instance
        .collection('companies')
        .where("docId", isEqualTo: lastRowCompanyID)
        .get()
        .then((value) => value.docs.first);

    // Here we are getting the next 10 documents from the FireStore collection

    // by using the last document from the previous query.

    var snap =
        await compsCollection.startAfterDocument(lastDoument).limit(10).get();

    // Here we are mapping the data to the DataGridRow

    // and adding it to the dataGridRows list.

    // The dataGridRows is the local collection

    // which is assigned to the `rows` property of DataGridSource.

    for (var data in snap.docs) {
      dataGridRows.add(DataGridRow(cells: [
        DataGridCell<String>(columnName: 'companyName', value: data['name']),
        DataGridCell<String>(
            columnName: 'companyNumber', value: data['number']),
        DataGridCell<String>(columnName: 'type', value: data['type']),
        DataGridCell<List>(columnName: 'sic', value: data['sic']),
        DataGridCell<String>(columnName: 'address', value: data['postal_code']),
        DataGridCell<String>(columnName: 'directors', value: data['directors']),
        DataGridCell<String>(
            columnName: 'nationality', value: data['director_nationality']),
        DataGridCell<String>(
            columnName: 'incorporationDate', value: data['dateOfCreation']),
        DataGridCell<String>(
            columnName: 'confirmationStatement',
            value: data['confirmationStatement']['next_due'].toString()),
        DataGridCell<String>(columnName: 'status', value: data['status']),
      ]));
    }

    notifyListeners();
  }


Thanks in advance!


3 Replies

TP Tamilarasan Paranthaman Syncfusion Team November 30, 2023 02:41 PM UTC

Hi Dorin,

We have reviewed the code snippets you provided, and it appears that additional data from Firebase Firestore is already being loaded properly in the `handleLoadMoreRows` method. However, there's a distinction in the Firestore query between `handleLoadMoreRows` and `_getMoreData` methods.


To provide the best assistance, we would appreciate more precise details about your specific requirements or the issues you're encountering. This additional information will enable us to better understand your needs and offer a more accurate solution.


Regards,

Tamilarasan



DO Dorin November 30, 2023 10:43 PM UTC

Hi  Tamilarasan,

Sorry for the miss understanding, I attached a few videos and my scripts to be more detailed about my problem, I'm trying to retrieve data from an external class, and it's working, but to display the new data I need to call again setState method, soo again my selected fields still unselected on every loadMoreData, just to understand I'm passing the list from other class to class the script where SfGridData is:

My parent class 
ProjectOverview :

SfDataGridCompanies(
                    timeFilterIndex: timeFilterValue,
                    dataGridController: _dataGridController,
                    dataGridKey: _key,
                    enableListSelection: _enableSelection,
                    fieldSearch: _filterSerchFieldParam,
                    fieldInput: _filterInputFieldParam,
                    compsListData: customCompsListData,
                    funcTriggerLoadMoreItems: (lastDocId) async {
                      await loadMoreCompsNow(lastDocId).whenComplete(() {
                        if (mounted) {
                          setState(() {});
                        }
                      });
                    },
                    funcSelectionChanged: (selectedComps) {
                      _funcUpdateSelectedComps();

                      if (!_dataGridController.selectedRows.length.isNegative) {
                        selectedCompsList = selectedComps;
                        //print('SELECTED COMPS: ${selectedComps.length}');

                        // for (var c in selectedComps) {
                        //   print('COMPANIE SELECTATA: ${c.companyName}');
                        // }
                      }
                    },
                  )



In this class I'm getting the data for the grid list like this:

  void _getCustomCompaniesList() async {
    customCompsListData.clear();
        print('CASE NAT: $_filterSerchFieldParam');
        QuerySnapshot queryDocs = await FirebaseFirestore.instance
            .collection('companies')
            .where('director_nationality', isEqualTo: _filterInputCtrl.text)
            .orderBy('dateOfCreation', descending: true)
            .limit(15)
            .get();

        //print('GETT DOCS: ${queryDocs.docs.length}');

        if (queryDocs.docs.isNotEmpty) {
          // customCompsListData = queryDocs.docs;
          for (var d in queryDocs.docs) {
            customCompsListData.add(ComppGlobalModel.fromDocument(d));
            //print('COMP DATA: ${d['name']}  --- SICS: ${d['sic']}');
          }
        }
    }
  }


And passing to 
SfDataGridCompanies.dart ​(the list data) because I can't load the data in SfGridData class because of the filters I need to apply, so I need to retrieve and apply the data in build like this:

  Widget _buildDataGrid() {
    //print('DATA: ${widget.compsListData.length}');
    //compsDataSource = ComppanyDataSource(widget.compsListData);
    companiesListData = widget.compsListData;
    compsDataSource = ComppanyDataSource(companiesListData);



So I managed to make some kind of callback in 
loadMoreViewBuilder method to reload in my parent widget the next docs, I don't know if this is the best way and I need to call the setState() ​to update the UI, because the @overriding method of Future<void> handleLoadMoreRows()  nothing happens :(...please check my code if you need more details I'm here anytime, please guide me asap, thanks in advance! 


My files are attached below:


Attachment: Tamilarasan_load_MoreData_e823ed76.zip


TP Tamilarasan Paranthaman Syncfusion Team December 1, 2023 01:31 PM UTC

Dorin,

Upon reviewing the provided code snippet, it appears that in the `loadMoreCompsNow` method, data is fetched from Firestore and added to the `customCompsListData`, followed by calling `setState`.


Subsequently, in the `SfDataGridCompaniesState` class, a new instance of `DataGridSource` is created with the updated `companiesListData`. This approach leads to the creation of a new `DataGridSource` object every time the build method is called, resulting in the loss of DataGrid states such as selection and other related states. Please check the following code snippet of your sample.


class SfDataGridCompaniesState extends State<SfDataGridCompanies> {



  Widget _buildDataGrid() {

    //print('DATA: ${widget.compsListData.length}');

    //compsDataSource = ComppanyDataSource(widget.compsListData);

    companiesListData = widget.compsListData;

    compsDataSource = ComppanyDataSource(companiesListData);

    //print('DATA RECEIVED: ${widget.compsListData.length}');

    return Column(

      mainAxisAlignment: MainAxisAlignment.start,

      crossAxisAlignment: CrossAxisAlignment.center,

      mainAxisSize: MainAxisSize.max,


We suspect this is the root cause of the issue you are facing. To resolve this issue, it's crucial to maintain the `DataGridSource` object as a long-lived object. Instead of creating a new `DataGridSource` instance each time, consider generating `DataGridRow` objects based on the fetched data and adding them to the `DataGridRows` collection. To refresh the view, call `notifyListeners` from the `DataGridSource` class. Please check the following code snippet.


class EmployeeDataSource extends DataGridSource {


  // local collection which has the DataGridSource.rows

  List<DataGridRow> dataGridRows = [];


  @override

  List<DataGridRow> get rows => dataGridRows;


  void _addMoreRows(int count) {

    final Random random = Random();

    int startIndex = dataGridRows.isNotEmpty ? dataGridRows.length : 0,

        endIndex = startIndex + count;

    for (int i = startIndex; i < endIndex; i++) {

      // add the new row data in the dataGridRows collection.

      dataGridRows.add(DataGridRow(cells: [

        DataGridCell<int>(columnName: 'id', value: 1000 + i),

        DataGridCell<String>(

            columnName: 'name',

            value: _names[random.nextInt(_names.length - 1)]),

        DataGridCell<String>(

            columnName: 'designation',

            value: _designation[random.nextInt(_designation.length - 1)]),

        DataGridCell<int>(

            columnName: 'salary', value: 10000 + random.nextInt(10000)),

      ]));

    }

  }


  @override

  Future<void> handleLoadMoreRows() async {

    await Future.delayed(const Duration(seconds: 5));

    _addMoreRows(10);

    notifyListeners();

  }
...
}


We have included a basic 'load more' sample in this response. In this sample, selecting a row and vertically scrolling the DataGrid to load more data will ensure that the selection is retained. Please refer to the attached basic sample for further details.

Regards,

Tamilarasan


Attachment: sample_dbee1f2c.zip

Loader.
Up arrow icon