imaNNeo / fl_chart

FL Chart is a highly customizable Flutter chart library that supports Line Chart, Bar Chart, Pie Chart, Scatter Chart, and Radar Chart.
https://flchart.dev
MIT License
6.87k stars 1.78k forks source link

How can i display a specific tooltip in a line chart, but also display the tooltip when dragging. #1571

Closed generalChang closed 9 months ago

generalChang commented 9 months ago

I'm creating a line chart, but there are a few conditions.

  1. The maximum and minimum values ​​should be displayed as tooltips in the line chart.
  2. When dragging on the line chart, the tooltip should be displayed automatically.

I was making a chart while looking at example 5, but The tooltip is not displayed when dragging because the handleBuiltInTouches of LineTouchData is set to false.

I want a chart similar to the following photo. image

Thank you for your help.

imaNNeo commented 9 months ago

Hi, you can just disable the built-in touches (just like what we did in example 5), then implement your custom touch handling. (just take a look at the default touch handling that we have built-in)

minhtaama commented 9 months ago

I have another approach, create another class that extends FlDotPainter. And inside that class, we will draw the information that we want to display.

With this implementation, I think it is more straight-forward and we can display in a different kind to the built-in tooltip.

This is how I display a small box indicate "Today" under the dot (you can add another property to the class FLDotTodayPainter below to display other information). I took all the code from FlRoundedDotPainter, move the code inside draw function to _drawDot, add some properties that I need and a new _drawText function:

There is a problem when some spot is behind my "Today" box, user tap it then the chart will draw the touched indicator above my "Today" box. But I think some tweaking in touchCallback to hide the "Today" box when user touch near the "Today" spot can solve the problem. I haven't tried it but some one did, please share the code for me because I'm lazy :(

class FlDotTodayPainter extends FlDotPainter {

  /// This class is an implementation of a [FlDotPainter] that draws
  /// a circled shape and a "today" indicator
  FlDotTodayPainter({
    required this.color,
    this.dotRadius = 4.0,
    this.cornerRadius = 4.0,
    this.dotColor,
    this.dotStrokeWidth = 0.0,
    this.offsetFromDot = 8.0,
    this.textPadding = EdgeInsets.zero,
  });

  Color color;

  double dotRadius;

  /// Optional color for dot color
  Color? dotColor;

  /// The stroke width to use for the circle
  double dotStrokeWidth;

  double offsetFromDot;

  EdgeInsets textPadding;

  double cornerRadius;

  double get _colorLum => color.computeLuminance();

  void _drawDot(Canvas canvas, FlSpot spot, Offset offsetInCanvas) {
    if (dotStrokeWidth != 0.0) {
      canvas.drawCircle(
        offsetInCanvas,
        dotRadius + (dotStrokeWidth / 2),
        Paint()
          ..color = dotColor ?? color
          ..strokeWidth = dotStrokeWidth
          ..style = PaintingStyle.stroke,
      );
    }

    canvas.drawCircle(
      offsetInCanvas,
      dotRadius,
      Paint()
        ..color = color
        ..style = PaintingStyle.fill,
    );
  }

  void _drawText(Canvas canvas, FlSpot spot, Offset offsetInCanvas) {
    final textSpan = TextSpan(
      text: 'Today'.hardcoded,
      style: kHeader3TextStyle.copyWith(
        color: _colorLum < 0.5 ? AppColors.white : AppColors.black,
        fontSize: 10,
      ),
    );

    final textPainter = TextPainter(
      text: textSpan,
      textDirection: TextDirection.ltr,
      textAlign: TextAlign.center,
    )..layout(
        minWidth: 0,
        maxWidth: 150,
      );

    // Offset to center with the dot
    final textOffset = Offset(
      offsetInCanvas.dx - (textPainter.width / 2),
      offsetInCanvas.dy + offsetFromDot + textPadding.top,
    );

    canvas.drawRRect(
      RRect.fromLTRBAndCorners(
        textOffset.dx - textPadding.left,
        textOffset.dy - textPadding.top,
        textOffset.dx + textPainter.width + textPadding.right,
        textOffset.dy + textPainter.height + textPadding.bottom,
        topLeft: Radius.circular(cornerRadius),
        topRight: Radius.circular(cornerRadius),
        bottomRight: Radius.circular(cornerRadius),
        bottomLeft: Radius.circular(cornerRadius),
      ),
      Paint()
        ..color = color
        ..style = PaintingStyle.fill,
    );

    textPainter.paint(canvas, textOffset);
  }

  /// Implementation of the parent class to draw the circle
  @override
  void draw(Canvas canvas, FlSpot spot, Offset offsetInCanvas) {
    _drawDot(canvas, spot, offsetInCanvas);

    _drawText(canvas, spot, offsetInCanvas);
  }

  ...
}

Screenshot 2024-02-08 143228

cchenyun commented 5 months ago

I have the same problem. How did you solve it,pleease tell me,Thanks