Reputation: 6766
I'm trying to implement the PaginatedDataTable
class in flutter. A required field in the constructor of this class, is the class DataTableSource
. Looking at the data table example in the material section of the flutter gallery examples here. There is a member variable for the DataTableSource called List<Dessert> _desserts
where it's values are hard coded. In my implementation, I'm making a http call and returning some json to be decoded.
List<Result> parseResults(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Result>((json) => Result.fromJson(json)).toList();
}
Future<List<Result>> fetchResults(http.Client client) async {
final response = await client.get('https://api.myjson.com/bins/j5xau');
// Use the compute function to run parseResults in a separate isolate
return compute(parseResults, response.body);
In my DataTableSource class I'm not sure how to instantiate the list.
`final List<Result> results = fetchResults(http.Client);`
doesn't compile because fetchResults()
returns a future. If I change the return type to a future the results
does compile but I need my returned json to be of type List
so I can use methods like sort
etc..How should I convert the future to a list.
Upvotes: 2
Views: 12241
Reputation: 6537
In your DataTableSource
class just remove the results
variable. Then in your build
function, you can use a FutureBuilder
widget like this:
FutureBuilder<List<Result>>(
future: fetchResults(http.Client),
builder: (BuildContext context, AsyncSnapshot<List<Result>> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Press button to start.');
case ConnectionState.active:
case ConnectionState.waiting:
return Text('Awaiting result...');
case ConnectionState.done:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
return Text('Result: ${snapshot.data}');
}
return null; // unreachable
},
)
Note that snapshot.data
is a List<Result>
now and you can use it in the same way you were using it back when it was a hard coded value.
Edit:
If you don't want to use a FutureBuilder
I would suggest that you have a function that basically modifies the value of the results
when the http call completes. Here's an example of what I mean:
Make DessertDataSource
take in a List<Result>
in its constructor to define the value of results
like so:
class DessertDataSource extends DataTableSource {
final List<Result> results;
DessertDataSource(this.results);
// rest of the class
}
In the _DataTableDemoState
, make _dessertsDataSource
no longer final and change its initial value to DessertDataSource([])
. Also, add a boolean indicating when the data is loaded already or not.
class _DataTableDemoState extends State<DataTableDemo> {
// other fields!
DessertDataSource _dessertsDataSource = DessertDataSource([]);
bool isLoaded = false;
Then add the following function to _DataTableDemoState
. The boolean ensures we only make the http call once.
Future<void> getData() async {
final results = await fetchResults(http.Client);
if (!isLoaded) {
setState(() {
_dessertsDataSource = DessertDataSource(results);
isLoaded = true;
});
}
}
Finally call that function upon pressing a button or some other trigger or maybe just at the beginning of your build function.
@override
Widget build(BuildContext context) {
getData();
return MYWidget();
}
Then whenever the data is returned from the http call, the widget will automatically update with the new data.
Upvotes: 4