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

Giving users control over min/maxIncluded for axis titles by including them into SideTitles (addresses #906) #1686

Merged
merged 8 commits into from
Aug 26, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## 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
* **Improvement** (by @sczesla) Allow users to control minIncluded and maxIncluded using SideTitles, #906

## 0.68.0
* **Improvement** (by @imaNNeo) Update LineChartSample6 to implement a way to show a tooltip on a single spot, #1620
Expand Down
18 changes: 18 additions & 0 deletions lib/src/chart/base/axis_chart/axis_chart_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -178,13 +180,23 @@ 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(
showTitles: b.showTitles,
getTitlesWidget: b.getTitlesWidget,
reservedSize: lerpDouble(a.reservedSize, b.reservedSize, t)!,
interval: lerpDouble(a.interval, b.interval, t),
minIncluded: b.minIncluded,
maxIncluded: b.maxIncluded,
);
}

Expand All @@ -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,
);
}

Expand All @@ -211,6 +227,8 @@ class SideTitles with EquatableMixin {
getTitlesWidget,
reservedSize,
interval,
minIncluded,
maxIncluded,
];
}

Expand Down
3 changes: 3 additions & 0 deletions lib/src/chart/base/axis_chart/axis_chart_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
Expand Down
3 changes: 3 additions & 0 deletions repo_files/documentations/base_chart.md
Original file line number Diff line number Diff line change
Expand Up @@ -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|
Expand Down
81 changes: 81 additions & 0 deletions test/chart/base/axis_chart/side_titles/side_titles_test.dart
Original file line number Diff line number Diff line change
@@ -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);
}
}
},
);
}