diff --git a/example/lib/presentation/samples/bar/bar_chart_sample1.dart b/example/lib/presentation/samples/bar/bar_chart_sample1.dart index 30ffa61f2..ec4686d29 100644 --- a/example/lib/presentation/samples/bar/bar_chart_sample1.dart +++ b/example/lib/presentation/samples/bar/bar_chart_sample1.dart @@ -164,7 +164,7 @@ class BarChartSample1State extends State { touchTooltipData: BarTouchTooltipData( getTooltipColor: (_) => Colors.blueGrey, tooltipHorizontalAlignment: FLHorizontalAlignment.right, - tooltipMargin: -10, + tooltipVerticalOffset: -10, getTooltipItem: (group, groupIndex, rod, rodIndex) { String weekDay; switch (group.x) { diff --git a/example/lib/presentation/samples/bar/bar_chart_sample3.dart b/example/lib/presentation/samples/bar/bar_chart_sample3.dart index 5883a8f08..7026b5f83 100644 --- a/example/lib/presentation/samples/bar/bar_chart_sample3.dart +++ b/example/lib/presentation/samples/bar/bar_chart_sample3.dart @@ -26,7 +26,7 @@ class _BarChart extends StatelessWidget { touchTooltipData: BarTouchTooltipData( getTooltipColor: (group) => Colors.transparent, tooltipPadding: EdgeInsets.zero, - tooltipMargin: 8, + tooltipVerticalOffset: 8, getTooltipItem: ( BarChartGroupData group, int groupIndex, diff --git a/example/lib/presentation/samples/bar/bar_chart_sample7.dart b/example/lib/presentation/samples/bar/bar_chart_sample7.dart index ecb36f9dc..7693fc46b 100644 --- a/example/lib/presentation/samples/bar/bar_chart_sample7.dart +++ b/example/lib/presentation/samples/bar/bar_chart_sample7.dart @@ -122,8 +122,8 @@ class _BarChartSample7State extends State { enabled: true, handleBuiltInTouches: false, touchTooltipData: BarTouchTooltipData( + tooltipVerticalOffset: 0, getTooltipColor: (group) => Colors.transparent, - tooltipMargin: 0, getTooltipItem: ( BarChartGroupData group, int groupIndex, diff --git a/example/lib/presentation/samples/line/line_chart_sample8.dart b/example/lib/presentation/samples/line/line_chart_sample8.dart index 2c72c5279..b2e0af9b9 100644 --- a/example/lib/presentation/samples/line/line_chart_sample8.dart +++ b/example/lib/presentation/samples/line/line_chart_sample8.dart @@ -150,7 +150,8 @@ class _LineChartSample8State extends State { label: HorizontalLineLabel( show: true, alignment: Alignment.topRight, - padding: const EdgeInsets.only(right: 5, bottom: 5), + verticalOffset: 5, + horizontalOffset: -5, style: const TextStyle( fontSize: 9, fontWeight: FontWeight.bold, @@ -167,8 +168,9 @@ class _LineChartSample8State extends State { dashArray: [5, 10], label: VerticalLineLabel( show: true, + verticalOffset: 5, + horizontalOffset: -5, alignment: Alignment.bottomRight, - padding: const EdgeInsets.only(left: 5, bottom: 5), style: const TextStyle( fontSize: 9, fontWeight: FontWeight.bold, diff --git a/example/lib/presentation/samples/scatter/scatter_chart_sample2.dart b/example/lib/presentation/samples/scatter/scatter_chart_sample2.dart index 25b3ad4a9..3b9f96d6c 100644 --- a/example/lib/presentation/samples/scatter/scatter_chart_sample2.dart +++ b/example/lib/presentation/samples/scatter/scatter_chart_sample2.dart @@ -126,6 +126,7 @@ class _ScatterChartSample2State extends State { : SystemMouseCursors.click; }, touchTooltipData: ScatterTouchTooltipData( + tooltipVerticalOffset: 10, getTooltipColor: (ScatterSpot touchedBarSpot) { return touchedBarSpot.dotPainter.mainColor; }, @@ -155,7 +156,6 @@ class _ScatterChartSample2State extends State { color: color1, fontStyle: FontStyle.italic, ), - bottomMargin: 10, children: [ TextSpan( text: '${touchedBarSpot.x.toInt()} \n', diff --git a/lib/src/chart/bar_chart/bar_chart_data.dart b/lib/src/chart/bar_chart/bar_chart_data.dart index 70fd27580..42cb0cca3 100644 --- a/lib/src/chart/bar_chart/bar_chart_data.dart +++ b/lib/src/chart/bar_chart/bar_chart_data.dart @@ -692,8 +692,9 @@ class BarTouchTooltipData with EquatableMixin { /// otherwise you can show it manually using [BarChartGroupData.showingTooltipIndicators]. /// Tooltip shows on top of rods, with [getTooltipColor] as a background color, /// and you can set corner radius using [tooltipRoundedRadius]. - /// If you want to have a padding inside the tooltip, fill [tooltipPadding], - /// or If you want to have a bottom margin, set [tooltipMargin]. + /// If you want to have tooltip padding, fill [tooltipPadding], + /// If you want to adjust tooltip vertical position, set [tooltipVerticalOffset] + /// If you want to adjust tooltip horizontal position, set [tooltipHorizontalOffset] /// Content of the tooltip will provide using [getTooltipItem] callback, you can override it /// and pass your custom data to show in the tooltip. /// You can restrict the tooltip's width using [maxContentWidth]. @@ -703,8 +704,8 @@ class BarTouchTooltipData with EquatableMixin { BarTouchTooltipData({ double? tooltipRoundedRadius, EdgeInsets? tooltipPadding, - double? tooltipMargin, FLHorizontalAlignment? tooltipHorizontalAlignment, + double? tooltipVerticalOffset, double? tooltipHorizontalOffset, double? maxContentWidth, GetBarTooltipItem? getTooltipItem, @@ -717,7 +718,7 @@ class BarTouchTooltipData with EquatableMixin { }) : tooltipRoundedRadius = tooltipRoundedRadius ?? 4, tooltipPadding = tooltipPadding ?? const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - tooltipMargin = tooltipMargin ?? 16, + tooltipVerticalOffset = tooltipVerticalOffset ?? 16, tooltipHorizontalAlignment = tooltipHorizontalAlignment ?? FLHorizontalAlignment.center, tooltipHorizontalOffset = tooltipHorizontalOffset ?? 0, @@ -737,15 +738,15 @@ class BarTouchTooltipData with EquatableMixin { /// Applies a padding for showing contents inside the tooltip. final EdgeInsets tooltipPadding; - /// Applies a bottom margin for showing tooltip on top of rods. - final double tooltipMargin; - /// Controls showing tooltip on left side, right side or center aligned with rod, default is center final FLHorizontalAlignment tooltipHorizontalAlignment; /// Applies horizontal offset for showing tooltip, default is zero. final double tooltipHorizontalOffset; + /// Adds a offset for tooltip vertical position (offset is applied to bottom of the tooltip) for showing tooltip on top of rods. + final double tooltipVerticalOffset; + /// Restricts the tooltip's width. final double maxContentWidth; @@ -775,7 +776,7 @@ class BarTouchTooltipData with EquatableMixin { List get props => [ tooltipRoundedRadius, tooltipPadding, - tooltipMargin, + tooltipVerticalOffset, tooltipHorizontalAlignment, tooltipHorizontalOffset, maxContentWidth, diff --git a/lib/src/chart/bar_chart/bar_chart_painter.dart b/lib/src/chart/bar_chart/bar_chart_painter.dart index 39691f5a6..d2a500f89 100644 --- a/lib/src/chart/bar_chart/bar_chart_painter.dart +++ b/lib/src/chart/bar_chart/bar_chart_painter.dart @@ -7,6 +7,7 @@ import 'package:fl_chart/src/chart/base/base_chart/base_chart_painter.dart'; import 'package:fl_chart/src/extensions/bar_chart_data_extension.dart'; import 'package:fl_chart/src/extensions/paint_extension.dart'; import 'package:fl_chart/src/extensions/path_extension.dart'; +import 'package:fl_chart/src/extensions/rect_extension.dart'; import 'package:fl_chart/src/extensions/rrect_extension.dart'; import 'package:fl_chart/src/utils/canvas_wrapper.dart'; import 'package:fl_chart/src/utils/utils.dart'; @@ -344,8 +345,6 @@ class BarChartPainter extends AxisChartPainter { ) { final viewSize = canvasWrapper.size; - const textsBelowMargin = 4; - final tooltipItem = tooltipData.getTooltipItem( showOnBarGroup, barGroupIndex, @@ -373,16 +372,6 @@ class BarChartPainter extends AxisChartPainter { /// creating TextPainters to calculate the width and height of the tooltip final drawingTextPainter = tp; - /// 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 - final textWidth = drawingTextPainter.width; - final textHeight = drawingTextPainter.height + 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 @@ -395,9 +384,8 @@ class BarChartPainter extends AxisChartPainter { groupPositions[barGroupIndex].barsX[barRodIndex], getPixelY(showOnRodData.fromY, viewSize, holder), ); - - final tooltipWidth = textWidth + tooltipData.tooltipPadding.horizontal; - final tooltipHeight = textHeight + tooltipData.tooltipPadding.vertical; + final tooltipWidth = drawingTextPainter.width; + final tooltipHeight = drawingTextPainter.height; final barTopY = min(barToYPixel.dy, barFromYPixel.dy); final barBottomY = max(barToYPixel.dy, barFromYPixel.dy); @@ -405,24 +393,27 @@ class BarChartPainter extends AxisChartPainter { (tooltipData.direction == TooltipDirection.auto && showOnRodData.isUpward()); final tooltipTop = drawTooltipOnTop - ? barTopY - tooltipHeight - tooltipData.tooltipMargin - : barBottomY + tooltipData.tooltipMargin; - + ? barTopY - + tooltipHeight - + tooltipData.tooltipVerticalOffset - + (tooltipData.tooltipPadding.vertical / 2) + : barBottomY + + tooltipData.tooltipVerticalOffset + + (tooltipData.tooltipPadding.vertical / 2); final tooltipLeft = getTooltipLeft( barToYPixel.dx, tooltipWidth, tooltipData.tooltipHorizontalAlignment, tooltipData.tooltipHorizontalOffset, + tooltipData.tooltipPadding, ); - /// draw the background rect with rounded radius - // ignore: omit_local_variable_types - Rect rect = Rect.fromLTWH( + var rect = Rect.fromLTWH( tooltipLeft, tooltipTop, tooltipWidth, tooltipHeight, - ); + ).applyPadding(tooltipData.tooltipPadding); if (tooltipData.fitInsideHorizontally) { if (rect.left < 0) { diff --git a/lib/src/chart/base/axis_chart/axis_chart_data.dart b/lib/src/chart/base/axis_chart/axis_chart_data.dart index 156d98df1..6bc337920 100644 --- a/lib/src/chart/base/axis_chart/axis_chart_data.dart +++ b/lib/src/chart/base/axis_chart/axis_chart_data.dart @@ -1147,14 +1147,16 @@ class VerticalLine extends FlLine with EquatableMixin { /// Draws a title on the [HorizontalLine] class HorizontalLineLabel extends FlLineLabel with EquatableMixin { /// Draws a title on the [HorizontalLine], align it with [alignment] over the line, - /// applies [padding] for spaces, and applies [style for changing color, + /// applies [padding] for label padding, applies [verticalOffset] and [horizontalOffset] for label space and applies [style] for changing color, /// size, ... of the text. /// Drawing text will retrieve through [labelResolver], /// you can override it with your custom data. /// [show] determines showing label or not. /// [direction] determines if the direction of the text should be horizontal or vertical. HorizontalLineLabel({ - super.padding = const EdgeInsets.all(6), + super.padding = const EdgeInsets.all(2), + super.horizontalOffset = 0.0, + super.verticalOffset = 0.0, super.style, super.alignment = Alignment.topLeft, super.show = false, @@ -1176,8 +1178,9 @@ class HorizontalLineLabel extends FlLineLabel with EquatableMixin { double t, ) { return HorizontalLineLabel( - padding: - EdgeInsets.lerp(a.padding as EdgeInsets, b.padding as EdgeInsets, t)!, + padding: EdgeInsets.lerp(a.padding, b.padding, t)!, + horizontalOffset: lerpDouble(a.horizontalOffset, b.horizontalOffset, t)!, + verticalOffset: lerpDouble(a.verticalOffset, b.verticalOffset, t)!, style: TextStyle.lerp(a.style, b.style, t), alignment: Alignment.lerp(a.alignment, b.alignment, t)!, labelResolver: b.labelResolver, @@ -1192,6 +1195,8 @@ class HorizontalLineLabel extends FlLineLabel with EquatableMixin { labelResolver, show, padding, + horizontalOffset, + verticalOffset, style, alignment, direction, @@ -1201,14 +1206,16 @@ class HorizontalLineLabel extends FlLineLabel with EquatableMixin { /// Draws a title on the [VerticalLine] class VerticalLineLabel extends FlLineLabel with EquatableMixin { /// Draws a title on the [VerticalLine], align it with [alignment] over the line, - /// applies [padding] for spaces, and applies [style for changing color, + /// applies [padding] for label padding, applies [verticalOffset] and [horizontalOffset] for label space and applies [style] for changing color, /// size, ... of the text. /// Drawing text will retrieve through [labelResolver], /// you can override it with your custom data. /// [show] determines showing label or not. /// [direction] determines if the direction of the text should be horizontal or vertical. VerticalLineLabel({ - super.padding = const EdgeInsets.all(6), + super.padding = const EdgeInsets.all(2), + super.horizontalOffset = 0.0, + super.verticalOffset = 0.0, super.style = const TextStyle( color: Colors.black, fontWeight: FontWeight.bold, @@ -1234,8 +1241,9 @@ class VerticalLineLabel extends FlLineLabel with EquatableMixin { double t, ) { return VerticalLineLabel( - padding: - EdgeInsets.lerp(a.padding as EdgeInsets, b.padding as EdgeInsets, t)!, + padding: EdgeInsets.lerp(a.padding, b.padding, t)!, + horizontalOffset: lerpDouble(a.horizontalOffset, b.horizontalOffset, t)!, + verticalOffset: lerpDouble(a.verticalOffset, b.verticalOffset, t)!, style: TextStyle.lerp(a.style, b.style, t), alignment: Alignment.lerp(a.alignment, b.alignment, t)!, labelResolver: b.labelResolver, @@ -1250,6 +1258,8 @@ class VerticalLineLabel extends FlLineLabel with EquatableMixin { labelResolver, show, padding, + horizontalOffset, + verticalOffset, style, alignment, direction, diff --git a/lib/src/chart/base/axis_chart/axis_chart_painter.dart b/lib/src/chart/base/axis_chart/axis_chart_painter.dart index 3b0ff7f03..eca497c15 100644 --- a/lib/src/chart/base/axis_chart/axis_chart_painter.dart +++ b/lib/src/chart/base/axis_chart/axis_chart_painter.dart @@ -4,6 +4,7 @@ import 'package:fl_chart/src/chart/base/axis_chart/axis_chart_helper.dart'; import 'package:fl_chart/src/chart/base/base_chart/base_chart_painter.dart'; import 'package:fl_chart/src/chart/line_chart/line_chart_painter.dart'; import 'package:fl_chart/src/extensions/paint_extension.dart'; +import 'package:fl_chart/src/extensions/rect_extension.dart'; import 'package:fl_chart/src/utils/canvas_wrapper.dart'; import 'package:fl_chart/src/utils/utils.dart'; import 'package:flutter/material.dart'; @@ -22,6 +23,8 @@ abstract class AxisChartPainter _rangeAnnotationPaint = Paint()..style = PaintingStyle.fill; + _labelBackgroundPaint = Paint()..style = PaintingStyle.fill; + _extraLinesPaint = Paint()..style = PaintingStyle.stroke; _imagePaint = Paint(); @@ -29,6 +32,7 @@ abstract class AxisChartPainter late Paint _gridPaint; late Paint _backgroundPaint; late Paint _extraLinesPaint; + late Paint _labelBackgroundPaint; late Paint _imagePaint; /// [_rangeAnnotationPaint] draws range annotations; @@ -291,46 +295,53 @@ abstract class AxisChartPainter final label = line.label; final style = TextStyle(fontSize: 11, color: line.color).merge(label.style); - final padding = label.padding as EdgeInsets; + final padding = label.padding; + final verticalOffset = label.verticalOffset; + final horizontalOffset = label.horizontalOffset; + final alignment = label.alignment; + + final backgroundColor = + Utils().getThemeAwareTextStyle(context, style).backgroundColor ?? + Colors.transparent; final span = TextSpan( text: label.labelResolver(line), - style: Utils().getThemeAwareTextStyle(context, style), + style: Utils().getThemeAwareTextStyle(context, style).copyWith( + backgroundColor: Colors.transparent, + ), ); final tp = TextPainter( text: span, textDirection: TextDirection.ltr, + )..layout(); + + final textArea = switch (label.direction) { + LabelDirection.horizontal => Rect.fromLTRB( + from.dx, + from.dy - tp.height, + to.dx - tp.width, + to.dy, + ), + _ => Rect.fromLTRB( + from.dx + tp.height, + from.dy - tp.width, + to.dx, + to.dy, + ), + }; + + drawLineLabel( + label.direction, + backgroundColor, + alignment, + textArea, + padding, + verticalOffset, + horizontalOffset, + tp, + canvasWrapper, ); - // ignore: cascade_invocations - tp.layout(); - - switch (label.direction) { - case LabelDirection.horizontal: - canvasWrapper.drawText( - tp, - label.alignment.withinRect( - Rect.fromLTRB( - from.dx + padding.left, - from.dy - padding.bottom - tp.height, - to.dx - padding.right - tp.width, - to.dy + padding.top, - ), - ), - ); - case LabelDirection.vertical: - canvasWrapper.drawVerticalText( - tp, - label.alignment.withinRect( - Rect.fromLTRB( - from.dx + padding.left + tp.height, - from.dy - padding.bottom - tp.width, - to.dx - padding.right, - to.dy + padding.top, - ), - ), - ); - } } } } @@ -399,51 +410,107 @@ abstract class AxisChartPainter final label = line.label; final style = TextStyle(fontSize: 11, color: line.color).merge(label.style); - final padding = label.padding as EdgeInsets; + final padding = label.padding; + final verticalOffset = label.verticalOffset; + final horizontalOffset = label.horizontalOffset; + final alignment = label.alignment; + + final backgroundColor = + Utils().getThemeAwareTextStyle(context, style).backgroundColor ?? + Colors.transparent; final span = TextSpan( text: label.labelResolver(line), - style: Utils().getThemeAwareTextStyle(context, style), + style: Utils().getThemeAwareTextStyle(context, style).copyWith( + backgroundColor: Colors.transparent, + ), ); final tp = TextPainter( text: span, textDirection: TextDirection.ltr, + )..layout(); + + final textArea = switch (label.direction) { + LabelDirection.horizontal => Rect.fromLTRB( + from.dx - tp.width, + from.dy, + to.dx, + to.dy - tp.height, + ), + _ => Rect.fromLTRB( + from.dx, + from.dy, + to.dx + tp.height, + to.dy - tp.width, + ), + }; + + drawLineLabel( + label.direction, + backgroundColor, + alignment, + textArea, + padding, + verticalOffset, + horizontalOffset, + tp, + canvasWrapper, ); - // ignore: cascade_invocations - tp.layout(); - - switch (label.direction) { - case LabelDirection.horizontal: - canvasWrapper.drawText( - tp, - label.alignment.withinRect( - Rect.fromLTRB( - from.dx - padding.right - tp.width, - from.dy + padding.top, - to.dx + padding.left, - to.dy - padding.bottom - tp.height, - ), - ), - ); - case LabelDirection.vertical: - canvasWrapper.drawVerticalText( - tp, - label.alignment.withinRect( - Rect.fromLTRB( - from.dx - padding.right, - from.dy + padding.top, - to.dx + padding.left + tp.height, - to.dy - padding.bottom - tp.width, - ), - ), - ); - } } } } } + @visibleForTesting + void drawLineLabel( + LabelDirection direction, + Color backgroundColor, + Alignment alignment, + Rect textArea, + EdgeInsets padding, + double verticalOffset, + double horizontalOffset, + TextPainter tp, + CanvasWrapper canvasWrapper, + ) { + final currenfOffset = alignment.withinRect( + textArea, + ); + + final offset = Offset( + currenfOffset.dx + horizontalOffset, + currenfOffset.dy - verticalOffset, + ); + + final backgroundRect = Rect.fromLTWH( + offset.dx, + offset.dy, + tp.width, + tp.height, + ).applyPadding(padding); + + canvasWrapper.save(); + + if (direction == LabelDirection.vertical) { + canvasWrapper + ..translate(currenfOffset.dx, currenfOffset.dy) + ..rotate(Utils().radians(90)) + ..translate(-currenfOffset.dx, -currenfOffset.dy); + } + + canvasWrapper + ..drawRect( + backgroundRect, + _labelBackgroundPaint..color = backgroundColor, + ) + ..drawText( + tp, + offset, + ) + ..restore(); + } + /// With this function we can convert our [FlSpot] x /// to the view base axis x . /// the view 0, 0 is on the top/left, but the spots is bottom/left @@ -474,14 +541,18 @@ abstract class AxisChartPainter double tooltipWidth, FLHorizontalAlignment tooltipHorizontalAlignment, double tooltipHorizontalOffset, + EdgeInsets tooltipPadding, ) { switch (tooltipHorizontalAlignment) { case FLHorizontalAlignment.center: return dx - (tooltipWidth / 2) + tooltipHorizontalOffset; case FLHorizontalAlignment.right: - return dx + tooltipHorizontalOffset; + return dx + tooltipHorizontalOffset + (tooltipPadding.horizontal / 2); case FLHorizontalAlignment.left: - return dx - tooltipWidth + tooltipHorizontalOffset; + return dx - + tooltipWidth + + tooltipHorizontalOffset - + (tooltipPadding.horizontal / 2); } } } diff --git a/lib/src/chart/line_chart/line_chart_data.dart b/lib/src/chart/line_chart/line_chart_data.dart index 02ffb9eb7..aec44db96 100644 --- a/lib/src/chart/line_chart/line_chart_data.dart +++ b/lib/src/chart/line_chart/line_chart_data.dart @@ -810,13 +810,15 @@ enum LabelDirection { horizontal, vertical } /// Shows a text label abstract class FlLineLabel with EquatableMixin { /// Draws a title on the line, align it with [alignment] over the line, - /// applies [padding] for spaces, and applies [style] for changing color, + /// applies [padding] for label padding, applies [horizontalOffset] and [verticalOffset] for label space andapplies [style] for changing color, /// size, ... of the text. /// [show] determines showing label or not. /// [direction] determines if the direction of the text should be horizontal or vertical. const FlLineLabel({ required this.show, required this.padding, + required this.horizontalOffset, + required this.verticalOffset, required this.style, required this.alignment, required this.direction, @@ -826,7 +828,11 @@ abstract class FlLineLabel with EquatableMixin { final bool show; /// Inner spaces around the drawing text. - final EdgeInsetsGeometry padding; + final EdgeInsets padding; + + final double verticalOffset; + + final double horizontalOffset; /// Sets style of the drawing text. final TextStyle? style; @@ -842,6 +848,8 @@ abstract class FlLineLabel with EquatableMixin { List get props => [ show, padding, + verticalOffset, + horizontalOffset, style, alignment, direction, @@ -1035,8 +1043,9 @@ class LineTouchTooltipData with EquatableMixin { /// otherwise you can show it manually using [LineChartData.showingTooltipIndicators]. /// Tooltip shows on top of spots, with [getTooltipColor] as a background color, /// and you can set corner radius using [tooltipRoundedRadius]. - /// If you want to have a padding inside the tooltip, fill [tooltipPadding], - /// or If you want to have a bottom margin, set [tooltipMargin]. + /// If you want to have tooltip padding, fill [tooltipPadding], + /// If you want to adjust tooltip vertical position, set [tooltipVerticalOffset] + /// If you want to adjust tooltip horizontal position, set [tooltipHorizontalOffset] /// Content of the tooltip will provide using [getTooltipItems] callback, you can override it /// and pass your custom data to show in the tooltip. /// You can restrict the tooltip's width using [maxContentWidth]. @@ -1047,8 +1056,8 @@ class LineTouchTooltipData with EquatableMixin { this.tooltipRoundedRadius = 4, this.tooltipPadding = const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - this.tooltipMargin = 16, this.tooltipHorizontalAlignment = FLHorizontalAlignment.center, + this.tooltipVerticalOffset = 16, this.tooltipHorizontalOffset = 0, this.maxContentWidth = 120, this.getTooltipItems = defaultLineTooltipItem, @@ -1066,15 +1075,15 @@ class LineTouchTooltipData with EquatableMixin { /// Applies a padding for showing contents inside the tooltip. final EdgeInsets tooltipPadding; - /// Applies a bottom margin for showing tooltip on top of rods. - final double tooltipMargin; - /// Controls showing tooltip on left side, right side or center aligned with spot, default is center final FLHorizontalAlignment tooltipHorizontalAlignment; /// Applies horizontal offset for showing tooltip, default is zero. final double tooltipHorizontalOffset; + /// Adds a offset for tooltip vertical position (offset is applied to bottom of the tooltip) for showing tooltip on top of rods. + final double tooltipVerticalOffset; + /// Restricts the tooltip's width. final double maxContentWidth; @@ -1104,7 +1113,7 @@ class LineTouchTooltipData with EquatableMixin { List get props => [ tooltipRoundedRadius, tooltipPadding, - tooltipMargin, + tooltipVerticalOffset, tooltipHorizontalAlignment, tooltipHorizontalOffset, maxContentWidth, diff --git a/lib/src/chart/line_chart/line_chart_painter.dart b/lib/src/chart/line_chart/line_chart_painter.dart index 9090c13ca..84d83aefc 100644 --- a/lib/src/chart/line_chart/line_chart_painter.dart +++ b/lib/src/chart/line_chart/line_chart_painter.dart @@ -6,6 +6,7 @@ import 'package:fl_chart/src/chart/base/axis_chart/axis_chart_painter.dart'; import 'package:fl_chart/src/chart/base/base_chart/base_chart_painter.dart'; import 'package:fl_chart/src/extensions/paint_extension.dart'; import 'package:fl_chart/src/extensions/path_extension.dart'; +import 'package:fl_chart/src/extensions/rect_extension.dart'; import 'package:fl_chart/src/extensions/text_align_extension.dart'; import 'package:fl_chart/src/utils/canvas_wrapper.dart'; import 'package:fl_chart/src/utils/utils.dart'; @@ -987,8 +988,6 @@ class LineChartPainter extends AxisChartPainter { ) { final viewSize = canvasWrapper.size; - const textsBelowMargin = 4; - /// creating TextPainters to calculate the width and height of the tooltip final drawingTextPainters = []; @@ -1037,7 +1036,8 @@ class LineChartPainter extends AxisChartPainter { } sumTextsHeight += tp.height; } - sumTextsHeight += (drawingTextPainters.length - 1) * textsBelowMargin; + sumTextsHeight += + (drawingTextPainters.length - 1) * tooltipData.tooltipPadding.bottom; /// if we have multiple bar lines, /// there are more than one FlCandidate on touch area, @@ -1047,15 +1047,20 @@ class LineChartPainter extends AxisChartPainter { getPixelY(showOnSpot.y, viewSize, holder), ); - final tooltipWidth = biggerWidth + tooltipData.tooltipPadding.horizontal; - final tooltipHeight = sumTextsHeight + tooltipData.tooltipPadding.vertical; + final tooltipWidth = biggerWidth; + final tooltipHeight = sumTextsHeight; double tooltipTopPosition; if (tooltipData.showOnTopOfTheChartBoxArea) { - tooltipTopPosition = 0 - tooltipHeight - tooltipData.tooltipMargin; + tooltipTopPosition = 0 - + tooltipHeight - + tooltipData.tooltipVerticalOffset - + (tooltipData.tooltipPadding.vertical / 2); } else { - tooltipTopPosition = - mostTopOffset.dy - tooltipHeight - tooltipData.tooltipMargin; + tooltipTopPosition = mostTopOffset.dy - + tooltipHeight - + tooltipData.tooltipVerticalOffset - + (tooltipData.tooltipPadding.vertical / 2); } final tooltipLeftPosition = getTooltipLeft( @@ -1063,6 +1068,7 @@ class LineChartPainter extends AxisChartPainter { tooltipWidth, tooltipData.tooltipHorizontalAlignment, tooltipData.tooltipHorizontalOffset, + tooltipData.tooltipPadding, ); /// draw the background rect with rounded radius @@ -1071,7 +1077,7 @@ class LineChartPainter extends AxisChartPainter { tooltipTopPosition, tooltipWidth, tooltipHeight, - ); + ).applyPadding(tooltipData.tooltipPadding); if (tooltipData.fitInsideHorizontally) { if (rect.left < 0) { @@ -1173,7 +1179,7 @@ class LineChartPainter extends AxisChartPainter { final xOffset = switch (align) { HorizontalAlignment.left => rect.left + tooltipData.tooltipPadding.left, HorizontalAlignment.right => - rect.right - tooltipData.tooltipPadding.right - tp.width, + rect.right - tp.width - tooltipData.tooltipPadding.right, _ => rect.center.dx - (tp.width / 2), }; @@ -1192,7 +1198,7 @@ class LineChartPainter extends AxisChartPainter { }, ); topPosSeek += tp.height; - topPosSeek += textsBelowMargin; + topPosSeek += tooltipData.tooltipPadding.bottom; } } diff --git a/lib/src/chart/scatter_chart/scatter_chart_data.dart b/lib/src/chart/scatter_chart/scatter_chart_data.dart index cd27e50ed..3034fb253 100644 --- a/lib/src/chart/scatter_chart/scatter_chart_data.dart +++ b/lib/src/chart/scatter_chart/scatter_chart_data.dart @@ -400,7 +400,9 @@ class ScatterTouchTooltipData with EquatableMixin { /// otherwise you can show it manually using [ScatterChartData.showingTooltipIndicators]. /// Tooltip shows on top of spots, with [getTooltipColor] as a background color, /// and you can set corner radius using [tooltipRoundedRadius]. - /// If you want to have a padding inside the tooltip, fill [tooltipPadding]. + /// If you want to have tooltip padding, fill [tooltipPadding], + /// If you want to adjust tooltip vertical position, set [tooltipVerticalOffset] + /// If you want to adjust tooltip horizontal position, set [tooltipHorizontalOffset] /// Content of the tooltip will provide using [getTooltipItems] callback, you can override it /// and pass your custom data to show in the tooltip. /// You can restrict the tooltip's width using [maxContentWidth]. @@ -411,6 +413,7 @@ class ScatterTouchTooltipData with EquatableMixin { double? tooltipRoundedRadius, EdgeInsets? tooltipPadding, FLHorizontalAlignment? tooltipHorizontalAlignment, + double? tooltipVerticalOffset, double? tooltipHorizontalOffset, double? maxContentWidth, GetScatterTooltipItems? getTooltipItems, @@ -425,6 +428,7 @@ class ScatterTouchTooltipData with EquatableMixin { tooltipHorizontalAlignment = tooltipHorizontalAlignment ?? FLHorizontalAlignment.center, tooltipHorizontalOffset = tooltipHorizontalOffset ?? 0, + tooltipVerticalOffset = tooltipVerticalOffset ?? 8, maxContentWidth = maxContentWidth ?? 120, getTooltipItems = getTooltipItems ?? defaultScatterTooltipItem, fitInsideHorizontally = fitInsideHorizontally ?? false, @@ -446,6 +450,9 @@ class ScatterTouchTooltipData with EquatableMixin { /// Applies horizontal offset for showing tooltip, default is zero. final double tooltipHorizontalOffset; + /// Applies vertical offset for showing tooltip, default is zero. + final double tooltipVerticalOffset; + /// Restricts the tooltip's width. final double maxContentWidth; @@ -474,6 +481,7 @@ class ScatterTouchTooltipData with EquatableMixin { tooltipPadding, tooltipHorizontalAlignment, tooltipHorizontalOffset, + tooltipVerticalOffset, maxContentWidth, getTooltipItems, fitInsideHorizontally, @@ -489,6 +497,7 @@ class ScatterTouchTooltipData with EquatableMixin { double? tooltipRoundedRadius, EdgeInsets? tooltipPadding, FLHorizontalAlignment? tooltipHorizontalAlignment, + double? tooltipVerticalOffset, double? tooltipHorizontalOffset, double? maxContentWidth, GetScatterTooltipItems? getTooltipItems, @@ -505,6 +514,8 @@ class ScatterTouchTooltipData with EquatableMixin { tooltipHorizontalAlignment ?? this.tooltipHorizontalAlignment, tooltipHorizontalOffset: tooltipHorizontalOffset ?? this.tooltipHorizontalOffset, + tooltipVerticalOffset: + tooltipVerticalOffset ?? this.tooltipVerticalOffset, maxContentWidth: maxContentWidth ?? this.maxContentWidth, getTooltipItems: getTooltipItems ?? this.getTooltipItems, fitInsideHorizontally: @@ -564,16 +575,13 @@ Color defaultScatterTooltipColor(ScatterSpot touchedSpot) { /// Holds data of showing each item in the tooltip popup. class ScatterTooltipItem with EquatableMixin { /// Shows a [text] with [textStyle], [textDirection], and optional [children] in the tooltip popup, - /// [bottomMargin] is the bottom space from spot. ScatterTooltipItem( this.text, { this.textStyle, - double? bottomMargin, TextAlign? textAlign, TextDirection? textDirection, this.children, - }) : bottomMargin = bottomMargin ?? 8, - textAlign = textAlign ?? TextAlign.center, + }) : textAlign = textAlign ?? TextAlign.center, textDirection = textDirection ?? TextDirection.ltr; /// Showing text. @@ -582,9 +590,6 @@ class ScatterTooltipItem with EquatableMixin { /// Style of showing text. final TextStyle? textStyle; - /// Defines bottom space from spot. - final double bottomMargin; - /// TextAlign of the showing content. final TextAlign textAlign; @@ -599,7 +604,6 @@ class ScatterTooltipItem with EquatableMixin { List get props => [ text, textStyle, - bottomMargin, textAlign, textDirection, children, @@ -610,7 +614,6 @@ class ScatterTooltipItem with EquatableMixin { ScatterTooltipItem copyWith({ String? text, TextStyle? textStyle, - double? bottomMargin, TextAlign? textAlign, TextDirection? textDirection, List? children, @@ -618,7 +621,6 @@ class ScatterTooltipItem with EquatableMixin { return ScatterTooltipItem( text ?? this.text, textStyle: textStyle ?? this.textStyle, - bottomMargin: bottomMargin ?? this.bottomMargin, textAlign: textAlign ?? this.textAlign, textDirection: textDirection ?? this.textDirection, children: children ?? this.children, diff --git a/lib/src/chart/scatter_chart/scatter_chart_painter.dart b/lib/src/chart/scatter_chart/scatter_chart_painter.dart index ad7117037..3008c60e6 100644 --- a/lib/src/chart/scatter_chart/scatter_chart_painter.dart +++ b/lib/src/chart/scatter_chart/scatter_chart_painter.dart @@ -1,6 +1,7 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/src/chart/base/axis_chart/axis_chart_painter.dart'; import 'package:fl_chart/src/chart/base/base_chart/base_chart_painter.dart'; +import 'package:fl_chart/src/extensions/rect_extension.dart'; import 'package:fl_chart/src/utils/canvas_wrapper.dart'; import 'package:fl_chart/src/utils/utils.dart'; import 'package:flutter/material.dart'; @@ -238,14 +239,15 @@ class ScatterChartPainter extends AxisChartPainter { getPixelY(showOnSpot.y, viewSize, holder), ); - final tooltipWidth = width + tooltipData.tooltipPadding.horizontal; - final tooltipHeight = height + tooltipData.tooltipPadding.vertical; + final tooltipWidth = width; + final tooltipHeight = height; final tooltipLeftPosition = getTooltipLeft( mostTopOffset.dx, tooltipWidth, tooltipData.tooltipHorizontalAlignment, tooltipData.tooltipHorizontalOffset, + tooltipData.tooltipPadding, ); /// draw the background rect with rounded radius @@ -254,10 +256,11 @@ class ScatterChartPainter extends AxisChartPainter { mostTopOffset.dy - tooltipHeight - (showOnSpot.size.height / 2) - - tooltipItem.bottomMargin, + tooltipData.tooltipVerticalOffset - + (tooltipData.tooltipPadding.vertical / 2), tooltipWidth, tooltipHeight, - ); + ).applyPadding(tooltipData.tooltipPadding); if (tooltipData.fitInsideHorizontally) { if (rect.left < 0) { diff --git a/lib/src/extensions/rect_extension.dart b/lib/src/extensions/rect_extension.dart new file mode 100644 index 000000000..f366ceb56 --- /dev/null +++ b/lib/src/extensions/rect_extension.dart @@ -0,0 +1,12 @@ +import 'package:flutter/material.dart'; + +extension RectExtension on Rect { + Rect applyPadding(EdgeInsets padding) { + return Rect.fromLTRB( + left - padding.left, + top - padding.top, + right + padding.right, + bottom + padding.bottom, + ); + } +} diff --git a/repo_files/documentations/bar_chart.md b/repo_files/documentations/bar_chart.md index aa72cd1a0..72789e520 100644 --- a/repo_files/documentations/bar_chart.md +++ b/repo_files/documentations/bar_chart.md @@ -96,9 +96,9 @@ enum values {`start`, `end`, `center`, `spaceEvenly`, `spaceAround`, `spaceBetwe |tooltipBorder|border of the tooltip bubble|BorderSide.none| |tooltipRoundedRadius|background corner radius of the tooltip bubble|4| |tooltipPadding|padding of the tooltip|EdgeInsets.symmetric(horizontal: 16, vertical: 8)| - |tooltipMargin|margin between the tooltip and the touched spot|16| |tooltipHorizontalAlignment|horizontal alginment of tooltip relative to the bar|FLHorizontalAlignment.center| |tooltipHorizontalOffset|horizontal offset of tooltip|0| + |tooltipVerticalOffset|vertical offset of tooltip|0| |maxContentWidth|maximum width of the tooltip (if a text row is wider than this, then the text breaks to a new line|120| |getTooltipItems|a callback that retrieve [BarTooltipItem](#BarTooltipItem) by the given [BarChartGroupData](#BarChartGroupData), groupIndex, [BarChartRodData](#BarChartRodData) and rodIndex |defaultBarTooltipItem| |fitInsideHorizontally| forces tooltip to horizontally shift inside the chart's bounding box| false| diff --git a/repo_files/documentations/base_chart.md b/repo_files/documentations/base_chart.md index 1017f9327..56980e98e 100644 --- a/repo_files/documentations/base_chart.md +++ b/repo_files/documentations/base_chart.md @@ -138,7 +138,7 @@ Base class for all supported touch/pointer events. |:-------|:----------|:------------| |y|draw straight line from left to right of the chart with dynamic y value|null| |color|color of the line|Colors.black| -|gradient|gradient of the line (you have to provide either `color` or `gradient`|null| +|gradient|gradient of the line (you have to provide either `color` or `gradient`)|null| |strokeWidth|strokeWidth of the line|2| |strokeCap|strokeCap of the line,e.g. Setting to StrokeCap.round will draw the tow ends of line rounded. NOTE: this might not work on dash lines.|StrokeCap.butt| |image|image to annotate the line. the Future must be complete at the time this is received by the chart|null| @@ -150,7 +150,7 @@ Base class for all supported touch/pointer events. |:-------|:----------|:------------| |x|draw straight line from bottom to top of the chart with dynamic x value|null| |color|color of the line|Colors.black| -|gradient|gradient of the line (you have to provide either `color` or `gradient`|null| +|gradient|gradient of the line (you have to provide either `color` or `gradient`)|null| |strokeWidth|strokeWidth of the line|2| |strokeCap|strokeCap of the line,e.g. Setting to StrokeCap.round will draw the tow ends of line rounded. NOTE: this might not work on dash lines.|StrokeCap.butt| |image|image to annotate the line. the Future must be complete at the time this is received by the chart|null| @@ -169,6 +169,8 @@ Base class for all supported touch/pointer events. |:-------|:----------|:------------| |show| Determines showing or not showing label|false| |padding|[EdgeInsets](https://api.flutter.dev/flutter/painting/EdgeInsets-class.html) object with label padding configuration|EdgeInsets.zero| +|horizontalOffset|Label horizontal offset|0| +|verticalOffset|Label horizontal offset|0| |style|[TextStyle](https://api.flutter.dev/flutter/dart-ui/TextStyle-class.html) which determines label text style|TextStyle(fontSize: 11, color: line.color)| |alignment|[Alignment](https://api.flutter.dev/flutter/painting/Alignment-class.html) with label position relative to line|Alignment.topLeft| |direction|Direction of the text (horizontal or vertical)|LabelDirection.horizontal| @@ -180,6 +182,8 @@ Base class for all supported touch/pointer events. |show| Determines showing or not showing label|false| |padding|[EdgeInsets](https://api.flutter.dev/flutter/painting/EdgeInsets-class.html) object with label padding configuration|EdgeInsets.zero| |style|[TextStyle](https://api.flutter.dev/flutter/dart-ui/TextStyle-class.html) which determines label text style|TextStyle(fontSize: 11, color: line.color)| +|horizontalOffset|Label horizontal offset|0| +|verticalOffset|Label horizontal offset|0| |alignment|[Alignment](https://api.flutter.dev/flutter/painting/Alignment-class.html) with label position relative to line|Alignment.topLeft| |direction|Direction of the text (horizontal or vertical)|LabelDirection.horizontal| |labelResolver|Getter function returning label title|defaultLineLabelResolver| diff --git a/repo_files/documentations/line_chart.md b/repo_files/documentations/line_chart.md index b2f1f369c..73ff25234 100644 --- a/repo_files/documentations/line_chart.md +++ b/repo_files/documentations/line_chart.md @@ -120,9 +120,9 @@ When you change the chart's state, it animates to the new state internally (usin |tooltipBorder|border of the tooltip bubble|BorderSide.none| |tooltipRoundedRadius|background corner radius of the tooltip bubble|4| |tooltipPadding|padding of the tooltip|EdgeInsets.symmetric(horizontal: 16, vertical: 8)| - |tooltipMargin|margin between the tooltip and the touched spot|16| |tooltipHorizontalAlignment|horizontal alginment of tooltip relative to the spot|FLHorizontalAlignment.center| |tooltipHorizontalOffset|horizontal offset of tooltip|0| + |tooltipVerticalOffset|Vertical offset of tooltip|16| |maxContentWidth|maximum width of the tooltip (if a text row is wider than this, then the text breaks to a new line|120| |getTooltipItems|a callback that retrieve list of [LineTooltipItem](#LineTooltipItem) by the given list of [LineBarSpot](#LineBarSpot) |defaultLineTooltipItem| |fitInsideHorizontally| forces tooltip to horizontally shift inside the chart's bounding box| false| diff --git a/repo_files/documentations/scatter_chart.md b/repo_files/documentations/scatter_chart.md index 2e953d76b..a9ac5f674 100644 --- a/repo_files/documentations/scatter_chart.md +++ b/repo_files/documentations/scatter_chart.md @@ -54,6 +54,7 @@ When you change the chart's state, it animates to the new state internally (usin |tooltipPadding|padding of the tooltip|EdgeInsets.symmetric(horizontal: 16, vertical: 8)| |tooltipHorizontalAlignment|horizontal alginment of tooltip relative to the spot|FLHorizontalAlignment.center| |tooltipHorizontalOffset|horizontal offset of tooltip|0| +|tooltipVerticalOffset|vertical offset of tooltip|0| |maxContentWidth|maximum width of the tooltip (if a text row is wider than this, then the text breaks to a new line|120| |getTooltipItems|a callback that retrieve a [ScatterTooltipItem](#ScatterTooltipItem) by the given [ScatterSpot](#ScatterSpot) |defaultScatterTooltipItem| |fitInsideHorizontally| forces tooltip to horizontally shift inside the chart's bounding box| false| @@ -66,7 +67,6 @@ When you change the chart's state, it animates to the new state internally (usin |text|text string of each row in the tooltip bubble|null| |textStyle|[TextStyle](https://api.flutter.dev/flutter/dart-ui/TextStyle-class.html) of the showing text row|null| |textDirection|[TextDirection](https://api.flutter.dev/flutter/dart-ui/TextDirection-class.html) of the showing text row|TextDirection.ltr| -|bottomMargin| bottom margin of the tooltip (to the top of most top spot) | 0| |children|[List](https://api.flutter.dev/flutter/painting/InlineSpan-class.html) pass additional InlineSpan children for a more advance tooltip|null| diff --git a/test/chart/bar_chart/bar_chart_painter_test.dart b/test/chart/bar_chart/bar_chart_painter_test.dart index 8a389f54c..74bb83642 100644 --- a/test/chart/bar_chart/bar_chart_painter_test.dart +++ b/test/chart/bar_chart/bar_chart_painter_test.dart @@ -1171,9 +1171,9 @@ void main() { final rrect = result1.captured[0] as RRect; expect(rrect.blRadius, const Radius.circular(8)); expect(rrect.width, 112); - expect(rrect.height, 90); + expect(rrect.height, 86); expect(rrect.left, -22.5); - expect(rrect.top, -106); + expect(rrect.top, -102); final bgTooltipPaint = result1.captured[1] as Paint; expect(bgTooltipPaint.color, const Color(0xf33f33f3)); @@ -1184,9 +1184,9 @@ void main() { expect(rRectBorder.blRadius, const Radius.circular(8)); expect(rRectBorder.width, 112); - expect(rRectBorder.height, 90); + expect(rRectBorder.height, 86); expect(rRectBorder.left, -22.5); - expect(rRectBorder.top, -106); + expect(rRectBorder.top, -102); expect(paintBorder.color, const Color(0xf33f33f3)); expect(paintBorder.strokeWidth, 2); expect(paintBorder.style, PaintingStyle.stroke); @@ -1211,7 +1211,7 @@ void main() { ); final drawOffset = result2.captured[1] as Offset; - expect(drawOffset, const Offset(-6.5, -98)); + expect(drawOffset, const Offset(-6.5, -94)); }); test('test 2', () { @@ -1371,7 +1371,7 @@ void main() { final rrect = result1.captured[0] as RRect; expect(rrect.blRadius, const Radius.circular(8)); expect(rrect.width, 112); - expect(rrect.height, 90); + expect(rrect.height, 86); expect(rrect.left, -80); expect(rrect.top, 116); @@ -1384,7 +1384,7 @@ void main() { expect(rRectBorder.blRadius, const Radius.circular(8)); expect(rRectBorder.width, 112); - expect(rRectBorder.height, 90); + expect(rRectBorder.height, 86); expect(rRectBorder.left, -80); expect(rRectBorder.top, 116); expect(paintBorder.color, const Color(0xf33f33f3)); @@ -1546,9 +1546,9 @@ void main() { final rrect = result1.captured[0] as RRect; expect(rrect.blRadius, const Radius.circular(8)); expect(rrect.width, 2636); - expect(rrect.height, 7034.0); + expect(rrect.height, 7030.0); expect(rrect.left, -2436); - expect(rrect.top, -6934.0); + expect(rrect.top, -6930.0); final bgTooltipPaint = result1.captured[1] as Paint; expect(bgTooltipPaint.color, const Color(0xf33f33f3)); @@ -1559,9 +1559,9 @@ void main() { expect(rRectBorder.blRadius, const Radius.circular(8)); expect(rRectBorder.width, 2636); - expect(rRectBorder.height, 7034.0); + expect(rRectBorder.height, 7030.0); expect(rRectBorder.left, -2436); - expect(rRectBorder.top, -6934.0); + expect(rRectBorder.top, -6930.0); expect(paintBorder.color, const Color(0xf33f33f3)); expect(paintBorder.strokeWidth, 2); expect(paintBorder.style, PaintingStyle.stroke); @@ -1573,7 +1573,7 @@ void main() { ..called(1); final drawOffset = result2.captured[1] as Offset; - expect(drawOffset, const Offset(-2420, -6926)); + expect(drawOffset, const Offset(-2420, -6922)); }); }); diff --git a/test/chart/bar_chart/bar_chart_renderer_test.mocks.dart b/test/chart/bar_chart/bar_chart_renderer_test.mocks.dart index 7e43d8ba3..681d967ec 100644 --- a/test/chart/bar_chart/bar_chart_renderer_test.mocks.dart +++ b/test/chart/bar_chart/bar_chart_renderer_test.mocks.dart @@ -14,6 +14,7 @@ import 'package:fl_chart/src/utils/canvas_wrapper.dart' as _i11; import 'package:flutter/foundation.dart' as _i5; import 'package:flutter/gestures.dart' as _i8; import 'package:flutter/material.dart' as _i6; +import 'package:flutter/painting.dart' as _i14; import 'package:flutter/rendering.dart' as _i3; import 'package:flutter/src/rendering/layer.dart' as _i4; import 'package:flutter/src/widgets/notification_listener.dart' as _i9; @@ -1473,6 +1474,36 @@ class MockBarChartPainter extends _i1.Mock implements _i10.BarChartPainter { returnValueForMissingStub: null, ); + @override + void drawLineLabel( + _i13.LabelDirection? direction, + _i2.Color? backgroundColor, + _i14.Alignment? alignment, + _i2.Rect? textArea, + _i14.EdgeInsets? padding, + double? verticalOffset, + double? horizontalOffset, + _i14.TextPainter? tp, + _i11.CanvasWrapper? canvasWrapper, + ) => + super.noSuchMethod( + Invocation.method( + #drawLineLabel, + [ + direction, + backgroundColor, + alignment, + textArea, + padding, + verticalOffset, + horizontalOffset, + tp, + canvasWrapper, + ], + ), + returnValueForMissingStub: null, + ); + @override double getPixelX( double? spotX, @@ -1515,6 +1546,7 @@ class MockBarChartPainter extends _i1.Mock implements _i10.BarChartPainter { double? tooltipWidth, _i13.FLHorizontalAlignment? tooltipHorizontalAlignment, double? tooltipHorizontalOffset, + _i14.EdgeInsets? tooltipPadding, ) => (super.noSuchMethod( Invocation.method( @@ -1524,6 +1556,7 @@ class MockBarChartPainter extends _i1.Mock implements _i10.BarChartPainter { tooltipWidth, tooltipHorizontalAlignment, tooltipHorizontalOffset, + tooltipPadding, ], ), returnValue: 0.0, diff --git a/test/chart/data_pool.dart b/test/chart/data_pool.dart index e22e62af3..e89d4f6ca 100644 --- a/test/chart/data_pool.dart +++ b/test/chart/data_pool.dart @@ -1205,7 +1205,7 @@ const LineTouchTooltipData lineTouchTooltipData1 = LineTouchTooltipData( getTooltipItems: lineChartGetTooltipItems, fitInsideHorizontally: true, tooltipRoundedRadius: 12, - tooltipMargin: 33, + tooltipVerticalOffset: 33, tooltipBorder: BorderSide(color: Colors.red), ); const LineTouchTooltipData lineTouchTooltipData1Clone = LineTouchTooltipData( @@ -1215,7 +1215,7 @@ const LineTouchTooltipData lineTouchTooltipData1Clone = LineTouchTooltipData( getTooltipItems: lineChartGetTooltipItems, fitInsideHorizontally: true, tooltipRoundedRadius: 12, - tooltipMargin: 33, + tooltipVerticalOffset: 33, tooltipBorder: BorderSide(color: Colors.red), ); @@ -1226,7 +1226,7 @@ const LineTouchTooltipData lineTouchTooltipData2 = LineTouchTooltipData( getTooltipItems: lineChartGetTooltipItems, fitInsideHorizontally: true, tooltipRoundedRadius: 12, - tooltipMargin: 33, + tooltipVerticalOffset: 33, tooltipBorder: BorderSide(color: Colors.red), ); const LineTouchTooltipData lineTouchTooltipData3 = LineTouchTooltipData( @@ -1236,7 +1236,7 @@ const LineTouchTooltipData lineTouchTooltipData3 = LineTouchTooltipData( getTooltipItems: lineChartGetTooltipItems, fitInsideHorizontally: true, tooltipRoundedRadius: 12, - tooltipMargin: 33, + tooltipVerticalOffset: 33, tooltipBorder: BorderSide(color: Colors.red), tooltipHorizontalAlignment: FLHorizontalAlignment.left, ); @@ -1247,7 +1247,7 @@ const LineTouchTooltipData lineTouchTooltipData4 = LineTouchTooltipData( getTooltipItems: lineChartGetTooltipItems, fitInsideHorizontally: true, tooltipRoundedRadius: 12, - tooltipMargin: 33, + tooltipVerticalOffset: 33, tooltipBorder: BorderSide(color: Colors.red), tooltipHorizontalAlignment: FLHorizontalAlignment.right, ); @@ -1258,7 +1258,7 @@ const LineTouchTooltipData lineTouchTooltipData5 = LineTouchTooltipData( getTooltipItems: lineChartGetTooltipItems, fitInsideHorizontally: true, tooltipRoundedRadius: 12, - tooltipMargin: 34, + tooltipVerticalOffset: 34, tooltipBorder: BorderSide(color: Colors.red), tooltipHorizontalOffset: 10, ); @@ -1269,7 +1269,7 @@ const LineTouchTooltipData lineTouchTooltipData6 = LineTouchTooltipData( getTooltipItems: lineChartGetTooltipItems, fitInsideHorizontally: true, tooltipRoundedRadius: 12, - tooltipMargin: 33, + tooltipVerticalOffset: 33, tooltipBorder: BorderSide(color: Colors.pink), tooltipHorizontalAlignment: FLHorizontalAlignment.left, tooltipHorizontalOffset: -10, @@ -1281,7 +1281,7 @@ const LineTouchTooltipData lineTouchTooltipData7 = LineTouchTooltipData( getTooltipItems: lineChartGetTooltipItems, fitInsideHorizontally: true, tooltipRoundedRadius: 12, - tooltipMargin: 33, + tooltipVerticalOffset: 33, tooltipBorder: BorderSide(color: Colors.red, width: 2), tooltipHorizontalAlignment: FLHorizontalAlignment.right, tooltipHorizontalOffset: 10, @@ -2249,7 +2249,6 @@ ScatterTooltipItem? scatterChartGetTooltipItems(ScatterSpot spots) { return ScatterTooltipItem( 'check', textStyle: const TextStyle(color: Colors.blue), - bottomMargin: 23, ); } @@ -2790,7 +2789,7 @@ final BarTouchTooltipData barTouchTooltipData1 = BarTouchTooltipData( getTooltipColor: getTooltipGreenColor, tooltipPadding: const EdgeInsets.all(23), getTooltipItem: getTooltipItem, - tooltipMargin: 12, + tooltipVerticalOffset: 12, tooltipBorder: const BorderSide(color: Colors.red), ); final BarTouchTooltipData barTouchTooltipData1Clone = BarTouchTooltipData( @@ -2801,7 +2800,7 @@ final BarTouchTooltipData barTouchTooltipData1Clone = BarTouchTooltipData( getTooltipColor: getTooltipGreenColor, tooltipPadding: const EdgeInsets.all(23), getTooltipItem: getTooltipItem, - tooltipMargin: 12, + tooltipVerticalOffset: 12, tooltipBorder: const BorderSide(color: Colors.red), ); final BarTouchTooltipData barTouchTooltipData2 = BarTouchTooltipData( @@ -2812,7 +2811,7 @@ final BarTouchTooltipData barTouchTooltipData2 = BarTouchTooltipData( getTooltipColor: getTooltipGreenColor, tooltipPadding: const EdgeInsets.all(23), getTooltipItem: getTooltipItem, - tooltipMargin: 12, + tooltipVerticalOffset: 12, tooltipBorder: const BorderSide(color: Colors.red), tooltipHorizontalAlignment: FLHorizontalAlignment.center, ); @@ -2824,7 +2823,7 @@ final BarTouchTooltipData barTouchTooltipData3 = BarTouchTooltipData( getTooltipColor: getTooltipGreenColor, tooltipPadding: const EdgeInsets.all(23), getTooltipItem: getTooltipItem, - tooltipMargin: 12, + tooltipVerticalOffset: 12, tooltipBorder: const BorderSide(color: Colors.red), tooltipHorizontalAlignment: FLHorizontalAlignment.left, ); @@ -2836,7 +2835,7 @@ final BarTouchTooltipData barTouchTooltipData4 = BarTouchTooltipData( getTooltipColor: getTooltipGreenColor, tooltipPadding: const EdgeInsets.all(23), getTooltipItem: getTooltipItem, - tooltipMargin: 12, + tooltipVerticalOffset: 12, tooltipBorder: const BorderSide(color: Colors.red), tooltipHorizontalAlignment: FLHorizontalAlignment.right, ); @@ -2848,7 +2847,7 @@ final BarTouchTooltipData barTouchTooltipData5 = BarTouchTooltipData( getTooltipColor: getTooltipGreenColor, tooltipPadding: const EdgeInsets.all(23), getTooltipItem: getTooltipItem, - tooltipMargin: 12, + tooltipVerticalOffset: 12, tooltipBorder: const BorderSide(color: Colors.red), tooltipHorizontalAlignment: FLHorizontalAlignment.center, tooltipHorizontalOffset: 10, @@ -2861,7 +2860,7 @@ final BarTouchTooltipData barTouchTooltipData6 = BarTouchTooltipData( getTooltipColor: getTooltipBlueColor, tooltipPadding: const EdgeInsets.all(23), getTooltipItem: getTooltipItem, - tooltipMargin: 12, + tooltipVerticalOffset: 12, tooltipBorder: const BorderSide(color: Colors.red), tooltipHorizontalAlignment: FLHorizontalAlignment.left, tooltipHorizontalOffset: -10, @@ -2873,7 +2872,7 @@ final BarTouchTooltipData barTouchTooltipData7 = BarTouchTooltipData( maxContentWidth: 23, getTooltipColor: getTooltipGreenColor, getTooltipItem: getTooltipItem, - tooltipMargin: 12, + tooltipVerticalOffset: 12, tooltipBorder: const BorderSide(color: Colors.red), tooltipHorizontalAlignment: FLHorizontalAlignment.right, tooltipHorizontalOffset: 10, @@ -2885,7 +2884,7 @@ final BarTouchTooltipData barTouchTooltipData8 = BarTouchTooltipData( maxContentWidth: 23, getTooltipColor: getTooltipGreenColor, tooltipPadding: const EdgeInsets.all(23), - tooltipMargin: 12, + tooltipVerticalOffset: 12, tooltipBorder: const BorderSide(color: Colors.red), ); final BarTouchTooltipData barTouchTooltipData9 = BarTouchTooltipData( @@ -2896,7 +2895,7 @@ final BarTouchTooltipData barTouchTooltipData9 = BarTouchTooltipData( getTooltipColor: getTooltipGreenColor, tooltipPadding: const EdgeInsets.all(23), getTooltipItem: getTooltipItem, - tooltipMargin: 333, + tooltipVerticalOffset: 333, tooltipBorder: const BorderSide(color: Colors.red), ); final BarTouchTooltipData barTouchTooltipData10 = BarTouchTooltipData( @@ -2907,7 +2906,7 @@ final BarTouchTooltipData barTouchTooltipData10 = BarTouchTooltipData( getTooltipColor: getTooltipGreenColor, tooltipPadding: const EdgeInsets.all(23), getTooltipItem: getTooltipItem, - tooltipMargin: 12, + tooltipVerticalOffset: 12, tooltipBorder: const BorderSide(color: Colors.blue), ); final BarTouchTooltipData barTouchTooltipData11 = BarTouchTooltipData( @@ -2918,7 +2917,7 @@ final BarTouchTooltipData barTouchTooltipData11 = BarTouchTooltipData( getTooltipColor: getTooltipGreenColor, tooltipPadding: const EdgeInsets.all(23), getTooltipItem: getTooltipItem, - tooltipMargin: 12, + tooltipVerticalOffset: 12, tooltipBorder: const BorderSide(color: Colors.red, width: 2), ); diff --git a/test/chart/line_chart/line_chart_painter_test.dart b/test/chart/line_chart/line_chart_painter_test.dart index 19925557f..1b200c5dc 100644 --- a/test/chart/line_chart/line_chart_painter_test.dart +++ b/test/chart/line_chart/line_chart_painter_test.dart @@ -14,10 +14,17 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; +import '../../helper_methods.dart'; import '../data_pool.dart'; import 'line_chart_painter_test.mocks.dart'; -@GenerateMocks([Canvas, CanvasWrapper, BuildContext, Utils, LineChartPainter]) +@GenerateNiceMocks([ + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), +]) void main() { group('paint()', () { test('test 1', () { @@ -2362,19 +2369,954 @@ void main() { ); final result1 = verify(mockCanvasWrapper.drawText(any, captureAny)) - ..called(2); - final result2 = - verify(mockCanvasWrapper.drawVerticalText(any, captureAny)) - ..called(2); + ..called(4); + + final offset1 = result1.captured[0] as Offset; + final offset2 = result1.captured[1] as Offset; + expect(offset1, const Offset(0, 56)); + expect(offset2, const Offset(14, -16)); + }); + }); + + group('drawExtraLines() - labels', () { + test('should not draw vertical label if show is false', () { + const viewSize = Size(100, 100); + + final data = LineChartData( + minY: -1, + maxY: 10, + minX: -1, + maxX: 10, + titlesData: const FlTitlesData(show: false), + extraLinesData: ExtraLinesData( + verticalLines: [ + VerticalLine( + x: 5, + color: Colors.white, + label: VerticalLineLabel( + style: const TextStyle( + color: Colors.white, + fontSize: 10, + ), + ), + ), + ], + ), + ); + + final lineChartPainter = LineChartPainter(); + + final holder = + PaintHolder(data, data, TextScaler.noScaling); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + final mockBuildContext = MockBuildContext(); + + final results = >[]; + when( + mockCanvasWrapper.drawRect( + captureAny, + captureAny, + ), + ).thenAnswer((inv) { + results.add({ + 'rect': inv.positionalArguments[0] as Rect?, + 'paint': inv.positionalArguments[1] as Paint?, + }); + }); + + lineChartPainter.drawExtraLines( + mockBuildContext, + mockCanvasWrapper, + holder, + ); + + expect(results.length, 0); + }); + + test('should draw vertical label if show is true', () { + const viewSize = Size(100, 100); + + final data = LineChartData( + minY: -1, + maxY: 10, + minX: -1, + maxX: 10, + titlesData: const FlTitlesData(show: false), + extraLinesData: ExtraLinesData( + verticalLines: [ + VerticalLine( + x: 5, + color: Colors.white, + label: VerticalLineLabel( + show: true, + style: const TextStyle( + color: Colors.white, + fontSize: 10, + ), + ), + ), + ], + ), + ); + + final lineChartPainter = LineChartPainter(); + + final holder = + PaintHolder(data, data, TextScaler.noScaling); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + final mockBuildContext = MockBuildContext(); + + final results = >[]; + when( + mockCanvasWrapper.drawRect( + captureAny, + captureAny, + ), + ).thenAnswer((inv) { + results.add({ + 'rect': inv.positionalArguments[0] as Rect?, + 'paint': inv.positionalArguments[1] as Paint?, + }); + }); + + lineChartPainter.drawExtraLines( + mockBuildContext, + mockCanvasWrapper, + holder, + ); + + expect(results.length, 1); + }); + + test('should not draw horizontal label if show is false', () { + const viewSize = Size(100, 100); + + final data = LineChartData( + minY: -1, + maxY: 10, + minX: -1, + maxX: 10, + titlesData: const FlTitlesData(show: false), + extraLinesData: ExtraLinesData( + horizontalLines: [ + HorizontalLine( + y: 5, + color: Colors.white, + label: HorizontalLineLabel( + style: const TextStyle( + color: Colors.white, + fontSize: 10, + ), + ), + ), + ], + ), + ); + + final lineChartPainter = LineChartPainter(); + + final holder = + PaintHolder(data, data, TextScaler.noScaling); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + final mockBuildContext = MockBuildContext(); + + final results = >[]; + when( + mockCanvasWrapper.drawRect( + captureAny, + captureAny, + ), + ).thenAnswer((inv) { + results.add({ + 'rect': inv.positionalArguments[0] as Rect?, + 'paint': inv.positionalArguments[1] as Paint?, + }); + }); + + lineChartPainter.drawExtraLines( + mockBuildContext, + mockCanvasWrapper, + holder, + ); + + expect(results.length, 0); + }); + + test('should draw horizontal label if show is true', () { + const viewSize = Size(100, 100); + + final data = LineChartData( + minY: -1, + maxY: 10, + minX: -1, + maxX: 10, + titlesData: const FlTitlesData(show: false), + extraLinesData: ExtraLinesData( + horizontalLines: [ + HorizontalLine( + y: 5, + color: Colors.white, + label: HorizontalLineLabel( + show: true, + style: const TextStyle( + color: Colors.white, + fontSize: 10, + ), + ), + ), + ], + ), + ); + + final lineChartPainter = LineChartPainter(); + + final holder = + PaintHolder(data, data, TextScaler.noScaling); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + final mockBuildContext = MockBuildContext(); + + final results = >[]; + when( + mockCanvasWrapper.drawRect( + captureAny, + captureAny, + ), + ).thenAnswer((inv) { + results.add({ + 'rect': inv.positionalArguments[0] as Rect?, + 'paint': inv.positionalArguments[1] as Paint?, + }); + }); + + lineChartPainter.drawExtraLines( + mockBuildContext, + mockCanvasWrapper, + holder, + ); + + expect(results.length, 1); + }); + + test('verticalOffset should move horizontal line label - 1', () { + const viewSize = Size(100, 100); + + final data = LineChartData( + minY: -1, + maxY: 10, + minX: -1, + maxX: 10, + titlesData: const FlTitlesData(show: false), + extraLinesData: ExtraLinesData( + horizontalLines: [ + HorizontalLine( + y: 5, + color: Colors.white, + label: HorizontalLineLabel( + verticalOffset: 10, + show: true, + style: const TextStyle( + color: Colors.white, + fontSize: 10, + ), + ), + ), + ], + ), + ); + + final lineChartPainter = LineChartPainter(); + + final holder = + PaintHolder(data, data, TextScaler.noScaling); + final mockCanvasWrapper = MockCanvasWrapper(); + + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + final mockBuildContext = MockBuildContext(); + + Rect? rect; + + when( + mockCanvasWrapper.drawRect( + captureAny, + captureAny, + ), + ).thenAnswer((inv) { + rect = inv.positionalArguments[0] as Rect?; + }); + + lineChartPainter.drawExtraLines( + mockBuildContext, + mockCanvasWrapper, + holder, + ); + + expect(rect != null, true, reason: 'Expected that Rect is not null'); + + const expected = Rect.fromLTRB(-2, 19.5, 44, 37.5); + + expect( + HelperMethods.equalsRects( + rect!, + expected, + ), + true, + reason: 'expected $rect to equal $expected', + ); + }); + + test('verticalOffset should move horizontal line label - 2', () { + const viewSize = Size(100, 100); + + final data = LineChartData( + minY: -1, + maxY: 10, + minX: -1, + maxX: 10, + titlesData: const FlTitlesData(show: false), + extraLinesData: ExtraLinesData( + horizontalLines: [ + HorizontalLine( + y: 5, + color: Colors.white, + label: HorizontalLineLabel( + verticalOffset: -10, + show: true, + style: const TextStyle( + color: Colors.white, + fontSize: 10, + ), + ), + ), + ], + ), + ); + + final lineChartPainter = LineChartPainter(); + + final holder = + PaintHolder(data, data, TextScaler.noScaling); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + final mockBuildContext = MockBuildContext(); + + final results = >[]; + when( + mockCanvasWrapper.drawRect( + captureAny, + captureAny, + ), + ).thenAnswer((inv) { + results.add({ + 'rect': inv.positionalArguments[0] as Rect?, + 'paint': inv.positionalArguments[1] as Paint?, + }); + }); + + lineChartPainter.drawExtraLines( + mockBuildContext, + mockCanvasWrapper, + holder, + ); + + expect(results.length, 1); + + final rect = results[0]['rect'] as Rect; + + const expected = Rect.fromLTRB(-2, 39.5, 44, 57.5); + + expect( + HelperMethods.equalsRects( + rect, + expected, + ), + true, + reason: 'expected $rect to equal $expected', + ); + }); + + test('horizontalOffset should move horizontal line label - 3', () { + const viewSize = Size(100, 100); + + final data = LineChartData( + minY: -1, + maxY: 10, + minX: -1, + maxX: 10, + titlesData: const FlTitlesData(show: false), + extraLinesData: ExtraLinesData( + horizontalLines: [ + HorizontalLine( + y: 5, + color: Colors.white, + label: HorizontalLineLabel( + horizontalOffset: 10, + show: true, + style: const TextStyle( + color: Colors.white, + fontSize: 10, + ), + ), + ), + ], + ), + ); + + final lineChartPainter = LineChartPainter(); + + final holder = + PaintHolder(data, data, TextScaler.noScaling); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + final mockBuildContext = MockBuildContext(); + + final results = >[]; + when( + mockCanvasWrapper.drawRect( + captureAny, + captureAny, + ), + ).thenAnswer((inv) { + results.add({ + 'rect': inv.positionalArguments[0] as Rect?, + 'paint': inv.positionalArguments[1] as Paint?, + }); + }); + + lineChartPainter.drawExtraLines( + mockBuildContext, + mockCanvasWrapper, + holder, + ); + + expect(results.length, 1); + + final rect = results[0]['rect'] as Rect; + + const expected = Rect.fromLTRB(8, 29.5, 54, 47.5); + + expect( + HelperMethods.equalsRects( + rect, + expected, + ), + true, + reason: 'expected $rect to equal $expected', + ); + }); + + test('horizontalOffset should move horizontal line label - 4', () { + const viewSize = Size(100, 100); + + final data = LineChartData( + minY: -1, + maxY: 10, + minX: -1, + maxX: 10, + titlesData: const FlTitlesData(show: false), + extraLinesData: ExtraLinesData( + horizontalLines: [ + HorizontalLine( + y: 5, + color: Colors.white, + label: HorizontalLineLabel( + horizontalOffset: -10, + show: true, + style: const TextStyle( + color: Colors.white, + fontSize: 10, + ), + ), + ), + ], + ), + ); + + final lineChartPainter = LineChartPainter(); + + final holder = + PaintHolder(data, data, TextScaler.noScaling); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + final mockBuildContext = MockBuildContext(); + + final results = >[]; + when( + mockCanvasWrapper.drawRect( + captureAny, + captureAny, + ), + ).thenAnswer((inv) { + results.add({ + 'rect': inv.positionalArguments[0] as Rect?, + 'paint': inv.positionalArguments[1] as Paint?, + }); + }); + + lineChartPainter.drawExtraLines( + mockBuildContext, + mockCanvasWrapper, + holder, + ); + + expect(results.length, 1); + + final rect = results[0]['rect'] as Rect; + + const expected = Rect.fromLTRB(-12, 29.5, 34, 47.5); + expect( + HelperMethods.equalsRects( + rect, + expected, + ), + true, + reason: 'expected $rect to equal $expected', + ); + }); + + test('verticalOffset should move vertical line label - 5', () { + const viewSize = Size(100, 100); + + final data = LineChartData( + minY: -1, + maxY: 10, + minX: -1, + maxX: 10, + titlesData: const FlTitlesData(show: false), + extraLinesData: ExtraLinesData( + verticalLines: [ + VerticalLine( + x: 5, + color: Colors.white, + label: VerticalLineLabel( + verticalOffset: -10, + show: true, + style: const TextStyle( + color: Colors.white, + fontSize: 10, + ), + ), + ), + ], + ), + ); + + final lineChartPainter = LineChartPainter(); + + final holder = + PaintHolder(data, data, TextScaler.noScaling); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + final mockBuildContext = MockBuildContext(); + + final results = >[]; + when( + mockCanvasWrapper.drawRect( + captureAny, + captureAny, + ), + ).thenAnswer((inv) { + results.add({ + 'rect': inv.positionalArguments[0] as Rect?, + 'paint': inv.positionalArguments[1] as Paint?, + }); + }); + + lineChartPainter.drawExtraLines( + mockBuildContext, + mockCanvasWrapper, + holder, + ); + + expect(results.length, 1); + + final rect = results[0]['rect'] as Rect; + + const expected = Rect.fromLTRB(52.5, 94, 98.5, 112); + expect( + HelperMethods.equalsRects( + rect, + expected, + ), + true, + reason: 'expected $rect to equal $expected', + ); + }); + + test('verticalOffset should move vertical line label - 6', () { + const viewSize = Size(100, 100); + + final data = LineChartData( + minY: -1, + maxY: 10, + minX: -1, + maxX: 10, + titlesData: const FlTitlesData(show: false), + extraLinesData: ExtraLinesData( + verticalLines: [ + VerticalLine( + x: 5, + color: Colors.white, + label: VerticalLineLabel( + verticalOffset: 10, + show: true, + style: const TextStyle( + color: Colors.white, + fontSize: 10, + ), + ), + ), + ], + ), + ); + + final lineChartPainter = LineChartPainter(); + + final holder = + PaintHolder(data, data, TextScaler.noScaling); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + final mockBuildContext = MockBuildContext(); + + final results = >[]; + when( + mockCanvasWrapper.drawRect( + captureAny, + captureAny, + ), + ).thenAnswer((inv) { + results.add({ + 'rect': inv.positionalArguments[0] as Rect?, + 'paint': inv.positionalArguments[1] as Paint?, + }); + }); + + lineChartPainter.drawExtraLines( + mockBuildContext, + mockCanvasWrapper, + holder, + ); + + expect(results.length, 1); + + final rect = results[0]['rect'] as Rect; + + const expected = Rect.fromLTRB(52.5, 74, 98.5, 92); + expect( + HelperMethods.equalsRects( + rect, + expected, + ), + true, + reason: 'expected $rect to equal $expected', + ); + }); + + test('horizontalOffset should move vertical line label - 7', () { + const viewSize = Size(100, 100); + + final data = LineChartData( + minY: -1, + maxY: 10, + minX: -1, + maxX: 10, + titlesData: const FlTitlesData(show: false), + extraLinesData: ExtraLinesData( + verticalLines: [ + VerticalLine( + x: 5, + color: Colors.white, + label: VerticalLineLabel( + horizontalOffset: 10, + show: true, + style: const TextStyle( + color: Colors.white, + fontSize: 10, + ), + ), + ), + ], + ), + ); + + final lineChartPainter = LineChartPainter(); + + final holder = + PaintHolder(data, data, TextScaler.noScaling); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + final mockBuildContext = MockBuildContext(); + + final results = >[]; + when( + mockCanvasWrapper.drawRect( + captureAny, + captureAny, + ), + ).thenAnswer((inv) { + results.add({ + 'rect': inv.positionalArguments[0] as Rect?, + 'paint': inv.positionalArguments[1] as Paint?, + }); + }); + + lineChartPainter.drawExtraLines( + mockBuildContext, + mockCanvasWrapper, + holder, + ); + + expect(results.length, 1); + + final rect = results[0]['rect'] as Rect; + + const expected = Rect.fromLTRB(62.5, 84, 108.5, 102); + expect( + HelperMethods.equalsRects( + rect, + expected, + ), + true, + reason: 'expected $rect to equal $expected', + ); + }); + + test('horizontalOffset should move vertical line label - 8', () { + const viewSize = Size(100, 100); + + final data = LineChartData( + minY: -1, + maxY: 10, + minX: -1, + maxX: 10, + titlesData: const FlTitlesData(show: false), + extraLinesData: ExtraLinesData( + verticalLines: [ + VerticalLine( + x: 5, + color: Colors.white, + label: VerticalLineLabel( + horizontalOffset: -10, + show: true, + style: const TextStyle( + color: Colors.white, + fontSize: 10, + ), + ), + ), + ], + ), + ); + + final lineChartPainter = LineChartPainter(); + + final holder = + PaintHolder(data, data, TextScaler.noScaling); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + final mockBuildContext = MockBuildContext(); + + final results = >[]; + when( + mockCanvasWrapper.drawRect( + captureAny, + captureAny, + ), + ).thenAnswer((inv) { + results.add({ + 'rect': inv.positionalArguments[0] as Rect?, + 'paint': inv.positionalArguments[1] as Paint?, + }); + }); + + lineChartPainter.drawExtraLines( + mockBuildContext, + mockCanvasWrapper, + holder, + ); + + expect(results.length, 1); + + final rect = results[0]['rect'] as Rect; + + const expected = Rect.fromLTRB(42.5, 84, 88.5, 102); + expect( + HelperMethods.equalsRects( + rect, + expected, + ), + true, + reason: 'expected $rect to equal $expected', + ); + }); + + test('vertical line label padding should apply padding - 9', () { + const viewSize = Size(100, 100); + + final data = LineChartData( + minY: -1, + maxY: 10, + minX: -1, + maxX: 10, + titlesData: const FlTitlesData(show: false), + extraLinesData: ExtraLinesData( + verticalLines: [ + VerticalLine( + x: 5, + color: Colors.white, + label: VerticalLineLabel( + show: true, + padding: const EdgeInsets.all(16), + style: const TextStyle( + color: Colors.white, + fontSize: 10, + ), + ), + ), + ], + ), + ); + + final lineChartPainter = LineChartPainter(); + + final holder = + PaintHolder(data, data, TextScaler.noScaling); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + final mockBuildContext = MockBuildContext(); + + final results = >[]; + when( + mockCanvasWrapper.drawRect( + captureAny, + captureAny, + ), + ).thenAnswer((inv) { + results.add({ + 'rect': inv.positionalArguments[0] as Rect?, + 'paint': inv.positionalArguments[1] as Paint?, + }); + }); + + lineChartPainter.drawExtraLines( + mockBuildContext, + mockCanvasWrapper, + holder, + ); + + expect(results.length, 1); + + final rect = results[0]['rect'] as Rect; + + const expected = Rect.fromLTRB(38.5, 70, 112.5, 116); + expect( + HelperMethods.equalsRects( + rect, + expected, + ), + true, + reason: 'expected $rect to equal $expected', + ); + }); + + test('horizontal line label padding should apply padding - 10', () { + const viewSize = Size(100, 100); + + final data = LineChartData( + minY: -1, + maxY: 10, + minX: -1, + maxX: 10, + titlesData: const FlTitlesData(show: false), + extraLinesData: ExtraLinesData( + horizontalLines: [ + HorizontalLine( + y: 5, + color: Colors.white, + label: HorizontalLineLabel( + show: true, + padding: const EdgeInsets.all(16), + style: const TextStyle( + color: Colors.white, + fontSize: 10, + ), + ), + ), + ], + ), + ); - final offset1 = result1.captured[0] as Offset; - final offset2 = result1.captured[1] as Offset; - final offset3 = result2.captured[0] as Offset; - final offset4 = result2.captured[1] as Offset; - expect(offset1, const Offset(6, 50)); - expect(offset2, const Offset(36, 80)); - expect(offset3, const Offset(20, -22)); - expect(offset4, const Offset(80, 38)); + final lineChartPainter = LineChartPainter(); + + final holder = + PaintHolder(data, data, TextScaler.noScaling); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + final mockBuildContext = MockBuildContext(); + + final results = >[]; + when( + mockCanvasWrapper.drawRect( + captureAny, + captureAny, + ), + ).thenAnswer((inv) { + results.add({ + 'rect': inv.positionalArguments[0] as Rect?, + 'paint': inv.positionalArguments[1] as Paint?, + }); + }); + + lineChartPainter.drawExtraLines( + mockBuildContext, + mockCanvasWrapper, + holder, + ); + + expect(results.length, 1); + + final rect = results[0]['rect'] as Rect; + + const expected = Rect.fromLTRB(-16, 15.5, 58, 61.5); + expect( + HelperMethods.equalsRects( + rect, + expected, + ), + true, + reason: 'expected $rect to equal $expected', + ); }); }); @@ -2398,7 +3340,7 @@ void main() { tooltipRoundedRadius: 12, rotateAngle: 43, maxContentWidth: 100, - tooltipMargin: 12, + tooltipVerticalOffset: 12, tooltipPadding: const EdgeInsets.all(12), fitInsideHorizontally: true, fitInsideVertically: true, @@ -2509,7 +3451,7 @@ void main() { tooltipRoundedRadius: 12, rotateAngle: 43, maxContentWidth: 100, - tooltipMargin: 12, + tooltipVerticalOffset: 12, tooltipHorizontalAlignment: FLHorizontalAlignment.left, tooltipPadding: const EdgeInsets.all(12), fitInsideVertically: true, @@ -2620,7 +3562,7 @@ void main() { tooltipRoundedRadius: 12, rotateAngle: 43, maxContentWidth: 100, - tooltipMargin: 12, + tooltipVerticalOffset: 12, tooltipHorizontalAlignment: FLHorizontalAlignment.right, tooltipPadding: const EdgeInsets.all(12), fitInsideVertically: true, @@ -2711,6 +3653,230 @@ void main() { expect((textPainter.text as TextSpan?)!.style, textStyle1); expect(drawOffset, const Offset(22, 52)); }); + + test('test 4 - should move tooltip 12 pixels to the right', () { + const viewSize = Size(100, 100); + + final barData = LineChartBarData( + spots: const [ + FlSpot(1, 1), + FlSpot(2, 2), + FlSpot(3, 3), + FlSpot(4, 4), + FlSpot.nullSpot, + FlSpot(5, 5), + ], + ); + + final tooltipData = LineTouchTooltipData( + getTooltipColor: (color) => const Color(0x11111111), + tooltipRoundedRadius: 12, + rotateAngle: 43, + maxContentWidth: 100, + tooltipVerticalOffset: 12, + tooltipHorizontalOffset: 12, + tooltipHorizontalAlignment: FLHorizontalAlignment.right, + tooltipPadding: const EdgeInsets.all(12), + fitInsideVertically: true, + getTooltipItems: (List touchedSpots) { + return touchedSpots + .map((e) => LineTooltipItem(e.barIndex.toString(), textStyle1)) + .toList(); + }, + tooltipBorder: const BorderSide(color: Color(0x11111111), width: 2), + ); + final data = LineChartData( + minY: 0, + maxY: 10, + minX: 0, + maxX: 10, + titlesData: const FlTitlesData(show: false), + lineTouchData: LineTouchData( + touchTooltipData: tooltipData, + ), + ); + + final lineChartPainter = LineChartPainter(); + final holder = + PaintHolder(data, data, TextScaler.noScaling); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + final mockBuildContext = MockBuildContext(); + final mockUtils = MockUtils(); + Utils.changeInstance(mockUtils); + when(mockUtils.getThemeAwareTextStyle(any, any)) + .thenAnswer((realInvocation) => textStyle1); + when(mockUtils.calculateRotationOffset(any, any)) + .thenAnswer((realInvocation) => Offset.zero); + when( + mockCanvasWrapper.drawRotated( + size: anyNamed('size'), + rotationOffset: anyNamed('rotationOffset'), + drawOffset: anyNamed('drawOffset'), + angle: anyNamed('angle'), + drawCallback: anyNamed('drawCallback'), + ), + ).thenAnswer((realInvocation) { + final callback = realInvocation + .namedArguments[const Symbol('drawCallback')] as DrawCallback; + callback(); + }); + lineChartPainter.drawTouchTooltip( + mockBuildContext, + mockCanvasWrapper, + tooltipData, + barData.spots.first, + ShowingTooltipIndicators([ + LineBarSpot( + barData, + 0, + barData.spots.first, + ), + ]), + holder, + ); + + final result1 = + verify(mockCanvasWrapper.drawRRect(captureAny, captureAny)) + ..called(2); + final rRect = result1.captured[0] as RRect; + final paint = result1.captured[1] as Paint; + expect( + rRect, + RRect.fromLTRBR(22, 40, 60, 78, const Radius.circular(12)), + ); + expect(paint.color, const Color(0x11111111)); + final rRectBorder = result1.captured[2] as RRect; + final paintBorder = result1.captured[3] as Paint; + expect( + rRectBorder, + RRect.fromLTRBR(22, 40, 60, 78, const Radius.circular(12)), + ); + expect(paintBorder.color, const Color(0x11111111)); + expect(paintBorder.strokeWidth, 2); + + final result2 = verify(mockCanvasWrapper.drawText(captureAny, captureAny)) + ..called(1); + final textPainter = result2.captured[0] as TextPainter; + final drawOffset = result2.captured[1] as Offset; + expect((textPainter.text as TextSpan?)!.text, '0'); + expect((textPainter.text as TextSpan?)!.style, textStyle1); + expect(drawOffset, const Offset(34, 52)); + }); + + test('test 5 - should move tooltip 12 pixels to the left', () { + const viewSize = Size(100, 100); + + final barData = LineChartBarData( + spots: const [ + FlSpot(1, 1), + FlSpot(2, 2), + FlSpot(3, 3), + FlSpot(4, 4), + FlSpot.nullSpot, + FlSpot(5, 5), + ], + ); + + final tooltipData = LineTouchTooltipData( + getTooltipColor: (color) => const Color(0x11111111), + tooltipRoundedRadius: 12, + rotateAngle: 43, + maxContentWidth: 100, + tooltipVerticalOffset: 12, + tooltipHorizontalOffset: -12, + tooltipHorizontalAlignment: FLHorizontalAlignment.right, + tooltipPadding: const EdgeInsets.all(12), + fitInsideVertically: true, + getTooltipItems: (List touchedSpots) { + return touchedSpots + .map((e) => LineTooltipItem(e.barIndex.toString(), textStyle1)) + .toList(); + }, + tooltipBorder: const BorderSide(color: Color(0x11111111), width: 2), + ); + final data = LineChartData( + minY: 0, + maxY: 10, + minX: 0, + maxX: 10, + titlesData: const FlTitlesData(show: false), + lineTouchData: LineTouchData( + touchTooltipData: tooltipData, + ), + ); + + final lineChartPainter = LineChartPainter(); + final holder = + PaintHolder(data, data, TextScaler.noScaling); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + final mockBuildContext = MockBuildContext(); + final mockUtils = MockUtils(); + Utils.changeInstance(mockUtils); + when(mockUtils.getThemeAwareTextStyle(any, any)) + .thenAnswer((realInvocation) => textStyle1); + when(mockUtils.calculateRotationOffset(any, any)) + .thenAnswer((realInvocation) => Offset.zero); + when( + mockCanvasWrapper.drawRotated( + size: anyNamed('size'), + rotationOffset: anyNamed('rotationOffset'), + drawOffset: anyNamed('drawOffset'), + angle: anyNamed('angle'), + drawCallback: anyNamed('drawCallback'), + ), + ).thenAnswer((realInvocation) { + final callback = realInvocation + .namedArguments[const Symbol('drawCallback')] as DrawCallback; + callback(); + }); + lineChartPainter.drawTouchTooltip( + mockBuildContext, + mockCanvasWrapper, + tooltipData, + barData.spots.first, + ShowingTooltipIndicators([ + LineBarSpot( + barData, + 0, + barData.spots.first, + ), + ]), + holder, + ); + + final result1 = + verify(mockCanvasWrapper.drawRRect(captureAny, captureAny)) + ..called(2); + final rRect = result1.captured[0] as RRect; + final paint = result1.captured[1] as Paint; + expect( + rRect, + RRect.fromLTRBR(-2, 40, 36, 78, const Radius.circular(12)), + ); + expect(paint.color, const Color(0x11111111)); + final rRectBorder = result1.captured[2] as RRect; + final paintBorder = result1.captured[3] as Paint; + expect( + rRectBorder, + RRect.fromLTRBR(-2, 40, 36, 78, const Radius.circular(12)), + ); + expect(paintBorder.color, const Color(0x11111111)); + expect(paintBorder.strokeWidth, 2); + + final result2 = verify(mockCanvasWrapper.drawText(captureAny, captureAny)) + ..called(1); + final textPainter = result2.captured[0] as TextPainter; + final drawOffset = result2.captured[1] as Offset; + expect((textPainter.text as TextSpan?)!.text, '0'); + expect((textPainter.text as TextSpan?)!.style, textStyle1); + expect(drawOffset, const Offset(10, 52)); + }); }); group('getBarLineXLength()', () { diff --git a/test/chart/line_chart/line_chart_painter_test.mocks.dart b/test/chart/line_chart/line_chart_painter_test.mocks.dart index a01595352..45ac04189 100644 --- a/test/chart/line_chart/line_chart_painter_test.mocks.dart +++ b/test/chart/line_chart/line_chart_painter_test.mocks.dart @@ -159,10 +159,6 @@ class _FakePath_9 extends _i1.SmartFake implements _i2.Path { /// /// See the documentation for Mockito's code generation for more information. class MockCanvas extends _i1.Mock implements _i2.Canvas { - MockCanvas() { - _i1.throwOnMissingStub(this); - } - @override void save() => super.noSuchMethod( Invocation.method( @@ -213,6 +209,7 @@ class MockCanvas extends _i1.Mock implements _i2.Canvas { [], ), returnValue: 0, + returnValueForMissingStub: 0, ) as int); @override @@ -288,6 +285,7 @@ class MockCanvas extends _i1.Mock implements _i2.Canvas { [], ), returnValue: _i5.Float64List(0), + returnValueForMissingStub: _i5.Float64List(0), ) as _i5.Float64List); @override @@ -349,6 +347,13 @@ class MockCanvas extends _i1.Mock implements _i2.Canvas { [], ), ), + returnValueForMissingStub: _FakeRect_0( + this, + Invocation.method( + #getLocalClipBounds, + [], + ), + ), ) as _i2.Rect); @override @@ -364,6 +369,13 @@ class MockCanvas extends _i1.Mock implements _i2.Canvas { [], ), ), + returnValueForMissingStub: _FakeRect_0( + this, + Invocation.method( + #getDestinationClipBounds, + [], + ), + ), ) as _i2.Rect); @override @@ -745,10 +757,6 @@ class MockCanvas extends _i1.Mock implements _i2.Canvas { /// /// See the documentation for Mockito's code generation for more information. class MockCanvasWrapper extends _i1.Mock implements _i6.CanvasWrapper { - MockCanvasWrapper() { - _i1.throwOnMissingStub(this); - } - @override _i2.Canvas get canvas => (super.noSuchMethod( Invocation.getter(#canvas), @@ -756,6 +764,10 @@ class MockCanvasWrapper extends _i1.Mock implements _i6.CanvasWrapper { this, Invocation.getter(#canvas), ), + returnValueForMissingStub: _FakeCanvas_1( + this, + Invocation.getter(#canvas), + ), ) as _i2.Canvas); @override @@ -765,6 +777,10 @@ class MockCanvasWrapper extends _i1.Mock implements _i6.CanvasWrapper { this, Invocation.getter(#size), ), + returnValueForMissingStub: _FakeSize_2( + this, + Invocation.getter(#size), + ), ) as _i2.Size); @override @@ -1091,10 +1107,6 @@ class MockCanvasWrapper extends _i1.Mock implements _i6.CanvasWrapper { /// /// See the documentation for Mockito's code generation for more information. class MockBuildContext extends _i1.Mock implements _i3.BuildContext { - MockBuildContext() { - _i1.throwOnMissingStub(this); - } - @override _i3.Widget get widget => (super.noSuchMethod( Invocation.getter(#widget), @@ -1102,18 +1114,24 @@ class MockBuildContext extends _i1.Mock implements _i3.BuildContext { this, Invocation.getter(#widget), ), + returnValueForMissingStub: _FakeWidget_3( + this, + Invocation.getter(#widget), + ), ) as _i3.Widget); @override bool get mounted => (super.noSuchMethod( Invocation.getter(#mounted), returnValue: false, + returnValueForMissingStub: false, ) as bool); @override bool get debugDoingBuild => (super.noSuchMethod( Invocation.getter(#debugDoingBuild), returnValue: false, + returnValueForMissingStub: false, ) as bool); @override @@ -1135,6 +1153,14 @@ class MockBuildContext extends _i1.Mock implements _i3.BuildContext { {#aspect: aspect}, ), ), + returnValueForMissingStub: _FakeInheritedWidget_4( + this, + Invocation.method( + #dependOnInheritedElement, + [ancestor], + {#aspect: aspect}, + ), + ), ) as _i3.InheritedWidget); @override @@ -1185,6 +1211,14 @@ class MockBuildContext extends _i1.Mock implements _i3.BuildContext { {#style: style}, ), ), + returnValueForMissingStub: _FakeDiagnosticsNode_5( + this, + Invocation.method( + #describeElement, + [name], + {#style: style}, + ), + ), ) as _i3.DiagnosticsNode); @override @@ -1206,6 +1240,14 @@ class MockBuildContext extends _i1.Mock implements _i3.BuildContext { {#style: style}, ), ), + returnValueForMissingStub: _FakeDiagnosticsNode_5( + this, + Invocation.method( + #describeWidget, + [name], + {#style: style}, + ), + ), ) as _i3.DiagnosticsNode); @override @@ -1218,6 +1260,7 @@ class MockBuildContext extends _i1.Mock implements _i3.BuildContext { {#expectedAncestorType: expectedAncestorType}, ), returnValue: <_i3.DiagnosticsNode>[], + returnValueForMissingStub: <_i3.DiagnosticsNode>[], ) as List<_i3.DiagnosticsNode>); @override @@ -1234,6 +1277,13 @@ class MockBuildContext extends _i1.Mock implements _i3.BuildContext { [name], ), ), + returnValueForMissingStub: _FakeDiagnosticsNode_5( + this, + Invocation.method( + #describeOwnershipChain, + [name], + ), + ), ) as _i3.DiagnosticsNode); } @@ -1241,10 +1291,6 @@ class MockBuildContext extends _i1.Mock implements _i3.BuildContext { /// /// See the documentation for Mockito's code generation for more information. class MockUtils extends _i1.Mock implements _i8.Utils { - MockUtils() { - _i1.throwOnMissingStub(this); - } - @override double radians(double? degrees) => (super.noSuchMethod( Invocation.method( @@ -1252,6 +1298,7 @@ class MockUtils extends _i1.Mock implements _i8.Utils { [degrees], ), returnValue: 0.0, + returnValueForMissingStub: 0.0, ) as double); @override @@ -1261,6 +1308,7 @@ class MockUtils extends _i1.Mock implements _i8.Utils { [radians], ), returnValue: 0.0, + returnValueForMissingStub: 0.0, ) as double); @override @@ -1276,6 +1324,13 @@ class MockUtils extends _i1.Mock implements _i8.Utils { [screenSize], ), ), + returnValueForMissingStub: _FakeSize_2( + this, + Invocation.method( + #getDefaultSize, + [screenSize], + ), + ), ) as _i2.Size); @override @@ -1292,6 +1347,7 @@ class MockUtils extends _i1.Mock implements _i8.Utils { ], ), returnValue: 0.0, + returnValueForMissingStub: 0.0, ) as double); @override @@ -1317,6 +1373,16 @@ class MockUtils extends _i1.Mock implements _i8.Utils { ], ), ), + returnValueForMissingStub: _FakeOffset_6( + this, + Invocation.method( + #calculateRotationOffset, + [ + size, + degree, + ], + ), + ), ) as _i2.Offset); @override @@ -1324,13 +1390,16 @@ class MockUtils extends _i1.Mock implements _i8.Utils { _i3.BorderRadius? borderRadius, double? width, ) => - (super.noSuchMethod(Invocation.method( - #normalizeBorderRadius, - [ - borderRadius, - width, - ], - )) as _i3.BorderRadius?); + (super.noSuchMethod( + Invocation.method( + #normalizeBorderRadius, + [ + borderRadius, + width, + ], + ), + returnValueForMissingStub: null, + ) as _i3.BorderRadius?); @override _i3.BorderSide normalizeBorderSide( @@ -1355,6 +1424,16 @@ class MockUtils extends _i1.Mock implements _i8.Utils { ], ), ), + returnValueForMissingStub: _FakeBorderSide_7( + this, + Invocation.method( + #normalizeBorderSide, + [ + borderSide, + width, + ], + ), + ), ) as _i3.BorderSide); @override @@ -1373,6 +1452,7 @@ class MockUtils extends _i1.Mock implements _i8.Utils { {#pixelPerInterval: pixelPerInterval}, ), returnValue: 0.0, + returnValueForMissingStub: 0.0, ) as double); @override @@ -1382,6 +1462,7 @@ class MockUtils extends _i1.Mock implements _i8.Utils { [input], ), returnValue: 0.0, + returnValueForMissingStub: 0.0, ) as double); @override @@ -1391,6 +1472,7 @@ class MockUtils extends _i1.Mock implements _i8.Utils { [value], ), returnValue: 0, + returnValueForMissingStub: 0, ) as int); @override @@ -1419,6 +1501,17 @@ class MockUtils extends _i1.Mock implements _i8.Utils { ], ), ), + returnValueForMissingStub: _i9.dummyValue( + this, + Invocation.method( + #formatNumber, + [ + axisMin, + axisMax, + axisValue, + ], + ), + ), ) as String); @override @@ -1444,6 +1537,16 @@ class MockUtils extends _i1.Mock implements _i8.Utils { ], ), ), + returnValueForMissingStub: _FakeTextStyle_8( + this, + Invocation.method( + #getThemeAwareTextStyle, + [ + context, + providedStyle, + ], + ), + ), ) as _i3.TextStyle); @override @@ -1464,6 +1567,7 @@ class MockUtils extends _i1.Mock implements _i8.Utils { {#baseline: baseline}, ), returnValue: 0.0, + returnValueForMissingStub: 0.0, ) as double); @override @@ -1473,6 +1577,7 @@ class MockUtils extends _i1.Mock implements _i8.Utils { [radius], ), returnValue: 0.0, + returnValueForMissingStub: 0.0, ) as double); } @@ -1480,10 +1585,6 @@ class MockUtils extends _i1.Mock implements _i8.Utils { /// /// See the documentation for Mockito's code generation for more information. class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter { - MockLineChartPainter() { - _i1.throwOnMissingStub(this); - } - @override void paint( _i3.BuildContext? context, @@ -1624,6 +1725,19 @@ class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter { {#appendToPath: appendToPath}, ), ), + returnValueForMissingStub: _FakePath_9( + this, + Invocation.method( + #generateBarPath, + [ + viewSize, + barData, + barSpots, + holder, + ], + {#appendToPath: appendToPath}, + ), + ), ) as _i2.Path); @override @@ -1658,6 +1772,19 @@ class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter { {#appendToPath: appendToPath}, ), ), + returnValueForMissingStub: _FakePath_9( + this, + Invocation.method( + #generateNormalBarPath, + [ + viewSize, + barData, + barSpots, + holder, + ], + {#appendToPath: appendToPath}, + ), + ), ) as _i2.Path); @override @@ -1692,6 +1819,19 @@ class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter { {#appendToPath: appendToPath}, ), ), + returnValueForMissingStub: _FakePath_9( + this, + Invocation.method( + #generateStepBarPath, + [ + viewSize, + barData, + barSpots, + holder, + ], + {#appendToPath: appendToPath}, + ), + ), ) as _i2.Path); @override @@ -1729,6 +1869,20 @@ class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter { {#fillCompletely: fillCompletely}, ), ), + returnValueForMissingStub: _FakePath_9( + this, + Invocation.method( + #generateBelowBarPath, + [ + viewSize, + barData, + barPath, + barSpots, + holder, + ], + {#fillCompletely: fillCompletely}, + ), + ), ) as _i2.Path); @override @@ -1766,6 +1920,20 @@ class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter { {#fillCompletely: fillCompletely}, ), ), + returnValueForMissingStub: _FakePath_9( + this, + Invocation.method( + #generateAboveBarPath, + [ + viewSize, + barData, + barPath, + barSpots, + holder, + ], + {#fillCompletely: fillCompletely}, + ), + ), ) as _i2.Path); @override @@ -1912,6 +2080,7 @@ class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter { ], ), returnValue: 0.0, + returnValueForMissingStub: 0.0, ) as double); @override @@ -1920,14 +2089,17 @@ class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter { _i2.Size? size, _i11.PaintHolder<_i7.LineChartData>? holder, ) => - (super.noSuchMethod(Invocation.method( - #handleTouch, - [ - localPosition, - size, - holder, - ], - )) as List<_i7.TouchLineBarSpot>?); + (super.noSuchMethod( + Invocation.method( + #handleTouch, + [ + localPosition, + size, + holder, + ], + ), + returnValueForMissingStub: null, + ) as List<_i7.TouchLineBarSpot>?); @override _i7.TouchLineBarSpot? getNearestTouchedSpot( @@ -1937,16 +2109,19 @@ class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter { int? barDataPosition, _i11.PaintHolder<_i7.LineChartData>? holder, ) => - (super.noSuchMethod(Invocation.method( - #getNearestTouchedSpot, - [ - viewSize, - touchedPoint, - barData, - barDataPosition, - holder, - ], - )) as _i7.TouchLineBarSpot?); + (super.noSuchMethod( + Invocation.method( + #getNearestTouchedSpot, + [ + viewSize, + touchedPoint, + barData, + barDataPosition, + holder, + ], + ), + returnValueForMissingStub: null, + ) as _i7.TouchLineBarSpot?); @override void drawGrid( @@ -2054,6 +2229,36 @@ class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter { returnValueForMissingStub: null, ); + @override + void drawLineLabel( + _i7.LabelDirection? direction, + _i2.Color? backgroundColor, + _i3.Alignment? alignment, + _i2.Rect? textArea, + _i3.EdgeInsets? padding, + double? verticalOffset, + double? horizontalOffset, + _i3.TextPainter? tp, + _i6.CanvasWrapper? canvasWrapper, + ) => + super.noSuchMethod( + Invocation.method( + #drawLineLabel, + [ + direction, + backgroundColor, + alignment, + textArea, + padding, + verticalOffset, + horizontalOffset, + tp, + canvasWrapper, + ], + ), + returnValueForMissingStub: null, + ); + @override double getPixelX( double? spotX, @@ -2070,6 +2275,7 @@ class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter { ], ), returnValue: 0.0, + returnValueForMissingStub: 0.0, ) as double); @override @@ -2088,6 +2294,7 @@ class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter { ], ), returnValue: 0.0, + returnValueForMissingStub: 0.0, ) as double); @override @@ -2096,6 +2303,7 @@ class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter { double? tooltipWidth, _i7.FLHorizontalAlignment? tooltipHorizontalAlignment, double? tooltipHorizontalOffset, + _i3.EdgeInsets? tooltipPadding, ) => (super.noSuchMethod( Invocation.method( @@ -2105,8 +2313,10 @@ class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter { tooltipWidth, tooltipHorizontalAlignment, tooltipHorizontalOffset, + tooltipPadding, ], ), returnValue: 0.0, + returnValueForMissingStub: 0.0, ) as double); } diff --git a/test/chart/line_chart/line_chart_renderer_test.mocks.dart b/test/chart/line_chart/line_chart_renderer_test.mocks.dart index b3e9c938c..2102bc80e 100644 --- a/test/chart/line_chart/line_chart_renderer_test.mocks.dart +++ b/test/chart/line_chart/line_chart_renderer_test.mocks.dart @@ -14,6 +14,7 @@ import 'package:fl_chart/src/utils/canvas_wrapper.dart' as _i11; import 'package:flutter/foundation.dart' as _i5; import 'package:flutter/gestures.dart' as _i8; import 'package:flutter/material.dart' as _i6; +import 'package:flutter/painting.dart' as _i14; import 'package:flutter/rendering.dart' as _i3; import 'package:flutter/src/rendering/layer.dart' as _i4; import 'package:flutter/src/widgets/notification_listener.dart' as _i9; @@ -1820,6 +1821,36 @@ class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter { returnValueForMissingStub: null, ); + @override + void drawLineLabel( + _i13.LabelDirection? direction, + _i2.Color? backgroundColor, + _i14.Alignment? alignment, + _i2.Rect? textArea, + _i14.EdgeInsets? padding, + double? verticalOffset, + double? horizontalOffset, + _i14.TextPainter? tp, + _i11.CanvasWrapper? canvasWrapper, + ) => + super.noSuchMethod( + Invocation.method( + #drawLineLabel, + [ + direction, + backgroundColor, + alignment, + textArea, + padding, + verticalOffset, + horizontalOffset, + tp, + canvasWrapper, + ], + ), + returnValueForMissingStub: null, + ); + @override double getPixelX( double? spotX, @@ -1862,6 +1893,7 @@ class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter { double? tooltipWidth, _i13.FLHorizontalAlignment? tooltipHorizontalAlignment, double? tooltipHorizontalOffset, + _i14.EdgeInsets? tooltipPadding, ) => (super.noSuchMethod( Invocation.method( @@ -1871,6 +1903,7 @@ class MockLineChartPainter extends _i1.Mock implements _i10.LineChartPainter { tooltipWidth, tooltipHorizontalAlignment, tooltipHorizontalOffset, + tooltipPadding, ], ), returnValue: 0.0, diff --git a/test/chart/scatter_chart/scatter_chart_data_test.dart b/test/chart/scatter_chart/scatter_chart_data_test.dart index 2d1d1bd2c..6b583f81a 100644 --- a/test/chart/scatter_chart/scatter_chart_data_test.dart +++ b/test/chart/scatter_chart/scatter_chart_data_test.dart @@ -452,33 +452,28 @@ void main() { final sample1 = ScatterTooltipItem( 'aa', textStyle: const TextStyle(color: Colors.red), - bottomMargin: 23, ); final sample2 = ScatterTooltipItem( 'aa', textStyle: const TextStyle(color: Colors.red), - bottomMargin: 23, ); expect(sample1 == sample2, true); var changed = ScatterTooltipItem( 'a3a', textStyle: const TextStyle(color: Colors.red), - bottomMargin: 23, ); expect(sample1 == changed, false); changed = ScatterTooltipItem( 'aa', textStyle: const TextStyle(color: Colors.green), - bottomMargin: 23, ); expect(sample1 == changed, false); changed = ScatterTooltipItem( 'aa', - textStyle: const TextStyle(color: Colors.red), - bottomMargin: 0, + textStyle: const TextStyle(color: Colors.red, fontSize: 11), ); expect(sample1 == changed, false); }); diff --git a/test/chart/scatter_chart/scatter_chart_renderer_test.mocks.dart b/test/chart/scatter_chart/scatter_chart_renderer_test.mocks.dart index d6380b076..2f6e045d0 100644 --- a/test/chart/scatter_chart/scatter_chart_renderer_test.mocks.dart +++ b/test/chart/scatter_chart/scatter_chart_renderer_test.mocks.dart @@ -15,6 +15,7 @@ import 'package:fl_chart/src/utils/canvas_wrapper.dart' as _i11; import 'package:flutter/foundation.dart' as _i5; import 'package:flutter/gestures.dart' as _i8; import 'package:flutter/material.dart' as _i6; +import 'package:flutter/painting.dart' as _i14; import 'package:flutter/rendering.dart' as _i3; import 'package:flutter/src/rendering/layer.dart' as _i4; import 'package:flutter/src/widgets/notification_listener.dart' as _i9; @@ -1439,6 +1440,36 @@ class MockScatterChartPainter extends _i1.Mock returnValueForMissingStub: null, ); + @override + void drawLineLabel( + _i13.LabelDirection? direction, + _i2.Color? backgroundColor, + _i14.Alignment? alignment, + _i2.Rect? textArea, + _i14.EdgeInsets? padding, + double? verticalOffset, + double? horizontalOffset, + _i14.TextPainter? tp, + _i11.CanvasWrapper? canvasWrapper, + ) => + super.noSuchMethod( + Invocation.method( + #drawLineLabel, + [ + direction, + backgroundColor, + alignment, + textArea, + padding, + verticalOffset, + horizontalOffset, + tp, + canvasWrapper, + ], + ), + returnValueForMissingStub: null, + ); + @override double getPixelX( double? spotX, @@ -1481,6 +1512,7 @@ class MockScatterChartPainter extends _i1.Mock double? tooltipWidth, _i13.FLHorizontalAlignment? tooltipHorizontalAlignment, double? tooltipHorizontalOffset, + _i14.EdgeInsets? tooltipPadding, ) => (super.noSuchMethod( Invocation.method( @@ -1490,6 +1522,7 @@ class MockScatterChartPainter extends _i1.Mock tooltipWidth, tooltipHorizontalAlignment, tooltipHorizontalOffset, + tooltipPadding, ], ), returnValue: 0.0, diff --git a/test/extensions/rect_extension_test.dart b/test/extensions/rect_extension_test.dart new file mode 100644 index 000000000..00693f4b7 --- /dev/null +++ b/test/extensions/rect_extension_test.dart @@ -0,0 +1,32 @@ +import 'package:fl_chart/src/extensions/rect_extension.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('RectExtension', () { + group('addPadding', () { + test('should not affect with EdgeInsets.zero', () { + const rect = Rect.fromLTWH(1, 1, 1, 1); + const padding = EdgeInsets.zero; + + expect(rect.applyPadding(padding), rect); + }); + + // Adjust tooltip start heights? + + test('should add correct amount of padding', () { + const rect = Rect.fromLTRB(25, 50, 100, 100); + const padding = EdgeInsets.symmetric(vertical: 16, horizontal: 8); + expect( + rect.applyPadding(padding), + Rect.fromLTRB( + 25 - padding.left, + 50 - padding.top, + 100 + padding.right, + 100 + padding.bottom, + ), + ); + }); + }); + }); +} diff --git a/test/helper_methods.dart b/test/helper_methods.dart index 1a2130ff0..6c31d46e7 100644 --- a/test/helper_methods.dart +++ b/test/helper_methods.dart @@ -45,6 +45,26 @@ class HelperMethods { return true; } + static bool equalsRects(Rect rect1, Rect rect2, {double tolerance = 0.05}) { + if ((rect1.left - rect2.left).abs() > tolerance) { + return false; + } + + if ((rect1.top - rect2.top).abs() > tolerance) { + return false; + } + + if ((rect1.right - rect2.right).abs() > tolerance) { + return false; + } + + if ((rect1.bottom - rect2.bottom).abs() > tolerance) { + return false; + } + + return true; + } + static bool equalsRRects( RRect rrect1, RRect rrect2, {