Flutter PaginatedDataTable rowsPerPage

2020-07-13 08:37发布

问题:

Can flutter PaginatedDataTable rowsPerPage be set to a number not divisible by 10?

UPDATE

Data used by the FutureBuilder is fetched from a local server. At first, the list contains 3 items which results into empty DataRows being created.

My Problem

  1. I don't want the empty DataRows to be created

What I've Tried

  1. Setting the rowsPerPage count to the length of the list IF the list-length is less than the defaultRowsPerPage ELSE set it (rowsPerPage) to defaultRowsPerPage.

SAMPLE CODE

import 'package:flutter/material.dart';
import 'package:nvip/constants.dart';
import 'package:nvip/data_repo/network/centers_repo.dart';
import 'package:nvip/data_repo/tables/data_source_centers.dart';
import 'package:nvip/models/vaccination_center.dart';

class VaccinationCentersTableScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) => _CentersScreenBody();
}

class _CentersScreenBody extends StatefulWidget {
  @override
  __CentersScreenBodyState createState() => __CentersScreenBodyState();
}

class __CentersScreenBodyState extends State<_CentersScreenBody> {
  int _rowsPerPage = PaginatedDataTable.defaultRowsPerPage; // This one works
  int _columnIndex = 1;
  bool _isSortAscending = true;
  Future<List<VaccineCenter>> _centers;
  CentersTableDataSource _tableDataSource;
  var _scaffoldKey = GlobalKey<ScaffoldState>();

  void _sort<T>(Comparable<T> getField(VaccineCenter c), int columnIndex,
      bool isSortAscending) {
    _tableDataSource?.sort(getField, isSortAscending);
    setState(() {
      _columnIndex = columnIndex;
      _isSortAscending = isSortAscending;
    });
  }

  @override
  void initState() {
    super.initState();
    _centers = VaccineCentersDataRepo().getCenters();
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () {
        Navigator.pushReplacementNamed(context, Routes.keyHome);
      },
      child: Scaffold(
        key: _scaffoldKey,
        appBar: AppBar(
          title: Text("Vaccination Centers"),
          centerTitle: true,
          leading: IconButton(
            icon: Icon(Constants.backIcon),
            onPressed: () {
              Navigator.pushReplacementNamed(context, Routes.keyHome);
            },
          ),
          actions: <Widget>[
            IconButton(
              icon: Icon(Icons.add),
              tooltip: "Add vaccination center",
              onPressed: () {
                Navigator.pushReplacementNamed(context, Routes.keyPovAdd);
              },
            )
          ],
        ),
        body: FutureBuilder<List<VaccineCenter>>(
          future: _centers,
          builder: (context, snapshot) {
            if (snapshot.hasError) {
              return Constants.showHasNoDataWidget(
                context,
                "No vaccination center(s) / place(s) of vaccinations found. "
                    "Press the (+) sign to add a new record.",
              );
            } else {
              if (snapshot.hasData) {
                var centerList = snapshot.data;
                var centersCount = centerList.length;
                var defaultRowsPerPage = PaginatedDataTable.defaultRowsPerPage;
                _rowsPerPage = centersCount < defaultRowsPerPage
                    ? centersCount
                    : defaultRowsPerPage;
                _tableDataSource = CentersTableDataSource(centerList);
                return SingleChildScrollView(
                  child: PaginatedDataTable(
                    header: Text("Places of Vaccination"),
                    rowsPerPage: _rowsPerPage,
                    onRowsPerPageChanged: (rowCount) {
                      setState(() {
                        _rowsPerPage = rowCount;
                      });
                    },
                    sortColumnIndex: _columnIndex,
                    sortAscending: _isSortAscending,
                    onSelectAll: (isAllChecked) =>
                        _tableDataSource?.selectAll(isAllChecked),
                    actions: <Widget>[
                      IconButton(
                        icon: Icon(Icons.add),
                        tooltip: "Add vaccination center",
                        onPressed: () {
                          Navigator.pushReplacementNamed(
                              context, Routes.keyPovAdd);
                        },
                      ),
                      IconButton(
                        icon: Icon(Icons.delete_forever),
                        tooltip: "Delete vaccination center(s).",
                        onPressed: () {
                          Constants.showSnackBar(
                              _scaffoldKey, "Delete button clicked");
                        },
                      )
                    ],
                    columns: <DataColumn>[
                      DataColumn(
                        label: Text("No."),
                        numeric: true,
                        onSort: (ci, isSortAscending) => _sort<num>(
                            (c) => centerList.indexOf(c), ci, isSortAscending),
                      ),
                      DataColumn(
                        label: Text("Name"),
                        onSort: (ci, isSortAscending) =>
                            _sort<String>((c) => c.name, ci, isSortAscending),
                      ),
                    ],
                    source: _tableDataSource,
                  ),
                );
              }
            }

            return Center(child: CircularProgressIndicator());
          },
        ),
      ),
    );
  }
}

ERROR LOG

I/flutter ( 8474): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 8474): The following assertion was thrown building FutureBuilder<List<VaccineCenter>>(dirty, state:
I/flutter ( 8474): _FutureBuilderState<List<VaccineCenter>>#92379):
I/flutter ( 8474): 'package:flutter/src/material/paginated_data_table.dart': Failed assertion: line 87 pos 19:
I/flutter ( 8474): 'availableRowsPerPage != null && availableRowsPerPage.contains(rowsPerPage)': is not true.
I/flutter ( 8474): 
I/flutter ( 8474): Either the assertion indicates an error in the framework itself, or we should provide substantially
I/flutter ( 8474): more information in this error message to help you determine and fix the underlying cause.
I/flutter ( 8474): In either case, please report this assertion by filing a bug on GitHub:
I/flutter ( 8474):   https://github.com/flutter/flutter/issues/new?template=BUG.md
I/flutter ( 8474): 
I/flutter ( 8474): When the exception was thrown, this was the stack:
I/flutter ( 8474): #2      new PaginatedDataTable.<anonymous closure> (package:flutter/src/material/paginated_data_table.dart:87:19)
I/flutter ( 8474): #3      new PaginatedDataTable (package:flutter/src/material/paginated_data_table.dart:89:9)
I/flutter ( 8474): #4      __CentersScreenBodyState.build.<anonymous closure> (package:nvip/scenes/vaccination_centers/screen_center_table.dart:86:26)
I/flutter ( 8474): #5      _FutureBuilderState.build (package:flutter/src/widgets/async.dart)
I/flutter ( 8474): #6      StatefulElement.build (package:flutter/src/widgets/framework.dart:3809:27)
I/flutter ( 8474): #7      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3721:15)
I/flutter ( 8474): #8      Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
I/flutter ( 8474): #9      BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2286:33)
I/flutter ( 8474): #10     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:676:20)
I/flutter ( 8474): #11     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:219:5)
I/flutter ( 8474): #12     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:990:15)
I/flutter ( 8474): #13     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:930:9)
I/flutter ( 8474): #14     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:842:5)
I/flutter ( 8474): #15     _invoke (dart:ui/hooks.dart:154:13)
I/flutter ( 8474): #16     _drawFrame (dart:ui/hooks.dart:143:3)
I/flutter ( 8474): (elided 2 frames from class _AssertionError)
I/flutter ( 8474): ════════════════════════════════════════════════════════════════════════════════════════════════════

UPDATE 2

My DataTableSource

import 'package:flutter/material.dart';
import 'package:nvip/models/vaccination_center.dart';

class CentersTableDataSource extends DataTableSource {
  final List<VaccineCenter> _centers;
  int _rowsSelectedCount = 0;

  CentersTableDataSource(this._centers);

  @override
  DataRow getRow(int index) {
    assert(index >= 0);
    if (index >= _centers.length) return null;
    final VaccineCenter center = _centers[index];
    return DataRow.byIndex(
      index: index,
      selected: center.isSelected,
      onSelectChanged: (selected) {
        if (center.isSelected != selected) {
          _rowsSelectedCount += selected ? 1 : -1;
          center.isSelected = selected;
          notifyListeners();
        }
      },
      cells: <DataCell>[
        DataCell(Text("${index + 1}")),
        DataCell(Text(center.name)),
      ],
    );
  }

  @override
  bool get isRowCountApproximate => false;

  @override
  int get rowCount => _centers.length;

  @override
  int get selectedRowCount => _rowsSelectedCount;

  void sort<T extends Object>(
      Comparable<T> getField(VaccineCenter d), bool isAscending) {
    _centers.sort((a, b) {
      if (isAscending) {
        final VaccineCenter c = a;
        a = b;
        b = c;
      }

      final Comparable<T> aValue = getField(a);
      final Comparable<T> bValue = getField(b);
      return Comparable.compare(aValue, bValue);
    });
    notifyListeners();
  }

  void selectAll(bool isAllChecked) {
    _centers.forEach((center) => center.isSelected = isAllChecked);
    _rowsSelectedCount = isAllChecked ? _centers.length : 0;
    notifyListeners();
  }
}

回答1:

  import 'package:flutter/material.dart';

    class DemoTable extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return _DemoTableBody();
      }
    }

    class _DemoTableBody extends StatefulWidget {
      @override
      __DemoTableBodyState createState() => __DemoTableBodyState();
    }

    class __DemoTableBodyState extends State<_DemoTableBody> {

      int _rowsPerPage = PaginatedDataTable.defaultRowsPerPage;

      // A Variable to hold the length of table based on the condition of comparing the actual data length with the PaginatedDataTable.defaultRowsPerPage

      int _rowsPerPage1 = PaginatedDataTable.defaultRowsPerPage;

      @override
      Widget build(BuildContext context) {

      //Obtain the data to be displayed from the Derived DataTableSource

        var dts = DTS(); 

        // dts.rowcount provides the actual data length, ForInstance, If we have 7 data stored in the DataTableSource Object, then we will get 12 as dts.rowCount

       var tableItemsCount = dts.rowCount; 

        // PaginatedDataTable.defaultRowsPerPage provides value as 10

        var defaultRowsPerPage = PaginatedDataTable.defaultRowsPerPage;

        // We are checking whether tablesItemCount is less than the defaultRowsPerPage which means we are actually checking the length of the data in DataTableSource with default PaginatedDataTable.defaultRowsPerPage i.e, 10

        var isRowCountLessDefaultRowsPerPage = tableItemsCount < defaultRowsPerPage;

        // Assigning rowsPerPage as 10 or acutal length of our data in stored in the DataTableSource Object

        _rowsPerPage =
            isRowCountLessDefaultRowsPerPage ? tableItemsCount : defaultRowsPerPage;
        return Scaffold(
          appBar: AppBar(
            title: Text("Demo Paginated Table"),
          ),
          body: SingleChildScrollView(
            child: PaginatedDataTable(
              header: Text('data with 7 rows per page'),
              // comparing the actual data length with the PaginatedDataTable.defaultRowsPerPage and then assigning it to _rowPerPage1 variable which then set using the setsState()
              onRowsPerPageChanged: isRowCountLessDefaultRowsPerPage // The source of problem!
                  ? null
                  : (rowCount) {
                      setState(() {
                        _rowsPerPage1 = rowCount;
                      });
                    },
              columns: <DataColumn>[
                DataColumn(label: Text('row')),
                DataColumn(label: Text('name')),
              ],
              source: dts,
              //Set Value for rowsPerPage based on comparing the actual data length with the PaginatedDataTable.defaultRowsPerPage 
              rowsPerPage:
                  isRowCountLessDefaultRowsPerPage ? _rowsPerPage : _rowsPerPage1,
            ),
          ),
        );
      }
    }

    class DTS extends DataTableSource {
      @override
      DataRow getRow(int index) {
        return DataRow.byIndex(
          index: index,
          cells: [
            DataCell(Text('row #$index')),
            DataCell(Text('name #$index')),
          ],
        );
      }

      @override
      int get rowCount => 9; // Manipulate this to which ever value you wish

      @override
      bool get isRowCountApproximate => false;

      @override
      int get selectedRowCount => 0;
    }


回答2:

This error is caused by the values provided by flutter. rowsPerPage can only be 10, 20, 50 and 100.

snapshot source PaginatedDataTable

This is specified in the source PaginatedDataTable.



回答3:

I'm leaving this for the next guy since I just battled this recently.

Right now, it seems like PaginatedDataTable has a couple of bugs in it... this is one of them. If the number of data elements doesn't exactly equal the selected number of rows per page, the PaginatedDataTable widget puts in 'filler rows'

Until this is fixed, I'd recommend taking the flutter PaginatedDataTable widget in the file paginated_data_table.dart and putting it in your project in order to customize it. (you'll have to replace all of the dependencies that aren't found with a simple import 'package:flutter/material.dart';

In order to limit the number of seen rows, add the lines with // <--- below in the _getRows method seen below:

  List<DataRow> _getRows(int firstRowIndex, int rowsPerPage) {
    final List<DataRow> result = <DataRow>[];
    final int nextPageFirstRowIndex = firstRowIndex + rowsPerPage;
    bool haveProgressIndicator = false;
    for (int index = firstRowIndex; index < nextPageFirstRowIndex; index += 1) {
      if (index < _rowCount) {  // <---
        // This stops "overflow" rows from appearing on the last page.
        DataRow row;
        if (index < _rowCount || _rowCountApproximate) {
          row = _rows.putIfAbsent(index, () => widget.source.getRow(index));
          if (row == null && !haveProgressIndicator) {
            row ??= _getProgressIndicatorRowFor(index);
            haveProgressIndicator = true;
          }
        }
        row ??= _getBlankRowFor(index);
        result.add(row);
      }                      // <---
    }
    return result;
  }

Additionally, the page count will now be off, so you'll have to change this section below in order to add the ternary operator to change the final count:

Text(
     localizations.pageRowsInfoTitle(
     _firstRowIndex + 1,
     (_firstRowIndex + widget.rowsPerPage <= _rowCount)?_firstRowIndex + widget.rowsPerPage:_rowCount,
     _rowCount,
     _rowCountApproximate,
    ),
  ),


回答4:

Let's say you have some method that fetches data from a remote server or locally and return a list of objects like this :

List<Map<String, String>> fetchedDataObj = [
  {
    "name": "Rami",
    "email": "dummyemail01@gmail.com"
  },
  {
    "name": "Kara",
    "email": "dummyemail02@gmail.com"
  },
  {
    "name": "Lina",
    "email": "dummyemail03@almakana.com"
  }
];

in PaginatedDataTable widget set :

rowsPerPage: fetchedDataObj.length,

and

onRowsPerPageChanged: (fetchedDataObj.length < PaginatedDataTable.defaultRowsPerPage) ? null : (r) {
          // setState(() {
          //   _rowsPerPage = r;
          // });
        },
      ),

Hope this helps. Thanks



标签: dart flutter