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

如何用Flutter Syncfusion SfCartesianChart实现不同长度的多图表系列

问题描述

我有一个已排序的嵌套列表,每个子列表存储特定日期的数据条目,且每个子列表的条目数量不固定。尝试用SfCartesianChart展示这些数据时,用多ColumnSeries的方式达不到预期效果——当前每个日期的柱子会被分散显示,而预期是同一日期的柱子集中在一起,同时要实现包含列序号与日期的二级X轴标签。

解决方案

多ColumnSeries的问题在于它会将不同日期的X轴值视为独立分类,导致柱子分散。正确的思路是合并所有数据为单一数据源,通过X轴分组实现同一日期下的柱子聚合,同时配置双X轴来显示序号和日期。

实现步骤

  • 合并所有子列表数据,为每个条目添加日期分组标识和组内序号
  • 使用单个ColumnSeries,通过自定义X轴映射实现分组显示
  • 配置双X轴:主X轴显示组内序号,二级X轴显示对应日期

完整代码

import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_charts/charts.dart';

void main() {
  runApp(const MyHomePage());
}

class MyEvent {
  final String date;
  final int value;
  final String groupDate; // 仅日期部分,用于分组
  final int groupIndex; // 组内序号

  MyEvent(this.date, this.value, this.groupDate, this.groupIndex);
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late List<MyEvent> mergedData;

  @override
  void initState() {
    super.initState();
    mergedData = _mergeAndFormatData();
  }

  List<MyEvent> _mergeAndFormatData() {
    final List<List<MyEvent>> originalList = [
      [
        MyEvent('2023-07-01T15.22', 11, '', 0),
        MyEvent('2023-07-01T17.43', 10, '', 0),
        MyEvent('2023-07-01T19.44', 16, '', 0),
        MyEvent('2023-07-01T23.34', 13, '', 0),
      ],
      [
        MyEvent('2023-12-23T08.13', 3, '', 0),
        MyEvent('2023-12-23T19.55', 4, '', 0),
      ],
      [
        MyEvent('2023-12-27T06.04', 14, '', 0),
        MyEvent('2023-12-27T10.13', 12, '', 0),
        MyEvent('2023-12-27T11.46', 11, '', 0),
      ],
      [
        MyEvent('2023-12-31T06.04', 5, '', 0),
      ],
    ];

    final List<MyEvent> result = [];
    for (var dayList in originalList) {
      if (dayList.isEmpty) continue;
      // 提取纯日期部分(如2023-07-01)
      final groupDate = dayList.first.date.substring(0, 10);
      for (int i = 0; i < dayList.length; i++) {
        result.add(MyEvent(
          dayList[i].date,
          dayList[i].value,
          groupDate,
          i + 1, // 组内序号从1开始
        ));
      }
    }
    return result;
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: SizedBox(
            height: 400.0,
            child: SfCartesianChart(
              // 主X轴:显示组内序号
              primaryXAxis: CategoryAxis(
                axisLabelFormatter: (details) {
                  return ChartAxisLabel(
                    mergedData[details.valueIndex].groupIndex.toString(),
                    const TextStyle(fontSize: 10),
                  );
                },
                axisLine: const AxisLine(width: 0), // 隐藏主X轴轴线
              ),
              // 二级X轴:显示日期分组
              axes: <ChartAxis>[
                CategoryAxis(
                  name: 'secondaryX',
                  interval: 1,
                  labelPlacement: LabelPlacement.betweenTicks,
                  axisLabelFormatter: (details) {
                    String currentDate = '';
                    int groupStartIndex = 0;
                    // 匹配当前索引对应的日期分组
                    for (int i = 0; i < mergedData.length; i++) {
                      if (mergedData[i].groupDate != currentDate) {
                        currentDate = mergedData[i].groupDate;
                        if (details.valueIndex == groupStartIndex) {
                          return ChartAxisLabel(
                            currentDate.substring(5), // 仅显示月-日
                            const TextStyle(fontSize: 10),
                          );
                        }
                        groupStartIndex = i;
                      }
                    }
                    return const ChartAxisLabel('', TextStyle());
                  },
                  plotOffset: 20, // 调整二级轴与主X轴的间距
                  axisLine: const AxisLine(width: 1),
                ),
              ],
              primaryYAxis: NumericAxis(),
              series: <ChartSeries>[
                ColumnSeries<MyEvent, String>(
                  dataSource: mergedData,
                  xValueMapper: (MyEvent event, index) => index.toString(),
                  yValueMapper: (MyEvent event, index) => event.value,
                  // 同一日期的柱子使用相同颜色
                  pointColorMapper: (MyEvent event, index) {
                    final dateGroups = mergedData.map((e) => e.groupDate).toSet().toList();
                    final colorIndex = dateGroups.indexOf(event.groupDate);
                    return [Colors.blue, Colors.orange, Colors.green, Colors.purple][colorIndex % 4];
                  },
                  spacing: 0.2, // 柱子间距
                  width: 0.8, // 柱子宽度
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

关键说明

  1. 数据预处理:将嵌套列表合并为单一列表,给每个条目添加分组信息,确保后续X轴可以按日期聚合。
  2. 双X轴配置:主X轴负责显示每个柱子的组内序号,二级X轴在下方显示对应的日期,通过axisLabelFormatter精准控制标签内容。
  3. 样式优化:通过pointColorMapper区分不同日期的柱子,调整spacing和width让同一日期的柱子紧密聚集,还原预期效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 22:35:03