Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Nullable values in line chart #252

Merged
merged 17 commits into from
Mar 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions example/lib/line_chart/line_chart_page2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
],
),
Expand Down
7 changes: 5 additions & 2 deletions example/lib/line_chart/samples/line_chart_sample8.dart
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,12 @@ class _LineChartSample8State extends State<LineChartSample8> {
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,
Expand Down
30 changes: 16 additions & 14 deletions lib/src/chart/line_chart/line_chart_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
}
Expand Down
107 changes: 70 additions & 37 deletions lib/src/chart/line_chart/line_chart_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -166,19 +166,39 @@ class LineChartPainter extends AxisChartPainter<LineChartData>
}

void _drawBarLine(Canvas canvas, Size viewSize, LineChartBarData barData) {
final barPath = _generateBarPath(viewSize, barData);
final List<List<FlSpot>> 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(<FlSpot>[]);
}
}
}

_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<FlSpot> 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(
Expand All @@ -188,9 +208,17 @@ class LineChartPainter extends AxisChartPainter<LineChartData>

final List<FlSpot> 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);
}
Expand Down Expand Up @@ -266,15 +294,16 @@ class LineChartPainter extends AxisChartPainter<LineChartData>
/// 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<FlSpot> 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 {
Expand All @@ -283,20 +312,20 @@ class LineChartPainter extends AxisChartPainter<LineChartData>
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;
Expand Down Expand Up @@ -338,14 +367,15 @@ class LineChartPainter extends AxisChartPainter<LineChartData>
/// 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<FlSpot> 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);
Expand All @@ -355,7 +385,7 @@ class LineChartPainter extends AxisChartPainter<LineChartData>
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 {
Expand All @@ -364,8 +394,8 @@ class LineChartPainter extends AxisChartPainter<LineChartData>
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();

Expand All @@ -376,14 +406,15 @@ class LineChartPainter extends AxisChartPainter<LineChartData>
/// 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<FlSpot> 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);
Expand All @@ -393,7 +424,7 @@ class LineChartPainter extends AxisChartPainter<LineChartData>
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 {
Expand All @@ -402,8 +433,8 @@ class LineChartPainter extends AxisChartPainter<LineChartData>
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();

Expand Down Expand Up @@ -1169,9 +1200,11 @@ class LineChartPainter extends AxisChartPainter<LineChartData>
/// 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);
}
}
}

Expand Down
Binary file modified repo_files/images/line_chart/line_chart_sample_8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.