From 267d9c1737043021673faca5ffefd295823c34f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6pke?= Date: Wed, 27 Nov 2019 09:52:29 +0100 Subject: [PATCH] added axis titles (without formatting) --- .../samples/line_chart_sample4.dart | 15 ++- .../samples/line_chart_sample5.dart | 7 ++ .../base/base_chart/base_chart_data.dart | 60 +++++++++ lib/src/chart/line_chart/line_chart_data.dart | 6 + .../chart/line_chart/line_chart_painter.dart | 116 ++++++++++++++++++ 5 files changed, 202 insertions(+), 2 deletions(-) diff --git a/example/lib/line_chart/samples/line_chart_sample4.dart b/example/lib/line_chart/samples/line_chart_sample4.dart index e7b03b766..84c9efccb 100644 --- a/example/lib/line_chart/samples/line_chart_sample4.dart +++ b/example/lib/line_chart/samples/line_chart_sample4.dart @@ -5,6 +5,8 @@ class LineChartSample4 extends StatelessWidget { @override Widget build(BuildContext context) { const cutOffYValue = 5.0; + const dateTextStyle = TextStyle( + fontSize: 10, color: Colors.purple, fontWeight: FontWeight.bold); return SizedBox( width: 300, @@ -54,8 +56,8 @@ class LineChartSample4 extends StatelessWidget { titlesData: FlTitlesData( bottomTitles: SideTitles( showTitles: true, - textStyle: - TextStyle(fontSize: 10, color: Colors.purple, fontWeight: FontWeight.bold), + reservedSize: 14, + textStyle: dateTextStyle, getTitles: (value) { switch (value.toInt()) { case 0: @@ -93,6 +95,15 @@ class LineChartSample4 extends StatelessWidget { }, ), ), + axisTitleData: const FlAxisTitleData( + leftTitle: + AxisTitle(showTitle: true, titleText: 'Value', margin: 4), + bottomTitle: AxisTitle( + showTitle: true, + margin: 0, + titleText: '2019', + textStyle: dateTextStyle, + textAlign: TextAlign.right)), gridData: FlGridData( show: true, checkToShowHorizontalGrid: (double value) { diff --git a/example/lib/line_chart/samples/line_chart_sample5.dart b/example/lib/line_chart/samples/line_chart_sample5.dart index 27065ae07..f8ca1c5cb 100644 --- a/example/lib/line_chart/samples/line_chart_sample5.dart +++ b/example/lib/line_chart/samples/line_chart_sample5.dart @@ -89,6 +89,13 @@ class LineChartSample5 extends StatelessWidget { fontSize: 18, )), ), + axisTitleData: const FlAxisTitleData( + rightTitle: AxisTitle(showTitle: true, titleText: 'count'), + leftTitle: AxisTitle(showTitle: true, titleText: 'count'), + topTitle: AxisTitle( + showTitle: true, + titleText: 'Wall clock', + textAlign: TextAlign.left)), gridData: const FlGridData(show: false), borderData: FlBorderData( show: true, diff --git a/lib/src/chart/base/base_chart/base_chart_data.dart b/lib/src/chart/base/base_chart/base_chart_data.dart index b5ae63e99..22decc802 100644 --- a/lib/src/chart/base/base_chart/base_chart_data.dart +++ b/lib/src/chart/base/base_chart/base_chart_data.dart @@ -72,6 +72,66 @@ class FlTouchData { const FlTouchData(this.enabled, this.enableNormalTouch); } +///***** AxisTitleData *****/ + +/// This class holds data about the description for each axis of the chart. +class FlAxisTitleData { + final bool show; + + final AxisTitle leftTitle, topTitle, rightTitle, bottomTitle; + + const FlAxisTitleData({ + this.show = true, + this.leftTitle = const AxisTitle(reservedSize: 16), + this.topTitle = const AxisTitle(reservedSize: 16), + this.rightTitle = const AxisTitle(reservedSize: 16), + this.bottomTitle = const AxisTitle(reservedSize: 16), + }); + + static FlAxisTitleData lerp(FlAxisTitleData a, FlAxisTitleData b, double t) { + return FlAxisTitleData( + show: b.show, + leftTitle: AxisTitle.lerp(a.leftTitle, b.leftTitle, t), + rightTitle: AxisTitle.lerp(a.rightTitle, b.rightTitle, t), + bottomTitle: AxisTitle.lerp(a.bottomTitle, b.bottomTitle, t), + topTitle: AxisTitle.lerp(a.topTitle, b.topTitle, t), + ); + } +} + +/// specify each axis titles data +class AxisTitle { + final bool showTitle; + final double reservedSize; + final TextStyle textStyle; + final TextAlign textAlign; + final double margin; + final String titleText; + + const AxisTitle({ + this.showTitle = false, + this.titleText = '', + this.reservedSize = 14, + this.textStyle = const TextStyle( + color: Colors.black, + fontSize: 11, + ), + this.textAlign = TextAlign.center, + this.margin = 4, + }); + + static AxisTitle lerp(AxisTitle a, AxisTitle b, double t) { + return AxisTitle( + showTitle: b.showTitle, + titleText: b.titleText, + reservedSize: lerpDouble(a.reservedSize, b.reservedSize, t), + textStyle: TextStyle.lerp(a.textStyle, b.textStyle, t), + textAlign: b.textAlign, + margin: lerpDouble(a.margin, b.margin, t), + ); + } +} + /***** TitlesData *****/ /// we use this typedef to determine which titles diff --git a/lib/src/chart/line_chart/line_chart_data.dart b/lib/src/chart/line_chart/line_chart_data.dart index 881e3f12a..2ec5558ac 100644 --- a/lib/src/chart/line_chart/line_chart_data.dart +++ b/lib/src/chart/line_chart/line_chart_data.dart @@ -11,12 +11,14 @@ import 'package:flutter/material.dart'; /// This class holds data to draw the line chart /// List [LineChartBarData] the data to draw the bar lines independently, /// [FlTitlesData] to show the bottom and left titles +/// [FlAxisTitleData] to show a description of each axis /// [ExtraLinesData] to draw extra horizontal and vertical lines on the chart /// [LineTouchData] holds data to handling touch and interactions /// [showingTooltipIndicators] show the tooltip based on provided position(x), and list of [LineBarSpot] class LineChartData extends AxisChartData { final List lineBarsData; final FlTitlesData titlesData; + final FlAxisTitleData axisTitleData; final ExtraLinesData extraLinesData; final LineTouchData lineTouchData; final List>> showingTooltipIndicators; @@ -24,6 +26,7 @@ class LineChartData extends AxisChartData { LineChartData({ this.lineBarsData = const [], this.titlesData = const FlTitlesData(), + this.axisTitleData = const FlAxisTitleData(), this.extraLinesData = const ExtraLinesData(), this.lineTouchData = const LineTouchData(), this.showingTooltipIndicators = const[], @@ -125,6 +128,7 @@ class LineChartData extends AxisChartData { extraLinesData: ExtraLinesData.lerp(a.extraLinesData, b.extraLinesData, t), gridData: FlGridData.lerp(a.gridData, b.gridData, t), titlesData: FlTitlesData.lerp(a.titlesData, b.titlesData, t), + axisTitleData: FlAxisTitleData.lerp(a.axisTitleData, b.axisTitleData, t), lineBarsData: lerpLineChartBarDataList(a.lineBarsData, b.lineBarsData, t), lineTouchData: b.lineTouchData, showingTooltipIndicators: b.showingTooltipIndicators, @@ -137,6 +141,7 @@ class LineChartData extends AxisChartData { LineChartData copyWith({ List lineBarsData, FlTitlesData titlesData, + FlAxisTitleData axisTitleData, ExtraLinesData extraLinesData, LineTouchData lineTouchData, List>> showingTooltipIndicators, @@ -152,6 +157,7 @@ class LineChartData extends AxisChartData { return LineChartData( lineBarsData: lineBarsData ?? this.lineBarsData, titlesData: titlesData ?? this.titlesData, + axisTitleData: axisTitleData ?? this.axisTitleData, extraLinesData: extraLinesData ?? this.extraLinesData, lineTouchData: lineTouchData ?? this.lineTouchData, showingTooltipIndicators: showingTooltipIndicators ?? this.showingTooltipIndicators, diff --git a/lib/src/chart/line_chart/line_chart_painter.dart b/lib/src/chart/line_chart/line_chart_painter.dart index 3da8a193a..443f06f64 100644 --- a/lib/src/chart/line_chart/line_chart_painter.dart +++ b/lib/src/chart/line_chart/line_chart_painter.dart @@ -1,3 +1,4 @@ +import 'dart:math' as math; import 'dart:ui' as ui; import 'package:fl_chart/fl_chart.dart'; @@ -97,6 +98,8 @@ class LineChartPainter extends AxisChartPainter with TouchHandler canvas.restore(); } + drawAxisTitles(canvas, size); + drawTitles(canvas, size); drawExtraLines(canvas, size); @@ -561,6 +564,89 @@ class LineChartPainter extends AxisChartPainter with TouchHandler canvas.drawRect(rect, clearAroundBorderPaint); } + void drawAxisTitles(Canvas canvas, Size viewSize) { + if (!data.axisTitleData.show) { + return; + } + viewSize = getChartUsableDrawSize(viewSize); + + final axisTitles = data.axisTitleData; + + // Left Title + final leftTitle = axisTitles.leftTitle; + if (leftTitle.showTitle) { + final TextSpan span = + TextSpan(style: leftTitle.textStyle, text: leftTitle.titleText); + final TextPainter tp = TextPainter( + text: span, + textAlign: leftTitle.textAlign, + textDirection: TextDirection.ltr); + tp.layout(minWidth: viewSize.height); + canvas.save(); + canvas.rotate(-math.pi * 0.5); + tp.paint( + canvas, + Offset(-viewSize.height - getTopOffsetDrawSize(), + leftTitle.reservedSize - tp.height)); + canvas.restore(); + } + + // Top title + final topTitle = axisTitles.topTitle; + if (topTitle.showTitle) { + final TextSpan span = + TextSpan(style: topTitle.textStyle, text: topTitle.titleText); + final TextPainter tp = TextPainter( + text: span, + textAlign: topTitle.textAlign, + textDirection: TextDirection.ltr); + tp.layout(minWidth: viewSize.width); + tp.paint(canvas, + Offset(getLeftOffsetDrawSize(), topTitle.reservedSize - tp.height)); + } + + // Right Title + final rightTitle = axisTitles.rightTitle; + if (rightTitle.showTitle) { + final TextSpan span = + TextSpan(style: rightTitle.textStyle, text: rightTitle.titleText); + final TextPainter tp = TextPainter( + text: span, + textAlign: rightTitle.textAlign, + textDirection: TextDirection.ltr); + tp.layout(minWidth: viewSize.height); + canvas.save(); + canvas.rotate(-math.pi * 0.5); + tp.paint( + canvas, + Offset( + -viewSize.height - getTopOffsetDrawSize(), + viewSize.width + + getExtraNeededHorizontalSpace() - + rightTitle.reservedSize)); + canvas.restore(); + } + + // Bottom title + final bottomTitle = axisTitles.bottomTitle; + if (bottomTitle.showTitle) { + final TextSpan span = + TextSpan(style: bottomTitle.textStyle, text: bottomTitle.titleText); + final TextPainter tp = TextPainter( + text: span, + textAlign: bottomTitle.textAlign, + textDirection: TextDirection.ltr); + tp.layout(minWidth: viewSize.width); + tp.paint( + canvas, + Offset( + getLeftOffsetDrawSize(), + getExtraNeededVerticalSpace() - + bottomTitle.reservedSize + + viewSize.height)); + } + } + void drawTitles(Canvas canvas, Size viewSize) { if (!data.titlesData.show) { return; @@ -803,6 +889,17 @@ class LineChartPainter extends AxisChartPainter with TouchHandler sum += rightSide.reservedSize + rightSide.margin; } } + if (data.axisTitleData.show) { + final leftSide = data.axisTitleData.leftTitle; + if (leftSide.showTitle) { + sum += leftSide.reservedSize + leftSide.margin; + } + + final rightSide = data.axisTitleData.rightTitle; + if (rightSide.showTitle) { + sum += rightSide.reservedSize + rightSide.margin; + } + } return sum; } @@ -825,6 +922,17 @@ class LineChartPainter extends AxisChartPainter with TouchHandler sum += bottomSide.reservedSize + bottomSide.margin; } } + if (data.axisTitleData.show) { + final topSide = data.axisTitleData.topTitle; + if (topSide.showTitle) { + sum += topSide.reservedSize + topSide.margin; + } + + final bottomSide = data.axisTitleData.bottomTitle; + if (bottomSide.showTitle) { + sum += bottomSide.reservedSize + bottomSide.margin; + } + } return sum; } @@ -839,6 +947,10 @@ class LineChartPainter extends AxisChartPainter with TouchHandler if (data.titlesData.show && leftTitles.showTitles) { sum += leftTitles.reservedSize + leftTitles.margin; } + final leftAxisTitle = data.axisTitleData.leftTitle; + if (data.axisTitleData.show && leftAxisTitle.showTitle) { + sum += leftAxisTitle.reservedSize + leftAxisTitle.margin; + } return sum; } @@ -853,6 +965,10 @@ class LineChartPainter extends AxisChartPainter with TouchHandler if (data.titlesData.show && topTitles.showTitles) { sum += topTitles.reservedSize + topTitles.margin; } + final topAxisTitle = data.axisTitleData.topTitle; + if (data.axisTitleData.show && topAxisTitle.showTitle) { + sum += topAxisTitle.reservedSize + topAxisTitle.margin; + } return sum; }