The fundamental usage of the FUIPaginatedDataTable2 is analogous to that of the FUIDataTable2. For further details, please refer to the preceding section on FUIDataTable2 :: Static Data (Without Pagination).
This concise tutorial demonstrates the construction of a book data table, incorporating static data and functionalities for sorting and pagination. This functionality closely resembles the one depicted in the introductory GIF.
Paginated Data Source
When utilizing the FUIPaginatedDataTable2 component, a data source must be defined. Typically, this involves creating a class that extends Flutter’s DataTableSource.
This concise tutorial provides a step-by-step guide to creating a table for books for sale.
Step 1 - Create a model class
Let us define a class named BookModel to represent the model structure of a book.
class BookModel {
final String title;
final String isbn13;
final double price;
final DateTime publishedDate;
bool selected;
BookModel({
required this.title,
required this.isbn13,
required this.price,
required this.publishedDate,
this.selected = false,
});
}
The selected field serves to indicate whether a row is currently selected.
Step 2 - Create the Event Object and Sort Controller (Bloc involved)
For sorting purposes, it is essential to provide a mechanism for the containing widget to receive notifications when a sort event occurs. In this section, we will utilize Bloc for state management. However, you are welcome to implement your preferred state management mechanism.
/// The sort event
class BookDataTableSortEvent {
final int sortColumnIndex; // 1st column is 0
final bool sortAscending; // true - asc, false - desc
BookDataTableSortEvent(this.sortColumnIndex, this.sortAscending);
}
/// The sort controller
class BookDataTableSortController extends Cubit<BookDataTableSortEvent> {
BookDataTableSortEvent event;
BookDataTableSortController({
required this.event,
}) : super(event);
trigger(BookDataTableSortEvent event) {
this.event = event;
emit(this.event);
}
}
Step 3 - Build the data list (DemoBooksWidget)
Let’s create a static data list of BookModel. We’ll initialize the BookDataTableSortController. All these elements will be contained within a Stateful widget named DemoBooksWidget.
class BookPaginatedDataTableSource extends DataTableSource {
List<BookModel> dataList; // Pass in the data list
BuildContext context; // Refers to the context in the build method of the widget
late FUIDataTableCellHelper dtCellHelper; // For convenience of cell generation
int _selectedCount = 0; // the selected row count
BookPaginatedDataTableSource(this.context, this.dataList) {
dtCellHelper = FUIDataTableCellHelper(context); // Initialize the dtCellHelper
}
}
Step 5 - Implement getRow method
Let’s override the getRow method in BookPaginatedDataTableSource. This method returns the structure of a table row.
class BookPaginatedDataTableSource extends DataTableSource {
// ...
@override
DataRow? getRow(int index) {
BookModel model = dataList[index]; // Get the model object
FUIDataTable2Theme dt2Theme = context.theme.fuiDataTable2; // Access the theme for certain pre-defined row colors
FUIThemeCommonColors fuiColors = context.theme.fuiColors;
return DataRow2.byIndex(
index: index,
selected: model.selected,
color: WidgetStateProperty.all(model.selected ? dt2Theme.rowSelectedColor : fuiColors.bg0), // Switch color
onSelectChanged: (checked) {
// ***
// If the row is checked, update the model's selected field and the _selectedCount variable.
// ***
if (checked != null && model.selected != checked) {
_selectedCount += checked ? 1 : -1;
assert(_selectedCount >= 0);
model.selected = checked;
// Calling notifyListeners is necessary. Please refer to the Flutter's DataTableSource documentation.
notifyListeners();
}
},
/// How the row data should be displayed in the table cell.
cells: [
DataCell(dtCellHelper.genData(text: model.title)),
DataCell(dtCellHelper.genData(text: model.isbn13)),
DataCell(dtCellHelper.genData(text: '\$${model.price}', alignment: FUIDataTable2Alignment.center)),
DataCell(dtCellHelper.genData(text: DateFormat('dd MMM yyyy').format(model.publishedDate), alignment: FUIDataTable2Alignment.right)),
],
);
}
// ...
}
Step 6 - Implement a selectAll method (BookPaginatedDataTableSource)
Let’s implement the selectAll method. This is the method that is called when the "Check All" box is toggled. This method will be utilized in the FUIPaginatedDataTable2 later.
void selectAll(bool? checked) {
if (checked != null) {
for (final model in dataList) {
model.selected = checked;
}
_selectedCount = (checked) ? dataList.length : 0;
}
notifyListeners();
}
Step 7 - Implement the sort method for sorting comparison (BookPaginatedDataTableSource)
When the sort event is triggered for a specific column, this method comes to play.
void sort<T>(Comparable<T> Function(BookModel model) getField, bool ascending) {
dataList.sort((a, b) {
final aValue = getField(a);
final bValue = getField(b);
return ascending ? Comparable.compare(aValue, bValue) : Comparable.compare(bValue, aValue);
});
notifyListeners();
}
Step 8 - Implement the rest of the methods (BookPaginatedDataTableSource)
In addition to the methods already mentioned, the DataTableSource class contains several other important methods that must be implemented.
/// More like the total row count
@override
int get rowCount => dataList.length;
/// The selected row count
@override
int get selectedRowCount => _selectedCount;
/// Just set this to false (no approximate)
@override
bool get isRowCountApproximate => false;
Step 9 - Implement the build method
Combining all the above, let's proceed with implementing the build method.
@override
Widget build(BuildContext context) {
FUIDataTableColumnHelper dtColumnHelper = FUIDataTableColumnHelper(context);
BookPaginatedDataTableSource dataSource = BookPaginatedDataTableSource(context, dataList);
return FUISectionPlain(
horizontalSpace: FUISectionHorizontalSpace.tight,
child: FUISectionContainer(
child: BlocBuilder(
bloc: sortCtrl,
builder: (BuildContext context, BookDataTableSortEvent event) {
// For every time when a sort even triggers, the sortColumnIndex and the sortAscending has to be re-evaluated.
int sortColumnIndex = event.sortColumnIndex;
bool sortAscending = event.sortAscending;
return FUIPaginatedDataTable2(
sortColumnIndex: sortColumnIndex,
sortAscending: sortAscending,
source: dataSource, // Assign the static data source
showCheckboxColumn: true, // Show the checkbox for the rows.
rowsPerPage: 5, // Set the rows per page limit to 5
columns: [ // Column definitions
DataColumn2(
label: dtColumnHelper.genLabel(text: 'Title'),
onSort: (idx, asc) {
dataSource.sort<String>((model) => model.title, asc);
sortCtrl.trigger(BookDataTableSortEvent(0, asc));
},
),
DataColumn2(
label: dtColumnHelper.genLabel(text: 'ISBN13'),
onSort: (idx, asc) {
dataSource.sort<String>((model) => model.title, asc);
sortCtrl.trigger(BookDataTableSortEvent(1, asc));
},
),
DataColumn2(
label: dtColumnHelper.genLabel(text: 'Price', alignment: FUIDataTable2Alignment.center),
onSort: (idx, asc) {
dataSource.sort<num>((model) => model.price, asc);
sortCtrl.trigger(BookDataTableSortEvent(2, asc));
},
),
DataColumn2(
label: dtColumnHelper.genLabel(text: 'Published On', alignment: FUIDataTable2Alignment.right),
onSort: (idx, asc) {
dataSource.sort<DateTime>((model) => model.publishedDate, asc);
sortCtrl.trigger(BookDataTableSortEvent(3, asc));
},
),
],
// What is the select all check box is clicked, this will be the function...
onSelectAll: (checked) => dataSource.selectAll(checked),
);
},
),
),
);
}
Other Parameters
Many of the parameters in FUIPaginatedDataTable2 corresponds to the data_table_2 package from https://pub.dev/packages/data_table_2. Please refer to this for more info.