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

Flutter中如何将API返回数据绑定到分类柱状图?

没问题,我来帮你把API里的宠物营养数据绑定到你的自定义柱状图组件上,分几步来做:

如何将宠物API数据绑定到自定义柱状图

1. 先定义数据模型类(解析JSON更顺手)

API返回的JSON结构比较复杂,我们先把它转成Dart对象,避免直接操作Map导致的麻烦:

// 对应每日膳食占比的子项模型
class AllowanceDetail {
  final String name;
  final bool bones;
  final int percentage;

  AllowanceDetail({required this.name, required this.bones, required this.percentage});

  // 从JSON转成对象的工厂方法
  factory AllowanceDetail.fromJson(Map<String, dynamic> json) {
    return AllowanceDetail(
      name: json['name'],
      bones: json['bones'],
      percentage: json['percentage'],
    );
  }
}

// 对应营养成分的子项模型
class NutrientDetail {
  final String name;
  final double amount;
  final String unit;
  final String imperialAmount;

  NutrientDetail({required this.name, required this.amount, required this.unit, required this.imperialAmount});

  factory NutrientDetail.fromJson(Map<String, dynamic> json) {
    return NutrientDetail(
      name: json['name'],
      amount: json['amount'].toDouble(),
      unit: json['unit'],
      imperialAmount: json['imperial_amount'],
    );
  }
}

// 整体的宠物数据模型
class PetData {
  final List<AllowanceDetail> dailyAllowance;
  final List<NutrientDetail> nutrientGuidelines;

  PetData({required this.dailyAllowance, required this.nutrientGuidelines});

  factory PetData.fromJson(Map<String, dynamic> json) {
    // 解析膳食占比列表
    var allowanceJsonList = json['daily_allowance_guidelines_details'] as List;
    List<AllowanceDetail> allowanceList = allowanceJsonList.map((item) => AllowanceDetail.fromJson(item)).toList();

    // 解析营养成分列表
    var nutrientJsonList = json['nutrient_guideline_detail'] as List;
    List<NutrientDetail> nutrientList = nutrientJsonList.map((item) => NutrientDetail.fromJson(item)).toList();

    return PetData(
      dailyAllowance: allowanceList,
      nutrientGuidelines: nutrientList,
    );
  }
}

2. 编写API请求方法

假设你用http包来请求API,这里写一个示例请求逻辑:

import 'dart:convert';
import 'package:http/http.dart' as http;

Future<PetData> fetchPetNutritionData() async {
  // 替换成你的真实API地址
  final response = await http.get(Uri.parse('https://your-api-url.com/pet/17'));

  if (response.statusCode == 200) {
    // 解析JSON并转成PetData对象
    Map<String, dynamic> rawData = jsonDecode(response.body);
    return PetData.fromJson(rawData);
  } else {
    throw Exception('加载宠物数据失败,请检查API连接');
  }
}

3. 计算比例并渲染柱状图

你的ChartLine组件需要rate(0-1之间的比例值),我们分别处理宏观/微观两类数据:

  • 宏观膳食数据:直接用百分比除以100得到rate(比如60%就是0.6)
  • 微观营养数据:先找到所有营养成分的最大值,再用每个成分的数值除以最大值得到相对比例(保证最长的柱子占满宽度)

然后用FutureBuilder来异步获取数据并渲染:

class PetNutritionChartPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Gummy的营养数据')),
      body: FutureBuilder<PetData>(
        future: fetchPetNutritionData(),
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            PetData petData = snapshot.data!;

            // 生成宏观膳食柱状图列表
            List<Widget> allowanceChartList = petData.dailyAllowance.map((detail) {
              double rate = detail.percentage / 100.0;
              return ChartLine(
                title: detail.name,
                number: detail.percentage, // 显示百分比数值
                rate: rate,
              );
            }).toList();

            // 生成微观营养柱状图列表
            double maxNutrientValue = petData.nutrientGuidelines
                .map((n) => n.amount)
                .reduce((a, b) => a > b ? a : b); // 找最大值
            List<Widget> nutrientChartList = petData.nutrientGuidelines.map((detail) {
              double rate = detail.amount / maxNutrientValue;
              return ChartLine(
                title: detail.name,
                number: detail.amount.round(), // 取整显示营养数值
                rate: rate,
              );
            }).toList();

            return SingleChildScrollView(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const Text('宏观膳食占比', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
                  const SizedBox(height: 16),
                  ...allowanceChartList,
                  const SizedBox(height: 32),
                  const Text('微观营养成分', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
                  const SizedBox(height: 16),
                  ...nutrientChartList,
                ],
              ),
            );
          } else if (snapshot.hasError) {
            return Center(child: Text('加载出错:${snapshot.error}'));
          }
          // 加载中状态
          return const Center(child: CircularProgressIndicator());
        },
      ),
    );
  }
}

小提示

  • 如果你的ChartLine需要显示小数数值,可以把number字段改成double类型,或者用detail.amount.toStringAsFixed(1)来保留一位小数
  • 如果API返回的营养数据可能为空,记得加空值判断,避免报错
  • 你可以根据需求调整ChartLine的样式(比如柱子颜色、文字大小等)

内容的提问来源于stack exchange,提问作者Jason Waku

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 15:22:41