diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8f7e04b81..ff6456304 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,11 @@
-## newVersion
-* **BUGFIX** Fix a memory leak issue in the axis-based charts, there was a logic to calculate and cache the minX, maxX, minY and maxY properties to reduce the computation cost. But it caused some memory issues, as we don't have a quick solution for this, we disabled the caching logic for now, later we can move the calculation logic to the render objects to keep and update them only when the data is changed, #1106, #1693
-* **BUGFIX** Fix showing grid lines even when there is no line to show in the LineChart, #1691
+## 0.69.0
+* **BUGFIX** (by @imaNNeo) Fix a memory leak issue in the axis-based charts, there was a logic to calculate and cache the minX, maxX, minY and maxY properties to reduce the computation cost. But it caused some memory issues, as we don't have a quick solution for this, we disabled the caching logic for now, later we can move the calculation logic to the render objects to keep and update them only when the data is changed, #1106, #1693
+* **BUGFIX** (by @imaNNeo) Fix showing grid lines even when there is no line to show in the LineChart, #1691
+* **IMPROVEMENT** (by @sczesla) Allow users to control minIncluded and maxIncluded using SideTitles, #906
+* **IMPROVEMENT** (by @elizabethzhenliu) Reverse the touch order in ScatterChart, so now the top spots are touched first, #1675
+* **IMPROVEMENT** (by @ksw2000) Remove redundant math import, #1683
+* **IMPROVEMENT** (by @Neer-Pathak) Fix linux example build issue, #1668
+* **IMPROVEMENT** (by @TobiasRump) Update the bar chart documentation, #1662
## 0.68.0
* **Improvement** (by @imaNNeo) Update LineChartSample6 to implement a way to show a tooltip on a single spot, #1620
diff --git a/README.md b/README.md
index fe219a7d2..ee939fa84 100644
--- a/README.md
+++ b/README.md
@@ -19,8 +19,8 @@ Your financial support acts as fuel for fl_chart's development. [Support here](h
@@ -47,7 +47,7 @@ Your financial support acts as fuel for fl_chart's development. [Support here](h
### Overview
FL Chart is a highly customizable Flutter chart library that supports **[Line Chart](https://app.flchart.dev/#/line)**, **[Bar Chart](https://app.flchart.dev/#/bar)**, **[Pie Chart](https://app.flchart.dev/#/pie)**, **[Scatter Chart](https://app.flchart.dev/#/scatter)**, and **[Radar Chart](https://app.flchart.dev/#/radar)**.
-
+
### Chart Types
diff --git a/example/linux/CMakeLists.txt b/example/linux/CMakeLists.txt
index 49b2e3101..228a43f98 100644
--- a/example/linux/CMakeLists.txt
+++ b/example/linux/CMakeLists.txt
@@ -4,7 +4,7 @@ project(runner LANGUAGES CXX)
# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
-set(BINARY_NAME "FL Chart App")
+set(BINARY_NAME "fLChartApp")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "dev.flchart.app")
diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock
index bf60fa4d3..8fc3fc4ef 100644
--- a/example/macos/Podfile.lock
+++ b/example/macos/Podfile.lock
@@ -26,9 +26,9 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
- package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce
- path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
- url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
+ package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c
+ path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
+ url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399
PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7
diff --git a/example/macos/Runner/AppDelegate.swift b/example/macos/Runner/AppDelegate.swift
index d53ef6437..8e02df288 100644
--- a/example/macos/Runner/AppDelegate.swift
+++ b/example/macos/Runner/AppDelegate.swift
@@ -1,7 +1,7 @@
import Cocoa
import FlutterMacOS
-@NSApplicationMain
+@main
class AppDelegate: FlutterAppDelegate {
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 29165d565..4c1d636ff 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -10,24 +10,24 @@ dependencies:
sdk: flutter
flutter_web_plugins:
sdk: flutter
- cupertino_icons: ^1.0.6
- google_fonts: ^6.1.0
- flutter_svg: ^2.0.7
- universal_platform: ^1.0.0+1
+ cupertino_icons: ^1.0.8
+ google_fonts: ^6.2.1
+ flutter_svg: ^2.0.10+1
+ universal_platform: ^1.1.0
flutter_staggered_grid_view: ^0.7.0
- url_launcher: ^6.1.14
- go_router: ^11.1.2
+ url_launcher: ^6.3.0
+ go_router: ^14.2.7
dartx: ^1.2.0
fl_chart:
path: ../
- flutter_bloc: ^8.1.3
- package_info_plus: ^4.2.0
+ flutter_bloc: ^8.1.6
+ package_info_plus: ^8.0.2
equatable: ^2.0.5
dev_dependencies:
flutter_test:
sdk: flutter
- flutter_lints: ^2.0.3
+ flutter_lints: ^4.0.0
flutter:
uses-material-design: true
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 a6064b737..156d98df1 100644
--- a/lib/src/chart/base/axis_chart/axis_chart_data.dart
+++ b/lib/src/chart/base/axis_chart/axis_chart_data.dart
@@ -161,6 +161,8 @@ class SideTitles with EquatableMixin {
this.getTitlesWidget = defaultGetTitle,
this.reservedSize = 22,
this.interval,
+ this.minIncluded = true,
+ this.maxIncluded = true,
}) : assert(interval != 0, "SideTitles.interval couldn't be zero");
/// Determines showing or hiding this side titles
@@ -178,6 +180,14 @@ class SideTitles with EquatableMixin {
/// we try to find a suitable value to set as [interval] under the hood.
final double? interval;
+ /// If true (default), a title for the minimum data value is included
+ /// independent of the sampling interval
+ final bool minIncluded;
+
+ /// If true (default), a title for the maximum data value is included
+ /// independent of the sampling interval
+ final bool maxIncluded;
+
/// Lerps a [SideTitles] based on [t] value, check [Tween.lerp].
static SideTitles lerp(SideTitles a, SideTitles b, double t) {
return SideTitles(
@@ -185,6 +195,8 @@ class SideTitles with EquatableMixin {
getTitlesWidget: b.getTitlesWidget,
reservedSize: lerpDouble(a.reservedSize, b.reservedSize, t)!,
interval: lerpDouble(a.interval, b.interval, t),
+ minIncluded: b.minIncluded,
+ maxIncluded: b.maxIncluded,
);
}
@@ -195,12 +207,16 @@ class SideTitles with EquatableMixin {
GetTitleWidgetFunction? getTitlesWidget,
double? reservedSize,
double? interval,
+ bool? minIncluded,
+ bool? maxIncluded,
}) {
return SideTitles(
showTitles: showTitles ?? this.showTitles,
getTitlesWidget: getTitlesWidget ?? this.getTitlesWidget,
reservedSize: reservedSize ?? this.reservedSize,
interval: interval ?? this.interval,
+ minIncluded: minIncluded ?? this.minIncluded,
+ maxIncluded: maxIncluded ?? this.maxIncluded,
);
}
@@ -211,6 +227,8 @@ class SideTitles with EquatableMixin {
getTitlesWidget,
reservedSize,
interval,
+ minIncluded,
+ maxIncluded,
];
}
diff --git a/lib/src/chart/base/axis_chart/axis_chart_helper.dart b/lib/src/chart/base/axis_chart/axis_chart_helper.dart
index 4d16a411b..b287b6730 100644
--- a/lib/src/chart/base/axis_chart/axis_chart_helper.dart
+++ b/lib/src/chart/base/axis_chart/axis_chart_helper.dart
@@ -32,6 +32,8 @@ class AxisChartHelper {
var axisSeek = initialValue;
final firstPositionOverlapsWithMin = axisSeek == min;
if (!minIncluded && firstPositionOverlapsWithMin) {
+ // If inital value is equal to data minimum,
+ // move first label one interval further
axisSeek += interval;
}
final diff = max - min;
@@ -43,6 +45,7 @@ class AxisChartHelper {
final epsilon = interval / 100000;
if (minIncluded && !firstPositionOverlapsWithMin) {
+ // Data minimum shall be included and is not yet covered
yield min;
}
while (axisSeek <= end + epsilon) {
diff --git a/lib/src/chart/base/axis_chart/side_titles/side_titles_widget.dart b/lib/src/chart/base/axis_chart/side_titles/side_titles_widget.dart
index aa3f58ea5..c1363b39a 100644
--- a/lib/src/chart/base/axis_chart/side_titles/side_titles_widget.dart
+++ b/lib/src/chart/base/axis_chart/side_titles/side_titles_widget.dart
@@ -133,6 +133,8 @@ class SideTitlesWidget extends StatelessWidget {
final axisValues = AxisChartHelper().iterateThroughAxis(
min: axisMin,
max: axisMax,
+ minIncluded: sideTitles.minIncluded,
+ maxIncluded: sideTitles.maxIncluded,
baseLine: axisBaseLine,
interval: interval,
);
diff --git a/lib/src/chart/scatter_chart/scatter_chart_painter.dart b/lib/src/chart/scatter_chart/scatter_chart_painter.dart
index 109091ae2..ad7117037 100644
--- a/lib/src/chart/scatter_chart/scatter_chart_painter.dart
+++ b/lib/src/chart/scatter_chart/scatter_chart_painter.dart
@@ -364,7 +364,8 @@ class ScatterChartPainter extends AxisChartPainter {
) {
final data = holder.data;
- for (var i = 0; i < data.scatterSpots.length; i++) {
+ for (var i = data.scatterSpots.length - 1; i >= 0; i--) {
+ // Reverse the loop to check the topmost spot first
final spot = data.scatterSpots[i];
final spotPixelX = getPixelX(spot.x, viewSize, holder);
diff --git a/lib/src/utils/utils.dart b/lib/src/utils/utils.dart
index dc5cdffc8..8f63bf5a0 100644
--- a/lib/src/utils/utils.dart
+++ b/lib/src/utils/utils.dart
@@ -1,5 +1,4 @@
import 'dart:math' as math;
-import 'dart:math';
import 'package:flutter/material.dart';
@@ -45,9 +44,9 @@ class Utils {
Offset calculateRotationOffset(Size size, double degree) {
final rotatedHeight = (size.width * math.sin(radians(degree))).abs() +
- (size.height * cos(radians(degree))).abs();
- final rotatedWidth = (size.width * cos(radians(degree))).abs() +
- (size.height * sin(radians(degree))).abs();
+ (size.height * math.cos(radians(degree))).abs();
+ final rotatedWidth = (size.width * math.cos(radians(degree))).abs() +
+ (size.height * math.sin(radians(degree))).abs();
return Offset(
(size.width - rotatedWidth) / 2,
(size.height - rotatedHeight) / 2,
@@ -178,7 +177,7 @@ class Utils {
precisionCount -= numbersToRemove;
}
- final pow10onPrecision = pow(10, precisionCount);
+ final pow10onPrecision = math.pow(10, precisionCount);
input *= pow10onPrecision;
return _roundIntervalAboveOne(input) / pow10onPrecision;
}
@@ -186,18 +185,18 @@ class Utils {
double _roundIntervalAboveOne(double input) {
assert(input >= 1.0);
final decimalCount = input.toInt().toString().length - 1;
- input /= pow(10, decimalCount);
+ input /= math.pow(10, decimalCount);
final scaled = input >= 10 ? input.round() / 10 : input;
if (scaled >= 7.6) {
- return 10 * pow(10, decimalCount).toInt().toDouble();
+ return 10 * math.pow(10, decimalCount).toInt().toDouble();
} else if (scaled >= 2.6) {
- return 5 * pow(10, decimalCount).toInt().toDouble();
+ return 5 * math.pow(10, decimalCount).toInt().toDouble();
} else if (scaled >= 1.6) {
- return 2 * pow(10, decimalCount).toInt().toDouble();
+ return 2 * math.pow(10, decimalCount).toInt().toDouble();
} else {
- return 1 * pow(10, decimalCount).toInt().toDouble();
+ return 1 * math.pow(10, decimalCount).toInt().toDouble();
}
}
diff --git a/pubspec.yaml b/pubspec.yaml
index 2c8058f95..fc4cd4a67 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
name: fl_chart
description: A highly customizable Flutter chart library that supports Line Chart, Bar Chart, Pie Chart, Scatter Chart, and Radar Chart.
-version: 0.68.0
+version: 0.69.0
homepage: https://flchart.dev/
repository: https://github.com/imaNNeo/fl_chart
issue_tracker: https://github.com/imaNNeo/fl_chart/issues
diff --git a/repo_files/documentations/bar_chart.md b/repo_files/documentations/bar_chart.md
index 8d1f0f5b0..aa72cd1a0 100644
--- a/repo_files/documentations/bar_chart.md
+++ b/repo_files/documentations/bar_chart.md
@@ -1,4 +1,4 @@
-
+
### How to use
```dart
@@ -19,7 +19,7 @@ When you change the chart's state, it animates to the new state internally (usin
|:---------------|:---------------|:-------|
|barGroups| list of [BarChartGroupData ](#BarChartGroupData) to show the bar lines together, you can provide one item per group to show normal bar chart|[]|
|groupsSpace| space between groups, it applies only when the [alignment](#BarChartAlignment) is `BarChartAlignment.start`, `BarChartAlignment.center` or `BarChartAlignment.end`|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|
+|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.spaceEvenly|
|titlesData| check the [FlTitlesData](base_chart.md#FlTitlesData)|FlTitlesData()|
|axisTitleData| check the [FlAxisTitleData](base_chart.md#FlAxisTitleData)| FlAxisTitleData()|
|rangeAnnotations| show range annotations behind the chart, check [RangeAnnotations](base_chart.md#RangeAnnotations) | RangeAnnotations()|
diff --git a/repo_files/documentations/base_chart.md b/repo_files/documentations/base_chart.md
index 7f40ca0d0..1017f9327 100644
--- a/repo_files/documentations/base_chart.md
+++ b/repo_files/documentations/base_chart.md
@@ -31,6 +31,9 @@
|getTitlesWidget| A function to retrieve the title widget with given value on the related axis.|defaultGetTitle|
|reservedSize| It determines the maximum space that your titles need, |22|
|interval| Texts are showing with provided `interval`. If you don't provide anything, we try to find a suitable value to set as `interval` under the hood. | null |
+|minIncluded| Determines whether to include title for minimum data value | true |
+|maxIncluded| Determines whether to include title for maximum data value | true |
+
### SideTitleFitInsideData
|PropName |Description |default value|
diff --git a/repo_files/documentations/line_chart.md b/repo_files/documentations/line_chart.md
index 65b4e21a6..b2f1f369c 100644
--- a/repo_files/documentations/line_chart.md
+++ b/repo_files/documentations/line_chart.md
@@ -1,4 +1,4 @@
-
+
### How to use
```dart
diff --git a/repo_files/documentations/pie_chart.md b/repo_files/documentations/pie_chart.md
index 87a5e1395..b573b0f6c 100644
--- a/repo_files/documentations/pie_chart.md
+++ b/repo_files/documentations/pie_chart.md
@@ -1,4 +1,4 @@
-
+
### How to use
```dart
diff --git a/repo_files/sponsors/become_a_sponsor_dark.png b/repo_files/sponsors/become_a_sponsor_dark.png
new file mode 100644
index 000000000..899c81625
Binary files /dev/null and b/repo_files/sponsors/become_a_sponsor_dark.png differ
diff --git a/repo_files/sponsors/become_a_sponsor_light.png b/repo_files/sponsors/become_a_sponsor_light.png
new file mode 100644
index 000000000..0059184e7
Binary files /dev/null and b/repo_files/sponsors/become_a_sponsor_light.png differ
diff --git a/test/chart/base/axis_chart/side_titles/side_titles_test.dart b/test/chart/base/axis_chart/side_titles/side_titles_test.dart
new file mode 100644
index 000000000..72a283f17
--- /dev/null
+++ b/test/chart/base/axis_chart/side_titles/side_titles_test.dart
@@ -0,0 +1,81 @@
+import 'package:fl_chart/fl_chart.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+ final data = [
+ const FlSpot(0, 0.5),
+ const FlSpot(1, 1.3),
+ const FlSpot(2, 1.9),
+ ];
+
+ const viewSize = Size(400, 400);
+
+ testWidgets(
+ 'Test the effect of minIncluded and maxIncluded in sideTitles',
+ (WidgetTester tester) async {
+ // Minimum/maximum included
+ final mima = [
+ [true, true],
+ [true, false],
+ [false, true],
+ [false, false],
+ ];
+
+ for (final e in mima) {
+ final titlesData = FlTitlesData(
+ leftTitles: AxisTitles(
+ sideTitles: SideTitles(
+ showTitles: true,
+ minIncluded: e[0],
+ maxIncluded: e[1],
+ reservedSize: 50,
+ interval: 1,
+ ),
+ ),
+ rightTitles: const AxisTitles(),
+ topTitles: const AxisTitles(),
+ bottomTitles: const AxisTitles(),
+ );
+
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: Center(
+ child: SizedBox(
+ width: viewSize.width,
+ height: viewSize.height,
+ child: LineChart(
+ LineChartData(
+ titlesData: titlesData,
+ lineBarsData: [
+ LineChartBarData(
+ spots: data,
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ // Number of expected text widgets (titles) on the y-axis
+ expect(
+ find.byType(Text),
+ findsNWidgets((e[0] ? 1 : 0) + (e[1] ? 1 : 0) + 1),
+ );
+ // Always there
+ expect(find.text('1'), findsOneWidget);
+ if (e[0]) {
+ // Minimum included
+ expect(find.text('0.5'), findsOneWidget);
+ }
+ if (e[1]) {
+ // Maximum included
+ expect(find.text('1.9'), findsOneWidget);
+ }
+ }
+ },
+ );
+}