diff --git a/example/lib/line_chart/line_chart_page2.dart b/example/lib/line_chart/line_chart_page2.dart index b58b46a55..c7ce74b48 100644 --- a/example/lib/line_chart/line_chart_page2.dart +++ b/example/lib/line_chart/line_chart_page2.dart @@ -24,16 +24,6 @@ class LineChartPage2 extends StatelessWidget { LineChartSample4(), LineChartSample7(), LineChartSample5(), - const SizedBox( - height: 22, - ), - Text( - 'Range annotations, dashes, image annotation using svg', - style: TextStyle( - color: Colors.black87, - fontSize: 12, - ), - ), LineChartSample8(), ], ), diff --git a/example/lib/line_chart/samples/line_chart_sample8.dart b/example/lib/line_chart/samples/line_chart_sample8.dart index f363f36fe..acb9fa6e5 100644 --- a/example/lib/line_chart/samples/line_chart_sample8.dart +++ b/example/lib/line_chart/samples/line_chart_sample8.dart @@ -177,9 +177,12 @@ class _LineChartSample8State extends State { FlSpot(2, 1), FlSpot(4.9, 5), FlSpot(6.8, 5), + FlSpot(7.5, 4), + FlSpot(null, null), + FlSpot(7.5, 2), FlSpot(8, 1), - FlSpot(9.5, 2), - FlSpot(11, 4), + FlSpot(10, 2), + FlSpot(11, 2.5), ], dashArray: [2, 4], isCurved: true, diff --git a/lib/src/chart/line_chart/line_chart_data.dart b/lib/src/chart/line_chart/line_chart_data.dart index 80c6b2092..48c1dbe81 100644 --- a/lib/src/chart/line_chart/line_chart_data.dart +++ b/lib/src/chart/line_chart/line_chart_data.dart @@ -132,20 +132,22 @@ class LineChartData extends AxisChartData { final LineChartBarData barData = lineBarsData[i]; for (int j = 0; j < barData.spots.length; j++) { final FlSpot spot = barData.spots[j]; - if (canModifyMaxX && spot.x > maxX) { - maxX = spot.x; - } - - if (canModifyMinX && spot.x < minX) { - minX = spot.x; - } - - if (canModifyMaxY && spot.y > maxY) { - maxY = spot.y; - } - - if (canModifyMinY && spot.y < minY) { - minY = spot.y; + if (spot.x != null && spot.y != null) { + if (canModifyMaxX && spot.x > maxX) { + maxX = spot.x; + } + + if (canModifyMinX && spot.x < minX) { + minX = spot.x; + } + + if (canModifyMaxY && spot.y > maxY) { + maxY = spot.y; + } + + if (canModifyMinY && spot.y < minY) { + minY = spot.y; + } } } } diff --git a/lib/src/chart/line_chart/line_chart_painter.dart b/lib/src/chart/line_chart/line_chart_painter.dart index 1a81b1542..1003ebce3 100644 --- a/lib/src/chart/line_chart/line_chart_painter.dart +++ b/lib/src/chart/line_chart/line_chart_painter.dart @@ -166,19 +166,39 @@ class LineChartPainter extends AxisChartPainter } void _drawBarLine(Canvas canvas, Size viewSize, LineChartBarData barData) { - final barPath = _generateBarPath(viewSize, barData); + final List> barList = [[]]; - final belowBarPath = _generateBelowBarPath(viewSize, barData, barPath); - final completelyFillBelowBarPath = - _generateBelowBarPath(viewSize, barData, barPath, fillCompletely: true); - - final aboveBarPath = _generateAboveBarPath(viewSize, barData, barPath); - final completelyFillAboveBarPath = - _generateAboveBarPath(viewSize, barData, barPath, fillCompletely: true); + // handle nullability by splitting off the list into multiple + // separate lists when separated by nulls + // and ignore nulls when they're first or last + for (int i = 0; i < barData.spots.length; i++) { + if (barData.spots[i].x != null && barData.spots[i].y != null) { + barList.last.add(barData.spots[i]); + } else { + if (i != 0 && i != barData.spots.length - 1) { + barList.add([]); + } + } + } - _drawBelowBar(canvas, viewSize, belowBarPath, completelyFillAboveBarPath, barData); - _drawAboveBar(canvas, viewSize, aboveBarPath, completelyFillBelowBarPath, barData); - _drawBar(canvas, viewSize, barPath, barData); + // paint each sublist that was built above + // bar is passed in seperately from barData + // because barData is the whole line + // and bar is a piece of that line + for (List bar in barList) { + final barPath = _generateBarPath(viewSize, barData, bar); + + final belowBarPath = _generateBelowBarPath(viewSize, barData, barPath, bar); + final completelyFillBelowBarPath = + _generateBelowBarPath(viewSize, barData, barPath, bar, fillCompletely: true); + final aboveBarPath = _generateAboveBarPath(viewSize, barData, barPath, bar); + final completelyFillAboveBarPath = + _generateAboveBarPath(viewSize, barData, barPath, bar, fillCompletely: true); + + _drawBelowBar(canvas, viewSize, belowBarPath, completelyFillAboveBarPath, barData); + _drawAboveBar(canvas, viewSize, aboveBarPath, completelyFillBelowBarPath, barData); + _drawBar(canvas, viewSize, barPath, barData); + } } void _drawBetweenBarsArea( @@ -188,9 +208,17 @@ class LineChartPainter extends AxisChartPainter final List spots = []; spots.addAll(toBarData.spots.reversed.toList()); - final fromBarPath = _generateBarPath(viewSize, fromBarData); - final barPath = - _generateBarPath(viewSize, toBarData.copyWith(spots: spots), appendToPath: fromBarPath); + final fromBarPath = _generateBarPath( + viewSize, + fromBarData, + fromBarData.spots, + ); + final barPath = _generateBarPath( + viewSize, + toBarData.copyWith(spots: spots), + toBarData.copyWith(spots: spots).spots, + appendToPath: fromBarPath, + ); _drawBetweenBar(canvas, viewSize, barPath, betweenBarsData); } @@ -266,15 +294,16 @@ class LineChartPainter extends AxisChartPainter /// and we use isCurved to find out how we should generate it, /// If you want to concatenate paths together for creating an area between /// multiple bars for example, you can pass the appendToPath - Path _generateBarPath(Size viewSize, LineChartBarData barData, {Path appendToPath}) { + Path _generateBarPath(Size viewSize, LineChartBarData barData, List barSpots, + {Path appendToPath}) { viewSize = getChartUsableDrawSize(viewSize); final Path path = appendToPath ?? Path(); - final int size = barData.spots.length; + final int size = barSpots.length; var temp = const Offset(0.0, 0.0); - final double x = getPixelX(barData.spots[0].x, viewSize); - final double y = getPixelY(barData.spots[0].y, viewSize); + final double x = getPixelX(barSpots[0].x, viewSize); + final double y = getPixelY(barSpots[0].y, viewSize); if (appendToPath == null) { path.moveTo(x, y); } else { @@ -283,20 +312,20 @@ class LineChartPainter extends AxisChartPainter for (int i = 1; i < size; i++) { /// CurrentSpot final current = Offset( - getPixelX(barData.spots[i].x, viewSize), - getPixelY(barData.spots[i].y, viewSize), + getPixelX(barSpots[i].x, viewSize), + getPixelY(barSpots[i].y, viewSize), ); /// previous spot final previous = Offset( - getPixelX(barData.spots[i - 1].x, viewSize), - getPixelY(barData.spots[i - 1].y, viewSize), + getPixelX(barSpots[i - 1].x, viewSize), + getPixelY(barSpots[i - 1].y, viewSize), ); /// next point final next = Offset( - getPixelX(barData.spots[i + 1 < size ? i + 1 : i].x, viewSize), - getPixelY(barData.spots[i + 1 < size ? i + 1 : i].y, viewSize), + getPixelX(barSpots[i + 1 < size ? i + 1 : i].x, viewSize), + getPixelY(barSpots[i + 1 < size ? i + 1 : i].y, viewSize), ); final controlPoint1 = previous + temp; @@ -338,14 +367,15 @@ class LineChartPainter extends AxisChartPainter /// if cutOffY is provided by the [BarAreaData], it cut the area to the provided cutOffY value, /// if [fillCompletely] is true, the cutOffY will be ignored, /// and a completely filled path will return, - Path _generateBelowBarPath(Size viewSize, LineChartBarData barData, Path barPath, + Path _generateBelowBarPath( + Size viewSize, LineChartBarData barData, Path barPath, List barSpots, {bool fillCompletely = false}) { final belowBarPath = Path.from(barPath); final chartViewSize = getChartUsableDrawSize(viewSize); /// Line To Bottom Right - double x = getPixelX(barData.spots[barData.spots.length - 1].x, chartViewSize); + double x = getPixelX(barSpots[barSpots.length - 1].x, chartViewSize); double y; if (!fillCompletely && barData.belowBarData.applyCutOffY) { y = getPixelY(barData.belowBarData.cutOffY, chartViewSize); @@ -355,7 +385,7 @@ class LineChartPainter extends AxisChartPainter belowBarPath.lineTo(x, y); /// Line To Bottom Left - x = getPixelX(barData.spots[0].x, chartViewSize); + x = getPixelX(barSpots[0].x, chartViewSize); if (!fillCompletely && barData.belowBarData.applyCutOffY) { y = getPixelY(barData.belowBarData.cutOffY, chartViewSize); } else { @@ -364,8 +394,8 @@ class LineChartPainter extends AxisChartPainter belowBarPath.lineTo(x, y); /// Line To Top Left - x = getPixelX(barData.spots[0].x, chartViewSize); - y = getPixelY(barData.spots[0].y, chartViewSize); + x = getPixelX(barSpots[0].x, chartViewSize); + y = getPixelY(barSpots[0].y, chartViewSize); belowBarPath.lineTo(x, y); belowBarPath.close(); @@ -376,14 +406,15 @@ class LineChartPainter extends AxisChartPainter /// if cutOffY is provided by the [BarAreaData], it cut the area to the provided cutOffY value, /// if [fillCompletely] is true, the cutOffY will be ignored, /// and a completely filled path will return, - Path _generateAboveBarPath(Size viewSize, LineChartBarData barData, Path barPath, + Path _generateAboveBarPath( + Size viewSize, LineChartBarData barData, Path barPath, List barSpots, {bool fillCompletely = false}) { final aboveBarPath = Path.from(barPath); final chartViewSize = getChartUsableDrawSize(viewSize); /// Line To Top Right - double x = getPixelX(barData.spots[barData.spots.length - 1].x, chartViewSize); + double x = getPixelX(barSpots[barSpots.length - 1].x, chartViewSize); double y; if (!fillCompletely && barData.aboveBarData.applyCutOffY) { y = getPixelY(barData.aboveBarData.cutOffY, chartViewSize); @@ -393,7 +424,7 @@ class LineChartPainter extends AxisChartPainter aboveBarPath.lineTo(x, y); /// Line To Top Left - x = getPixelX(barData.spots[0].x, chartViewSize); + x = getPixelX(barSpots[0].x, chartViewSize); if (!fillCompletely && barData.aboveBarData.applyCutOffY) { y = getPixelY(barData.aboveBarData.cutOffY, chartViewSize); } else { @@ -402,8 +433,8 @@ class LineChartPainter extends AxisChartPainter aboveBarPath.lineTo(x, y); /// Line To Bottom Left - x = getPixelX(barData.spots[0].x, chartViewSize); - y = getPixelY(barData.spots[0].y, chartViewSize); + x = getPixelX(barSpots[0].x, chartViewSize); + y = getPixelY(barSpots[0].y, chartViewSize); aboveBarPath.lineTo(x, y); aboveBarPath.close(); @@ -1169,9 +1200,11 @@ class LineChartPainter extends AxisChartPainter /// Find the nearest spot (on X axis) for (int i = 0; i < barData.spots.length; i++) { final spot = barData.spots[i]; - if ((touchedPoint.dx - getPixelX(spot.x, chartViewSize)).abs() <= - data.lineTouchData.touchSpotThreshold) { - return LineBarSpot(barData, barDataPosition, spot); + if (spot.y != null && spot.x != null) { + if ((touchedPoint.dx - getPixelX(spot.x, chartViewSize)).abs() <= + data.lineTouchData.touchSpotThreshold) { + return LineBarSpot(barData, barDataPosition, spot); + } } } diff --git a/repo_files/images/line_chart/line_chart_sample_8.png b/repo_files/images/line_chart/line_chart_sample_8.png index d380364e6..9246cdf94 100644 Binary files a/repo_files/images/line_chart/line_chart_sample_8.png and b/repo_files/images/line_chart/line_chart_sample_8.png differ