How to draw a tooltip rect when touching one of the custompainter's shapes?

138 views Asked by At

I have this simple chart made from custompainter, i also used a touchable lib to detect touches, but I dont know how to have it repaint or draw a rect when the user touches it.

Tried Methods:

  • draw directly after clicking

    onTapDown: (details) {
          //display details
          toolTipDisplay = true;
          toolTipPos =
              Offset(details.globalPosition.dx, details.globalPosition.dy);
          canvas.drawRect(
              Rect.fromLTRB(toolTipPos.dx, toolTipPos.dy, 100, 100), paint);
        },

This results in the error Unhandled Exception: Bad state: A Dart object attempted to access a native peer, but the native peer has been collected (nullptr). This is usually the result of calling methods on a native-backed object when the native resources have already been disposed.

  • Tried setting up a variable to change in the onTap Func, and draw it on paint Func. Does not draw anything when clicked.

Here is the paint Func of the Painter:


    void paint(Canvas canvas, Size size) {
        //turn canvas to canvas with hittest
        touchable.TouchyCanvas touchCanvas =
            touchable.TouchyCanvas(context, canvas);
    
        // calculate series
        // 20% empty space
        // 80% series
        double seriesWidth = chartWidth / barData[0].valueList.length;
        double seriesLeft = chartLeft;
    
        // and then work on adding the height of the bars
    
        barData.asMap().forEach((index, series) {
             paintBars(seriesWidth, seriesLeft, series, maxY, chartHeight, touchCanvas,
              redPaint, index);
        });
    
        Paint linePaint = Paint();
        linePaint.color = Colors.red;
        linePaint.strokeWidth = 3;
        lineData.asMap().forEach((index, series) {
            paintLines(seriesWidth, seriesLeft, series, maxY, chartHeight,
              touchCanvas, linePaint, index);
        });
    
        // axis titles
        // xTitle
        if (xAxisTitle != null) {
            paintXAxisTitle(chartLeft, chartWidth, chartHeight, size, canvas);
        }
        // yTitle
        if (yAxisTitle != null) {
            paintYAxisTitle(chartLeft, size, chartHeight, canvas);
        }
        paintXTitles(seriesLeft, seriesWidth, chartHeight, size, canvas);
        paintYTitles(
            nextUnit, maxY, chartHeight, size, chartWidth, canvas, blackPaint);
    
        //display tooltip
        if (toolTipDisplay) {
            Paint toolTipPaint = Paint();
            toolTipPaint.color = Colors.blue;
            canvas.drawRRect(
                RRect.fromLTRBR(
                    toolTipPos.dx, toolTipPos.dy, 100, 100, Radius.circular(20)),
                toolTipPaint);
        }
    }

The onTap functions are inside paintLines and paintBars.

2

There are 2 answers

1
ankushlokhande On

Tooltip is already designed as a Widget in flutter: https://api.flutter.dev/flutter/material/Tooltip-class.html

Tooltip(
  key: tooltipkey,
  triggerMode: TooltipTriggerMode.manual,
  showDuration: const Duration(seconds: 1),
  message: 'I am a Tooltip',
  child: const Text('Tap on the FAB'),
),

You can find multiple cases and customization examples from the above link.

Also, achieve this with package: https://pub.dev/packages/just_the_tooltip
(This package provide customization for the Tooltip)

0
Duy Tran On

I assume that you would like to show a tooltip when clicking on a particular point like the gif below, right?

enter image description here

The idea here is that we should control the show/hide through value of each item in data list, and when clicking, expose a callback to parent widget, then setState data list to rebuild your CustomPainter -> update tooltip showing

E.g: We have a list of progress weights (7 weights for 7 days) that need to show on the chart, my model should include the field (isFocusing) to check which day should show the tooltip

class MyWeight {
  final DateTime dateTime;
  final double weight;
  bool isFocusing; // <- to control which point showing tooltip

  MyWeight({required this.dateTime, required this.weight, this.isFocusing = false});
}

From data list -> render points, at that time, we can map point to item model (which includes isFocusing field) to check whether this point should show tooltip above it

When onTapDown is triggered when clicking on a particular point, we can expose event to parent -> setState to change item focusing in data list -> rebuild your CustomPainter

Refer my code here https://github.com/duytq94/flutter-custom-line-chart