You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 09:06:50