syncfusion / flutter-widgets

Syncfusion Flutter widgets libraries include high quality UI widgets and file-format packages to help you create rich, high-quality applications for iOS, Android, and web from a single code base.
1.44k stars 672 forks source link

Chart tooltip pop-up bug on multi-series #1819

Closed DominicOrga closed 2 weeks ago

DominicOrga commented 1 month ago

Bug description

When a tooltip is shown on series B, then decide to hide that series, attempting to show the tooltip of series A gets overlapped by a "ghost" tooltip from series B.

Steps to reproduce

  1. Create a chart with 2 series (Let's call them Series A and Series B)
  2. Open a tooltip from Series B.
  3. Hide Series B.
  4. Open a tooltip from Series A.

Code sample

Code sample ```dart SfCartesianChart( onActualRangeChanged: (args) => onActualRangeChanged(context: context, args: args), zoomPanBehavior: zoomPanBehavior(), primaryXAxis: primaryXAxis(chartRange: selectedChartRange), tooltipBehavior: tooltipBehavior(context: context, chartRange: selectedChartRange), legend: legend(chartRangeDataSeriesList: chartRangeDataSeriesList), series: chartRangeDataSeriesList.mapIndexed( (index, dataSeries) { return ColumnSeries( legendItemText: dataSeries.legend, dataSource: dataSeries.emptyAndFilledDataPoints(selectedChartRange), color: seriesColors[index % seriesColors.length], borderRadius: BorderRadius.circular(8), animationDuration: 0, xValueMapper: (dataPoint, _) => dataPoint.dateTime, yValueMapper: (dataPoint, _) { switch (dataPoint) { case TestEmptyDataPoint(): return null; case TestFilledDataPoint(): return dataPoint.yValue(); } }, ); }, ).toList(), ); /// The possible colors assigned to the series in the Syncfusion chart. List get seriesColors => [ Colors.tealAccent.shade200, Colors.amberAccent.shade200, Colors.deepPurpleAccent.shade200, Colors.pinkAccent.shade200, ]; /// The primary X-axis assigned to the Syncfusion chart. ChartAxis primaryXAxis({required TestChartRange chartRange}) { switch (chartRange) { case TestChartRange.week: return DateTimeAxis( autoScrollingDelta: 7, autoScrollingDeltaType: DateTimeIntervalType.days, dateFormat: DateFormat('E'), interval: 1, ); case TestChartRange.month: return DateTimeAxis( autoScrollingDelta: 30, autoScrollingDeltaType: DateTimeIntervalType.days, dateFormat: DateFormat('d'), interval: 7, ); case TestChartRange.sixMonth: return DateTimeAxis( autoScrollingDelta: 6, autoScrollingDeltaType: DateTimeIntervalType.months, dateFormat: DateFormat('MMM'), interval: 1, ); case TestChartRange.year: return DateTimeAxis( autoScrollingDelta: 12, autoScrollingDeltaType: DateTimeIntervalType.months, dateFormat: DateFormat('MMM'), interval: 3, ); } } Legend legend({required List chartRangeDataSeriesList}) { return Legend( isVisible: chartRangeDataSeriesList.length > 1, position: LegendPosition.bottom, ); } /// The zoom and pan behavior assigned to the Syncfusion chart. ZoomPanBehavior zoomPanBehavior() { return ZoomPanBehavior( // Enables panning on the x-axis. enablePanning: true, // Shows only the necessary Y-labels based on the max Y-value upon // scrolling. zoomMode: ZoomMode.x, ); } /// The tooltip customization for the Syncfusion chart. TooltipBehavior tooltipBehavior({ required BuildContext context, required TestChartRange chartRange, }) { return TooltipBehavior( enable: true, color: context.colorScheme.surface5, builder: (data, point, series, pointIndex, seriesIndex) { switch (data as TestDataPoint) { case TestEmptyDataPoint(): return const SizedBox(); case TestFilledDataPoint(): return tooltip( context: context, dataPoint: data as TestFilledDataPoint, chartRange: chartRange, seriesName: series.legendItemText ?? '', startDate: data.dateTime, isDataPointMedian: chartRange.isSixMonth || chartRange.isYear, ); } }, ); } /// The tooltip displayed when the user taps on a data point in the /// Syncfusion chart. /// /// The [dataPoint] is the data point whose value is displayed in the tooltip. /// The [startDate] and [endDate] are the date range of the data point, which /// is useful when the [dataPoint] is a median value as specified by the /// [isDataPointMedian]. Widget tooltip({ required BuildContext context, required TestChartRange chartRange, required String seriesName, required bool isDataPointMedian, required TestFilledDataPoint? dataPoint, required DateTime? startDate, DateTime? endDate, }) { final formattedSeriesName = isDataPointMedian ? 'Mdn. $seriesName' : seriesName; final String formattedDateRange; // The date format of the tooltip depends on the chart range. // // For week and months, the date format is 'MMM d, yyyy since data is // displayed per day. // // For six months and year, the date format is 'MMM yyyy' since data // is displayed per month. switch (chartRange) { case TestChartRange.week: case TestChartRange.month: if (startDate == null) { formattedDateRange = ''; } else if (endDate == null || startDate.eqvYearMonthDay(endDate)) { // Same year, same month, same day // - Start Date: Oct 15, 2021 // - End Date: Oct 15, 2021 // - Format: Oct 15, 2021 formattedDateRange = DateFormat.yMMMd().format(startDate); } else if (startDate.eqvYear(endDate) && startDate.eqvMonth(endDate)) { // Same year, same month, different day // Start Date: Oct 15, 2021 // End Date: Oct 21, 2021 // Format: Oct 15 - 21, 2021 formattedDateRange = '${DateFormat.MMMd().format(startDate)} - ' '${DateFormat('d, y').format(endDate)}'; } else if (startDate.eqvYear(endDate)) { // Same year, different month // Start Date: Oct 15, 2021 // End Date: Nov 21, 2021 // Format: Oct 15 - Nov 21, 2021 formattedDateRange = '${DateFormat.MMMd().format(startDate)} - ' '${DateFormat.yMMMd().format(endDate)}'; } else { // Different year // Start Date: Dec 15, 2021 // End Date Date: Jan 15, 2022 // Format: Dec 15, 2021 - Jan 21, 2022 formattedDateRange = '${DateFormat.yMMMd().format(startDate)} - ' '${DateFormat.yMMMd().format(endDate)}'; } case TestChartRange.sixMonth: case TestChartRange.year: if (startDate == null) { formattedDateRange = ''; } else if (endDate == null || startDate.eqvYear(endDate) && startDate.eqvMonth(endDate)) { // Same year, same month // Start Date: Oct 2021 // End Date: Oct 2021 // Format: Oct 2021 formattedDateRange = DateFormat.yMMM().format(startDate); } else if (startDate.eqvYear(endDate)) { // Same year, different month // Start Date: Nov 2021 // End Date: Feb 2021 // Format: Nov - Feb 2022 formattedDateRange = '${DateFormat.MMM().format(startDate)} - ' '${DateFormat.yMMM().format(endDate)}'; } else { // Different year // Start Date: Dec 2021 // End Date: Jan 2022 // Format: Dec 2021 - Jan 2022 formattedDateRange = '${DateFormat.yMMM().format(startDate)} - ' '${DateFormat.yMMM().format(endDate)}'; } } return Padding( padding: const EdgeInsets.all(Gap.paddingSmall), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( formattedSeriesName, style: context.textTheme.labelLarge?.copyWith( color: context.colorScheme.onSurface, ), ), const SizedBox(height: Gap.paddingTiny), if (dataPoint != null) dataPoint.tooltipValue(context) else const Text('–'), if (dataPoint?.isOptimal != null) Padding( padding: const EdgeInsets.only(top: Gap.paddingSmall), child: OptimalityChip(isOptimal: dataPoint!.isOptimal!), ), const SizedBox(height: Gap.paddingSmall), Text( formattedDateRange, style: context.textTheme.labelMedium ?.copyWith(color: context.colorScheme.onSurface), ), ], ), ); } /// Called when the actual range of the Syncfusion chart changes. /// /// If the x-axis has been panned, the visible range of the chart will be /// reported to the parent [TestChartRangeTabCubit]. void onActualRangeChanged({ required BuildContext context, required ActualRangeChangedArgs args, }) { if (args.orientation == AxisOrientation.horizontal) { context.read().setChartVisibleRange( (args.visibleMin as num).ceil(), (args.visibleMax as num).floor(), ); } } ```

Screenshots or Video

https://github.com/syncfusion/flutter-widgets/assets/12520299/5ba0be99-bbbc-4c48-a444-e780910071b2

Stack Traces

No exception thrown.

On which target platforms have you observed this bug?

Android

Flutter Doctor output

Doctor output ```console [✓] Flutter (Channel stable, 3.19.5, on macOS 14.3.1 23D60 darwin-arm64, locale en-PH) • Flutter version 3.19.5 on channel stable at /Users/test/fvm/versions/3.19.5 • Upstream repository https://github.com/flutter/flutter.git • Framework revision 300451adae (3 weeks ago), 2024-03-27 21:54:07 -0500 • Engine revision e76c956498 • Dart version 3.3.3 • DevTools version 2.31.1 [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) • Android SDK at /Users/test/Library/Android/sdk • Platform android-34, build-tools 34.0.0 • ANDROID_HOME = /Users/test/Library/Android/sdk • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b829.9-10027231) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 15.3) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 15E204a • CocoaPods version 1.15.0 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2022.3) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b829.9-10027231) [✓] VS Code (version 1.88.1) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.86.0 [✓] Connected device (4 available) • Pixel 8 (mobile) • 3B031FDJH005H1 • android-arm64 • Android 14 (API 34) • iPhone 13 mini (mobile) • 00008110-00046C3411B8801E • ios • iOS 17.4.1 21E236 • macOS (desktop) • macos • darwin-arm64 • macOS 14.3.1 23D60 darwin-arm64 • Chrome (web) • chrome • web-javascript • Google Chrome 123.0.6312.124 [✓] Network resources • All expected network resources are available. • No issues found!```
PreethikaSelvam commented 3 weeks ago

Hi @DominicOrga,

The reported issue is already fixed, and the fix published in the below version. Therefore, we kindly request that you upgrade the syncfusion_flutter_charts package to the latest version below to avoid this issue.

Version: https://pub.dev/packages/syncfusion_flutter_charts/versions/25.1.41+1

Root cause: Series visibility is missed to consider while getting the tooltip value.

If you have any further queries, please feel free to reach out to us.

Regards, Preethika Selvam.

DominicOrga commented 2 weeks ago

I can confirm that this is now fixed. Thanks!