summaryrefslogtreecommitdiffstats
path: root/lib/screen
diff options
context:
space:
mode:
authorAakash Solanki <tech2aks@gmail.com>2022-08-31 15:23:53 +0200
committerAakash Solanki <tech2aks@gmail.com>2022-09-14 11:50:03 +0200
commite39f2a69fde316b4e260c151757fb739494fbd56 (patch)
tree3ea8a65eee101457264d0000b5bcf122d428b0b8 /lib/screen
parent5957cfa0609ff57adfafa4538fb151d00f3c72e3 (diff)
Upload Flutter Instrument Cluster app
Instrument Cluster demo app which shows speedometer tachometer guages, temperature and fuel bars and some indicators like turn indicators, engine malfunction, lights, cruise control, lane assist. KUKSA.VAL is the data source for the widgets. This app depends on several plugins and all the plugins have an OSI-approved license. Bug-AGL: SPEC-4543 Change-Id: I2698c66f9d8d824690ae7e567ca7c93ceeb17e08 Signed-off-by: Aakash Solanki <tech2aks@gmail.com>
Diffstat (limited to 'lib/screen')
-rw-r--r--lib/screen/home.dart348
-rw-r--r--lib/screen/paints/arc_painter.dart116
-rw-r--r--lib/screen/paints/bottombar_paint.dart48
-rw-r--r--lib/screen/paints/guage_paint.dart144
-rw-r--r--lib/screen/paints/topbar_paint.dart42
-rw-r--r--lib/screen/widgets/guages/guage_props.dart48
-rw-r--r--lib/screen/widgets/guages/guage_widget.dart109
-rw-r--r--lib/screen/widgets/guages/rpm_guage_animation_wrapper.dart51
-rw-r--r--lib/screen/widgets/guages/speed_guage_animation_wrapper.dart66
-rw-r--r--lib/screen/widgets/left_bar.dart40
-rw-r--r--lib/screen/widgets/left_signal.dart31
-rw-r--r--lib/screen/widgets/performance_mode.dart32
-rw-r--r--lib/screen/widgets/right_bar.dart39
-rw-r--r--lib/screen/widgets/right_signal.dart31
-rw-r--r--lib/screen/widgets/signals.dart66
-rw-r--r--lib/screen/widgets/turn_signal.dart64
16 files changed, 1275 insertions, 0 deletions
diff --git a/lib/screen/home.dart b/lib/screen/home.dart
new file mode 100644
index 0000000..70bc5c6
--- /dev/null
+++ b/lib/screen/home.dart
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_cluster_dashboard/map/navigationHome.dart';
+import 'package:flutter_cluster_dashboard/provider.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/guage_props.dart';
+import 'package:flutter_cluster_dashboard/screen/paints/bottombar_paint.dart';
+import 'package:flutter_cluster_dashboard/screen/paints/topbar_paint.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/rpm_guage_animation_wrapper.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/left_bar.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/performance_mode.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/right_bar.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/speed_guage_animation_wrapper.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/signals.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/turn_signal.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_provider.dart';
+import 'package:intl/intl.dart';
+
+class Home extends ConsumerWidget {
+ const Home({Key? key}) : super(key: key);
+ GuageColors? getGuageColor(String mode) {
+ return (mode == "economy")
+ ? GuageProps.ecoModeColor
+ : (mode == "sport")
+ ? GuageProps.sportModeColor
+ : null;
+ }
+
+ String addZero(int val) {
+ String res = val.toString();
+ if (res.length < 2) {
+ if (res.isEmpty) {
+ return "00";
+ } else if (res.length == 1) {
+ return "0$res";
+ }
+ }
+ return res;
+ }
+
+ double calcPadding(double value, double height) {
+ // values wrt to values at 720 height
+ return (value * height) / 720;
+ }
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final vehicle = ref.watch(vehicleSignalProvider);
+ final clock = ref.watch(clockProvider);
+ final windowHeight = MediaQuery.of(context).size.height;
+ final windowWidth = MediaQuery.of(context).size.width;
+
+ double screenHeight = windowHeight;
+ double screenWidth = windowWidth;
+
+ double ratHeight = (windowWidth * 9) / 16;
+ double ratWidth = (windowHeight * 16) / 9;
+
+ if (ratWidth <= windowWidth) {
+ screenHeight = windowHeight;
+ screenWidth = ratWidth;
+ } else {
+ screenHeight = ratHeight;
+ screenWidth = windowWidth;
+ }
+
+ return Scaffold(
+ backgroundColor: GuageProps.bgColor,
+ body: SafeArea(
+ child: Center(
+ child: Center(
+ child: SizedBox(
+ width: screenWidth,
+ height: screenHeight,
+ child: Flex(
+ direction: Axis.vertical,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ //TopBarPainter
+ Flexible(
+ flex: 1,
+ child: Stack(
+ children: [
+ TurnSignal(
+ screenHeight: screenHeight,
+ isLefton: vehicle.isLeftIndicator,
+ isRighton: vehicle.isRightIndicator,
+ ),
+ Flex(
+ direction: Axis.horizontal,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ // mid section of top bar
+ Flexible(
+ flex: 1,
+ child: SizedBox(
+ width: (400 * screenHeight) / 480,
+ height: (65 * screenHeight) / 480,
+ child: CustomPaint(
+ painter: TopBarPainter(),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ DateFormat('EEEE')
+ .format(clock)
+ .substring(0, 3),
+ style: TextStyle(
+ color: const Color.fromARGB(
+ 255, 184, 183, 183),
+ fontSize: (20 * screenHeight) / 480,
+ fontWeight: FontWeight.bold),
+ ),
+ SizedBox(
+ width: (40 * screenHeight) / 480),
+ Text(
+ "${clock.hour}:${addZero(clock.minute)}",
+ style: TextStyle(
+ color: const Color.fromARGB(
+ 255, 255, 255, 255),
+ fontSize: (30 * screenHeight) / 480,
+ fontWeight: FontWeight.bold),
+ ),
+ SizedBox(
+ width: (30 * screenHeight) / 480),
+ Text(
+ "${vehicle.ambientAirTemp} ${"\u00B0"}C",
+ style: TextStyle(
+ color: const Color.fromARGB(
+ 255, 184, 183, 183),
+ fontSize: (20 * screenHeight) / 480,
+ fontWeight: FontWeight.bold),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ // mid section
+ Flexible(
+ flex: 4,
+ fit: FlexFit.tight,
+ child: Stack(
+ children: [
+ //left and right arc
+ Padding(
+ padding: EdgeInsets.fromLTRB(
+ calcPadding(60, screenHeight),
+ calcPadding(60, screenHeight),
+ calcPadding(60, screenHeight),
+ 0),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Stack(
+ alignment: Alignment.bottomRight,
+ children: [
+ LeftArc(screenHeight: screenHeight),
+ Positioned(
+ child: Image.asset(
+ "images/temperature-icon.png",
+ color: Colors.white,
+ width: (13 * screenHeight) / 480,
+ ),
+ ),
+ ],
+ ),
+ Stack(
+ alignment: Alignment.bottomLeft,
+ children: [
+ RightArc(screenHeight: screenHeight),
+ Positioned(
+ child: Image.asset(
+ "images/fuel-icon.png",
+ color: Colors.white,
+ width: (18 * screenHeight) / 480,
+ ))
+ ],
+ ),
+ ],
+ ),
+ ),
+ //logo area
+ Padding(
+ padding: EdgeInsets.fromLTRB(
+ calcPadding(60, screenHeight),
+ calcPadding(10, screenHeight),
+ calcPadding(60, screenHeight),
+ 0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ SizedBox(
+ width: (550 * screenHeight) / 720,
+ height: (450 * screenHeight) / 720,
+ child: Flex(
+ direction: Axis.vertical,
+ mainAxisAlignment:
+ MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ // performance mode
+ Flexible(
+ flex: 1,
+ child: PerformanceMode(
+ size: Size((90 * screenHeight) / 480,
+ (20 * screenHeight) / 480),
+ mode: vehicle.performanceMode),
+ ),
+ // logo
+ Flexible(
+ flex: 6,
+ fit: FlexFit.tight,
+ child: SizedBox(
+ width: (330 * screenHeight) / 720,
+ child: (vehicle.isSteeringInfo)
+ ? const NavigationHome()
+ : Padding(
+ padding: EdgeInsets.all(
+ (48.0 * screenHeight) /
+ 720),
+ child: Image.asset(
+ "images/logo_agl.png",
+ width:
+ (90 * screenHeight) / 480,
+ color: Colors.grey.shade600,
+ ),
+ ),
+ ),
+ ),
+ const Flexible(
+ flex: 1,
+ child: SizedBox(),
+ )
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ //warning signals
+ Padding(
+ padding: EdgeInsets.fromLTRB(
+ calcPadding(0, screenHeight),
+ calcPadding(430, screenHeight),
+ calcPadding(0, screenHeight),
+ 0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Signals(
+ screenHeight: screenHeight,
+ vehicle: vehicle,
+ ),
+ ],
+ ),
+ ),
+ // guages
+ Padding(
+ padding: EdgeInsets.fromLTRB(
+ calcPadding(70, screenHeight),
+ calcPadding(30, screenHeight),
+ calcPadding(70, screenHeight),
+ 0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ // Speed Guage
+ SpeedGauge(
+ screenHeight: screenHeight,
+ guageColor:
+ getGuageColor(vehicle.performanceMode),
+ ),
+ //RPM Guage
+ RPMGauge(
+ screenHeight: screenHeight,
+ guageColor:
+ getGuageColor(vehicle.performanceMode),
+ ),
+ ],
+ ),
+ )
+ ],
+ ),
+ ),
+ // bottomBarPainter
+ Flexible(
+ flex: 1,
+ child: SizedBox(
+ width: (400 * screenHeight) / 480,
+ height: (50 * screenHeight) / 480,
+ child: CustomPaint(
+ size: Size((400 * screenHeight) / 480,
+ (50 * screenHeight) / 480),
+ painter: BottomBarPainter(),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ (vehicle.selectedGear == Gear.parking)
+ ? Text("P",
+ style: GuageProps.activeGearIconStyle(
+ screenHeight))
+ : Text("P",
+ style: GuageProps.gearIconStyle(
+ screenHeight)),
+ (vehicle.selectedGear == Gear.reverse)
+ ? Text("R",
+ style: GuageProps.activeGearIconStyle(
+ screenHeight))
+ : Text("R",
+ style: GuageProps.gearIconStyle(
+ screenHeight)),
+ (vehicle.selectedGear == Gear.neutral)
+ ? Text("N",
+ style: GuageProps.activeGearIconStyle(
+ screenHeight))
+ : Text("N",
+ style: GuageProps.gearIconStyle(
+ screenHeight)),
+ (vehicle.selectedGear == Gear.drive)
+ ? Text("D",
+ style: GuageProps.activeGearIconStyle(
+ screenHeight))
+ : Text("D",
+ style: GuageProps.gearIconStyle(
+ screenHeight)),
+ ]),
+ ),
+ )),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/screen/paints/arc_painter.dart b/lib/screen/paints/arc_painter.dart
new file mode 100644
index 0000000..66e38d4
--- /dev/null
+++ b/lib/screen/paints/arc_painter.dart
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:math';
+import 'package:flutter/material.dart';
+
+class LeftPainter extends CustomPainter {
+ LeftPainter(
+ {required this.radi,
+ required this.currentValue,
+ required this.bottomPadding,
+ required this.color});
+
+ late final double radi;
+ late final double currentValue;
+ late final double bottomPadding;
+ late final Color color;
+
+ Offset getLeftPoints(Size size, double radius, double value) {
+ final double diam = 2 * radius;
+ final double arcHalfAngle = asin(size.height / diam); //thetha
+ final double currentAngle = (arcHalfAngle * value) / 50; //alpha
+ return Offset(
+ (radi * cos(arcHalfAngle)) +
+ (size.width) -
+ (radi * cos(arcHalfAngle - currentAngle)),
+ (radi * sin(arcHalfAngle)) + (radi * sin(arcHalfAngle - currentAngle)));
+ }
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ Offset startPoint = getLeftPoints(size, radi, bottomPadding);
+
+ final paint = Paint()
+ ..color = const Color.fromARGB(255, 49, 48, 48)
+ ..style = PaintingStyle.stroke
+ ..strokeCap = StrokeCap.round
+ ..strokeWidth = radi / 15;
+ final paint2 = Paint()
+ ..color = color
+ ..strokeCap = StrokeCap.round
+ ..style = PaintingStyle.stroke
+ ..strokeWidth = radi / 15;
+
+ final path = Path()
+ ..moveTo(startPoint.dx, startPoint.dy)
+ ..arcToPoint(Offset(size.width, 0), radius: Radius.circular(radi));
+ canvas.drawPath(path, paint);
+ final path2 = Path()
+ ..moveTo(startPoint.dx, startPoint.dy)
+ ..arcToPoint(
+ getLeftPoints(size, radi,
+ bottomPadding + ((1 - (bottomPadding / 100)) * currentValue)),
+ radius: Radius.circular(radi));
+ canvas.drawPath(path, paint);
+ canvas.drawPath(path2, paint2);
+ }
+
+ @override
+ bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
+}
+
+class RightPainter extends CustomPainter {
+ RightPainter(
+ {required this.radi,
+ required this.currentValue,
+ required this.bottomPadding,
+ required this.color});
+ final double radi;
+ final double currentValue;
+ late final double bottomPadding;
+ late final Color color;
+
+ Offset getRightPoints(Size size, double radius, double value) {
+ final double diam = 2 * radius;
+ final double arcHalfAngle = asin(size.height / diam); //thetha
+ final double currentAngle = (arcHalfAngle * value) / 50; //alpha
+ return Offset(
+ (radi * cos(arcHalfAngle - currentAngle)) - (radi * cos(arcHalfAngle)),
+ (radi * sin(arcHalfAngle)) + (radi * sin(arcHalfAngle - currentAngle)));
+ }
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ Offset startPoint = getRightPoints(size, radi, bottomPadding);
+ final paint = Paint()
+ ..color = const Color.fromARGB(255, 49, 48, 48)
+ ..style = PaintingStyle.stroke
+ ..strokeCap = StrokeCap.round
+ ..strokeWidth = radi / 15;
+
+ final paint2 = Paint()
+ ..color = color
+ ..style = PaintingStyle.stroke
+ ..strokeCap = StrokeCap.round
+ ..strokeWidth = radi / 15;
+ final path = Path()
+ ..moveTo(startPoint.dx, startPoint.dy)
+ ..arcToPoint(
+ const Offset(0, 0),
+ radius: Radius.circular(radi),
+ clockwise: false,
+ );
+ final path2 = Path()
+ ..moveTo(startPoint.dx, startPoint.dy)
+ ..arcToPoint(
+ getRightPoints(size, radi,
+ bottomPadding + ((1 - (bottomPadding / 100)) * currentValue)),
+ radius: Radius.circular(radi),
+ clockwise: false);
+ canvas.drawPath(path, paint);
+ canvas.drawPath(path2, paint2);
+ }
+
+ @override
+ bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
+}
diff --git a/lib/screen/paints/bottombar_paint.dart b/lib/screen/paints/bottombar_paint.dart
new file mode 100644
index 0000000..85c129e
--- /dev/null
+++ b/lib/screen/paints/bottombar_paint.dart
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:math';
+import 'dart:ui' as ui;
+import 'package:flutter/material.dart';
+
+class BottomBarPainter extends CustomPainter {
+ @override
+ void paint(Canvas canvas, Size size) {
+ final double height = size.height;
+ const double angle = 40;
+
+ double x = height * tan((angle / 180) * pi);
+ double y = height;
+
+ final double width = size.width - 2 * x;
+ final paint = Paint()
+ ..color = const ui.Color.fromARGB(255, 36, 36, 36)
+ ..strokeWidth = 5
+ ..strokeCap = StrokeCap.round
+ ..style = PaintingStyle.stroke;
+ final paint2 = Paint()
+ ..style = PaintingStyle.fill
+ ..shader = ui.Gradient.linear(
+ Offset((x + width / 2), 0), Offset((x + width / 2), height), [
+ const ui.Color.fromARGB(255, 25, 24, 24),
+ Colors.black,
+ ]);
+
+ final path = Path()
+ ..moveTo(0, size.height)
+ ..lineTo(x, size.height - y)
+ ..lineTo(x + width, size.height - y)
+ ..lineTo(size.width, size.height);
+
+ final path2 = Path()
+ ..moveTo(0, size.height)
+ ..lineTo(x, size.height - y)
+ ..lineTo(x + width, size.height - y)
+ ..lineTo(size.width, size.height);
+
+ canvas.drawPath(path, paint);
+ canvas.drawPath(path2, paint2);
+ }
+
+ @override
+ bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
+}
diff --git a/lib/screen/paints/guage_paint.dart b/lib/screen/paints/guage_paint.dart
new file mode 100644
index 0000000..22f7544
--- /dev/null
+++ b/lib/screen/paints/guage_paint.dart
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'dart:math';
+import 'dart:ui' as ui;
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/guage_props.dart';
+
+class GuagePainter extends CustomPainter {
+ final double low, high;
+ double currentSpeed;
+ final Color? outPrimaryColor;
+ final Color? inPrimaryColor;
+ final Color? secondaryColor;
+ GuagePainter({
+ required this.low,
+ required this.high,
+ required this.currentSpeed,
+ this.outPrimaryColor,
+ this.inPrimaryColor,
+ this.secondaryColor,
+ });
+ @override
+ void paint(Canvas canvas, Size size) {
+ Offset center = Offset(size.width / 2, size.height / 2);
+ double radius = min(size.width / 2, size.height / 2);
+ double radius1 = radius - ((20 / 200) * (radius));
+ double speedAngle =
+ GuageProps.degToRad((GuageProps.majorAngle / high) * currentSpeed);
+
+ final zeroTickPaint = Paint()
+ ..strokeWidth = ((7 / 200) * (radius))
+ ..shader = ui.Gradient.radial(center, radius1,
+ [Colors.black, const Color.fromARGB(255, 244, 242, 231)], [1, 0.5]);
+ final maxTickPaint = Paint()
+ ..strokeWidth = ((7 / 200) * (radius))
+ ..shader = ui.Gradient.radial(center, radius1,
+ [Colors.black, const Color.fromARGB(255, 244, 242, 231)], [1, 0.5]);
+
+ final speedPathStrokePaint = Paint()
+ ..style = PaintingStyle.stroke
+ ..strokeWidth = ((7 / 200) * (radius))
+ ..shader = ui.Gradient.radial(
+ center, radius1, [Colors.black, Colors.white], [0.6, 1]);
+
+ final speedPathFillPaint = Paint()
+ ..style = PaintingStyle.fill
+ ..shader = ui.Gradient.radial(center, radius1, [
+ const ui.Color.fromARGB(0, 0, 0, 0),
+ secondaryColor ?? const Color.fromARGB(156, 226, 226, 200)
+ ], [
+ 0.8,
+ 1
+ ]);
+
+ final outerPathPaint = ui.Paint()
+ ..style = PaintingStyle.fill
+ ..shader = ui.Gradient.radial(
+ center,
+ radius,
+ [
+ Colors.black,
+ outPrimaryColor ?? const Color.fromARGB(255, 120, 120, 120)
+ ],
+ [0.8, 0.9],
+ );
+
+ final innerPathPaint = Paint()
+ ..style = PaintingStyle.fill
+ ..shader = ui.Gradient.radial(center, radius1, [
+ Colors.black,
+ inPrimaryColor ?? const Color.fromARGB(255, 67, 67, 67)
+ ], [
+ 0.65,
+ 0.9
+ ]);
+
+ final outerPathPaintRed = ui.Paint()
+ ..style = PaintingStyle.fill
+ ..shader = ui.Gradient.radial(
+ center,
+ radius,
+ [Colors.black, const Color.fromARGB(255, 187, 59, 57)],
+ [0.8, 0.9],
+ );
+
+ final innerPathPaintRed = Paint()
+ ..style = PaintingStyle.fill
+ ..shader = ui.Gradient.radial(center, radius1,
+ [Colors.black, const Color.fromARGB(255, 142, 35, 39)], [0.65, 0.9]);
+
+ for (double i = 0; i < 13; i++) {
+ double startAngle = GuageProps.degToRad(i * 20);
+ double gapAngle = GuageProps.degToRad(19);
+
+ var outerPath = Path();
+ outerPath.addArc(Rect.fromCircle(center: center, radius: radius),
+ startAngle, gapAngle);
+ outerPath.lineTo(center.dx, center.dy);
+ outerPath.close();
+
+ var innerPath = Path();
+ innerPath.addArc(Rect.fromCircle(center: center, radius: radius1),
+ startAngle, gapAngle);
+ innerPath.lineTo(center.dx, center.dy);
+ innerPath.close();
+ if (i >= 11) {
+ canvas.drawPath(outerPath, outerPathPaintRed);
+ canvas.drawPath(innerPath, innerPathPaintRed);
+ } else {
+ canvas.drawPath(outerPath, outerPathPaint);
+ canvas.drawPath(innerPath, innerPathPaint);
+ }
+ }
+
+ var speedStrokePath = Path();
+ speedStrokePath.moveTo(center.dx, center.dy);
+ speedStrokePath.addArc(
+ Rect.fromCircle(center: center, radius: radius), 0, speedAngle);
+ speedStrokePath.lineTo(center.dx, center.dy);
+
+ var speedFillPath = Path();
+ speedFillPath.addArc(
+ Rect.fromCircle(center: center, radius: radius), 0, speedAngle);
+ speedFillPath.lineTo(center.dx, center.dy);
+ speedFillPath.close();
+
+ canvas.drawPath(speedFillPath, speedPathFillPaint);
+ canvas.drawPath(speedStrokePath, speedPathStrokePaint);
+ canvas.drawLine(
+ center,
+ Offset(center.dx + (radius + ((3 / 200) * (radius))), center.dy),
+ zeroTickPaint);
+ canvas.drawLine(
+ center,
+ Offset(center.dx + (radius) * cos(GuageProps.majorAngleRad),
+ center.dy + (radius) * sin(GuageProps.majorAngleRad)),
+ maxTickPaint);
+ }
+
+ @override
+ bool shouldRepaint(covariant CustomPainter oldDelegate) {
+ return true;
+ }
+}
diff --git a/lib/screen/paints/topbar_paint.dart b/lib/screen/paints/topbar_paint.dart
new file mode 100644
index 0000000..f7c79e8
--- /dev/null
+++ b/lib/screen/paints/topbar_paint.dart
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:math';
+import 'dart:ui' as ui;
+import 'package:flutter/material.dart';
+
+class TopBarPainter extends CustomPainter {
+ @override
+ void paint(Canvas canvas, Size size) {
+ final double height = size.height;
+ const double angle = 40;
+
+ double x = height * tan((angle / 180) * pi);
+ double y = height;
+
+ final double width = size.width - 2 * x;
+ final paint = Paint()
+ ..color = const ui.Color.fromARGB(255, 49, 47, 47)
+ ..strokeWidth = 5
+ ..strokeCap = StrokeCap.round
+ ..style = PaintingStyle.stroke;
+ final paint2 = Paint()
+ ..style = PaintingStyle.fill
+ ..shader = ui.Gradient.linear(
+ Offset((x + width / 2), 0),
+ Offset((x + width / 2), height),
+ [Colors.black, const ui.Color.fromARGB(255, 32, 31, 31)]);
+ final path = Path()
+ ..lineTo(x, y)
+ ..lineTo(x + width, y)
+ ..lineTo(size.width, 0);
+ final path2 = Path()
+ ..lineTo(x, y)
+ ..lineTo(x + width, y)
+ ..lineTo(size.width, 0);
+ canvas.drawPath(path, paint);
+ canvas.drawPath(path2, paint2);
+ }
+
+ @override
+ bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
+}
diff --git a/lib/screen/widgets/guages/guage_props.dart b/lib/screen/widgets/guages/guage_props.dart
new file mode 100644
index 0000000..bb56a31
--- /dev/null
+++ b/lib/screen/widgets/guages/guage_props.dart
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:math';
+import 'package:flutter/material.dart';
+
+class GuageProps {
+ static GuageColors normalModeColor = GuageColors(
+ inPrimary: const Color.fromARGB(255, 67, 67, 67),
+ outPrimary: const Color.fromARGB(255, 120, 120, 120),
+ secondary: const Color.fromARGB(156, 226, 226, 200),
+ );
+ static GuageColors sportModeColor = GuageColors(
+ inPrimary: Colors.deepPurple,
+ outPrimary: Colors.blue,
+ secondary: const Color.fromARGB(214, 202, 202, 202));
+ static GuageColors ecoModeColor = GuageColors(
+ inPrimary: const Color.fromARGB(255, 85, 165, 87),
+ outPrimary: const Color.fromARGB(255, 40, 92, 42),
+ secondary: const Color.fromARGB(202, 194, 238, 195));
+ static double majorAngle = 260;
+ static double majorAngleRad = 260 * (pi / 180);
+ static double minorAngle = 360 - majorAngle;
+ static Color bgColor = const Color.fromARGB(255, 0, 0, 0);
+ static const leftLowColor = Color(0x000000ff);
+ static const leftHighColor = Color(0x00ff0000);
+
+ static double degToRad(double deg) => deg * (pi / 180.0);
+ static TextStyle gearIconStyle(screenHeight) {
+ return TextStyle(
+ color: const Color.fromARGB(255, 84, 83, 83),
+ fontSize: (20 * screenHeight) / 480,
+ fontWeight: FontWeight.bold);
+ }
+
+ static TextStyle activeGearIconStyle(screenHeight) {
+ return TextStyle(
+ color: Colors.white,
+ fontSize: (20 * screenHeight) / 480,
+ fontWeight: FontWeight.bold);
+ }
+}
+
+class GuageColors {
+ Color? inPrimary;
+ Color? outPrimary;
+ Color? secondary;
+ GuageColors({this.inPrimary, this.outPrimary, this.secondary});
+}
diff --git a/lib/screen/widgets/guages/guage_widget.dart b/lib/screen/widgets/guages/guage_widget.dart
new file mode 100644
index 0000000..fa43958
--- /dev/null
+++ b/lib/screen/widgets/guages/guage_widget.dart
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:math';
+import 'package:flutter/material.dart';
+import 'package:flutter_cluster_dashboard/screen/paints/guage_paint.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/guage_props.dart';
+
+class CustomGuage extends StatelessWidget {
+ const CustomGuage({
+ Key? key,
+ required this.mainValue,
+ required this.low,
+ required this.high,
+ required this.label,
+ this.zeroTickLabel,
+ this.maxTickLabel,
+ this.distanceBWTicks,
+ this.size,
+ this.distLabelTop,
+ this.distMainTop,
+ this.distTicksBottom,
+ this.inPrimaryColor,
+ this.outPrimaryColor,
+ this.secondaryColor,
+ }) : super(key: key);
+
+ final double mainValue;
+ final double low;
+ final double high;
+ final String label;
+ final String? zeroTickLabel;
+ final String? maxTickLabel;
+ final double? distanceBWTicks;
+ final double? distTicksBottom;
+ final double? distMainTop;
+ final double? distLabelTop;
+ final double? size;
+ final Color? outPrimaryColor;
+ final Color? inPrimaryColor;
+ final Color? secondaryColor;
+
+ @override
+ Widget build(BuildContext context) {
+ TextStyle tickStyle = TextStyle(
+ color: Colors.white,
+ fontSize: ((26 / 400) * (size ?? 400)),
+ fontWeight: FontWeight.bold); //20
+ TextStyle mainValueTextStyle = TextStyle(
+ color: Colors.white,
+ fontSize: ((85 / 400) * (size ?? 400)),
+ fontWeight: FontWeight.bold); //65
+ TextStyle labelTextStyle = TextStyle(
+ color: Colors.white,
+ fontSize: ((26 / 400) * (size ?? 400)),
+ fontWeight: FontWeight.normal); //20
+ return SizedBox(
+ width: size ?? 400,
+ height: size ?? 400,
+ child: Stack(
+ alignment: Alignment.topCenter,
+ children: [
+ // Guage painter
+ Positioned(
+ top: 0,
+ child: Transform.rotate(
+ angle: (pi / 2) + (GuageProps.minorAngle * (pi / 360.0)),
+ child: CustomPaint(
+ size: Size(size ?? 400, size ?? 400),
+ painter: GuagePainter(
+ low: low,
+ high: high,
+ currentSpeed: mainValue,
+ inPrimaryColor: inPrimaryColor,
+ outPrimaryColor: outPrimaryColor,
+ secondaryColor: secondaryColor,
+ ),
+ ),
+ ),
+ ),
+ // Guage Label
+ Positioned(
+ top: distLabelTop ?? ((100 / 400) * (size ?? 400)),
+ child: Text(label, style: labelTextStyle),
+ ),
+ // Guage Main Value
+ Positioned(
+ top: distMainTop ?? ((150 / 400) * (size ?? 400)),
+ child: Text("${mainValue.toInt()}", style: mainValueTextStyle),
+ ),
+ // Guage Ticks value
+ Positioned(
+ bottom: distTicksBottom ?? ((80 / 400) * (size ?? 400)),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(zeroTickLabel ?? "", style: tickStyle),
+ SizedBox(
+ width: (size != null)
+ ? ((180 * size!) / 400)
+ : (distanceBWTicks ?? 180)),
+ Text(maxTickLabel ?? "", style: tickStyle)
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/screen/widgets/guages/rpm_guage_animation_wrapper.dart b/lib/screen/widgets/guages/rpm_guage_animation_wrapper.dart
new file mode 100644
index 0000000..95403dd
--- /dev/null
+++ b/lib/screen/widgets/guages/rpm_guage_animation_wrapper.dart
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/guage_props.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/guage_widget.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_model.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_provider.dart';
+
+class RPMGauge extends HookConsumerWidget {
+ final double screenHeight;
+ final GuageColors? guageColor;
+ const RPMGauge({Key? key, required this.screenHeight, this.guageColor})
+ : super(key: key);
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final VehicleSignal vehicle = ref.watch(vehicleSignalProvider);
+
+ const double minRPM = 0;
+ const double maxRPM = 8000;
+ const Duration sweepDuration = Duration(milliseconds: 200);
+
+ final animationController = useAnimationController(
+ lowerBound: minRPM,
+ upperBound: maxRPM,
+ )..animateTo(vehicle.rpm,
+ duration: sweepDuration, curve: Curves.linearToEaseOut);
+ return AnimatedBuilder(
+ animation: animationController,
+ builder: (context, child) {
+ return Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: CustomGuage(
+ size: (248 * screenHeight) / 480,
+ low: minRPM,
+ high: maxRPM,
+ mainValue: animationController.value,
+ label: "rpm",
+ zeroTickLabel: minRPM.toInt().toString(),
+ maxTickLabel: maxRPM.toInt().toString(),
+ inPrimaryColor: guageColor?.inPrimary,
+ outPrimaryColor: guageColor?.outPrimary,
+ secondaryColor: guageColor?.secondary,
+ ),
+ );
+ });
+ }
+}
diff --git a/lib/screen/widgets/guages/speed_guage_animation_wrapper.dart b/lib/screen/widgets/guages/speed_guage_animation_wrapper.dart
new file mode 100644
index 0000000..8704fcd
--- /dev/null
+++ b/lib/screen/widgets/guages/speed_guage_animation_wrapper.dart
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/guage_props.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/guage_widget.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_model.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_provider.dart';
+
+class SpeedGauge extends HookConsumerWidget {
+ final double screenHeight;
+ final GuageColors? guageColor;
+ const SpeedGauge({Key? key, required this.screenHeight, this.guageColor})
+ : super(key: key);
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final VehicleSignal vehicle = ref.watch(vehicleSignalProvider);
+
+ const double minSpeed = 0;
+ const double maxSpeed = 240;
+ const Duration sweepDuration = Duration(milliseconds: 200);
+ double speedScaling =
+ (vehicle.vehicleDistanceUnit == "mi") ? 0.621504 : 1.0;
+
+ final animationController = useAnimationController(
+ lowerBound: minSpeed,
+ upperBound: maxSpeed,
+ )..animateTo(speedScaling * (vehicle.speed),
+ duration: sweepDuration, curve: Curves.linearToEaseOut);
+
+ return AnimatedBuilder(
+ animation: animationController,
+ builder: (context, child) {
+ return Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: CustomGuage(
+ size: (248 * screenHeight) / 480,
+ low: minSpeed,
+ high: maxSpeed,
+ mainValue: animationController.value,
+ label: (vehicle.vehicleDistanceUnit == "mi") ? "mph" : "Km/h",
+ zeroTickLabel: minSpeed.toInt().toString(),
+ maxTickLabel: maxSpeed.toInt().toString(),
+ inPrimaryColor: guageColor?.inPrimary,
+ outPrimaryColor: guageColor?.outPrimary,
+ secondaryColor: guageColor?.secondary,
+ ),
+ );
+ });
+ }
+}
+
+final guageColorProvider = Provider.family<GuageColors, String>((ref, mode) {
+ switch (mode) {
+ case "normal":
+ return GuageColors(inPrimary: Colors.red);
+ case "sports":
+ return GuageColors(inPrimary: Colors.blue);
+ case "eco":
+ return GuageColors(inPrimary: Colors.green);
+ default:
+ return GuageColors();
+ }
+});
diff --git a/lib/screen/widgets/left_bar.dart b/lib/screen/widgets/left_bar.dart
new file mode 100644
index 0000000..3192c28
--- /dev/null
+++ b/lib/screen/widgets/left_bar.dart
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:flutter_cluster_dashboard/screen/paints/arc_painter.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_model.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_provider.dart';
+
+class LeftArc extends HookConsumerWidget {
+ final double screenHeight;
+ const LeftArc({Key? key, required this.screenHeight}) : super(key: key);
+ // final Color color;
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final VehicleSignal vehicle = ref.watch(vehicleSignalProvider);
+ final animationController = useAnimationController(
+ lowerBound: 0,
+ upperBound: 100,
+ )..animateTo(vehicle.coolantTemp,
+ duration: const Duration(milliseconds: 1000));
+
+ return AnimatedBuilder(
+ animation: animationController,
+ builder: (context, child) {
+ return CustomPaint(
+ size: Size(0, (220 * screenHeight) / 480),
+ painter: LeftPainter(
+ radi: (170 * screenHeight) / 480,
+ currentValue: animationController.value,
+ bottomPadding: 15,
+ color: Color.lerp(Colors.yellow, Colors.red,
+ (animationController.value / 100)) ??
+ Colors.orange,
+ ),
+ );
+ });
+ }
+}
diff --git a/lib/screen/widgets/left_signal.dart b/lib/screen/widgets/left_signal.dart
new file mode 100644
index 0000000..693c762
--- /dev/null
+++ b/lib/screen/widgets/left_signal.dart
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+
+class LeftSignal extends HookConsumerWidget {
+ final double screenHeight;
+ const LeftSignal({Key? key, required this.screenHeight}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final animationController = useAnimationController(
+ lowerBound: 0.9,
+ upperBound: 1.1,
+ duration: const Duration(milliseconds: 1000),
+ )..repeat();
+ return AnimatedBuilder(
+ animation: animationController,
+ builder: (context, child) {
+ return Image.asset(
+ "images/left.png",
+ color: Color.lerp(
+ Colors.black,
+ const Color.fromARGB(255, 99, 251, 104),
+ animationController.value.floorToDouble()),
+ width: 0.125 * screenHeight,
+ );
+ });
+ }
+}
diff --git a/lib/screen/widgets/performance_mode.dart b/lib/screen/widgets/performance_mode.dart
new file mode 100644
index 0000000..256365b
--- /dev/null
+++ b/lib/screen/widgets/performance_mode.dart
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+
+class PerformanceMode extends StatelessWidget {
+ const PerformanceMode({Key? key, this.size, required this.mode})
+ : super(key: key);
+ final Size? size;
+ final String mode;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ width: size?.width ?? 20,
+ height: size?.height ?? 40,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(10),
+ color: (mode == "sport")
+ ? Colors.deepPurple
+ : (mode == "economy")
+ ? Colors.green
+ : Colors.transparent),
+ child: Center(
+ child: Text(
+ mode.toUpperCase(),
+ style: const TextStyle(
+ color: Colors.black, fontWeight: FontWeight.bold, fontSize: 12),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/screen/widgets/right_bar.dart b/lib/screen/widgets/right_bar.dart
new file mode 100644
index 0000000..e5ed44d
--- /dev/null
+++ b/lib/screen/widgets/right_bar.dart
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:flutter_cluster_dashboard/screen/paints/arc_painter.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_model.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_provider.dart';
+
+class RightArc extends HookConsumerWidget {
+ final double screenHeight;
+ const RightArc({Key? key, required this.screenHeight}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final VehicleSignal vehicle = ref.watch(vehicleSignalProvider);
+ final animationController = useAnimationController(
+ lowerBound: 0,
+ upperBound: 100,
+ )..animateTo(vehicle.fuelLevel,
+ duration: const Duration(milliseconds: 500));
+
+ return AnimatedBuilder(
+ animation: animationController,
+ builder: (context, child) {
+ return CustomPaint(
+ size: Size(0, (220 * screenHeight) / 480),
+ painter: RightPainter(
+ radi: (170 * screenHeight) / 480,
+ currentValue: animationController.value,
+ bottomPadding: 17,
+ color: Color.lerp(Colors.red, Colors.green,
+ (animationController.value / 100)) ??
+ Colors.blue),
+ );
+ },
+ );
+ }
+}
diff --git a/lib/screen/widgets/right_signal.dart b/lib/screen/widgets/right_signal.dart
new file mode 100644
index 0000000..5c3aa5b
--- /dev/null
+++ b/lib/screen/widgets/right_signal.dart
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+
+class RightSignal extends HookConsumerWidget {
+ final double screenHeight;
+ const RightSignal({Key? key, required this.screenHeight}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final animationController = useAnimationController(
+ lowerBound: 0.9,
+ upperBound: 1.1,
+ duration: const Duration(milliseconds: 1000),
+ )..repeat();
+ return AnimatedBuilder(
+ animation: animationController,
+ builder: (context, child) {
+ return Image.asset(
+ "images/right.png",
+ color: Color.lerp(
+ Colors.black,
+ const Color.fromARGB(255, 99, 251, 104),
+ animationController.value.floorToDouble()),
+ width: 0.125 * screenHeight,
+ );
+ });
+ }
+}
diff --git a/lib/screen/widgets/signals.dart b/lib/screen/widgets/signals.dart
new file mode 100644
index 0000000..788d379
--- /dev/null
+++ b/lib/screen/widgets/signals.dart
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_model.dart';
+
+class Signals extends StatelessWidget {
+ final VehicleSignal vehicle;
+ final double screenHeight;
+ static Color idleColor = const Color.fromARGB(194, 55, 53, 53);
+ const Signals({
+ Key? key,
+ required this.screenHeight,
+ required this.vehicle,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Wrap(
+ spacing: 14,
+ runAlignment: WrapAlignment.spaceBetween,
+ alignment: WrapAlignment.spaceEvenly,
+ children: [
+ (vehicle.isLowBeam)
+ ? Image.asset("images/low-beam.png",
+ color: Colors.green, width: (20 * screenHeight) / 480)
+ : (vehicle.isHighBeam)
+ ? Image.asset("images/high-beam.png",
+ color: Colors.green, width: (20 * screenHeight) / 480)
+ : Image.asset("images/high-beam.png",
+ color: idleColor, width: (20 * screenHeight) / 480),
+ Image.asset("images/hazard.png",
+ color: (vehicle.isHazardLightOn) ? Colors.red : idleColor,
+ width: (20 * screenHeight) / 480),
+ Image.asset("images/parking.png",
+ color: (vehicle.isParkingOn) ? Colors.green : idleColor,
+ width: (20 * screenHeight) / 480),
+ Image.asset("images/battery.png",
+ color: (vehicle.isBatteryCharging) ? Colors.green : Colors.red,
+ width: (20 * screenHeight) / 480),
+ Image.asset("images/malfunction.png",
+ color: (vehicle.isMILon) ? Colors.red : idleColor,
+ width: (20 * screenHeight) / 480),
+ //
+ Image.asset("images/openDoor.png",
+ color: (vehicle.isMILon) ? Colors.white : idleColor,
+ width: (20 * screenHeight) / 480),
+
+ Image.asset("images/seatBelt.png",
+ color: (vehicle.isMILon) ? Colors.white : idleColor,
+ width: (20 * screenHeight) / 480),
+
+ //
+ Image.asset("images/lane.png",
+ color: (vehicle.isSteeringLaneWarning) ? Colors.white : idleColor,
+ width: (25 * screenHeight) / 480),
+ Image.asset("images/cruise.png",
+ color: (vehicle.isSteeringCruiseEnable)
+ ? (vehicle.isSteeringCruiseSet)
+ ? Colors.green
+ : Colors.orange
+ : idleColor,
+ width: (20 * screenHeight) / 480),
+ ],
+ );
+ }
+}
diff --git a/lib/screen/widgets/turn_signal.dart b/lib/screen/widgets/turn_signal.dart
new file mode 100644
index 0000000..a447cbe
--- /dev/null
+++ b/lib/screen/widgets/turn_signal.dart
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+
+class TurnSignal extends HookConsumerWidget {
+ final double screenHeight;
+ final bool isLefton;
+ final bool isRighton;
+ const TurnSignal({
+ Key? key,
+ required this.screenHeight,
+ required this.isLefton,
+ required this.isRighton,
+ }) : super(key: key);
+ double calcPadding(double value, double height) {
+ // values wrt to values at 720 height
+ return (value * height) / 720;
+ }
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final animationController = useAnimationController(
+ lowerBound: 0.9,
+ upperBound: 1.1,
+ duration: const Duration(milliseconds: 1000),
+ )..repeat();
+ return AnimatedBuilder(
+ animation: animationController,
+ builder: (context, child) {
+ return Padding(
+ padding: EdgeInsets.fromLTRB(calcPadding(150, screenHeight), 0,
+ calcPadding(150, screenHeight), 0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Image.asset(
+ "images/left.png",
+ color: (isLefton)
+ ? Color.lerp(
+ Colors.black,
+ const Color.fromARGB(255, 99, 251, 104),
+ animationController.value.floorToDouble())
+ : const Color.fromARGB(255, 49, 48, 48),
+ width: 0.125 * screenHeight,
+ ),
+ Image.asset(
+ "images/right.png",
+ color: (isRighton)
+ ? Color.lerp(
+ Colors.black,
+ const Color.fromARGB(255, 99, 251, 104),
+ animationController.value.floorToDouble())
+ : const Color.fromARGB(255, 49, 48, 48),
+ width: 0.125 * screenHeight,
+ ),
+ ],
+ ),
+ );
+ });
+ }
+}