diff --git a/example/assets/icons/ic_gauge_chart.svg b/example/assets/icons/ic_gauge_chart.svg
new file mode 100644
index 000000000..0147dab82
--- /dev/null
+++ b/example/assets/icons/ic_gauge_chart.svg
@@ -0,0 +1,3 @@
+
diff --git a/example/lib/presentation/resources/app_assets.dart b/example/lib/presentation/resources/app_assets.dart
index 3575724f8..c3d8aca33 100644
--- a/example/lib/presentation/resources/app_assets.dart
+++ b/example/lib/presentation/resources/app_assets.dart
@@ -13,6 +13,8 @@ class AppAssets {
return 'assets/icons/ic_scatter_chart.svg';
case ChartType.radar:
return 'assets/icons/ic_radar_chart.svg';
+ case ChartType.gauge:
+ return 'assets/icons/ic_gauge_chart.svg';
}
}
diff --git a/example/lib/presentation/samples/chart_sample.dart b/example/lib/presentation/samples/chart_sample.dart
index 6d753be25..83ad72c41 100644
--- a/example/lib/presentation/samples/chart_sample.dart
+++ b/example/lib/presentation/samples/chart_sample.dart
@@ -40,3 +40,9 @@ class RadarChartSample extends ChartSample {
@override
ChartType get type => ChartType.radar;
}
+
+class GaugeChartSample extends ChartSample {
+ GaugeChartSample(super.number, super.builder);
+ @override
+ ChartType get type => ChartType.gauge;
+}
diff --git a/example/lib/presentation/samples/chart_samples.dart b/example/lib/presentation/samples/chart_samples.dart
index 4114cd5f6..fb54df010 100644
--- a/example/lib/presentation/samples/chart_samples.dart
+++ b/example/lib/presentation/samples/chart_samples.dart
@@ -1,3 +1,4 @@
+import 'package:fl_chart_app/presentation/samples/gauge/gauge_chart_sample1.dart';
import 'package:fl_chart_app/util/app_helper.dart';
import 'bar/bar_chart_sample1.dart';
@@ -62,5 +63,8 @@ class ChartSamples {
ChartType.radar: [
RadarChartSample(1, (context) => RadarChartSample1()),
],
+ ChartType.gauge: [
+ GaugeChartSample(1, (context) => const GaugeChartSample1()),
+ ],
};
}
diff --git a/example/lib/presentation/samples/gauge/gauge_chart_sample1.dart b/example/lib/presentation/samples/gauge/gauge_chart_sample1.dart
new file mode 100644
index 000000000..219209233
--- /dev/null
+++ b/example/lib/presentation/samples/gauge/gauge_chart_sample1.dart
@@ -0,0 +1,63 @@
+import 'package:fl_chart_app/presentation/resources/app_resources.dart';
+import 'package:fl_chart/fl_chart.dart';
+import 'package:flutter/material.dart';
+
+class GaugeChartSample1 extends StatefulWidget {
+ const GaugeChartSample1({super.key});
+
+ @override
+ State createState() => GaugeChartSample1State();
+}
+
+class GaugeChartSample1State extends State {
+ double _value = 0.7;
+ bool _isSelected = false;
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.all(28),
+ child: Column(
+ children: [
+ SizedBox(
+ width: 250,
+ height: 250,
+ child: GaugeChart(
+ GaugeChartData(
+ value: _value,
+ valueColor: VariableGaugeColor(
+ colors: [
+ AppColors.contentColorYellow,
+ AppColors.contentColorBlue,
+ AppColors.contentColorRed
+ ],
+ limits: [0.35, 0.5],
+ ),
+ backgroundColor: AppColors.contentColorPurple
+ .withOpacity(_isSelected ? 0.2 : 1),
+ strokeWidth: 30,
+ startAngle: 45,
+ endAngle: -225,
+ strokeCap: StrokeCap.round,
+ ticks: const GaugeTicks(
+ count: 11,
+ color: AppColors.contentColorCyan,
+ radius: 5,
+ position: GaugeTickPosition.inner,
+ margin: 5,
+ ),
+ touchData: GaugeTouchData(
+ enabled: true,
+ touchCallback: (_, value) => setState(() {
+ _isSelected = value?.spot != null;
+ }),
+ ),
+ ),
+ ),
+ ),
+ Slider(value: _value, onChanged: (v) => setState(() => _value = v)),
+ ],
+ ),
+ );
+ }
+}
diff --git a/example/lib/util/app_helper.dart b/example/lib/util/app_helper.dart
index 38c5ac882..4d6f2a7d8 100644
--- a/example/lib/util/app_helper.dart
+++ b/example/lib/util/app_helper.dart
@@ -1,7 +1,7 @@
import 'package:fl_chart_app/presentation/resources/app_resources.dart';
import 'package:fl_chart_app/urls.dart';
-enum ChartType { line, bar, pie, scatter, radar }
+enum ChartType { line, bar, pie, scatter, radar, gauge }
extension ChartTypeExtension on ChartType {
String get displayName => '$simpleName Chart';
@@ -12,6 +12,7 @@ extension ChartTypeExtension on ChartType {
ChartType.pie => 'Pie',
ChartType.scatter => 'Scatter',
ChartType.radar => 'Radar',
+ ChartType.gauge => 'Gauge',
};
String get documentationUrl => Urls.getChartDocumentationUrl(this);
diff --git a/lib/fl_chart.dart b/lib/fl_chart.dart
index d3597fb81..95cea2b67 100644
--- a/lib/fl_chart.dart
+++ b/lib/fl_chart.dart
@@ -7,6 +7,8 @@ export 'src/chart/base/axis_chart/axis_chart_data.dart';
export 'src/chart/base/axis_chart/axis_chart_widgets.dart';
export 'src/chart/base/base_chart/base_chart_data.dart';
export 'src/chart/base/base_chart/fl_touch_event.dart';
+export 'src/chart/gauge_chart/gauge_chart.dart';
+export 'src/chart/gauge_chart/gauge_chart_data.dart';
export 'src/chart/line_chart/line_chart.dart';
export 'src/chart/line_chart/line_chart_data.dart';
export 'src/chart/pie_chart/pie_chart.dart';
diff --git a/lib/src/chart/gauge_chart/gauge_chart.dart b/lib/src/chart/gauge_chart/gauge_chart.dart
new file mode 100644
index 000000000..c73816025
--- /dev/null
+++ b/lib/src/chart/gauge_chart/gauge_chart.dart
@@ -0,0 +1,62 @@
+import 'package:fl_chart/src/chart/gauge_chart/gauge_chart_data.dart';
+import 'package:fl_chart/src/chart/gauge_chart/gauge_chart_renderer.dart';
+import 'package:flutter/widgets.dart';
+
+class GaugeChart extends ImplicitlyAnimatedWidget {
+ /// [data] determines how the [GaugeChart] should be look like,
+ /// when you make any change in the [GaugeChartData], it updates
+ /// new values with animation, and duration is [swapAnimationDuration].
+ /// also you can change the [swapAnimationCurve]
+ /// which default is [Curves.linear].
+ const GaugeChart(
+ this.data, {
+ this.chartRendererKey,
+ super.key,
+ Duration swapAnimationDuration = const Duration(milliseconds: 150),
+ Curve swapAnimationCurve = Curves.linear,
+ }) : super(
+ duration: swapAnimationDuration,
+ curve: swapAnimationCurve,
+ );
+
+ /// Determines how the [GaugeChartData] should be look like.
+ final GaugeChartData data;
+
+ /// We pass this key to our renderers which are supposed to
+ /// render the chart itself (without anything around the chart).
+ final Key? chartRendererKey;
+
+ /// Creates a [_GaugeChartState]
+ @override
+ _GaugeChartState createState() => _GaugeChartState();
+}
+
+class _GaugeChartState extends AnimatedWidgetBaseState {
+ /// we handle under the hood animations (implicit animations) via this tween,
+ /// it lerps between the old [GaugeChartData] to the new one.
+ GaugeChartDataTween? _gaugeChartDataTween;
+
+ @override
+ Widget build(BuildContext context) {
+ final showingData = _getData();
+
+ return GaugeChartLeaf(
+ data: _gaugeChartDataTween!.evaluate(animation),
+ targetData: showingData,
+ );
+ }
+
+ GaugeChartData _getData() {
+ return widget.data;
+ }
+
+ @override
+ void forEachTween(TweenVisitor visitor) {
+ _gaugeChartDataTween = visitor(
+ _gaugeChartDataTween,
+ widget.data,
+ (dynamic value) =>
+ GaugeChartDataTween(begin: value as GaugeChartData, end: widget.data),
+ ) as GaugeChartDataTween?;
+ }
+}
diff --git a/lib/src/chart/gauge_chart/gauge_chart_data.dart b/lib/src/chart/gauge_chart/gauge_chart_data.dart
new file mode 100644
index 000000000..bd7a0aed9
--- /dev/null
+++ b/lib/src/chart/gauge_chart/gauge_chart_data.dart
@@ -0,0 +1,268 @@
+import 'dart:ui';
+
+import 'package:equatable/equatable.dart';
+import 'package:fl_chart/fl_chart.dart';
+import 'package:fl_chart/src/utils/lerp.dart';
+import 'package:flutter/widgets.dart';
+
+class ColoredTick with EquatableMixin {
+ const ColoredTick(this.position, this.color);
+ final double position;
+ final Color color;
+
+ @override
+ List