I want to display a DataTable where the data input varies based on the user's input (the user can pick a csv file and its content will then be displayed in the DataTable widget).
My problem is that the DataTable creation can take a few seconds depending on the number of rows and columns in the user's csv file and the screen freezes in the meantime, which is obviously a bad user experience. Therefore, my idea was to create the DataTable in a separate isolate, await the result (see code below) and show a CircularProgressIndicator while waiting for the result.
Unfortunately, this approach is not working (I assume because CustomDataTable is not a valid message type).
My approach using isolates here might be wrong (I am fairly new to flutter) and I am happy to switch to a different strategy if one can guide me in the right direction.
// In the class of my Stateful Widget that will display the CustomDataTable I have this function spawning the isolate and awaiting the CustomDataTable
Future<CustomDataTable> createCustomTableInIsolate(List<Map<String, String>> data, List<Map<String, String>> selectedRows, List<String> columnNames, Function onSelectedRow) async {
var map = {
'data': data,
'selectedRows': selectedRows,
'columnNames': columnNames,
'onSelectedRow': onSelectedRow,
};
CustomDataTable dataTable = await compute(createCustomTable, map);
return dataTable;
}
//... somewhere in my widget tree
FutureBuilder<CustomDataTable>(
future: createCustomTableInIsolate(),
builder: (BuildContext ctx,
AsyncSnapshot snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else if (!snapshot.hasData) {
return Center(
child: Text('Please check your input data...'),
);
} else {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: ConstrainedBox(
constraints: BoxConstraints(
minWidth: constraints.minWidth,
),
child: snapshot.data),
);
}
},
),
// ...
// Function creating the CustomDataTable in different isolate (outside of stateful widget class)
CustomDataTable createCustomTable(Map<String, dynamic> map) {
return CustomDataTable(
data: map['data'],
selectedRows: map['selectedRows'],
columnNames: map['columnNames'],
onSelectedRow: map['onSelectedRow'],
);
}
// Separate class to create the DataTable widget
class CustomDataTable extends StatelessWidget {
final List<Map<String, String>> data;
final List<Map<String, String>> selectedRows;
final List<String> columnNames;
final Function onSelectedRow;
CustomDataTable({
@required this.data,
@required this.selectedRows,
@required this.columnNames,
@required this.onSelectedRow,
});
@override
Widget build(BuildContext context) {
return DataTable(
columns: columnNames.map((columnName) {
return DataColumn(
label: Text(
columnName,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
);
}).toList(),
rows: data.map((column) {
var columnValues = column.values;
return DataRow(
selected: selectedRows.contains(column),
onSelectChanged: (selected) {
onSelectedRow(selected, column);
},
cells: columnValues.map((cellValue) {
return DataCell(
Text(
cellValue,
style: TextStyle(
color: Colors.white,
),
),
);
}).toList());
}).toList(),
);
}
}