Flutter中List<dynamic>转List<DataRow>失败及数据加载优化咨询
错误原因分析
出现type 'List<dynamic>' is not a subtype of type 'List<DataRow>'错误的核心原因是类型不匹配:
DataTable的rows参数要求传入List<DataRow>类型的数据- 你直接将API返回的嵌套
lots数组(List<dynamic>或List<Lot>)赋值给了rows,两种类型无法直接转换,导致类型转换异常
解决方法
需要遍历lots数组,将每个Lot对象手动转换为DataRow对象,每个字段对应DataCell。以下是完整示例:
1. 确保模型类正确解析API数据
假设你的API响应结构如下:
{ "lots": [ {"id": "L001", "name": "Sample Lot 1", "quantity": 100}, {"id": "L002", "name": "Sample Lot 2", "quantity": 200} ] }
对应的模型类:
class Lot { final String id; final String name; final int quantity; Lot({required this.id, required this.name, required this.quantity}); factory Lot.fromJson(Map<String, dynamic> json) => Lot( id: json['id'], name: json['name'], quantity: json['quantity'], ); } class ApiResponse { final List<Lot> lots; ApiResponse({required this.lots}); factory ApiResponse.fromJson(Map<String, dynamic> json) { final List<dynamic> lotsJson = json['lots']; return ApiResponse( lots: lotsJson.map((lot) => Lot.fromJson(lot)).toList(), ); } }
2. 在FutureBuilder中正确转换数据
将lots数组转换为List<DataRow>后再赋值给DataTable.rows:
FutureBuilder<ApiResponse>( future: fetchLotsData(), // 你的API请求方法 builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); } if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } final lots = snapshot.data?.lots ?? []; return DataTable( columns: const [ DataColumn(label: Text('ID')), DataColumn(label: Text('Lot Name')), DataColumn(label: Text('Quantity')), ], // 核心:将Lot列表转换为DataRow列表 rows: lots.map((lot) { return DataRow(cells: [ DataCell(Text(lot.id)), DataCell(Text(lot.name)), DataCell(Text(lot.quantity.toString())), ]); }).toList(), ); }, )
数据缓存的必要性与实现
是否需要缓存?
如果你的页面会频繁重建(比如屏幕旋转、父组件刷新),建议在initState中缓存数据,避免重复发起网络请求,提升性能并减少资源消耗。如果数据是一次性加载且不会频繁变化,缓存是更优的选择。
实现方式
方式1:缓存API请求的Future(推荐)
将API请求的Future保存在State成员变量中,避免每次build都重新发起请求:
class _LotScreenState extends State<LotScreen> { late Future<ApiResponse> _apiRequestFuture; @override void initState() { super.initState(); // 在initState中发起请求并缓存Future _apiRequestFuture = fetchLotsData(); } Future<ApiResponse> fetchLotsData() async { final response = await http.get(Uri.parse('your_api_endpoint')); if (response.statusCode == 200) { return ApiResponse.fromJson(json.decode(response.body)); } else { throw Exception('Failed to load lots data'); } } @override Widget build(BuildContext context) { return FutureBuilder<ApiResponse>( future: _apiRequestFuture, // 使用缓存的Future builder: (context, snapshot) { // 构建UI逻辑... }, ); } }
方式2:缓存解析后的List数据
如果需要更灵活地操作数据(比如本地修改、筛选),可以直接缓存解析后的列表:
class _LotScreenState extends State<LotScreen> { List<Lot>? _cachedLots; bool _isLoading = true; String? _errorMsg; @override void initState() { super.initState(); _loadAndCacheData(); } Future<void> _loadAndCacheData() async { try { final response = await http.get(Uri.parse('your_api_endpoint')); if (response.statusCode == 200) { final apiResponse = ApiResponse.fromJson(json.decode(response.body)); setState(() { _cachedLots = apiResponse.lots; _isLoading = false; }); } else { throw Exception('HTTP error: ${response.statusCode}'); } } catch (e) { setState(() { _errorMsg = e.toString(); _isLoading = false; }); } } @override Widget build(BuildContext context) { if (_isLoading) return const CircularProgressIndicator(); if (_errorMsg != null) return Text('Error: $_errorMsg'); return DataTable( columns: const [/* 列定义 */], rows: _cachedLots?.map((lot) => DataRow(/* 转换逻辑 */)).toList() ?? [], ); } }
内容的提问来源于stack exchange,提问作者Najmul.myself
相关产品推荐
相关产品推荐

