summaryrefslogtreecommitdiffstats
path: root/lib/presentation/screens/dashboard
diff options
context:
space:
mode:
authorLisandro Pérez Meyer <lpmeyer@ics.com>2023-11-14 17:20:58 -0300
committerLisandro Pérez Meyer <lpmeyer@ics.com>2023-11-14 17:31:12 -0300
commit70ec8a79a121471a004e7e4c23157d10157e136f (patch)
treea4f9c0a4fac4e4274ec4324a289b6ef62e1c5653 /lib/presentation/screens/dashboard
Initial cleanup push.
Based on agldemo2024 on commit 2a5dc04d801134338150c3f6afc67eaa65599763 Disable device preview. Disable Lottie animation. The original commit was b3c493c340fcb4bb0a937692838fc830bec3e9ea but I am just keeping this change, because the json did not really needed to change. I think. Signed-off-by: Lisandro Pérez Meyer <lpmeyer@ics.com>
Diffstat (limited to 'lib/presentation/screens/dashboard')
-rw-r--r--lib/presentation/screens/dashboard/dashboard.dart33
-rw-r--r--lib/presentation/screens/dashboard/widgets/car_status.dart251
-rw-r--r--lib/presentation/screens/dashboard/widgets/child_lock.dart96
-rw-r--r--lib/presentation/screens/dashboard/widgets/circle_indicator.dart305
-rw-r--r--lib/presentation/screens/dashboard/widgets/custom_circle.dart107
-rw-r--r--lib/presentation/screens/dashboard/widgets/dashboard_content.dart108
-rw-r--r--lib/presentation/screens/dashboard/widgets/hybrid/hybrid.dart142
-rw-r--r--lib/presentation/screens/dashboard/widgets/hybrid_mode.dart30
-rw-r--r--lib/presentation/screens/dashboard/widgets/range.dart85
-rw-r--r--lib/presentation/screens/dashboard/widgets/temperature.dart143
10 files changed, 1300 insertions, 0 deletions
diff --git a/lib/presentation/screens/dashboard/dashboard.dart b/lib/presentation/screens/dashboard/dashboard.dart
new file mode 100644
index 0000000..977bb31
--- /dev/null
+++ b/lib/presentation/screens/dashboard/dashboard.dart
@@ -0,0 +1,33 @@
+
+import '/export.dart';
+import 'widgets/dashboard_content.dart';
+
+class DasboardPage extends ConsumerWidget {
+ const DasboardPage({super.key});
+
+ static Page<void> page() => const MaterialPage<void>(child: DasboardPage());
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ return Stack(
+ children: [
+
+ Padding(
+ padding: const EdgeInsets.only(top: 150.0),
+ child: SizedBox(
+ width: double.infinity,
+ height: double.infinity,
+ child: SvgPicture.asset(
+ 'assets/dashboardTextures.svg',
+ alignment: Alignment.center,
+ ),
+ ),
+ ),
+ const Padding(
+ padding: EdgeInsets.symmetric(vertical: 50, horizontal: 140),
+ child: DashBoard(),
+ ),
+ ],
+
+ );
+ }
+}
diff --git a/lib/presentation/screens/dashboard/widgets/car_status.dart b/lib/presentation/screens/dashboard/widgets/car_status.dart
new file mode 100644
index 0000000..b824871
--- /dev/null
+++ b/lib/presentation/screens/dashboard/widgets/car_status.dart
@@ -0,0 +1,251 @@
+// ignore_for_file: prefer_const_constructors, prefer_const_literals_to_create_immutables
+
+import 'package:gradient_borders/gradient_borders.dart';
+
+import '../../../../export.dart';
+
+class CarStatus extends ConsumerStatefulWidget {
+ const CarStatus({super.key});
+
+ @override
+ CarStatusState createState() => CarStatusState();
+}
+
+class CarStatusState extends ConsumerState<CarStatus> {
+ @override
+ void initState() {
+ super.initState();
+ // "ref" can be used in all life-cycles of a StatefulWidget.
+ //ref.read(counterProvider);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.fromLTRB(0,0,0,84),
+ child: SizedBox(
+ height: 440,
+ width: 652,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const LeftCarStatus(),
+ Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 47.0), // Adding horizontal padding
+ child: SvgPicture.asset(
+ 'assets/Car Illustration.svg',
+ width: 625,
+ height: 440,
+ fit: BoxFit.fitHeight,
+ ),
+ ),
+ const RightCarStatus(),
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+class LeftCarStatus extends ConsumerWidget {
+ const LeftCarStatus({
+ super.key,
+ });
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final frontLeftTire =
+ ref.watch(vehicleProvider.select((vehicle) => vehicle.frontLeftTire));
+ final rearLeftTire =
+ ref.watch(vehicleProvider.select((vehicle) => vehicle.rearLeftTire));
+
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [
+ PSIProgressIndicator(value: frontLeftTire.toDouble()),
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ Text(
+ frontLeftTire.toStringAsFixed(1),
+ style: GoogleFonts.brunoAce(
+ textStyle: TextStyle(
+ color: Colors.white, fontSize: 44),
+ ),
+ ),
+ SizedBox(
+ width: 5,
+ ),
+ PSIWidget(),
+ ],
+ ),
+ ],
+ ),
+ ChildLockLeft(),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [
+ PSIProgressIndicator(value: rearLeftTire.toDouble()),
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ Text(
+ rearLeftTire.toStringAsFixed(1),
+ style: GoogleFonts.brunoAce(
+ textStyle: TextStyle(
+ color: Colors.white, fontSize: 44),
+ ),
+ ),
+ SizedBox(
+ width: 5,
+ ),
+ PSIWidget(),
+ ],
+ ),
+ ],
+ ),
+ ],
+ );
+ }
+}
+
+class RightCarStatus extends ConsumerWidget {
+ const RightCarStatus({
+ super.key,
+ });
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final frontRightTire =
+ ref.watch(vehicleProvider.select((vehicle) => vehicle.frontRightTire));
+ final rearRightTire =
+ ref.watch(vehicleProvider.select((vehicle) => vehicle.rearRightTire));
+
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+
+ children: [
+ PSIProgressIndicator(value: frontRightTire.toDouble()),
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [
+ Text(
+ frontRightTire.toStringAsFixed(1),
+ style: GoogleFonts.brunoAce(
+ textStyle: TextStyle(
+ color: Colors.white, fontSize: 44),
+ ),
+ ),
+ SizedBox(
+ width: 5,
+ ),
+ PSIWidget(),
+ ],
+ ),
+ ],
+ ),
+ const ChildLockRight(),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ PSIProgressIndicator(value: rearRightTire.toDouble()),
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ Text(
+ rearRightTire.toStringAsFixed(1),
+ style: GoogleFonts.brunoAce(
+ textStyle: TextStyle(
+ color: Colors.white, fontSize: 44),
+ ),
+ ),
+ SizedBox(
+ width: 5,
+ ),
+ PSIWidget(),
+ ],
+ ),
+ ],
+ ),
+ ],
+ );
+ }
+}
+
+class PSIProgressIndicator extends StatelessWidget {
+ final double value;
+ const PSIProgressIndicator({
+ Key? key,
+ required this.value, // Require the value to be passed
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ // Calculate the width as a percentage of the full width (74 in this case)
+ final double fillWidth = (value / 35) * 74;
+
+ return Stack(
+ alignment: AlignmentDirectional.centerStart,
+ children: [
+ Container(
+ width: 100,
+ height: 24,
+ decoration: BoxDecoration(
+ border: GradientBoxBorder(
+ gradient:
+ LinearGradient(colors: const [Colors.white30, Colors.white]),
+ ),
+ ),
+
+ ),
+ Positioned(
+ left: 3,
+ child: Container(
+ width: fillWidth, // Use the calculated width here
+ height: 18, // Match the height of the progress bar
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ colors: const [AGLDemoColors.periwinkleColor, Colors.white],
+ stops: [
+ 0.8,
+ 1,
+ ],
+ ),
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+}
+
+class PSIWidget extends StatelessWidget {
+ const PSIWidget({
+ super.key,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.only(left: 4.0, right: 1.0, bottom: 2.0),
+ child: Text(
+ 'PSI',
+ style: TextStyle(
+ fontSize: 26,
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/screens/dashboard/widgets/child_lock.dart b/lib/presentation/screens/dashboard/widgets/child_lock.dart
new file mode 100644
index 0000000..b8701d7
--- /dev/null
+++ b/lib/presentation/screens/dashboard/widgets/child_lock.dart
@@ -0,0 +1,96 @@
+
+import 'package:flutter_ics_homescreen/export.dart';
+
+class ChildLockLeft extends ConsumerWidget {
+ const ChildLockLeft({
+ super.key,
+ });
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final isChildLockActiveLeft = ref.watch(
+ vehicleProvider.select((vehicle) => vehicle.isChildLockActiveLeft));
+
+ return GestureDetector(
+ onTap: () {
+ debugPrint('Tapped child lock left');
+ ref.read(vehicleProvider.notifier).setChildLock(side: 'left');
+ },
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [
+ const Text(
+ 'Child Lock',
+ style: TextStyle(
+ fontSize: 26, // Set the font size to 26
+ ),
+ ),
+ Wrap(
+ crossAxisAlignment: WrapCrossAlignment.center,
+ children: [
+ Icon(
+ isChildLockActiveLeft ? Icons.lock : Icons.lock_open,
+ color: isChildLockActiveLeft ? Colors.white : Colors.redAccent,
+ size: 16,
+ ),
+ Text(
+ isChildLockActiveLeft ? 'Activated' : 'Unlocked',
+ style: TextStyle(
+ color: isChildLockActiveLeft ? Colors.white : Colors.redAccent,
+ fontSize: 26, // Set the font size to 26
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+class ChildLockRight extends ConsumerWidget {
+ const ChildLockRight({
+ super.key,
+ });
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final isChildLockActiveRight = ref.watch(
+ vehicleProvider.select((vehicle) => vehicle.isChildLockActiveRight));
+
+ return GestureDetector(
+ onTap: () {
+ debugPrint('Tapped child lock right');
+ ref.read(vehicleProvider.notifier).setChildLock(side: 'right');
+ },
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const Text(
+ 'Child Lock',
+ style: TextStyle(
+ fontSize: 26, // Set the font size to 26
+ ),
+ ),
+ Wrap(
+ crossAxisAlignment: WrapCrossAlignment.center,
+ children: [
+ Text(
+ isChildLockActiveRight ? 'Activated' : 'Unlocked',
+ style: TextStyle(
+ color: isChildLockActiveRight ? Colors.white : Colors.redAccent,
+ fontSize: 26, // Set the font size to 26
+ ),
+ ),
+ Icon(
+ isChildLockActiveRight ? Icons.lock : Icons.lock_open,
+ color: isChildLockActiveRight ? Colors.white : Colors.redAccent,
+ size: 16,
+ ),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+} \ No newline at end of file
diff --git a/lib/presentation/screens/dashboard/widgets/circle_indicator.dart b/lib/presentation/screens/dashboard/widgets/circle_indicator.dart
new file mode 100644
index 0000000..7a4e724
--- /dev/null
+++ b/lib/presentation/screens/dashboard/widgets/circle_indicator.dart
@@ -0,0 +1,305 @@
+import 'package:flutter_ics_homescreen/export.dart';
+
+import 'custom_circle.dart';
+
+class RPMProgressIndicator extends ConsumerStatefulWidget {
+ const RPMProgressIndicator({super.key});
+
+ @override
+ RPMProgressIndicatorState createState() => RPMProgressIndicatorState();
+}
+
+class RPMProgressIndicatorState extends ConsumerState<RPMProgressIndicator>
+ with TickerProviderStateMixin {
+ late AnimationController controller;
+
+ @override
+ void initState() {
+ controller = AnimationController(
+ /// [AnimationController]s can be created with `vsync: this` because of
+ /// [TickerProviderStateMixin].
+ vsync: this,
+ duration: const Duration(seconds: 5),
+ )..addListener(() {
+ //setState(() {});
+ });
+ controller.repeat(reverse: true);
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ controller.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final rpm =
+ ref.watch(vehicleProvider.select((vehicle) => vehicle.engineSpeed));
+ return Column(
+ children: [
+ SizedBox(
+ height: 252,
+ child: Stack(
+ alignment: Alignment.center,
+ children: [
+
+ Text(
+ rpm.toString(),
+ style: GoogleFonts.brunoAce(
+ textStyle: const TextStyle(color: Colors.white, fontSize: 44),
+ ),
+ ),
+ Stack(
+ children: [
+ if (rpm > 6500)
+ SizedBox(
+ height: 200,
+ width: 200,
+ child: CircularProgressIndicator(
+ strokeWidth: 12,
+ backgroundColor: Colors.transparent,
+ //value: controller.value,
+ valueColor: const AlwaysStoppedAnimation<Color>(
+ AGLDemoColors.redProgressStrokeColor),
+ value: rpm * (1 / maxRpm),
+ ),
+ ),
+ SizedBox(
+ height: 200,
+ width: 200,
+ child: CircularProgressIndicator(
+ strokeWidth: 12,
+ backgroundColor: Colors.transparent,
+ //value: controller.value,
+ valueColor: const AlwaysStoppedAnimation<Color>(
+ AGLDemoColors.jordyBlueColor),
+ value: rpm >= 6500
+ ? 6500 * (1 / maxRpm)
+ : rpm * (1 / maxRpm),
+ ),
+ ),
+ ],
+ ),
+ SizedBox(
+ height: 220,
+ width: 220,
+ child: CustomPaint(
+ foregroundPainter: CirclePainter(
+ value: rpm.toDouble(),
+ maxValue: maxRpm.toDouble(),
+ isRPM: true,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ const Text(
+ 'RPM',
+ style: TextStyle(color: Colors.white, fontSize: 40),
+ ),
+ ],
+ );
+ }
+}
+
+
+
+class SpeedProgressIndicator extends ConsumerStatefulWidget {
+ const SpeedProgressIndicator({super.key});
+
+ @override
+ SpeedProgressIndicatorState createState() => SpeedProgressIndicatorState();
+}
+
+class SpeedProgressIndicatorState extends ConsumerState<SpeedProgressIndicator>
+ with TickerProviderStateMixin {
+ late AnimationController controller;
+
+ @override
+ void initState() {
+ controller = AnimationController(
+ /// [AnimationController]s can be created with `vsync: this` because of
+ /// [TickerProviderStateMixin].
+ vsync: this,
+ duration: const Duration(seconds: 5),
+ )..addListener(() {
+ //setState(() {});
+ });
+ controller.repeat(reverse: true);
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ controller.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final speed = ref.watch(vehicleProvider.select((vehicle) => vehicle.speed));
+ final unit =
+ ref.watch(unitStateProvider.select((unit) => unit.distanceUnit));
+ return Column(
+ children: [
+
+ SizedBox(
+ height: 252,
+ child: Stack(
+ alignment: Alignment.center,
+ children: [
+
+ Text(
+ unit == DistanceUnit.kilometers
+ ? speed.toStringAsFixed(0)
+ : (speed * 1.609).toStringAsFixed(0),
+ style: GoogleFonts.brunoAce(
+ textStyle: const TextStyle(
+ color: Colors.white,
+ fontSize: 44,
+ ),
+ ),
+ ),
+ SizedBox(
+ height: 200,
+ width: 200,
+ child: CircularProgressIndicator(
+ strokeWidth: 12,
+ //backgroundColor: const Color(0xFF2962FF),
+ //value: controller.value,
+ value: unit == DistanceUnit.kilometers
+ ? speed * (1 / maxSpeed)
+ : (speed * (1 / maxSpeed) * 1.609),
+ semanticsLabel: 'Speed progress indicator',
+ ),
+ ),
+ SizedBox(
+ height: 220,
+ width: 220,
+ child: CustomPaint(
+ foregroundPainter:
+ CirclePainter(value: speed, maxValue: maxSpeed),
+ ),
+ ),
+ ],
+ ),
+
+ ),
+ Text(
+ unit == DistanceUnit.kilometers ? 'Km/h' : 'Mph',
+ style: const TextStyle(color: Colors.white, fontSize: 40),
+ ),
+ ],
+ );
+ }
+}
+
+class FuelProgressIndicator extends ConsumerStatefulWidget {
+ const FuelProgressIndicator({super.key});
+
+ @override
+ FuelProgressIndicatorState createState() => FuelProgressIndicatorState();
+}
+
+class FuelProgressIndicatorState extends ConsumerState<FuelProgressIndicator>
+ with TickerProviderStateMixin {
+ late AnimationController controller;
+
+ @override
+ void initState() {
+ controller = AnimationController(
+ /// [AnimationController]s can be created with `vsync: this` because of
+ /// [TickerProviderStateMixin].
+ vsync: this,
+ duration: const Duration(seconds: 5),
+ )..addListener(() {
+ //setState(() {});
+ });
+ controller.repeat(reverse: true);
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ controller.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final fuelLevel =
+ ref.watch(vehicleProvider.select((vehicle) => vehicle.fuelLevel));
+ return Column(
+ children: [
+ SizedBox(
+ height: 252,
+ child: Stack(
+ alignment: Alignment.center,
+ children: [
+
+ Text(
+ '${(fuelLevel * (1 / maxFuelLevel) * 100).toStringAsFixed(0)}%',
+ style: GoogleFonts.brunoAce(
+ textStyle: const TextStyle(
+ color: Colors.white,
+ fontSize: 44,
+ ),
+ ),
+ ),
+ Stack(
+ children: [
+ SizedBox(
+ height: 200,
+ width: 200,
+ child: CircularProgressIndicator(
+ strokeWidth: 12,
+ backgroundColor: Colors.transparent,
+ value: fuelLevel >= 12
+ ? 12 * (1 / maxFuelLevel)
+ : fuelLevel * (1 / maxFuelLevel),
+ valueColor: const AlwaysStoppedAnimation<Color>(
+ AGLDemoColors.redProgressStrokeColor),
+ ),
+ ),
+ if (fuelLevel > 12)
+ SizedBox(
+ height: 200,
+ width: 200,
+ child: CircularProgressIndicator(
+ strokeWidth: 12,
+ backgroundColor: Colors.transparent,
+ //value: controller.value,
+ valueColor: const AlwaysStoppedAnimation<Color>(
+ AGLDemoColors.jordyBlueColor),
+ value: fuelLevel * (1 / maxFuelLevel),
+ ),
+ ),
+
+ ],
+ ),
+ SizedBox(
+ height: 220,
+ width: 220,
+ child: CustomPaint(
+ foregroundPainter: CirclePainter(
+ value: fuelLevel,
+ maxValue: maxFuelLevel,
+ isFuel: true,
+ isRPM: false),
+ ),
+ ),
+ ],
+ ),
+ ),
+ const Text(
+ 'Fuel',
+ style: TextStyle(color: Colors.white, fontSize: 40),
+ ),
+ ],
+ );
+ }
+}
+
diff --git a/lib/presentation/screens/dashboard/widgets/custom_circle.dart b/lib/presentation/screens/dashboard/widgets/custom_circle.dart
new file mode 100644
index 0000000..4e26f0b
--- /dev/null
+++ b/lib/presentation/screens/dashboard/widgets/custom_circle.dart
@@ -0,0 +1,107 @@
+import 'dart:math' as math;
+
+import 'package:flutter_ics_homescreen/export.dart';
+
+class CirclePainter extends CustomPainter {
+ final double value;
+ final double maxValue;
+ final bool? isRPM;
+ final bool? isFuel;
+
+ CirclePainter({
+ required this.value,
+ required this.maxValue,
+ this.isRPM = false,
+ this.isFuel = false,
+ });
+ @override
+ void paint(Canvas canvas, Size size) {
+ final paint = Paint()
+ ..color = AGLDemoColors.neonBlueColor
+ ..strokeWidth = 2
+ ..style = PaintingStyle.stroke;
+ final paintRed = Paint()
+ ..color = const Color(0xFFBF360C)
+ ..strokeWidth = 2
+ ..style = PaintingStyle.stroke;
+
+ final smallCirclePaint = Paint()
+ ..color = AGLDemoColors.resolutionBlueColor
+ ..strokeWidth = 10
+ // Use [PaintingStyle.fill] if you want the circle to be filled.
+ ..style = PaintingStyle.fill;
+
+ final center = Offset(size.width / 2, size.height / 2);
+
+ final double radius = (size.width / 2) - 10;
+
+ const totalDegree = 360;
+
+ // Total ticks to display
+ var totalTicks = isFuel! ? 4 : 8;
+
+ var values = [];
+ for (int i = 0; i < totalTicks; i++) {
+ values.add(i * (maxValue / totalTicks));
+ }
+
+ /// The angle between each tick
+ var unitAngle = totalDegree / totalTicks;
+ for (int i = 0; i < totalTicks; i++) {
+ final angle = -90.0.radians + (i * unitAngle).radians;
+ final xOffset = radius * math.cos(angle);
+ final yOffset = radius * math.sin(angle);
+ final offset = Offset(center.dx + xOffset, center.dy + yOffset);
+ if (value > values[i]) {
+ canvas.drawCircle(offset, 3, smallCirclePaint);
+ } else {
+ canvas.drawCircle(offset, 3, smallCirclePaint..color = Colors.white);
+ }
+ }
+
+ final rect = Rect.fromCenter(
+ center: center,
+ width: ((size.width / 2.4) * 2) + 2,
+ height: (size.width / 2.4) * 2 + 2,
+ );
+ canvas.drawArc(
+ rect,
+ _deg2Rads(-90),
+ _deg2Rads(360),
+ false,
+ paint,
+ );
+ if (isRPM == true) {
+ canvas.drawArc(
+ rect,
+ _deg2Rads(202),
+ _deg2Rads(68),
+ false,
+ paintRed,
+ );
+ }
+ if (isFuel == true) {
+ canvas.drawArc(
+ rect,
+ _deg2Rads(-90),
+ _deg2Rads(80),
+ false,
+ paintRed,
+ );
+ }
+
+ //canvas.drawArc(rect, pi / 4, pi * 3 / 4, false, paint);
+ }
+
+ double _deg2Rads(num deg) {
+ return (deg * math.pi) / 180.0;
+ }
+
+ @override
+ bool shouldRepaint(oldDelegate) => false;
+}
+
+extension on num {
+ /// This is an extension we created so we can easily convert a value /// to a radian value
+ double get radians => (this * math.pi) / 180.0;
+}
diff --git a/lib/presentation/screens/dashboard/widgets/dashboard_content.dart b/lib/presentation/screens/dashboard/widgets/dashboard_content.dart
new file mode 100644
index 0000000..74f0d2a
--- /dev/null
+++ b/lib/presentation/screens/dashboard/widgets/dashboard_content.dart
@@ -0,0 +1,108 @@
+import 'dart:math';
+
+import 'package:flutter_ics_homescreen/export.dart';
+
+class DashBoard extends ConsumerStatefulWidget {
+ const DashBoard({super.key});
+
+ @override
+ DashBoardState createState() => DashBoardState();
+}
+
+class DashBoardState extends ConsumerState<DashBoard>
+ with SingleTickerProviderStateMixin {
+ late AnimationController _animationController;
+ late Animation<double> _animation;
+ static bool _isAnimationPlayed = false;
+
+ @override
+ void initState() {
+ super.initState();
+ _animationController = AnimationController(
+ duration: const Duration(milliseconds: 1800),
+ vsync: this,
+ value: _isAnimationPlayed ? 1.0 : 0.0,
+ );
+
+ _animation = CurvedAnimation(
+ parent: _animationController,
+ curve: Curves.easeIn,
+ );
+
+ // Start the animation on first build.
+ if (!_isAnimationPlayed) {
+ Future.delayed(const Duration(milliseconds: 500), () {
+ _animationController.forward();
+ _isAnimationPlayed = true;
+ });
+ }
+ }
+
+ @override
+ void dispose() {
+ _animationController.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ Widget svgImage = Align(
+ alignment: Alignment.bottomCenter,
+ child: SvgPicture.asset(
+ 'assets/Car Illustration.svg',
+ width: 625,
+ height: 440,
+ fit: BoxFit.fitHeight,
+ ),
+ );
+
+ Widget fadeContent = FadeTransition(
+ opacity: _animation,
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: <Widget>[
+ const Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ //mainAxisSize: MainAxisSize.max,
+ children: [
+ RPMProgressIndicator(),
+ SpeedProgressIndicator(),
+ FuelProgressIndicator(),
+ ],
+ ),
+ GestureDetector(
+ onTap: () {
+ Random random = Random();
+ int randomState = random.nextInt(4);
+ var hybridState = HybridState.values[randomState];
+ ref
+ .read(hybridtateProvider.notifier)
+ .setHybridState(hybridState);
+ },
+ child: const HybridModel()),
+ const Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ TemperatureWidget(),
+ RangeWidget(),
+ ],
+ ),
+ const CarStatus(),
+ ],
+ ));
+
+ return Stack(
+ alignment: Alignment.center,
+ children: [
+ Positioned.fill(
+ child: fadeContent,
+ ),
+ Positioned(
+ bottom: 138,
+ child: svgImage,
+ ),
+ ],
+ );
+ }
+}
diff --git a/lib/presentation/screens/dashboard/widgets/hybrid/hybrid.dart b/lib/presentation/screens/dashboard/widgets/hybrid/hybrid.dart
new file mode 100644
index 0000000..b6844de
--- /dev/null
+++ b/lib/presentation/screens/dashboard/widgets/hybrid/hybrid.dart
@@ -0,0 +1,142 @@
+import 'package:flutter_ics_homescreen/export.dart';
+
+class HybridBackround extends StatelessWidget {
+ const HybridBackround({
+ super.key,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return SvgPicture.asset('animations/hybrid_model/hybrid_bg.svg');
+ }
+}
+
+class TopArrow extends StatelessWidget {
+ const TopArrow({
+ super.key,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Align(
+ alignment: const Alignment(0, -0.75),
+ child: Consumer(builder: (context, ref, child) {
+ final state = ref.watch(hybridtateProvider.select((hybrid) => hybrid));
+ Widget? widget;
+ switch (state.topArrowState) {
+ case ArrowState.blue:
+ widget = SvgPicture.asset(
+ 'animations/hybrid_model/top_blue.svg',
+ );
+ break;
+ case ArrowState.red:
+ widget = Lottie.asset('animations/hybrid_model/top_arrow_red.json');
+
+ break;
+
+ default:
+ }
+
+ return widget ??
+ SvgPicture.asset(
+ 'animations/hybrid_model/left_blue.svg',
+ );
+ }),
+ );
+ }
+}
+
+class LeftArrow extends StatelessWidget {
+ const LeftArrow({
+ super.key,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Align(
+ alignment: const Alignment(-0.7, 0.5),
+ child: Consumer(builder: (context, ref, child) {
+ final state = ref.watch(hybridtateProvider.select((hybrid) => hybrid));
+ Widget? widget;
+ switch (state.leftArrowState) {
+ case ArrowState.blue:
+ widget = SvgPicture.asset(
+ 'animations/hybrid_model/left_blue.svg',
+ );
+ break;
+ case ArrowState.red:
+ widget =
+ Lottie.asset('animations/hybrid_model/left_arrow_red.json');
+
+ break;
+
+ default:
+ }
+
+ return widget ??
+ SvgPicture.asset(
+ 'animations/hybrid_model/left_blue.svg',
+ );
+ }),
+ );
+ }
+}
+
+class RightArrow extends StatelessWidget {
+ const RightArrow({
+ super.key,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Align(
+ alignment: const Alignment(0.70, 0.5),
+ child: Consumer(builder: (context, ref, child) {
+ final state = ref.watch(hybridtateProvider.select((hybrid) => hybrid));
+
+ Widget? widget;
+ switch (state.rightArrowState) {
+ case ArrowState.blue:
+ widget = SvgPicture.asset(
+ 'animations/hybrid_model/right_blue.svg',
+ );
+ break;
+ case ArrowState.yellow:
+ widget =
+ Lottie.asset('animations/hybrid_model/right_arrow_yellow.json');
+
+ break;
+ case ArrowState.green:
+ widget =
+ Lottie.asset('animations/hybrid_model/right_arrow_green.json');
+
+ break;
+ default:
+ }
+
+ return widget ??
+ SvgPicture.asset(
+ 'animations/hybrid_model/right_blue.svg',
+ );
+ }),
+ );
+ }
+}
+
+class BatteryHybrid extends ConsumerWidget {
+ const BatteryHybrid({
+ super.key,
+ });
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final batteryState =
+ ref.watch(hybridtateProvider.select((hybrid) => hybrid.batteryState));
+ return Align(
+ alignment: const Alignment(0, 0.8),
+ child: SvgPicture.asset(
+ 'animations/hybrid_model/battery_${batteryState.name}.svg',
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/screens/dashboard/widgets/hybrid_mode.dart b/lib/presentation/screens/dashboard/widgets/hybrid_mode.dart
new file mode 100644
index 0000000..9a657b8
--- /dev/null
+++ b/lib/presentation/screens/dashboard/widgets/hybrid_mode.dart
@@ -0,0 +1,30 @@
+import 'package:flutter_ics_homescreen/export.dart';
+
+class HybridModel extends StatefulWidget {
+ const HybridModel({super.key});
+
+ @override
+ State<HybridModel> createState() => _HybridModelState();
+}
+
+class _HybridModelState extends State<HybridModel> {
+ @override
+ Widget build(BuildContext context) {
+
+ return GestureDetector(
+ child: const SizedBox(
+ width: 500,
+ height: 500,
+ child: Stack(
+ children: [
+ HybridBackround(),
+ TopArrow(),
+ LeftArrow(),
+ RightArrow(),
+ BatteryHybrid(),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/screens/dashboard/widgets/range.dart b/lib/presentation/screens/dashboard/widgets/range.dart
new file mode 100644
index 0000000..aea92af
--- /dev/null
+++ b/lib/presentation/screens/dashboard/widgets/range.dart
@@ -0,0 +1,85 @@
+import 'package:flutter_ics_homescreen/presentation/custom_icons/custom_icons.dart';
+
+import '../../../../export.dart';
+
+class RangeWidget extends ConsumerWidget {
+ const RangeWidget({super.key});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final range = ref.watch(vehicleProvider.select((vehicle) => vehicle.range));
+ final unit =
+ ref.watch(unitStateProvider.select((unit) => unit.distanceUnit));
+ return Container(
+ height:130,
+ width: 306,
+ // padding: const EdgeInsets.all(10),
+ decoration: const ShapeDecoration(
+ gradient: RadialGradient(
+ colors: [
+ Color.fromARGB(255, 19, 24, 75),
+ Color.fromARGB(127, 0, 0, 0)
+ ],
+ stops: [0, 0.7],
+ radius: 1,
+ ),
+ //color: Colors.grey,
+ shape: StadiumBorder(
+ side: BorderSide(
+ color: Color.fromARGB(156, 0, 0, 0),
+ width: 2,
+ )),
+ ),
+ alignment: Alignment.topLeft,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const Icon(
+ CustomIcons.range,
+ color: Color(0xFF2962FF),
+ size: 48,
+ ),
+ const SizedBox(width: 8),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const Text(
+ 'Range',
+ textAlign: TextAlign.start,
+ style: TextStyle(
+ color: Color(0xFFC1D8FF),
+ fontSize: 26,
+ ),
+ ),
+ RichText(
+ text: TextSpan(
+
+ text: '$range',
+ style: GoogleFonts.brunoAce(
+
+ textStyle:
+ const TextStyle(
+ color: Colors.white,
+ fontSize: 44,
+ ),
+ ),
+ children: <TextSpan>[
+ TextSpan(
+ text:
+ unit == DistanceUnit.kilometers ? ' Km' : ' Mi',
+ style: GoogleFonts.brunoAce(
+ textStyle: const TextStyle(
+ color: Color(0xFFC1D8FF),
+ fontSize: 38),
+ ),
+ ),
+ ]),
+ ),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/screens/dashboard/widgets/temperature.dart b/lib/presentation/screens/dashboard/widgets/temperature.dart
new file mode 100644
index 0000000..0817b53
--- /dev/null
+++ b/lib/presentation/screens/dashboard/widgets/temperature.dart
@@ -0,0 +1,143 @@
+import '../../../../export.dart';
+
+class TemperatureWidget extends ConsumerWidget {
+ const TemperatureWidget({super.key});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final temperature = ref.watch(vehicleProvider.select((vehicle) => vehicle));
+ // final outsideTemperature = ref
+ // .watch(vehicleProvider.select((vehicle) => vehicle.outsideTemperature));
+ final tempUnit =
+ ref.watch(unitStateProvider.select((unit) => unit.temperatureUnit));
+
+ TextStyle temperatureTextStyle = const TextStyle(
+ fontFamily: 'BrunoAce',
+ color: Colors.white,
+ fontSize: 44,
+ );
+
+ TextStyle unitTextStyle = const TextStyle(
+ fontFamily: 'BrunoAce',
+ color: Color(0xFFC1D8FF),
+ fontSize: 38,
+ );
+
+ return Container(
+ width:
+ 442, // needs to be adjusted after the celsius and farenheight symbols are fixed
+ height: 130, // Height of the oval
+ //padding: const EdgeInsets.all(10),
+ decoration: ShapeDecoration(
+ gradient: const RadialGradient(
+ colors: [
+ Color.fromARGB(255, 19, 24, 75),
+ Color.fromARGB(127, 0, 0, 0)
+ ],
+ stops: [0.0, 0.7],
+ radius: 1,
+ ),
+ //color: Colors.grey,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(65), // Half of the height for an oval effect
+ side: const BorderSide(
+ color: Color.fromARGB(156, 0, 0, 0),
+ width: 2,
+ ),
+ ),
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ // Inside temperature
+ buildTemperatureRow(
+ context,
+ Icons.thermostat_outlined,
+ "Inside",
+ temperature.insideTemperature,
+ tempUnit,
+ temperatureTextStyle,
+ unitTextStyle,
+ false,
+ ),
+ const SizedBox(width: 10),
+ // Outside temperature
+ buildTemperatureRow(
+ context,
+ Icons.thermostat_outlined,
+ "Outside",
+ temperature.outsideTemperature,
+ tempUnit,
+ temperatureTextStyle,
+ unitTextStyle,
+ true,
+ ),
+ ],
+ ),
+ );
+ }
+
+ Widget buildTemperatureRow(
+ BuildContext context,
+ IconData icon,
+ String label,
+ double temperatureValue,
+ TemperatureUnit tempUnit,
+ TextStyle tempTextStyle,
+ TextStyle unitTextStyle,
+ bool isOutside,
+
+ ) {
+ int temperatureAsInt = temperatureValue.toInt();
+ double convertedTemperature = tempUnit == TemperatureUnit.celsius
+ ? temperatureAsInt.toDouble()
+ : (temperatureAsInt * 9 / 5) + 32;
+
+ // Format the temperature for display.
+ String temperatureDisplay = tempUnit == TemperatureUnit.celsius
+ ? '$temperatureAsInt'
+ : '${convertedTemperature.toStringAsFixed(0)}';
+
+ return Padding(
+ padding: isOutside
+ ? const EdgeInsets.only(right: 22) // Padding for the outside temperature
+ : const EdgeInsets.only(left: 12), // Padding for the inside temperature
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Icon(
+ icon,
+ color: const Color(0xFF2962FF),
+ size: 48,
+ ),
+ const SizedBox(width: 4), // Space between icon and text
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ label,
+ style: const TextStyle(
+ color: Color(0xFFC1D8FF),
+ fontSize: 26,
+ ),
+ ),
+ RichText(
+ text: TextSpan(
+ text: temperatureDisplay,
+ style: tempTextStyle,
+ children: <TextSpan>[
+ TextSpan(
+ text: tempUnit == TemperatureUnit.celsius ? '°C' : '°F',
+ style: unitTextStyle,
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+} \ No newline at end of file