Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
cc274b1
feat: gauge chart
glemartret May 15, 2025
dfc3c1d
fix: leaf type
glemartret May 15, 2025
8f66876
chore: revert unchanged files
glemartret Aug 21, 2025
a7a740d
chore: revert unchanged files
glemartret Aug 21, 2025
f69691f
chore: coverage
glemartret Aug 24, 2025
98df9d4
feat: merge SimpleGaugeColor and VariableGaugeColor in GaugeColor
glemartret Feb 13, 2026
85fc523
feat: update gauge chart implementation to use startDegreeOffset and …
glemartret Feb 13, 2026
57a81c5
Merge branch 'main' into glemartret/main
imaNNeo Apr 20, 2026
dade3f5
Add some changes in GaugeChart to address the PR reviews #1933
imaNNeo Apr 20, 2026
97b446b
feat: Multi-ring gauge chart with progress rings, zones, and zonesSpace
imaNNeo Apr 20, 2026
31faeab
Update gauge_chart_example4
imaNNeo Apr 20, 2026
4bca35d
fix: Carve zonesSpace as perpendicular strips for uniform gap width
imaNNeo Apr 20, 2026
18314d2
fix: Preserve StrokeCap.round on internal zone boundaries after carve
imaNNeo Apr 20, 2026
fdf698c
Minor change on gauge_chart_sample4
imaNNeo Apr 20, 2026
f3d5d31
Consider threshold in the gauge chart when touching the gauge rings
imaNNeo Apr 20, 2026
9b5bfe6
Improve gauge chart samples
imaNNeo Apr 20, 2026
b55151f
Add gauge_chart_sample4
imaNNeo Apr 20, 2026
9a7c06c
feat: Add ticks and labels to GaugeChart
imaNNeo Apr 21, 2026
d7da179
feat: Add pointers to the GaugeChart
imaNNeo Apr 21, 2026
b6380fe
Update gauge_chart_sample4 to add a slider
imaNNeo Apr 21, 2026
03be295
test: Cover remaining uncovered lines on gauge classes
imaNNeo Apr 21, 2026
909d518
Add GaugeTickInfo in the tick painter to allow more customization
imaNNeo Apr 29, 2026
5dbe795
Simplify the lerp functionality in GaugePointerPainter
imaNNeo Apr 29, 2026
aa1ef0c
Change Exception to StateError
imaNNeo Apr 29, 2026
5db8e81
Fix test issue
imaNNeo Apr 29, 2026
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
3 changes: 3 additions & 0 deletions example/assets/icons/ic_gauge_chart.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/url_launcher_ios/ios"

SPEC CHECKSUMS:
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe

PODFILE CHECKSUM: 0dbd5a87e0ace00c9610d2037ac22083a01f861d
PODFILE CHECKSUM: c4c93c5f6502fe2754f48404d3594bf779584011

COCOAPODS: 1.16.2
207 changes: 48 additions & 159 deletions example/ios/Runner.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
Expand Down
2 changes: 2 additions & 0 deletions example/lib/presentation/resources/app_assets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class AppAssets {
return 'assets/icons/ic_radar_chart.svg';
case ChartType.candlestick:
return 'assets/icons/ic_candle_chart.svg';
case ChartType.gauge:
return 'assets/icons/ic_gauge_chart.svg';
}
}

Expand Down
2 changes: 2 additions & 0 deletions example/lib/presentation/resources/app_colors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ class AppColors {
static const Color mainGridLineColor = Colors.white10;
static const Color borderColor = Colors.white54;
static const Color gridLinesColor = Color(0x11FFFFFF);
static const Color grey = Color(0xFFD9D9D9);

static const Color contentColorBlack = Colors.black;
static const Color contentColorWhite = Colors.white;
static const Color contentColorWhite12 = Colors.white12;
static const Color contentColorBlue = Color(0xFF2196F3);
static const Color contentColorYellow = Color(0xFFFFC300);
static const Color contentColorOrange = Color(0xFFFF683B);
Expand Down
23 changes: 11 additions & 12 deletions example/lib/presentation/samples/bar/bar_chart_sample1.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,18 +138,17 @@ class BarChartSample1State extends State<BarChartSample1> {
}

List<BarChartGroupData> showingGroups() => List.generate(
7,
(i) => switch (i) {
0 => makeGroupData(0, 5, isTouched: i == touchedIndex),
1 => makeGroupData(1, 6.5, isTouched: i == touchedIndex),
2 => makeGroupData(2, 5, isTouched: i == touchedIndex),
3 => makeGroupData(3, 7.5, isTouched: i == touchedIndex),
4 => makeGroupData(4, 9, isTouched: i == touchedIndex),
5 => makeGroupData(5, 11.5, isTouched: i == touchedIndex),
6 => makeGroupData(6, 6.5, isTouched: i == touchedIndex),
_ => throw Error(),
}
);
7,
(i) => switch (i) {
0 => makeGroupData(0, 5, isTouched: i == touchedIndex),
1 => makeGroupData(1, 6.5, isTouched: i == touchedIndex),
2 => makeGroupData(2, 5, isTouched: i == touchedIndex),
3 => makeGroupData(3, 7.5, isTouched: i == touchedIndex),
4 => makeGroupData(4, 9, isTouched: i == touchedIndex),
5 => makeGroupData(5, 11.5, isTouched: i == touchedIndex),
6 => makeGroupData(6, 6.5, isTouched: i == touchedIndex),
_ => throw Error(),
});

BarChartData mainBarData() {
return BarChartData(
Expand Down
6 changes: 6 additions & 0 deletions example/lib/presentation/samples/chart_sample.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,9 @@ class CandlestickChartSample extends ChartSample {
@override
ChartType get type => ChartType.candlestick;
}

class GaugeChartSample extends ChartSample {
GaugeChartSample(super.number, super.builder);
@override
ChartType get type => ChartType.gauge;
}
12 changes: 11 additions & 1 deletion example/lib/presentation/samples/chart_samples.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import 'package:fl_chart_app/presentation/samples/candlestick/candlestick_chart_sample1.dart';
import 'package:fl_chart_app/presentation/samples/gauge/gauge_chart_sample1.dart';
import 'package:fl_chart_app/presentation/samples/gauge/gauge_chart_sample3.dart';
import 'package:fl_chart_app/presentation/samples/gauge/gauge_chart_sample4.dart';
import 'package:fl_chart_app/util/app_helper.dart';

import 'bar/bar_chart_sample1.dart';
Expand All @@ -10,6 +13,7 @@ import 'bar/bar_chart_sample6.dart';
import 'bar/bar_chart_sample7.dart';
import 'bar/bar_chart_sample8.dart';
import 'chart_sample.dart';
import 'gauge/gauge_chart_sample2.dart';
import 'line/line_chart_sample1.dart';
import 'line/line_chart_sample10.dart';
import 'line/line_chart_sample11.dart';
Expand Down Expand Up @@ -73,6 +77,12 @@ class ChartSamples {
],
ChartType.candlestick: [
CandlestickChartSample(1, (context) => const CandlestickChartSample1()),
]
],
ChartType.gauge: [
GaugeChartSample(1, (context) => const GaugeChartSample1()),
GaugeChartSample(2, (context) => const GaugeChartSample2()),
GaugeChartSample(3, (context) => const GaugeChartSample3()),
GaugeChartSample(4, (context) => const GaugeChartSample4()),
],
};
}
191 changes: 191 additions & 0 deletions example/lib/presentation/samples/gauge/gauge_chart_sample1.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import 'dart:math';
import 'dart:ui';

import 'package:fl_chart/fl_chart.dart';
import 'package:fl_chart_app/presentation/resources/app_resources.dart';
import 'package:flutter/material.dart';

class GaugeChartSample1 extends StatefulWidget {
const GaugeChartSample1({super.key});

@override
State<StatefulWidget> createState() => GaugeChartSample1State();
}

class GaugeChartSample1State extends State<GaugeChartSample1> {
double _value = 0.5;

@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(28),
child: Column(
children: [
SizedBox(
width: 250,
height: 250,
child: Stack(
children: [
GaugeChart(
GaugeChartData.progress(
value: _value,
color: AppColors.contentColorPurple,
width: 30,
backgroundColor:
AppColors.contentColorPurple.withValues(alpha: 0.2),
startDegreeOffset: -200,
sweepAngle: 220,
touchData: GaugeTouchData(enabled: true),
ticks: GaugeTicks(
position: GaugeTickPosition.center,
count: 11,
offset: 0,
painter: _MyCustomGaugeMinMaxTickPainter(
color: AppColors.contentColorWhite,
radius: 5,
minIndex: 2,
maxIndex: 8,
minMaxLineLength: 40,
minMaxLineStrokeWidth: 4,
),
checkToShowTick: GaugeTicks.hideEndpoints,
),
pointers: [
// Needle extending from center toward the current value.
GaugePointer(
value: _value,
painter: GaugePointerNeedlePainter(
length: 88,
width: 20,
tailLength: 40,
color: AppColors.contentColorWhite,
),
),
// Pivot cap sitting at the gauge center, on top of the
// needle's base — just a second pointer with a small
// circle painter (anchorRadius: 0).
GaugePointer(
value: 0.3,
painter: GaugePointerCirclePainter(
radius: 24,
color: AppColors.contentColorBlack,
strokeWidth: 2,
strokeColor: AppColors.contentColorWhite,
),
),
],
),
),
Center(
child: Text(
"${(_value * 100).toInt()}",
style: TextStyle(
color: AppColors.contentColorWhite,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
)
],
),
),
Slider(value: _value, onChanged: (v) => setState(() => _value = v)),
],
),
);
}
}

class _MyCustomGaugeMinMaxTickPainter extends GaugeTickCirclePainter {
_MyCustomGaugeMinMaxTickPainter({
super.color,
super.radius,
required this.minIndex,
required this.maxIndex,
required this.minMaxLineLength,
required this.minMaxLineStrokeWidth,
this.textOffset = 6,
this.minTextStyle = const TextStyle(
color: Colors.green,
fontSize: 12,
fontWeight: FontWeight.bold,
),
this.maxTextStyle = const TextStyle(
color: Colors.red,
fontSize: 12,
fontWeight: FontWeight.bold,
),
}) : super();

final int minIndex;
final int maxIndex;
final double minMaxLineLength;
final double minMaxLineStrokeWidth;
final double textOffset;
final TextStyle minTextStyle;
final TextStyle maxTextStyle;

final _paint = Paint();

@override
void draw(Canvas canvas, GaugeTickInfo tickInfo) {
super.draw(canvas, tickInfo);

if (tickInfo.index != minIndex && tickInfo.index != maxIndex) {
return;
}
final isMin = tickInfo.index == minIndex;
final text = isMin ? "Min" : "Max";
final textStyle = isMin ? minTextStyle : maxTextStyle;
final rotation = isMin ? pi : 0.0;
final textPainter = TextPainter(
text: TextSpan(text: text, style: textStyle),
textDirection: TextDirection.ltr,
)..layout();
// 180 degrees
canvas.rotate(rotation);
final offsetX = isMin
? 0 + textPainter.width + textOffset
: 0 - textPainter.width - minMaxLineLength / 2 - textOffset;
textPainter.paint(canvas, Offset(offsetX, 0 - textPainter.height / 2));
_paint.color = textStyle.color!;
_paint.strokeWidth = minMaxLineStrokeWidth;
canvas.drawLine(
Offset(0 - minMaxLineLength / 2, 0),
Offset(0 + minMaxLineLength / 2, 0),
_paint,
);
}

@override
GaugeTickPainter lerp(GaugeTickPainter b, double t) {
if (b is! _MyCustomGaugeMinMaxTickPainter) {
return b;
}
return _MyCustomGaugeMinMaxTickPainter(
color: Color.lerp(color, b.color, t)!,
radius: lerpDouble(radius, b.radius, t)!,
minIndex: minIndex,
maxIndex: maxIndex,
minMaxLineLength: lerpDouble(minMaxLineLength, b.minMaxLineLength, t)!,
minMaxLineStrokeWidth:
lerpDouble(minMaxLineStrokeWidth, b.minMaxLineStrokeWidth, t)!,
textOffset: lerpDouble(textOffset, b.textOffset, t)!,
minTextStyle: TextStyle.lerp(minTextStyle, b.minTextStyle, t)!,
maxTextStyle: TextStyle.lerp(maxTextStyle, b.maxTextStyle, t)!,
);
}

@override
List<Object?> get props =>
[
super.props,
minIndex,
maxIndex,
minMaxLineLength,
minMaxLineStrokeWidth,
textOffset,
minTextStyle,
maxTextStyle,
];
}
Loading
Loading