diff options
author | Lisandro Pérez Meyer <lpmeyer@ics.com> | 2023-11-14 17:20:58 -0300 |
---|---|---|
committer | Lisandro Pérez Meyer <lpmeyer@ics.com> | 2023-11-14 17:31:12 -0300 |
commit | 70ec8a79a121471a004e7e4c23157d10157e136f (patch) | |
tree | a4f9c0a4fac4e4274ec4324a289b6ef62e1c5653 /lib/presentation/screens/dashboard/widgets |
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/widgets')
9 files changed, 1267 insertions, 0 deletions
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 |