Get stream data from Firebase Cloud Firestore in SfDataGrid Paging

Hi friends,

I want to load data from my Firebase Cloud Firestore database and paginate all data, I tried all the samples and examples but I'm stuck for more than a week, please guide me with this, thanks!

My OrderInfoDataGridSource script:

// import 'dart:ffi';
import 'dart:math' as math;
import 'package:b2bboostify/_playground/datagrid/oreder_info.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_datagrid/datagrid.dart';

/// Set order's data collection to data grid source.
class OrderInfoDataGridSource extends DataGridSource {
  /// Creates the order data source class with required details.
  OrderInfoDataGridSource({
    //this.collection,
    this.orderDataCount,
    this.ordersCollection,
    bool? isFilteringSample,
    required this.funcCallback,
  }) {
    this.isFilteringSample = isFilteringSample ?? false;
    // orders = ordersCollection ??
    //     getOrders(
    //       orders,
    //       orderDataCount ?? 100,
    //     );

    // collection = FirebaseFirestore.instance
    //     .collection('stream')
    //     .doc('companies')
    //     .collection('all');

    buildDataGridRows();
  }

  // ADDITIONAL PARAMS
  final List<String> _statusList = <String>['active', 'dissolved'];
  //CollectionReference collection;
  CollectionReference collection = FirebaseFirestore.instance
      .collection('stream')
      .doc('companies')
      .collection('all');

  /// Get data count of an order.
  int? orderDataCount;
  final math.Random _random = math.Random();

  /// Instance of an order.
  List<CompanyModelOrderInfo> orders = <CompanyModelOrderInfo>[];

  /// Instance of an order collection for rtl sample
  List<CompanyModelOrderInfo>? ordersCollection;

  /// Instance of DataGridRow.
  List<DataGridRow> dataGridRows = <DataGridRow>[];

  /// Checks whether the source is used for the filtering sample or not.
  late bool isFilteringSample;

  final Function(String) funcCallback;

  /// Building DataGridRows.
  void buildDataGridRows() {
    dataGridRows = orders.map<DataGridRow>((CompanyModelOrderInfo order) {
      return DataGridRow(cells: <DataGridCell>[
        DataGridCell<String>(
            columnName: 'companyName', value: order.companyName),
        DataGridCell<String>(
            columnName: 'companyNumber', value: order.companyNumber),
        DataGridCell<String>(columnName: 'type', value: order.type),
        DataGridCell<String>(columnName: 'sic', value: order.sic),
        DataGridCell<String>(columnName: 'address', value: order.address),
        DataGridCell<String>(columnName: 'directors', value: order.directors),
        DataGridCell<String>(
            columnName: 'confirmationDate', value: order.confirmationDate),
        DataGridCell<String>(
            columnName: 'incorporationDate', value: order.incorporationDate),
        //DataGridCell<String>(columnName: 'postalCode', value: order.postalCode),
        DataGridCell<String>(columnName: 'status', value: order.status),
      ]);
    }).toList();
  }

  //! FIREBASE STREAM-----------------------------------------------------------
  Stream<QuerySnapshot> getStream() {
    //return collection.snapshots();
    return collection.orderBy('companyName', descending: false).snapshots();
  }

  Future<void> buildStream(AsyncSnapshot snapShot) async {
    print('BUILD STREAM DATA: ${snapShot.data}');
    if (snapShot.hasError ||
        snapShot.data == null ||
        snapShot.data.docs.length == 0) {
      print('BUILD STREAM DATA ERROR: ${snapShot.error}');
      return Future<void>.value();
    }

    await Future.forEach(snapShot.data.docs, (element) {
      final CompanyModelOrderInfo data = CompanyModelOrderInfo.fromSnapshot(
          element as DocumentSnapshot<Object>);
      if (!orders.any((element) => element.companyName == data.companyName)) {
        orders.add(data);
        print('STREAM DOC ADDED: ${element['companyName']}');
      }
    });

    updateDataGridDataSource();

    return Future<void>.value();
  }

  void updateDataGridDataSource() {
    print('BUILD STREAM DATA: ${orders.length}');
    notifyListeners();
  }

  //!---------------------------------------------------------------------------

  // Overrides
  @override
  List<DataGridRow> get rows => dataGridRows;

  @override
  DataGridRowAdapter buildRow(DataGridRow row) {
    final int rowIndex = dataGridRows.indexOf(row);
    Color backgroundColor = Colors.transparent;
    if ((rowIndex % 2) == 0) {
      backgroundColor = Colors.grey.withOpacity(0.07);
    }

    return DataGridRowAdapter(color: backgroundColor, cells: <Widget>[
      Container(
          padding: const EdgeInsets.all(8),
          alignment: Alignment.centerLeft,
          child: _buildCellTitle(
            row.getCells()[0].value.toString(),
            row.getCells()[1].value.toString(),
          )

          // Text(
          //   row.getCells()[0].value.toString(),
          //   overflow: TextOverflow.ellipsis,
          // ),
          ),
      Container(
        padding: const EdgeInsets.all(8),
        alignment: Alignment.centerLeft,
        child: Text(
          row.getCells()[1].value.toString(),
          overflow: TextOverflow.ellipsis,
        ),
      ),
      Container(
        padding: const EdgeInsets.all(8),
        alignment: Alignment.centerLeft,
        child: Text(
          row.getCells()[2].value.toString(),
        ),
      ),
      Container(
        padding: const EdgeInsets.all(8),
        alignment: Alignment.centerLeft,
        child: Text(
          row.getCells()[3].value.toString(),
          overflow: TextOverflow.ellipsis,
        ),
      ),
      Container(
        padding: const EdgeInsets.all(8),
        alignment: Alignment.centerLeft,
        child: Text(
          row.getCells()[4].value.toString(),
          overflow: TextOverflow.ellipsis,
        ),
      ),
      Container(
        padding: const EdgeInsets.all(8),
        alignment: Alignment.centerLeft,
        child: Text(
          row.getCells()[5].value.toString(),
          overflow: TextOverflow.ellipsis,
        ),
      ),
      Container(
        padding: const EdgeInsets.all(8),
        alignment: Alignment.centerLeft,
        child: Text(
          row.getCells()[6].value.toString(),
          overflow: TextOverflow.ellipsis,
        ),
      ),
      Container(
        padding: const EdgeInsets.all(8),
        alignment: Alignment.centerLeft,
        child: Text(
          row.getCells()[7].value.toString(),
          overflow: TextOverflow.ellipsis,
        ),
      ),
      Container(
        padding: const EdgeInsets.all(2.0),
        alignment: Alignment.center,
        child: _getStatusTypeWidget(row.getCells()[8].value.toString()),
      ),
    ]);
  }

  Widget _buildCellTitle(String title, String companyId) {
    return Container(
      color: Colors.transparent,
      child: InkWell(
        onTap: () {
          funcCallback('commp');

          print('TAP ON COMPANY: $companyId');
        },
        child: Text(
          title,
          overflow: TextOverflow.ellipsis,
        ),
      ),
      // TextButton(
      //   onPressed: () {
      //     print('TAP ON COMPANY: $companyId');
      //   },
      //   child: Text(title),
      // ),
    );
  }

  Widget _getStatusTypeWidget(dynamic val) {
    if (val == 'active') {
      return InkWell(
        onTap: () {
          print('TAP ON: ${val.toString()}');
        },
        child: Container(
          padding: const EdgeInsets.only(top: 3, bottom: 3, left: 5, right: 5),
          decoration: BoxDecoration(
            // color: Provider.of<PathProvider>(ctx, listen: false).isDarkMode
            //     ? Colors.grey.shade800
            //     : Colors.grey.shade200,
            borderRadius: BorderRadius.circular(5),
            border: Border.all(width: 0.6),
          ),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            mainAxisSize: MainAxisSize.min,
            children: [
              Container(
                width: 8,
                height: 8,
                decoration: const BoxDecoration(
                  color: Colors.greenAccent,
                  shape: BoxShape.circle,
                ),
              ),
              const SizedBox(
                width: 5,
              ),
              Text('active'.tr().toUpperCase(),
                  style: const TextStyle(
                      fontSize: 12, fontWeight: FontWeight.w800)),
            ],
          ),
        ),
      );
    } else {
      return Container(
        padding: const EdgeInsets.only(top: 3, bottom: 3, left: 5, right: 5),
        decoration: BoxDecoration(
          // color: Provider.of<PathProvider>(ctx, listen: false).isDarkMode
          //     ? Colors.grey.shade800
          //     : Colors.grey.shade200,
          borderRadius: BorderRadius.circular(5),
          border: Border.all(width: 0.6),
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisSize: MainAxisSize.min,
          children: [
            Container(
              width: 8,
              height: 8,
              decoration: const BoxDecoration(
                color: Colors.redAccent,
                shape: BoxShape.circle,
              ),
            ),
            const SizedBox(
              width: 5,
            ),
            Text('dissolved'.tr().toUpperCase(),
                style:
                    const TextStyle(fontSize: 12, fontWeight: FontWeight.w800)),
          ],
        ),
      );
    }
  }

  @override
  Future<void> handleLoadMoreRows() async {
    await Future<void>.delayed(const Duration(seconds: 1));
    orders = getOrders(orders, 15);
    buildDataGridRows();
    notifyListeners();
  }

  @override
  Future<void> handleRefresh() async {
    await Future<void>.delayed(const Duration(seconds: 1));
    orders = getOrders(orders, 15);
    buildDataGridRows();
    notifyListeners();
  }

  /// Update DataSource
  void updateDataSource() {
    notifyListeners();
  }

  //  Order Data's
  final List<String> _names = <String>[
    'Crowley',
    'Blonp',
    'Folko',
    'Irvine',
    'Folig',
    'Picco',
    'Frans',
    'Warth',
    'Linod',
    'Simop',
    'Merep',
    'Riscu',
    'Seves',
    'Vaffe',
    'Alfki',
  ];

  /// Get orders collection
  List<CompanyModelOrderInfo> getOrders(
    List<CompanyModelOrderInfo> orderData,
    int count,
  ) {
    final int startIndex = orderData.isNotEmpty ? orderData.length : 0,
        endIndex = startIndex + count;
    List<String> names = _names;

    for (int i = startIndex; i < endIndex; i++) {
      orderData.add(CompanyModelOrderInfo(
        'Company name $i',
        (78392 + i).toString(),
        'Ltd',
        _random.nextInt(1000000).toString(),
        'Address ${1 + i}',
        names[i < names.length ? i : _random.nextInt(names.length - 1)],
        //'Address ${500 + i}',
        '03/03/2024',
        '05/10/2023',
        _statusList[i < _statusList.length
            ? i
            : _random.nextInt(_statusList.length - 1)],
        //'HA4 7QP',
      ));
    }
    return orderData;
  }
}


But all the time I'm getting null data from Firebase :( and I don't know why, any help will be much appreciated!

The main widget:

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<QuerySnapshot>(
        stream: orderInfoDataSource.getStream(),
        builder: (context, snapshot) {
          orderInfoDataSource.buildStream(snapshot);
          return (snapshot.hasError && !snapshot.hasData)
              ? Container(
                  color: Colors.transparent,
                  child: const Center(child: Text('Error or empty')),
                )
              : _buildDataGrid();

          // SfDataGrid(
          //     source: orderInfoDataSource,
          //     columnWidthMode: ColumnWidthMode.fill,
          //     columns:
          //     [
          //       GridColumn(
          //         columnName: 'companyName',
          //         //width: 170,
          //         //columnWidthMode: ColumnWidthMode.auto,
          //         label: Container(
          //           padding: const EdgeInsets.all(8),
          //           alignment: Alignment.centerLeft,
          //           child: Text(
          //             'tableCompanyName'.tr(),
          //             overflow: TextOverflow.clip,
          //             softWrap: true,
          //           ),
          //         ),
          //       ),
          //     ],
          //   );

          // builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
          //   print(
          //       'ORDERS INFO ITEMS: ${orderInfoDataSource.dataGridRows.length}');
          //   return orderInfoDataSource.dataGridRows.length.isNegative
          //       // return jsonDataGridSource.companiesList.isEmpty
          //       //return realTimeUpdateDataGridSource.rows.isEmpty
          //       ? const Center(
          //           child: B2BBLoadingLogo(),
          //         )
          //       : _buildLayoutBuilder();
        });
  }


Please help or guide me with this issue, thanks in advance!






5 Replies

TP Tamilarasan Paranthaman Syncfusion Team October 13, 2023 03:51 PM UTC

Hi Dorin Buraca,

Based on the information provided, we thoroughly tested the application using the same Firebase Firestore backend structure, which includes a stream (collection) -> companies (document) -> all (collection) hierarchy. Our tests on our end showed no issues, and data retrieval from Firestore was successful.


Could you please provide details about the print methods that are executed within the `buildStream` method and the output values they produce? Additionally, kindly confirm whether `snapshot.hasData` returns false, leading to the display of an 'Error or empty' text on the screen. If you observe this behavior, please double-check that the collection and document names are correctly matched.


Furthermore, we've examined your code and are unclear about how you construct rows based on the underlying collection orders and the steps you follow in the `_buildDataGrid` method.


To assist you more effectively, we kindly request that you share the entire sample (excluding Firebase credentials) so that we can conduct debugging on our end. By providing the sample, you will greatly assist us in identifying and resolving the issue.


As a reference, we've attached a simple sample (excluding Firebase credentials) that we tested on our end. Please review the attached sample and the Firestore structure outlined in the provided image.


Regards,

Tamilarasan


Attachment: Sample_de3f9e66.zip



DO Dorin October 17, 2023 05:47 PM UTC

Hi @Tamilarasan,

I managed to make it work from your example, thanks a lot, I changed the code:

import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:syncfusion_flutter_datagrid/datagrid.dart';

// Future<void> main() async {
//   WidgetsFlutterBinding.ensureInitialized();
//   await Firebase.initializeApp(options: defaultFirebaseOptions);
//   runApp(MaterialApp(
//       title: 'Flutter Demo',
//       theme: ThemeData(primarySwatch: Colors.blue),
//       home: const SfDataGridDemo()));
// }

// // Replace the defaultFirebaseOptions with your own Firebase options.
// const defaultFirebaseOptions = FirebaseOptions(
//   apiKey: '',
//   authDomain: '',
//   projectId: '',
//   storageBucket: '',
//   messagingSenderId: '',
//   appId: '',
// );

class SfDataGridDemo extends StatefulWidget {
  const SfDataGridDemo({Key? key}) : super(key: key);

  @override
  SfDataGridDemoState createState() => SfDataGridDemoState();
}

class SfDataGridDemoState extends State<SfDataGridDemo> {
  late EmployeeDataSource employeeDataSource;
  List<SyncfusionCompModel> employeeData = [];

  @override
  void initState() {
    super.initState();
    employeeDataSource = EmployeeDataSource(employeeData);
  }

  Widget _buildDataGrid() {
    return StreamBuilder(
      stream: employeeDataSource.getStream(),
      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
        if (snapshot.hasData) {
          print('ALL DOCUMENTS IN COLLECTION: ${snapshot.data!.docs.length}');
          for (var data in snapshot.data!.docs) {
            employeeData.add(
              SyncfusionCompModel(
                //-----------------------------------------------------------/
                address: data['address'],
                companyName: data['companyName'],
                compNumber: data['companyNumber'],
                directors: data['directors'],
                companyType: data['type'],
                confirmationDate: data['confirmationDate'],
                incorporationDate: data['incorporationDate'],
                sic: data['sic'],
                status: data['status'],
              ),
            );
          }
          employeeDataSource = EmployeeDataSource(employeeData);

          return SfDataGrid(
            source: employeeDataSource,
            columns: getColumns,
            columnWidthMode: ColumnWidthMode.fill,
            showCheckboxColumn: true,
            selectionMode: SelectionMode.multiple,
          );
        } else {
          return const Center(
            child: CircularProgressIndicator(
              color: Colors.orange,
            ),
          );
        }
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // appBar: AppBar(
      //   title: const Text('Syncfusion Flutter Datagrid FireStore Demo'),
      // ),
      body: _buildDataGrid(),
    );
  }
}

class EmployeeDataSource extends DataGridSource {
  EmployeeDataSource(this.employeeData) {
    if (employeeData.isNotEmpty) {
      _buildDataRow();
    }
  }

  final collection = FirebaseFirestore.instance
      .collection('stream')
      .doc('companies')
      .collection('all');

  //final collection = refStream.doc('companies').collection('all').snapshots();

  Stream<QuerySnapshot> getStream() {
    //return collection.snapshots();
    // Future.delayed(const Duration(seconds: 5), () {
    //   print('STREAM DELAY');
    // });
    return collection.orderBy('companyName', descending: false).snapshots();
  }

  List<DataGridRow> dataGridRows = [];
  List<SyncfusionCompModel> employeeData;

  void _buildDataRow() {
    dataGridRows = employeeData
        .map<DataGridRow>((e) => DataGridRow(cells: [
              DataGridCell<String>(
                  columnName: 'companyName', value: e.companyName),
              DataGridCell<String>(
                  columnName: 'companyNumber', value: e.compNumber),
              DataGridCell<String>(columnName: 'type', value: e.companyType),
              DataGridCell<String>(columnName: 'sic', value: e.sic),
              DataGridCell<String>(columnName: 'address', value: e.address),
              DataGridCell<String>(columnName: 'directors', value: e.directors),
              DataGridCell<String>(
                  columnName: 'confirmationDate', value: e.confirmationDate),
              DataGridCell<String>(
                  columnName: 'incorporationDate', value: e.incorporationDate),
              DataGridCell<String>(columnName: 'status', value: e.status),
            ]))
        .toList();
  }

  //! ROWS----------------------------------------------------------------------
  @override
  List<DataGridRow> get rows => dataGridRows;

  @override
  DataGridRowAdapter buildRow(
    DataGridRow row,
  ) {
    return DataGridRowAdapter(
        cells: row.getCells().map<Widget>((e) {
      if (e.columnName == 'directors') {
        return Container(
          alignment: Alignment.centerLeft,
          padding: const EdgeInsets.all(8.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(e.value.toString()),
              Row(
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  Image.asset(
                    'assets/images/flags/gb.png',
                    width: 15,
                    height: 10,
                  ),
                  const SizedBox(
                    width: 5,
                  ),
                  const Text('British'),
                ],
              ),
            ],
          ),
        );
      } else {
        return Container(
          alignment: Alignment.centerLeft,
          padding: const EdgeInsets.all(8.0),
          child: Text(e.value.toString()),
        );
      }
    }).toList());
  }
}

//! TOP COLUMNS-----------------------------------------------------------------
List<GridColumn> get getColumns {
  return <GridColumn>[
    GridColumn(
        columnName: 'companyName',
        allowSorting: true,
        label: Container(
            padding: const EdgeInsets.all(8.0),
            alignment: Alignment.centerLeft,
            child: const Text('Company Name'))),
    GridColumn(
        columnName: 'companyNumber',
        label: Container(
            padding: const EdgeInsets.all(8.0),
            alignment: Alignment.centerLeft,
            child: const Text('Company Number'))),
    GridColumn(
        columnName: 'type',
        label: Container(
            padding: const EdgeInsets.all(8.0),
            alignment: Alignment.centerLeft,
            child: const Text('Type'))),
    GridColumn(
        columnName: 'sic',
        label: Container(
            padding: const EdgeInsets.all(8.0),
            alignment: Alignment.centerLeft,
            child: const Text('SIC'))),
    GridColumn(
        columnName: 'address',
        label: Container(
            padding: const EdgeInsets.all(8.0),
            alignment: Alignment.centerLeft,
            child: const Text(
              'Address',
            ))),
    GridColumn(
      columnName: 'directors',
      label: Container(
        padding: const EdgeInsets.all(8.0),
        alignment: Alignment.centerLeft,
        child: const Text(
          'Directors',
          overflow: TextOverflow.ellipsis,
        ),
      ),
    ),
    GridColumn(
        columnName: 'confirmationDate',
        label: Container(
            padding: const EdgeInsets.all(8.0),
            alignment: Alignment.centerLeft,
            child: const Text('Confirmation Date'))),
    GridColumn(
        columnName: 'incorporationDate',
        label: Container(
            padding: const EdgeInsets.all(8.0),
            alignment: Alignment.center,
            child: const Text('Incorporation Date'))),
    GridColumn(
        columnName: 'status',
        label: Container(
            padding: const EdgeInsets.all(8.0),
            alignment: Alignment.centerLeft,
            child: const Text('Status'))),
  ];
}

class SyncfusionCompModel {
  final String compNumber;
  final String directors;
  final String companyName;
  final String companyType;
  final String address;
  final String confirmationDate;
  final String incorporationDate;
  final String status;
  final String sic;

  SyncfusionCompModel(
      {required this.compNumber,
      required this.directors,
      required this.companyName,
      required this.companyType,
      required this.address,
      required this.confirmationDate,
      required this.incorporationDate,
      required this.status,
      required this.sic});
}


I'm loading the data from Firebase without problems now.
What I want to achieve and I'm not able is to paginate the data to load first for example 25 ​per page, after that maybe on Infinite scroll to load the next 25...25...25, and so on. I tried to combine this with your great examples, but I'm not able to, please help me or guide me, because I will have thousands of items on the list and this feature is mandatory for me.

Thanks in advance!



TP Tamilarasan Paranthaman Syncfusion Team October 18, 2023 02:06 PM UTC

Dorin Buraca,

Based on the information provided, we are not completely clear about your specific requirements. Could you please clarify whether you are looking to implement paging where 25 rows are loaded on each page and the next 25 rows are loaded when you switch to another page, or if your intention is to achieve continuous loading of additional rows through infinite scrolling? To provide you with the most accurate guidance and assistance, we would greatly appreciate more detailed information about your requirements.




DO Dorin October 18, 2023 02:52 PM UTC

Hi Tamilarasan,

I apologize for the confusion, I tried both ways with pagination and with infinite scrolling, but I think that for me the best option at this moment would be infinite scrolling.


I have tried in many ways but I am also confused and totally blocked at this moment on how I do this with live data from Firebase, please help me. 

Have a great day and thanks again!

Kind regards,




TP Tamilarasan Paranthaman Syncfusion Team October 19, 2023 02:08 PM UTC

Hi Dorin Buraca,


To implement infinite scrolling and load data from Firestore in response to scrolling, you can utilize the `handleLoadMoreRows` method. Within this method, you can fetch an additional set of 25 rows, map the details as `DataGridRow` instances, add them to the local DataGridRows collection, and subsequently invoke `notifyListeners` to refresh the DataGrid. We've prepared a sample that demonstrates this approach. Please refer to the provided sample and code snippet for a more detailed understanding.


 

In EmployeeDataSource class:

 

class EmployeeDataSource extends DataGridSource {

  EmployeeDataSource(this.employeeData) {

    if (employeeData.isNotEmpty) {

      _buildDataRow();

    }

  }

 

  final collection = FirebaseFirestore.instance

      .collection('stream')

      .doc('companies')

      .collection('all');

 

  Stream<QuerySnapshot> getStream() {

    return collection

        .limit(10)

        .orderBy('companyID', descending: false)

        .snapshots();

  }

 

  List<DataGridRow> dataGridRows = [];

  List<Employee> employeeData;

 

  void _buildDataRow() {

    dataGridRows = employeeData

        .map<DataGridRow>((e) => DataGridRow(cells: [

              DataGridCell<String>(columnName: 'id', value: e.id),

              DataGridCell<String>(

                  columnName: 'companyName', value: e.companyName),

              DataGridCell<String>(columnName: 'city', value: e.city),

              DataGridCell<String>(columnName: 'country', value: e.country),

            ]))

        .toList();

  }

 

  @override

  Future<void> handleLoadMoreRows() async {

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

 

    // 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()[0].value;

 

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

    var lastDoument = await collection

        .where("companyID", 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 collection.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: 'id', value: data['companyID']),

        DataGridCell<String>(

            columnName: 'companyName', value: data['companyName']),

        DataGridCell<String>(columnName: 'city', value: data['city']),

        DataGridCell<String>(columnName: 'country', value: data['country']),

      ]));

    }

 

    notifyListeners();

  }

 

  @override

  List<DataGridRow> get rows => dataGridRows;

}

 

In Main Class:

 

  Widget _buildDataGrid() {

    return StreamBuilder(

      stream: employeeDataSource.getStream(),

      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {

        if (snapshot.hasData) {

          for (var data in snapshot.data!.docs) {

            employeeData.add(Employee(

                id: data['companyID'],

                companyName: data['companyName'],

                city: data['city'],

                country: data['country']));

          }

          employeeDataSource = EmployeeDataSource(employeeData);

 

          return SfDataGrid(

            source: employeeDataSource,

            columns: getColumns,

            columnWidthMode: ColumnWidthMode.fill,

            loadMoreViewBuilder:

                (BuildContext context, LoadMoreRows loadMoreRows) {

              Future<String> loadRows() async {

                await loadMoreRows();

                return Future<String>.value('Completed');

              }

 

              return FutureBuilder<String>(

                initialData: 'loading',

                future: loadRows(),

                builder: (context, snapShot) {

                  if (snapShot.data == 'loading') {

                    return Container(

                        height: 60.0,

                        width: double.infinity,

                        decoration: const BoxDecoration(

                            color: Colors.white,

                            border: BorderDirectional(

                                top: BorderSide(

                                    width: 1.0,

                                    color: Color.fromRGBO(0, 0, 0, 0.26)))),

                        alignment: Alignment.center,

                        child: const CircularProgressIndicator());

                  } else {

                    return SizedBox.fromSize(size: Size.zero);

                  }

                },

              );

            },

          );

        } else {

          return const Center(

            child: CircularProgressIndicator(),

          );

        }

      },

    );

  }


We hope that this helps you. If you require further clarification or additional assistance, please let us know. We are happy to help you out.


Regards,

Tamilarasan


Attachment: Sample_c4c111c0.zip

Loader.
Up arrow icon