The datatable is a very useful widget. To my dismay, it doesn’t have automatic sorting, instead you have to manually sort the items using a custom function.
The code is trivial if you have a small number of sortable columns
List<EmployeesRecord> sortMyData(
List<EmployeesRecord> listToSort,
bool isAsc,
int sortColumIndex,
) {
/// MODIFY CODE ONLY BELOW THIS LINE
// Sort by 'name' for 0, 'age' for 1, 'position' for 2 in code.
switch (sortColumIndex) {
case 0:
listToSort.sort((a, b) => a.name.compareTo(b.name));
break;
case 1:
listToSort.sort((a, b) => a.age.compareTo(b.age));
break;
case 2:
listToSort.sort((a, b) => a.position.compareTo(b.position));
break;
default:
break;
}
if (!isAsc) {
listToSort = listToSort.reversed.toList();
}
return listToSort;
/// MODIFY CODE ONLY ABOVE THIS LINE
}
Even when I don’t use the sorting functionality very often I couldn’t stand having to repeat this code fore every structure that needed sorting. Static typing is one of the strengths of Dart, but having only static types seemed an awful limitation.
Enter Dynamic Types
Dart does have dynamic types, but it took me a while finding out how to use them in flutterflow.
When defining a function you can define a parameter or a return value of type JSON. What this means is it will use the dynamic type in Dart.
The only missing piece in order to have a dynamic sorting funtion is to have a way to convert objects into dynamic variables. This can be done using the map list items option in flutterflow.
The Sorting Function
By using dynamic variables, a general sorting function becomes trivial.
The function receives a dynamic list, a field index, the ascending flag and a string that contains the list of column names of the structure separated by commas. This last parameter is needed in order to obtain the field name from the column index passed by Flutterflow.
import 'dart:convert';
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:timeago/timeago.dart' as timeago;
import '/flutter_flow/lat_lng.dart';
import '/flutter_flow/place.dart';
import '/flutter_flow/uploaded_file.dart';
import '/flutter_flow/custom_functions.dart';
import '/backend/schema/structs/index.dart';
List<dynamic> sortByFieldIdx(
List<dynamic> list,
int fieldIdx,
bool asc,
String fields,
) {
/// MODIFY CODE ONLY BELOW THIS LINE
var flist = fields.split(',');
var field = flist[fieldIdx];
list.sort((a, b) {
var aValue = a[field];
var bValue = b[field];
if (asc == false) {
aValue = b[field];
bValue = a[field];
}
// Compare based on field values
if (aValue is String) {
return aValue.compareTo(bValue);
} else if (aValue is num) {
return aValue.compareTo(bValue);
} else {
throw Exception('Field $field contains unsupported data type.');
}
});
return list;
/// MODIFY CODE ONLY ABOVE THIS LINE
}
Invoking the function
The function has to be invoked form the onSortChanged() callback. We can use an “update page state action” and use a function call to update the list.
The field mapping is summarized in the image below. The list name is personList, it is a structure of type Person with three fields : firstName, lastName, country.
Converting the list to a list of dynamic objects seems trivial in the above table, but the mapping in the action editor is a bit complex as shown in the figure below.
First you pass the list to the function
Then you select map list items
In the last window you select to JSON
Since the return value of the function is Dynamic, we also have to convert back to the original type the list returned by the fnction.
Select map lit items
Then select "to datatype" and the data type , Person in the example
That’s all. Now sorting can be done with a single function. The downside is that invoking the sort function is a bit more complex.
Caveats
As a general rule dynamic data structures are slower than strongly typed structures. I would not advice using this sorting method for large volumes of data. I have tested this sorting function with up to 10,000 thousand rows on a paginated table and it works just fine.
Do not use more than a couple hundred rows of data with an non-paginated data table with unlimited rows. The sorting works fine, but Flutterflow will attempt to create all the widgets as the table is populated and re-create the table each time it is sorted. As a result the appplication will become unresponsive with as few as 300 rows.