Skip to content

StackedColumnSeries DataLabel intermittently not visible for stacked and transparent series (Total label missing randomly) #2507

@grintech

Description

@grintech

Bug description

In SfCartesianChart using multiple StackedColumnSeries, data labels are intermittently not rendered.

Observed Behavior

Sometimes the top “Total” label (implemented using a transparent stacked series) is not displayed.

Sometimes Check-In / Check-Out labels are not shown on the bars.

The issue appears randomly even though:

DataLabelSettings.isVisible = true

Values are non-zero

Axis maximum is correctly set

No exceptions are thrown.

This happens when:

Using multiple StackedColumnSeries

Using ChartDataLabelAlignment.outer

One series is transparent and used only to display total values

✅ Expected Behavior

Data labels should consistently render:

Check-In label

Check-Out label

Total label (top of stack)

Labels should not randomly disappear.

Steps to reproduce

Here is a clean “Steps to Reproduce” section you can add to your GitHub issue:


🔁 Steps to Reproduce

  1. Create a SfCartesianChart with:

    • CategoryAxis as primary X-axis
    • NumericAxis with a dynamic maximum value
    • Legend enabled
  2. Add three StackedColumnSeries:

    • Series 1 → Check-Out (with DataLabelSettings.isVisible = true)
    • Series 2 → Check-In (with DataLabelSettings.isVisible = true)
    • Series 3 → Transparent series used to display Total using dataLabelMapper
  3. Set:

    dataLabelSettings: const DataLabelSettings(
      isVisible: true,
      labelAlignment: ChartDataLabelAlignment.outer,
    )
  4. Use non-zero values for stacked data (e.g., 3 and 6).

  5. Run the app on iOS Simulator or Android device.

  6. Perform one or more of the following:

    • Hot reload
    • Navigate away and back to the screen
    • Rebuild the widget with new data
    • Change chart values dynamically

🐞 Result

  • Sometimes the Total label is not rendered
  • Sometimes Check-In or Check-Out labels disappear
  • No errors are logged in console

✅ Expected Result

All visible data labels (Check-In, Check-Out, and Total) should render consistently on every build.

Code sample

class ChartBugExample extends StatelessWidget {
const ChartBugExample({super.key});

String _dateKey(DateTime date) => '${date.year}-${date.month}-${date.day}';

@OverRide
Widget build(BuildContext context) {
final today = DateTime.now();

final next7Days = List.generate(
  7,
  (index) => DateTime(today.year, today.month, today.day + index),
);

// Static data instead of provider
final Map<String, BookingChartData> chartMap = {
  for (final date in next7Days)
    _dateKey(date): BookingChartData(
      label: dateLabel(date),
      checkIn: 0,
      checkOut: 0,
      date: date,
    ),
};

// Manually assign some values
chartMap[_dateKey(today)] = chartMap[_dateKey(today)]!.copyWith(
  checkIn: 6,
  checkOut: 3,
);

chartMap[_dateKey(
  today.add(const Duration(days: 1)),
)] = chartMap[_dateKey(today.add(const Duration(days: 1)))]!.copyWith(
  checkIn: 2,
  checkOut: 2,
);

final List<BookingChartData> chartData = next7Days
    .map((d) => chartMap[_dateKey(d)]!)
    .toList();

final int maxValue = chartData.isEmpty
    ? 0
    : chartData.map((e) => e.total).reduce((a, b) => a > b ? a : b);

return Scaffold(
  body: Center(
    child: Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(18),
      ),
      child: SizedBox(
        height: 220,
        child: SfCartesianChart(
          plotAreaBorderWidth: 0,
          primaryXAxis: CategoryAxis(
            majorGridLines: const MajorGridLines(width: 0),
            axisLine: const AxisLine(width: 0),
            majorTickLines: const MajorTickLines(size: 0),
          ),
          primaryYAxis: NumericAxis(
            isVisible: false,
            maximum: maxValue * 1.4,
          ),
          legend: const Legend(
            isVisible: true,
            position: LegendPosition.bottom,
          ),
          series: <CartesianSeries>[
            // Check-Out
            StackedColumnSeries<BookingChartData, String>(
              name: "Check-Out",
              dataSource: chartData,
              xValueMapper: (data, _) => data.label,
              yValueMapper: (data, _) => data.checkOut,
              color: Colors.red,
              width: 0.5,
              spacing: 0.3,
              dataLabelSettings: const DataLabelSettings(
                isVisible: true,
                labelAlignment: ChartDataLabelAlignment.outer,
              ),
              dataLabelMapper: (data, _) =>
                  data.checkOut == 0 ? "" : data.checkOut.toString(),
            ),

            // Check-In
            StackedColumnSeries<BookingChartData, String>(
              name: "Check-In",
              dataSource: chartData,
              xValueMapper: (data, _) => data.label,
              yValueMapper: (data, _) => data.checkIn,
              color: Colors.green,
              width: 0.5,
              spacing: 0.3,
              borderRadius: const BorderRadius.vertical(
                top: Radius.circular(8),
              ),
              dataLabelSettings: const DataLabelSettings(
                isVisible: true,
                labelAlignment: ChartDataLabelAlignment.outer,
              ),
              dataLabelMapper: (data, _) =>
                  data.checkIn == 0 ? "" : data.checkIn.toString(),
            ),

            // Total (Transparent series)
            StackedColumnSeries<BookingChartData, String>(
              name: "Total",
              dataSource: chartData,
              xValueMapper: (data, _) => data.label,
              yValueMapper: (data, _) => 4,
              color: Colors.transparent,
              width: 0.5,
              spacing: 0.3,
              dataLabelMapper: (data, _) => data.total.toString(),
              dataLabelSettings: const DataLabelSettings(
                isVisible: true,
                labelAlignment: ChartDataLabelAlignment.outer,
                textStyle: TextStyle(
                  color: Colors.black,
                  fontWeight: FontWeight.w700,
                  fontSize: 12,
                ),
              ),
            ),
          ],
        ),
      ),
    ),
  ),
);

}

String dateLabel(DateTime date) {
if (isSameDate(date, DateTime.now())) {
return "Today";
}
return DateFormat('dd MMM').format(date);
}

bool isSameDate(DateTime a, DateTime b) {
return a.year == b.year && a.month == b.month && a.day == b.day;
}
}

class BookingChartData {
final String label;
final int checkIn;
final int checkOut;
final DateTime date;

BookingChartData({
required this.label,
required this.checkIn,
required this.checkOut,
required this.date,
});

int get total => checkIn + checkOut;

BookingChartData copyWith({
String? label,
int? checkIn,
int? checkOut,
DateTime? date,
}) {
return BookingChartData(
label: label ?? this.label,
checkIn: checkIn ?? this.checkIn,
checkOut: checkOut ?? this.checkOut,
date: date ?? this.date,
);
}
}

Screenshots or Video

Screenshots / Video demonstration Image

Stack Traces

Stack Traces
[Add the Stack Traces here]

On which target platforms have you observed this bug?

Android, iOS

Flutter Doctor output

Doctor output
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.38.6, on macOS 15.6 24G84 darwin-arm64, locale en-IN)
[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0)
[✓] Xcode - develop for iOS and macOS (Xcode 26.2)
[✓] Chrome - develop for the web
[✓] Connected device (4 available)
[✓] Network resources

• No issues found!

Metadata

Metadata

Assignees

No one assigned

    Labels

    chartsCharts componentsolvedSolved the query using existing solutionswaiting for customer responseCannot make further progress until the customer responds.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions