Flutter Syncfusion堆叠条形图零值柱位显示无数据文本实现
Flutter Syncfusion 堆叠条形图零值显示「无可用数据」实现方案
需求说明
使用Syncfusion的SfCartesianChart控件构建堆叠条形图时,当柱形对应数值为0时,不在对应位置留空白,而是展示「无可用数据」文本。当前图表配置:Y轴为数值轴、X轴为类目轴,零值位置默认空白的实际效果、目标效果如下:
- 实际运行效果(零值位置为空):

- 预期实现效果:

核心实现思路
默认情况下StackedBarSeries对y值为0的数据点不会渲染柱形,也不会触发默认数据标签显示,通过两个核心配置即可实现需求:
- 开启系列的数据标签渲染能力
- 通过
onDataLabelRender回调拦截标签渲染逻辑,对y值为0的数据点强制渲染「无可用数据」文本,同时调整标签位置到对应类目行的居中位置,避免偏移隐藏
代码修改
原有问题版本核心代码
SfCartesianChart( crosshairBehavior: _crosshairBehavior, primaryXAxis: CategoryAxis( majorTickLines: MajorTickLines(size: 20, width: 0), axisBorderType: AxisBorderType.withoutTopAndBottom, labelAlignment: LabelAlignment.center, borderWidth: 2.0, labelStyle: TextStyle( fontFamily: 'Roboto', fontSize: 14, fontStyle: FontStyle.normal, fontWeight: FontWeight.w500), labelPosition: ChartDataLabelPosition.outside, borderColor: Colors.blueGrey[800]), primaryYAxis: CategoryAxis( plotBands: <PlotBand>[ PlotBand( isVisible: widget.isFleetAverageVisible, start: 30, end: 29, borderWidth: 0.3, borderColor: Colors.black, ) ], interval: 25, maximumLabels: 4, maximum: 100, minimum: 0, majorTickLines: MajorTickLines(size: 15, width: 0), majorGridLines: MajorGridLines(dashArray: [5, 5]), ), series: stackedWidget(sortSelected != null ? sortSelected == true ? SortingOrder.ascending : SortingOrder.descending : SortingOrder.none), legend: Legend( isVisible: true, position: LegendPosition.bottom, overflowMode: LegendItemOverflowMode.wrap, ), onLegendTapped: (LegendTapArgs args) { }, ), List<ChartSeries> stackedWidget(SortingOrder sortingOrder) { return <ChartSeries>[ StackedBarSeries<ChartData, String>( sortingOrder: sortingOrder, legendIconType: LegendIconType.horizontalLine, legendItemText: "series1", width: 0.3, spacing: 0.2, dataSource: chartData, xValueMapper: (ChartData data, _) => data.x, yValueMapper: (ChartData data, _) => data.y1, sortFieldValueMapper: (ChartData sales, _) => sales.y1, color: Color.fromRGBO(51, 102, 255, 1)) ]; }
注意:原代码中
primaryYAxis误用了CategoryAxis,Y轴是0-100的数值类型,需要替换为NumericAxis否则轴渲染会异常。
修改后可实现需求的代码
在SfCartesianChart层级添加数据标签渲染回调,同时给每个StackedBarSeries配置数据标签参数:
SfCartesianChart( crosshairBehavior: _crosshairBehavior, primaryXAxis: CategoryAxis( majorTickLines: MajorTickLines(size: 20, width: 0), axisBorderType: AxisBorderType.withoutTopAndBottom, labelAlignment: LabelAlignment.center, borderWidth: 2.0, labelStyle: TextStyle( fontFamily: 'Roboto', fontSize: 14, fontStyle: FontStyle.normal, fontWeight: FontWeight.w500), labelPosition: ChartDataLabelPosition.outside, borderColor: Colors.blueGrey[800]), primaryYAxis: NumericAxis( plotBands: <PlotBand>[ PlotBand( isVisible: widget.isFleetAverageVisible, start: 30, end: 29, borderWidth: 0.3, borderColor: Colors.black, ) ], interval: 25, maximumLabels: 4, maximum: 100, minimum: 0, majorTickLines: MajorTickLines(size: 15, width: 0), majorGridLines: MajorGridLines(dashArray: [5, 5]), ), // 全局数据标签渲染回调 onDataLabelRender: (DataLabelRenderArgs args) { // 获取当前渲染点的y值 final num yValue = args.yValues![args.pointIndex!]; if (yValue == 0) { // 零值替换为指定文本 args.text = "无可用数据"; // 配置零值文本样式 args.textStyle = TextStyle( fontSize: 12, color: Colors.grey[700], fontWeight: FontWeight.w400 ); // 调整标签位置到X轴类目行居中位置,避免被隐藏 args.labelAlignment = ChartDataLabelAlignment.middle; } }, series: stackedWidget(sortSelected != null ? sortSelected == true ? SortingOrder.ascending : SortingOrder.descending : SortingOrder.none), legend: Legend( isVisible: true, position: LegendPosition.bottom, overflowMode: LegendItemOverflowMode.wrap, ), onLegendTapped: (LegendTapArgs args) { }, ), List<ChartSeries> stackedWidget(SortingOrder sortingOrder) { return <ChartSeries>[ StackedBarSeries<ChartData, String>( sortingOrder: sortingOrder, legendIconType: LegendIconType.horizontalLine, legendItemText: "series1", width: 0.3, spacing: 0.2, dataSource: chartData, xValueMapper: (ChartData data, _) => data.x, yValueMapper: (ChartData data, _) => data.y1, sortFieldValueMapper: (ChartData sales, _) => sales.y1, color: Color.fromRGBO(51, 102, 255, 1), // 开启数据标签显示 dataLabelSettings: DataLabelSettings( isVisible: true, // 非零值标签默认显示在柱形内部居中 labelAlignment: ChartDataLabelAlignment.middle, textStyle: TextStyle( fontSize: 12, color: Colors.white, fontWeight: FontWeight.w500 ) ) ) // 多堆叠系列同理,每个系列都配置dataLabelSettings即可 ]; }
适配说明
- 如果是多系列堆叠场景,判断逻辑可修改为:当前X轴位置所有系列的y值总和为0时,才显示「无可用数据」,避免单系列为0但其他系列有值时错误显示
- 文本样式、位置可根据实际UI需求调整
textStyle和labelAlignment参数
内容的提问来源于stack exchange,提问作者gopinath
相关产品推荐
相关产品推荐

