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/bar_chart/bar_chart_data.dart b/lib/src/chart/bar_chart/bar_chart_data.dart index 719e920a8..04e5c02cb 100644 --- a/lib/src/chart/bar_chart/bar_chart_data.dart +++ b/lib/src/chart/bar_chart/bar_chart_data.dart @@ -11,6 +11,7 @@ import 'package:flutter/material.dart'; /// This class is responsible to holds data to draw Bar Chart /// [barGroups] holds list of bar groups to show together, /// [groupsSpace] space between groups, it applies only when the [alignment] is [Alignment.center], +/// [axisTitleData] to show a description of each axis /// [alignment] is the alignment of showing groups, /// [titlesData] holds data about drawing left and bottom titles. class BarChartData extends AxisChartData { @@ -30,11 +31,13 @@ class BarChartData extends AxisChartData { show: false, ), FlBorderData borderData, + FlAxisTitleData axisTitleData = const FlAxisTitleData(), double maxY, Color backgroundColor, }) : super( gridData: gridData, borderData: borderData, + axisTitleData: axisTitleData, backgroundColor: backgroundColor, touchData: barTouchData, ) { @@ -91,6 +94,7 @@ class BarChartData extends AxisChartData { double groupsSpace, BarChartAlignment alignment, FlTitlesData titlesData, + FlAxisTitleData axisTitleData, BarTouchData barTouchData, FlGridData gridData, FlBorderData borderData, @@ -102,6 +106,7 @@ class BarChartData extends AxisChartData { groupsSpace: groupsSpace ?? this.groupsSpace, alignment: alignment ?? this.alignment, titlesData: titlesData ?? this.titlesData, + axisTitleData: axisTitleData ?? this.axisTitleData, barTouchData: barTouchData ?? this.barTouchData, gridData: gridData ?? this.gridData, borderData: borderData ?? this.borderData, @@ -118,6 +123,7 @@ class BarChartData extends AxisChartData { groupsSpace: lerpDouble(a.groupsSpace, b.groupsSpace, t), alignment: b.alignment, titlesData: FlTitlesData.lerp(a.titlesData, b.titlesData, t), + axisTitleData: FlAxisTitleData.lerp(a.axisTitleData, b.axisTitleData, t), barTouchData: b.barTouchData, gridData: FlGridData.lerp(a.gridData, b.gridData, t), borderData: FlBorderData.lerp(a.borderData, b.borderData, t), diff --git a/lib/src/chart/bar_chart/bar_chart_painter.dart b/lib/src/chart/bar_chart/bar_chart_painter.dart index 14ea3c02d..97c95c8fd 100644 --- a/lib/src/chart/bar_chart/bar_chart_painter.dart +++ b/lib/src/chart/bar_chart/bar_chart_painter.dart @@ -41,6 +41,7 @@ class BarChartPainter extends AxisChartPainter with TouchHandler extends BaseChartPainte drawGrid(canvas, size); } + + 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)); + } + } + + @override + double getExtraNeededHorizontalSpace() { + double sum = super.getExtraNeededHorizontalSpace(); + + 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; + } + + @override + double getExtraNeededVerticalSpace() { + double sum = super.getExtraNeededVerticalSpace(); + + 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; + } + + @override + double getLeftOffsetDrawSize() { + var sum = super.getLeftOffsetDrawSize(); + + final leftAxisTitle = data.axisTitleData.leftTitle; + if (data.axisTitleData.show && leftAxisTitle.showTitle) { + sum += leftAxisTitle.reservedSize + leftAxisTitle.margin; + } + + return sum; + } + + @override + double getTopOffsetDrawSize() { + var sum = super.getTopOffsetDrawSize(); + + final topAxisTitle = data.axisTitleData.topTitle; + if (data.axisTitleData.show && topAxisTitle.showTitle) { + sum += topAxisTitle.reservedSize + topAxisTitle.margin; + } + + return sum; + } + void drawGrid(Canvas canvas, Size viewSize) { if (!data.gridData.show || data.gridData == null) { return; 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..1df11d88f 100644 --- a/lib/src/chart/base/base_chart/base_chart_data.dart +++ b/lib/src/chart/base/base_chart/base_chart_data.dart @@ -56,7 +56,7 @@ class FlBorderData { ); } - + } /***** TouchData *****/ @@ -72,6 +72,69 @@ 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.copyWith(fontSize: a.textStyle.fontSize), + b.textStyle.copyWith(fontSize: b.textStyle.fontSize), + 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..3f0ea95b2 100644 --- a/lib/src/chart/line_chart/line_chart_data.dart +++ b/lib/src/chart/line_chart/line_chart_data.dart @@ -11,6 +11,7 @@ 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] @@ -29,6 +30,7 @@ class LineChartData extends AxisChartData { this.showingTooltipIndicators = const[], FlGridData gridData = const FlGridData(), FlBorderData borderData, + FlAxisTitleData axisTitleData = const FlAxisTitleData(), double minX, double maxX, double minY, @@ -39,6 +41,7 @@ class LineChartData extends AxisChartData { gridData: gridData, touchData: lineTouchData, borderData: borderData, + axisTitleData: axisTitleData, clipToBorder: clipToBorder, backgroundColor: backgroundColor, ) { @@ -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..47dfdca67 100644 --- a/lib/src/chart/line_chart/line_chart_painter.dart +++ b/lib/src/chart/line_chart/line_chart_painter.dart @@ -97,6 +97,7 @@ class LineChartPainter extends AxisChartPainter with TouchHandler canvas.restore(); } + drawAxisTitles(canvas, size); drawTitles(canvas, size); drawExtraLines(canvas, size); diff --git a/lib/src/chart/scatter_chart/scatter_chart_data.dart b/lib/src/chart/scatter_chart/scatter_chart_data.dart index 38ab5be0c..c22904b57 100644 --- a/lib/src/chart/scatter_chart/scatter_chart_data.dart +++ b/lib/src/chart/scatter_chart/scatter_chart_data.dart @@ -19,6 +19,7 @@ class ScatterChartData extends AxisChartData { this.showingTooltipIndicators = const [], FlGridData gridData = const FlGridData(), FlBorderData borderData, + FlAxisTitleData axisTitleData = const FlAxisTitleData(), double minX, double maxX, double minY, @@ -29,6 +30,7 @@ class ScatterChartData extends AxisChartData { gridData: gridData, touchData: scatterTouchData, borderData: borderData, + axisTitleData: axisTitleData, clipToBorder: clipToBorder, backgroundColor: backgroundColor, ) { @@ -102,6 +104,7 @@ class ScatterChartData extends AxisChartData { showingTooltipIndicators: lerpIntList(a.showingTooltipIndicators, b.showingTooltipIndicators, t), gridData: FlGridData.lerp(a.gridData, b.gridData, t), borderData: FlBorderData.lerp(a.borderData, b.borderData, t), + axisTitleData: FlAxisTitleData.lerp(a.axisTitleData, b.axisTitleData, t), minX: lerpDouble(a.minX, b.minX, t), maxX: lerpDouble(a.maxX, b.maxX, t), minY: lerpDouble(a.minY, b.minY, t), @@ -121,6 +124,7 @@ class ScatterChartData extends AxisChartData { List showingTooltipIndicators, FlGridData gridData, FlBorderData borderData, + FlAxisTitleData axisTitleData, double minX, double maxX, double minY, @@ -135,6 +139,7 @@ class ScatterChartData extends AxisChartData { showingTooltipIndicators: showingTooltipIndicators ?? this.showingTooltipIndicators, gridData: gridData ?? this.gridData, borderData: borderData ?? this.borderData, + axisTitleData: axisTitleData ?? this.axisTitleData, minX: minX ?? this.minX, maxX: maxX ?? this.maxX, minY: minY ?? this.minY, diff --git a/lib/src/chart/scatter_chart/scatter_chart_painter.dart b/lib/src/chart/scatter_chart/scatter_chart_painter.dart index bdaf892d0..bec45315d 100644 --- a/lib/src/chart/scatter_chart/scatter_chart_painter.dart +++ b/lib/src/chart/scatter_chart/scatter_chart_painter.dart @@ -31,6 +31,7 @@ class ScatterChartPainter extends AxisChartPainter with TouchH void paint(Canvas canvas, Size size) { super.paint(canvas, size); + drawAxisTitles(canvas, size); drawTitles(canvas, size); drawSpots(canvas, size); diff --git a/repo_files/documentations/bar_chart.md b/repo_files/documentations/bar_chart.md index 2548c6a55..9c4c9297a 100644 --- a/repo_files/documentations/bar_chart.md +++ b/repo_files/documentations/bar_chart.md @@ -18,6 +18,7 @@ BarChart( |groupsSpace| space between groups, it applies only when the [alignment](#BarChartAlignment) is `Alignment.center`,|16| |alignment| a [BarChartAlignment](#BarChartAlignment) that determines the alignment of the barGroups, inspired by [Flutter MainAxisAlignment](https://docs.flutter.io/flutter/rendering/MainAxisAlignment-class.html)| BarChartAlignment.spaceBetween| |titlesData| check the [FlTitlesData](base_chart.md#FlTitlesData)|FlTitlesData()| +|axisTitleData| check the [FlAxisTitleData](base_chart.md#FlAxisTitleData)| FlAxisTitleData()| |backgroundColor| a background color which is drawn behind the chart| null | |barTouchData| [BarTouchData](#BarTouchData) holds the touch interactivity details|BarTouchData()| |gridData| check the [FlGridData](base_chart.md#FlGridData)|FlGridData()| diff --git a/repo_files/documentations/base_chart.md b/repo_files/documentations/base_chart.md index 883907923..2f477199b 100644 --- a/repo_files/documentations/base_chart.md +++ b/repo_files/documentations/base_chart.md @@ -77,4 +77,30 @@ currently we have these touch behaviors: |PropName|Description|default value| |:-------|:----------|:------------| |spot|the touched [FlSpot](#FlSpot)|null| -|offset|[Offset](https://api.flutter.dev/flutter/dart-ui/Offset-class.html) of the touched spot|null| \ No newline at end of file +|offset|[Offset](https://api.flutter.dev/flutter/dart-ui/Offset-class.html) of the touched spot|null| + + + +### FlAxisTitleData + +Can be used to display a title text for each axis. Titles for the vertical axes (left and right) will be rotated 90 degrees. + +|PropName |Description |default value| +|:---------------|:---------------|:-------| +|show| determines to show or hide the titles for the axes|true| +|leftTitle| an [AxisTitle](#AxisTitle) that holds data to draw the title of the left axis | `AxisTitle(reservedSize: 16)`| +|topTitle| an [AxisTitle](#AxisTitle) that holds data to draw the title of the top axis | `AxisTitle(reservedSize: 16)`| +|rightTitle| an [AxisTitle](#AxisTitle) that holds data to draw the title of the right axis | `AxisTitle(reservedSize: 16)`| +|bottomTitle| an [AxisTitle](#AxisTitle) that holds data to draw the title of the bottom axis | `AxisTitle(reservedSize: 16)`| + + + +### AxisTitle +|PropName |Description |default value| +|:---------------|:---------------|:-------| +|showTitle| determines to show or hide the title | `false`| +|titleText| the text to draw as a description for this axis| `''`| +|reservedSize| a reserved space for the text| `14`| +|margin| margin between the axis text and inner elements ([SideTitles](#SideTitles) or the chart) | `4`| +|textStyle| [TextStyle](https://api.flutter.dev/flutter/painting/TextStyle-class.html) to determine the style of the text | `TextStyle(color: Colors.black, fontSize: 11)`| +|textAlign| [TextAlign](https://api.flutter.dev/flutter/dart-ui/TextAlign-class.html) to determine the alignment of the text | `TextAlign.center`| diff --git a/repo_files/documentations/line_chart.md b/repo_files/documentations/line_chart.md index 7b5421eae..262a1f5da 100644 --- a/repo_files/documentations/line_chart.md +++ b/repo_files/documentations/line_chart.md @@ -16,6 +16,7 @@ LineChart( |:---------------|:---------------|:-------| |lineBarsData| list of [LineChartBarData ](#LineChartBarData ) to show the chart's lines, they stack and be drawn on top of each other|[]| |titlesData| check the [FlTitlesData](base_chart.md#FlTitlesData)| FlTitlesData()| +|axisTitleData| check the [FlAxisTitleData](base_chart.md#FlAxisTitleData)| FlAxisTitleData()| |extraLinesData| [ExtraLinesData](#ExtraLinesData) object to hold drawing details of extra horizontal and vertical lines.| |lineTouchData| [LineTouchData](#linetouchdata-read-about-touch-handling) holds the touch interactivity details| LineTouchData()| |showingTooltipIndicators| show the tooltip based on provided position(x), and list of [LineBarSpot]| {} | diff --git a/repo_files/documentations/scatter_chart.md b/repo_files/documentations/scatter_chart.md index ae6b2d451..e93487165 100644 --- a/repo_files/documentations/scatter_chart.md +++ b/repo_files/documentations/scatter_chart.md @@ -16,6 +16,7 @@ ScatterChart( |:---------------|:---------------|:-------| |scatterSpots| list of [ScatterSpot ](#ScatterSpot ) to show the scatter spots on the chart|[]| |titlesData| check the [FlTitlesData](base_chart.md#FlTitlesData)| FlTitlesData()| +|axisTitleData| check the [FlAxisTitleData](base_chart.md#FlAxisTitleData)| FlAxisTitleData()| |scatterTouchData| [ScatterTouchData](#scattertouchdata-read-about-touch-handling) holds the touch interactivity details| ScatterTouchData()| |showingTooltipIndicators| indices of showing tooltip|[]| diff --git a/repo_files/images/line_chart/line_chart_sample_4.png b/repo_files/images/line_chart/line_chart_sample_4.png index 4ade56bde..a85c99f03 100644 Binary files a/repo_files/images/line_chart/line_chart_sample_4.png and b/repo_files/images/line_chart/line_chart_sample_4.png differ diff --git a/repo_files/images/line_chart/line_chart_sample_5.png b/repo_files/images/line_chart/line_chart_sample_5.png index 2f8068321..f9833d21b 100644 Binary files a/repo_files/images/line_chart/line_chart_sample_5.png and b/repo_files/images/line_chart/line_chart_sample_5.png differ