Flutter中如何将API响应键值渲染到DataTable的DataColumn与DataRow
原有代码问题说明
- 异步时序错误:
fetchStopEventData为异步方法,原代码在接口未返回数据时就操作未赋值的decode变量,会触发空指针异常 - 类型不匹配:DataTable组件的
columns参数要求传入List<DataColumn>类型、rows参数要求传入List<DataRow>类型,原代码往列表中嵌套列表、存入字符串,不符合类型要求 - DataCell传参错误:DataCell的入参必须是Widget组件,原代码直接将接口返回的字符串、数字等基础类型值传入,会触发类型错误
- 缺少UI刷新逻辑:接口返回数据后未调用
setState触发页面重绘,数据无法渲染到页面 - 单元格顺序风险:原代码直接取Map的values作为单元格内容,极端场景下会出现和表头顺序不匹配的错位问题
- 横向溢出问题:表格列数较多时没有配置横向滚动,会触发布局溢出警告
可直接运行的修正代码
import 'dart:convert'; import "package:flutter/material.dart"; import 'package:http/http.dart' as http; class HorizontalTable extends StatefulWidget { const HorizontalTable({Key? key}) : super(key: key); @override State<HorizontalTable> createState() => _HorizontalTableState(); } class _HorizontalTableState extends State<HorizontalTable> { // 存储表头key,保证单元格和表头顺序一致 List<String> columnKeys = []; // 表格列配置 List<DataColumn> columnsList = []; // 表格行数据 List<DataRow> rowsList = []; // 加载状态 bool isLoading = true; @override void initState() { super.initState(); fetchStopEventData(); } @override Widget build(BuildContext context) { return Scaffold( body: isLoading ? const Center(child: CircularProgressIndicator()) : SingleChildScrollView( // 支持横向滚动,避免列数过多溢出 scrollDirection: Axis.horizontal, child: SingleChildScrollView( child: DataTable( headingTextStyle: TextStyle( fontWeight: FontWeight.normal, fontSize: 12, color: Colors.grey.shade600), dataTextStyle: TextStyle( fontWeight: FontWeight.normal, fontSize: 12, color: Colors.grey.shade600), headingRowHeight: 25, dataRowHeight: 25, headingRowColor: MaterialStateColor.resolveWith( (states) => Colors.grey.shade100, ), dividerThickness: 1.0, columnSpacing: 12.0, horizontalMargin: 5.0, showBottomBorder: false, decoration: BoxDecoration( border: Border.all( width: 0.5, color: Colors.grey.shade500, )), columns: columnsList, rows: rowsList, ), ), ), ); } Future<void> fetchStopEventData() async { try { final response = await http.get( Uri.parse('https://run.mocky.io/v3/23d7180b-7fba-4f31-a968-187f62ad68d9'), ); if (response.statusCode == 200) { final List<dynamic> responseData = jsonDecode(response.body.toString()); // 取第一条数据的key作为表头 if (responseData.isNotEmpty) { final firstRow = responseData.first as Map<String, dynamic>; columnKeys = firstRow.keys.toList(); // 生成列配置 columnsList = columnKeys .map((key) => DataColumn(label: Text(key))) .toList(); // 生成行数据 rowsList = responseData.map((rowItem) { final Map<String, dynamic> rowMap = rowItem as Map<String, dynamic>; // 严格按照表头key顺序生成单元格,避免错位 final List<DataCell> cells = columnKeys.map((key) { return DataCell(Text('${rowMap[key]}')); }).toList(); return DataRow(cells: cells); }).toList(); } // 数据处理完成后刷新页面 setState(() { isLoading = false; }); } else { throw Exception('接口请求失败'); } } catch (e) { print('请求异常: ${e.toString()}'); setState(() { isLoading = false; }); } } }
实现说明
- 新增
isLoading标记请求状态,数据加载完成前显示加载指示器,避免空白页 - 单独存储表头key列表,生成行单元格时严格按照表头顺序取值,彻底避免单元格错位
- 嵌套两层
SingleChildScrollView分别支持横向、纵向滚动,适配多列、多行的表格场景 - 所有列表指定明确类型,符合Dart类型校验要求,避免运行时类型错误
- DataCell内统一用Text组件包裹值,兼容所有基础类型的接口返回值
- 数据全部处理完成后再调用
setState,保证页面渲染时数据已经准备完毕
内容的提问来源于stack exchange,提问作者nitish chauhan
相关产品推荐
相关产品推荐

