Reputation: 1
Using Flutter 3.10 Web, building a List of Users for FutureBuilder with PaginatedDataTable. I have the following in a single dart file:
import 'dart:async';
import 'dart:convert';
import 'package:flutter_axum/providers/url.dart';
import 'package:flutter/material.dart';
import 'package:flutter_axum/providers/user_auth.dart';
import 'package:http/http.dart' as http;
import '../models/user.dart';
import '../providers/user_secure_storage.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('User Paginated Data Table'),
actions: [
IconButton(
icon: const Icon(Icons.exit_to_app),
onPressed: () {
///logout
AuthenticationProvider().logoutUser(context: context);
}),
],
),
body: FutureBuilder(
future: getUsers(),
builder: (context, snapshot) {
if (ConnectionState.active != null && !snapshot.hasData) {
print("ConnectionState is active, snapshot.hasData");
// print(snapshot);
// print(snapshot.data);
// print(snapshot.stackTrace);
return Center(child: Text('Loading'));
}
if (ConnectionState.done != null && snapshot.hasError) {
print("ConnectionState is done, snapshot.hasError");
return Center(child: Text(snapshot.error.toString()));
} else {
print("ConnectionState done, snapshot has data without errors.");
print(snapshot);
print(snapshot.data);
print("snapshot.data is about to reach PaginatedDataTable.");
return PaginatedDataTable(
columns: getColumns(),
source: UserDataTableSource(source: snapshot.data as List<User>)
);
}
}
)
);
}
}
// List<User> parseUsers(List<dynamic> snapshotData) {
// final parsed = jsonDecode(snapshotData).cast<Map<String,dynamic>>();
// return parsed.map<User>((json) => User.fromJson(json)).toList();
// }
// List<User> parseUsers(String requestBody) {
// final parsed = jsonDecode(requestBody).cast<Map<String,dynamic>>();
// return parsed.map<User>((json) => User.fromJson(json)).toList();
// }
Future getUsers() async {
final StorageService storageService = StorageService();
final bearer = await storageService.readSecureData('token');
final url = BaseUrl.baseUrl;
String _url = "$url/user/list";
try {
final response = await http.get(Uri.parse(_url), headers: {'Access-Control-Allow-Origin': '*', 'Authorization': 'Bearer $bearer'});
print("Status Code: "+response.statusCode.toString());
print(response.body);
if (response.statusCode == 200 || response.statusCode == 201){
print("user_fetch: after response received, before json.decode");
print(json.decode(response.body));
final list = json.decode(response.body);
// List<User> list = json.decode(response.body);
// List<User> list(String resBody) => List<User>.from(json.decode(response.body).map((x) => User.fromJson(x)));
// List<User> list = userFromJson(response.body);
// List<User> list = (response.body as List)
// .map((body) => User.fromJson(body))
// .toList();
print("user_fetch: after response received, after json.decode");
print(list);
if (list == null) {
print("user_fetch: after response received, after json.decode, request.body is null");
print(list);
return DefSource;
} else {
print("user_fetch: after response received, after json.decode, request.body is not null");
print(list);
return list;
}
} else {
print("WTF");
return DefSource;
}
} catch (e) {
return Future.error(e.toString());
}
}
class UserDataTableSource extends DataTableSource {
UserDataTableSource({required source}) : userList = source;
final List<User> userList;
@override
DataRow getRow(int index) {
print("Inside UserDataTableSource");
final User user = userList[index];
return DataRow.byIndex(index: index, cells: [
DataCell(Text('${user.id}')),
DataCell(Text(user.alias)),
DataCell(Text(user.nameFirst)),
DataCell(Text(user.nameLast)),
DataCell(Text(user.email)),
DataCell(Text(user.phone)),
DataCell(Text('${user.dateBirth}')),
//const DataCell(Text("Edit|Delete")),
]);
}
@override
bool get isRowCountApproximate => false;
@override
int get rowCount => userList.length;
@override
int get selectedRowCount => 0;
}
const String colID = 'ID';
const String colAlias = 'Alias';
const String colNameFirst = 'First Name';
const String colNameLast = 'Last Name';
const String colEmail = 'Email';
const String colPhone = 'Phone';
const String colDateBirth = 'Date of Birth';
List<DataColumn> getColumns(){
final List<DataColumn> columns =
<DataColumn>[
const DataColumn(
label: Text(colID),
tooltip: colID,
),
const DataColumn(
label: Text(colAlias),
tooltip: colAlias,
),
const DataColumn(
label: Text(colNameFirst),
tooltip: colNameFirst,
),
const DataColumn(
label: Text(colNameLast),
tooltip: colNameLast,
),
const DataColumn(
label: Text(colEmail),
tooltip: colEmail,
),
const DataColumn(
label: Text(colPhone),
tooltip: colPhone,
),
const DataColumn(
label: Text(colDateBirth),
tooltip: colDateBirth,
),
];
return columns;
}
class DefSource extends DataTableSource {
@override
DataRow getRow(int index) => DataRow.byIndex(
index: index,
cells: [
DataCell(Text('id #$index')),
DataCell(Text('alias $index')),
DataCell(Text('first name $index')),
DataCell(Text('last name $index')),
DataCell(Text('email $index')),
DataCell(Text('phone $index')),
DataCell(Text('birth date $index')),
],
);
@override
int get rowCount => 10;
@override
bool get isRowCountApproximate => false;
@override
int get selectedRowCount => 0;
}
User Model for reference:
import 'dart:convert';
List<User> userFromJson(String str) => List<User>.from(json.decode(str).map((x) => User.fromJson(x)));
String userToJson(List<User> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class User {
User({
required this.id,
required this.alias,
required this.nameFirst,
required this.nameLast,
required this.email,
required this.phone,
required this.dateBirth,
this.password,
});
int id;
String alias;
String nameFirst;
String nameLast;
String email;
String phone;
DateTime dateBirth;
String? password;
factory User.fromJson(Map<String, dynamic> json) => User(
id: json["id"],
alias: json["alias"],
nameFirst: json["nameFirst"],
nameLast: json["nameLast"],
email: json["email"],
phone: json["phone"],
dateBirth: DateTime.parse(json["date_birth"]),
password: json["password"],
);
// factory User.fromJson(Map<String, dynamic> json) {
// Iterable list = json['users'];
// print(list.runtimeType);
// List<User> users = list.map((i) =>
// User.fromJson(i)).toList();
// return User(
// id: json["id"],
// alias: json["alias"],
// nameFirst: json["nameFirst"],
// nameLast: json["nameLast"],
// email: json["email"],
// phone: json["phone"],
// dateBirth: DateTime.parse(json["date_birth"]),
// password: json["password"],
//
// );
// }
Map<String, dynamic> toJson() => {
"id": id,
"alias": alias,
"nameFirst": nameFirst,
"nameLast": nameLast,
"email": email,
"phone": phone,
"date_birth": "${dateBirth.year.toString().padLeft(4, '0')}-${dateBirth.month.toString().padLeft(2, '0')}-${dateBirth.day.toString().padLeft(2, '0')}",
"password": password,
};
}
I believe the source of the problem is within "Future getUsers" where I have attempted the following (over several failed attempts) to parse the Response:
final list = json.decode(response.body);
final List<User> list = json.decode(response.body);
List<User> list(String resBody) => List<User>.from(json.decode(response.body).map((x) => User.fromJson(x)));
List<User> list = userFromJson(response.body);
List<User> list = (response.body as List)
.map((body) => User.fromJson(body))
.toList();
Using final list = json.decode(response.body);
from the options above, I get closest to pushing snapshot through the PaginatedDataTable as part of the source (or so I believe based upon prints to console).
Expected a value of type 'List<User>', but got one of type 'List<dynamic>'
======== Exception caught by widgets library =======================================================
The following TypeErrorImpl was thrown building FutureBuilder<dynamic>(dirty, state: _FutureBuilderState<dynamic>#ff8f7):
Expected a value of type 'List<User>', but got one of type 'List<dynamic>'
The relevant error-causing widget was:
FutureBuilder<dynamic> FutureBuilder:file:///home/user/projects/flutter_axum/lib/views/user_data_table.dart:33:15
When the exception was thrown, this was the stack:
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 288:49 throw_
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 121:3 castError
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 529:12 cast
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/classes.dart 638:14 as_C
packages/flutter_axum/views/user_data_table.dart 152:34 new
packages/flutter_axum/views/user_data_table.dart 53:29 <fn>
packages/flutter/src/widgets/async.dart 612:48 build
packages/flutter/src/widgets/framework.dart 5198:27 build
packages/flutter/src/widgets/framework.dart 5086:15 performRebuild
packages/flutter/src/widgets/framework.dart 5251:11 performRebuild
packages/flutter/src/widgets/framework.dart 4805:7 rebuild
packages/flutter/src/widgets/framework.dart 2780:18 buildScope
packages/flutter/src/widgets/binding.dart 903:9 drawFrame
packages/flutter/src/rendering/binding.dart 358:5 [_handlePersistentFrameCallback]
packages/flutter/src/scheduler/binding.dart 1284:15 [_invokeFrameCallback]
packages/flutter/src/scheduler/binding.dart 1214:9 handleDrawFrame
packages/flutter/src/scheduler/binding.dart 1072:5 [_handleDrawFrame]
lib/_engine/engine/platform_dispatcher.dart 1236:13 invoke
lib/_engine/engine/platform_dispatcher.dart 244:5 invokeOnDrawFrame
lib/_engine/engine/initialization.dart 190:45 <fn>
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 367:37 _checkAndCall
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 372:39 dcall
====================================================================================================
The other attempts to parse are resulting in a fail on the HomePage, such that the "Loading" screen appears while waiting for a proper response/snapshot which never arrives. The stacktrace is empty and the app basically enters a hanging state.
I have included print statements and some commented code in the application to provide the following for debugging:
print("Status Code: "+response.statusCode.toString());
Status Code: 200
print(request.body)
[{"id":6,"alias":"username","name_first":"fIrStNaMe","name_last":"lAsTnAmE","email":"email","phone":"2222222222","date_birth":"2000-12-01"},{"id":1,"alias":"ALIAS","name_first":"FiRsTnAmE","name_last":"LaStNaMe","email":"email","phone":"9999999999","date_birth":"2000-01-31"}]
print(json.decode(request.body))
[{id: 6, alias: username, name_first: fIrStNaMe, name_last: lAsTnAmE, email: email, phone: 2222222222, date_birth: 2000-12-01}, {id: 1, alias: ALIAS, name_first: FiRsTnAmE, name_last: LaStNaMe, email: email, phone: 9999999999, date_birth: 2000-01-31}]
After returning the list from the request to the FutureBuilder for snapshot, validation is performed for snapshot.hasData and snapshot.hasError, and both are passing.
print(snapshot);
AsyncSnapshot<dynamic>(ConnectionState.done, [{id: 1, alias: ALIAS, name_first: FiRsTnAmE, name_last: LaStNaMe, email: email, phone: 9999999999, date_birth: 2000-01-31}, {id: 6, alias: username, name_first: fIrStNaMe, name_last: lAsTnAmE, email: email, phone: 2222222222, date_birth: 2000-12-01}], null, null)
print(snapshot.data);
[{id: 1, alias: ALIAS, name_first: FiRsTnAmE, name_last: LaStNaMe, email: email, phone: 9999999999, date_birth: 2000-01-31}, {id: 6, alias: username, name_first: fIrStNaMe, name_last: lAsTnAmE, email: email, phone: 2222222222, date_birth: 2000-12-01}]
I have referred to several stackflow links, without success. I am almost certain my failure is tied to the parsing of the API Response, but I'm running out of idea on how to move forward. A second pair of eyes, and some feedback would be greatly appreciated.
Upvotes: 0
Views: 73