drawTouchTooltip method

  1. @visibleForTesting
void drawTouchTooltip(
  1. BuildContext context,
  2. CanvasWrapper canvasWrapper,
  3. LineTouchTooltipData tooltipData,
  4. AFlSpot showOnSpot,
  5. ShowingTooltipIndicators showingTooltipSpots,
  6. PaintHolder<LineChartData> holder,
)

Implementation

@visibleForTesting
void drawTouchTooltip(
  BuildContext context,
  CanvasWrapper canvasWrapper,
  LineTouchTooltipData tooltipData,
  AFlSpot showOnSpot,
  ShowingTooltipIndicators showingTooltipSpots,
  PaintHolder<LineChartData> holder,
) {
  final viewSize = canvasWrapper.size;

  const textsBelowMargin = 4;

  /// creating TextPainters to calculate the width and height of the tooltip
  final drawingTextPainters = <TextPainter>[];

  final tooltipItems =
      tooltipData.getTooltipItems(showingTooltipSpots.showingSpots);
  if (tooltipItems.length != showingTooltipSpots.showingSpots.length) {
    throw Exception('tooltipItems and touchedSpots size should be same');
  }

  for (var i = 0; i < showingTooltipSpots.showingSpots.length; i++) {
    final tooltipItem = tooltipItems[i];
    if (tooltipItem == null) {
      continue;
    }

    final span = TextSpan(
      style: Utils().getThemeAwareTextStyle(context, tooltipItem.textStyle),
      text: tooltipItem.text,
      children: tooltipItem.children,
    );

    final tp = TextPainter(
      text: span,
      textAlign: tooltipItem.textAlign,
      textDirection: tooltipItem.textDirection,
      textScaleFactor: holder.textScale,
    )..layout(maxWidth: tooltipData.maxContentWidth);
    drawingTextPainters.add(tp);
  }
  if (drawingTextPainters.isEmpty) {
    return;
  }

  /// biggerWidth
  /// some texts maybe larger, then we should
  /// draw the tooltip' width as wide as biggerWidth
  ///
  /// sumTextsHeight
  /// sum up all Texts height, then we should
  /// draw the tooltip's height as tall as sumTextsHeight
  var biggerWidth = 0.0;
  var sumTextsHeight = 0.0;
  for (final tp in drawingTextPainters) {
    if (tp.width > biggerWidth) {
      biggerWidth = tp.width;
    }
    sumTextsHeight += tp.height;
  }
  sumTextsHeight += (drawingTextPainters.length - 1) * textsBelowMargin;

  /// if we have multiple bar lines,
  /// there are more than one FlCandidate on touch area,
  /// we should get the most top FlSpot Offset to draw the tooltip on top of it
  final mostTopOffset = Offset(
    getPixelX(showOnSpot.x, viewSize, holder),
    getPixelY(showOnSpot.y, viewSize, holder),
  );

  final tooltipWidth = biggerWidth + tooltipData.tooltipPadding.horizontal;
  final tooltipHeight = sumTextsHeight + tooltipData.tooltipPadding.vertical;

  double tooltipTopPosition;
  if (tooltipData.showOnTopOfTheChartBoxArea) {
    tooltipTopPosition = 0 - tooltipHeight - tooltipData.tooltipMargin;
  } else {
    tooltipTopPosition =
        mostTopOffset.dy - tooltipHeight - tooltipData.tooltipMargin;
  }

  final tooltipLeftPosition = getTooltipLeft(
    mostTopOffset.dx,
    tooltipWidth,
    tooltipData.tooltipHorizontalAlignment,
    tooltipData.tooltipHorizontalOffset,
  );

  /// draw the background rect with rounded radius
  var rect = Rect.fromLTWH(
    tooltipLeftPosition,
    tooltipTopPosition,
    tooltipWidth,
    tooltipHeight,
  );

  if (tooltipData.fitInsideHorizontally) {
    if (rect.left < 0) {
      final shiftAmount = 0 - rect.left;
      rect = Rect.fromLTRB(
        rect.left + shiftAmount,
        rect.top,
        rect.right + shiftAmount,
        rect.bottom,
      );
    }

    if (rect.right > viewSize.width) {
      final shiftAmount = rect.right - viewSize.width;
      rect = Rect.fromLTRB(
        rect.left - shiftAmount,
        rect.top,
        rect.right - shiftAmount,
        rect.bottom,
      );
    }
  }

  if (tooltipData.fitInsideVertically) {
    if (rect.top < 0) {
      final shiftAmount = 0 - rect.top;
      rect = Rect.fromLTRB(
        rect.left,
        rect.top + shiftAmount,
        rect.right,
        rect.bottom + shiftAmount,
      );
    }

    if (rect.bottom > viewSize.height) {
      final shiftAmount = rect.bottom - viewSize.height;
      rect = Rect.fromLTRB(
        rect.left,
        rect.top - shiftAmount,
        rect.right,
        rect.bottom - shiftAmount,
      );
    }
  }

  final radius = Radius.circular(tooltipData.tooltipRoundedRadius);
  final roundedRect = RRect.fromRectAndCorners(
    rect,
    topLeft: radius,
    topRight: radius,
    bottomLeft: radius,
    bottomRight: radius,
  );
  _bgTouchTooltipPaint.color = tooltipData.tooltipBgColor;

  final rotateAngle = tooltipData.rotateAngle;
  final rectRotationOffset =
      Offset(0, Utils().calculateRotationOffset(rect.size, rotateAngle).dy);
  final rectDrawOffset = Offset(roundedRect.left, roundedRect.top);

  final textRotationOffset =
      Utils().calculateRotationOffset(rect.size, rotateAngle);

  if (tooltipData.tooltipBorder != BorderSide.none) {
    _borderTouchTooltipPaint
      ..color = tooltipData.tooltipBorder.color
      ..strokeWidth = tooltipData.tooltipBorder.width;
  }

  canvasWrapper.drawRotated(
    size: rect.size,
    rotationOffset: rectRotationOffset,
    drawOffset: rectDrawOffset,
    angle: rotateAngle,
    drawCallback: () {
      canvasWrapper
        ..drawRRect(roundedRect, _bgTouchTooltipPaint)
        ..drawRRect(roundedRect, _borderTouchTooltipPaint);
    },
  );

  /// draw the texts one by one in below of each other
  var topPosSeek = tooltipData.tooltipPadding.top;
  for (final tp in drawingTextPainters) {
    final yOffset = rect.topCenter.dy +
        topPosSeek -
        textRotationOffset.dy +
        rectRotationOffset.dy;

    double xOffset;
    switch (tp.textAlign.getFinalHorizontalAlignment(tp.textDirection)) {
      case HorizontalAlignment.left:
        xOffset = rect.left + tooltipData.tooltipPadding.left;
        break;
      case HorizontalAlignment.right:
        xOffset = rect.right - tooltipData.tooltipPadding.right - tp.width;
        break;
      default:
        xOffset = rect.center.dx - (tp.width / 2);
        break;
    }

    final drawOffset = Offset(
      xOffset,
      yOffset,
    );

    canvasWrapper.drawRotated(
      size: rect.size,
      rotationOffset: rectRotationOffset,
      drawOffset: rectDrawOffset,
      angle: rotateAngle,
      drawCallback: () {
        canvasWrapper.drawText(tp, drawOffset);
      },
    );
    topPosSeek += tp.height;
    topPosSeek += textsBelowMargin;
  }
}