Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[syncfusion_flutter_charts-SfCartesianChart]Conflic beetween scroll horizontal and get index of series #2124

Open
quynhnb2021 opened this issue Oct 11, 2024 · 3 comments
Labels
charts Charts component waiting for customer response Cannot make further progress until the customer responds.

Comments

@quynhnb2021
Copy link

Bug description

When using SfCartesianChart in Flutter with horizontal scrolling enabled via ZoomPanBehavior and autoScrollingDelta, the chart experiences issues when trying to retrieve the index of the data points using the onTrackballPositionChanging callback. Specifically:

When panning to the rightmost data points, the chart glitches by snapping back to the initial position, preventing further horizontal scrolling.
Trackball and tooltip do not appear even though TrackballBehavior is enabled.
Additionally, I am trying to change the color of axis labels (to red) when the trackball is over a specific data point or when scrolling. However, the index from the onTrackballPositionChanging callback does not update properly, preventing the color change from happening.

Steps to reproduce

1: Create a SfCartesianChart with the following configuration:
Enable horizontal panning with ZoomPanBehavior(enablePanning: true).
Set auto-scrolling properties with autoScrollingDelta: 5 and autoScrollingMode: AutoScrollingMode.start.
Add a TrackballBehavior with activationMode: ActivationMode.singleTap and enable: true.
Implement the onTrackballPositionChanging callback to capture the index of the data point:
onTrackballPositionChanging: (trackballArgs) { setState(() { indexOfPoint = trackballArgs.chartPointInfo.dataPointIndex ?? 0; }); },
Use axisLabelFormatter to change the axis label color when the index matches the data point:
axisLabelFormatter: (AxisLabelRenderDetails details) { return ChartAxisLabel( details.text, TextStyle( color: indexOfPoint == details.value ? Colors.red : Colors.black, ), ); },

2: Scroll horizontally to the rightmost data points.

3: Observe the following issues:
The chart snaps back to the initial position, preventing continuous scrolling to the right.
The trackball and tooltip are not displayed when tapping on the chart, even though the TrackballBehavior is enabled.
The axis label color does not update based on the indexOfPoint due to improper index handling.
This issue seems to be a conflict between ZoomPanBehavior and TrackballBehavior when horizontal scrolling is enabled with autoScrollingMode.

Code sample

Code sample
[import 'dart:math';

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

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

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

class _MyHomePageState extends State<MyHomePage> {
  late List<ChartData> chartData;
  late TrackballBehavior _trackballBehavior;
  int indexOfPoint = 0;
  @override
  void initState() {
    chartData = List<ChartData>.generate(100,
            (int index) => ChartData(index.toString(), Random().nextInt(90)))
        .toList();
    _trackballBehavior = TrackballBehavior(
      activationMode: ActivationMode.singleTap,
      enable: true,
    );
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Padding(
        padding: const EdgeInsets.all(20.0),
        child: SfCartesianChart(
          onAxisLabelTapped: (axisLabelTapArgs) {
            setState(() {
              indexOfPoint = axisLabelTapArgs.value as int;
            });
          },
          trackballBehavior: _trackballBehavior,
          onTrackballPositionChanging: (trackballArgs) {
            setState(() {
              indexOfPoint = trackballArgs.chartPointInfo.dataPointIndex ?? 0;
            });
          },
          zoomPanBehavior: ZoomPanBehavior(
            enablePanning: true,
          ),
          primaryXAxis: CategoryAxis(
              autoScrollingDelta: 5,
              autoScrollingMode: AutoScrollingMode.start,
              axisLabelFormatter: (AxisLabelRenderDetails details) {
                return ChartAxisLabel(
                    details.text,
                    TextStyle(
                      color: indexOfPoint == details.value
                          ? Colors.red
                          : Colors.black,
                    ));
              }),
          series: <SplineSeries<ChartData, String>>[
            SplineSeries(
              dataSource: chartData,
              xValueMapper: (ChartData data, _) => data.x,
              yValueMapper: (ChartData data, _) => data.y,
            )
          ],
        ),
      ),
    );
  }
}

class ChartData {
  final String x;
  final num y;
  ChartData(this.x, this.y);
}
]

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Screen.Recording.2024-10-11.at.23.40.28.mov

Stack Traces

Stack Traces
[Add the Stack Traces here]
[demochart.zip](https://github.com/user-attachments/files/17345537/demochart.zip)

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.24.3, on macOS 14.5 23F79 darwin-x64, locale
    en-VN)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 15.0)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2023.1)
[✓] IntelliJ IDEA Ultimate Edition (version 2023.3.4)
[✓] VS Code (version 1.93.1)
[✓] Connected device (4 available)
[✓] Network resources

• No issues found!]
@quynhnb2021 quynhnb2021 changed the title Conflic beetween scroll horizontal and get index of series [syncfusion_flutter_charts-SfCartesianChart]Conflic beetween scroll horizontal and get index of series Oct 12, 2024
@VijayakumarMariappan VijayakumarMariappan added charts Charts component open Open labels Oct 14, 2024
@quynhnb2021
Copy link
Author

@VijayakumarMariappan Hello, Did you check this bug?

@Baranibharathip
Copy link

Hi @quynhnb2021,

We have validated the issue and would like to inform you that the reported problem 'Charts get resetting to old or previous position' occurs because of calling setState in the onAxisLabelTapped and onTrackballPositionChanging callbacks, causes the chart to rebuild every time, resetting when the trackball is moved or the chart is panned. We recommend you to avoid using setState inside the chart callbacks while doing interactions.

We have achieved your requirement by implementing a CustomTrackballBehavior, where we override the onPaint method of the TrackballBehavior. This onPaint method allows us to draw custom axis labels with specific styling, such as custom colors and font sizes. By using the _CustomTrackballBehavior class, you can control the appearance and positioning of the axis labels and tooltips as per your needs. Please refer to the following code snippet.

Code snippet:

class _CustomTrackballBehavior extends TrackballBehavior {
  @override
  bool get enable => true;

  @override
  ActivationMode get activationMode => ActivationMode.singleTap;

  @override
  void onPaint(PaintingContext context, Offset offset,
      SfChartThemeData chartThemeData, ThemeData themeData) {
    super.onPaint(context, offset, chartThemeData, themeData);
    if (chartPointInfo.isEmpty || parentBox == null) {
      return;
    }

    final Rect plotAreaBounds = parentBox!.paintBounds;
    final Offset position =
        Offset(chartPointInfo[0].xPosition!, chartPointInfo[0].yPosition!);
    _drawCustomAxisLabel(context.canvas, position, plotAreaBounds);
  }

  void _drawCustomAxisLabel(
      Canvas canvas, Offset position, Rect plotAreaBounds) {
    const TextStyle textStyle =
        TextStyle(color: Colors.red, fontSize: 13, fontWeight: FontWeight.bold);
    final Paint rectPaint = Paint()
      ..color = Colors.white
      ..style = PaintingStyle.fill;

    final Offset tooltipPos = Offset(position.dx, plotAreaBounds.bottom);
    final String label = chartPointInfo[0].header ?? '0';
    final Size labelSize = measureText(label, textStyle);
    final Rect rect = _calculateRect(labelSize, tooltipPos);
    final Offset alignedPos = tooltipPos.translate(-rect.width / 2, 5);

    final RRect tooltipRRect = RRect.fromRectAndRadius(
      Rect.fromLTWH(alignedPos.dx, alignedPos.dy, rect.width, rect.height),
      const Radius.circular(5),
    );

    canvas.drawRRect(tooltipRRect, rectPaint);
    _drawText(canvas, label, _textPosition(tooltipRRect, labelSize), textStyle);
  }

  Rect _calculateRect(Size labelSize, Offset tooltipPos) {
    const double padding = 5;
    return Rect.fromLTWH(
      tooltipPos.dx,
      tooltipPos.dy,
      labelSize.width + padding,
      labelSize.height + padding,
    );
  }

  Offset _textPosition(RRect tooltipRRect, Size labelSize) {
    return Offset(
        (tooltipRRect.left + tooltipRRect.width / 2) - labelSize.width / 2,
        (tooltipRRect.top + tooltipRRect.height / 2) - labelSize.height / 2);
  }

  void _drawText(Canvas canvas, String text, Offset point, TextStyle style) {
    final TextPainter textPainter = TextPainter(
      text: TextSpan(text: text, style: style),
      textAlign: TextAlign.center,
      textDirection: TextDirection.ltr,
    );

    textPainter
      ..layout()
      ..paint(canvas, point);
  }
}

Demo:

Recording.2024-10-28.160135.mp4

For more details, refer the following Knowledge Base link:
KB : https://support.syncfusion.com/kb/article/16112/how-to-customize-the-trackball-in-flutter-cartesianchart

Regards,
Baranibharathi P.

@LavanyaGowtham2021 LavanyaGowtham2021 added waiting for customer response Cannot make further progress until the customer responds. and removed open Open labels Oct 28, 2024
@quynhnb2021
Copy link
Author

quynhnb2021 commented Oct 29, 2024

Hi @Baranibharathip ,

Thanks, it works! By the way, could you help me check how to use CustomTrackballBehavior to showByIndex in your example?
I want to bold in red and display the tooltip and trackball on the first element of the chart without needing to tap on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
charts Charts component waiting for customer response Cannot make further progress until the customer responds.
Projects
None yet
Development

No branches or pull requests

4 participants