From 70ec8a79a121471a004e7e4c23157d10157e136f Mon Sep 17 00:00:00 2001 From: Lisandro Pérez Meyer Date: Tue, 14 Nov 2023 17:20:58 -0300 Subject: Initial cleanup push. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- lib/presentation/screens/hvac/hvac.dart | 45 ++++ lib/presentation/screens/hvac/hvac_content.dart | 249 ++++++++++++++++++++ .../screens/hvac/widgets/climate_controls.dart | 80 +++++++ .../screens/hvac/widgets/fan_focus.dart | 116 ++++++++++ .../screens/hvac/widgets/fan_speed_controls.dart | 251 ++++++++++++++++++++ .../screens/hvac/widgets/semi_circle_painter.dart | 109 +++++++++ .../screens/hvac/widgets/temperature_control.dart | 255 +++++++++++++++++++++ 7 files changed, 1105 insertions(+) create mode 100644 lib/presentation/screens/hvac/hvac.dart create mode 100644 lib/presentation/screens/hvac/hvac_content.dart create mode 100644 lib/presentation/screens/hvac/widgets/climate_controls.dart create mode 100644 lib/presentation/screens/hvac/widgets/fan_focus.dart create mode 100644 lib/presentation/screens/hvac/widgets/fan_speed_controls.dart create mode 100644 lib/presentation/screens/hvac/widgets/semi_circle_painter.dart create mode 100644 lib/presentation/screens/hvac/widgets/temperature_control.dart (limited to 'lib/presentation/screens/hvac') diff --git a/lib/presentation/screens/hvac/hvac.dart b/lib/presentation/screens/hvac/hvac.dart new file mode 100644 index 0000000..ebdaea4 --- /dev/null +++ b/lib/presentation/screens/hvac/hvac.dart @@ -0,0 +1,45 @@ +import '/export.dart'; + +class HvacPage extends StatelessWidget { + const HvacPage({super.key}); + + static Page page() => const MaterialPage(child: HvacPage()); + @override + Widget build(BuildContext context) { + // print(MediaQuery.of(context).size); + // print(MediaQuery.of(context).size.width * + // MediaQuery.of(context).devicePixelRatio); + // print(MediaQuery.of(context).size.height * + // MediaQuery.of(context).devicePixelRatio); + return Stack( + children: [ + // SizedBox( + // width: double.infinity, + // height: double.infinity, + // //color: Colors.black, + // // decoration: + // // BoxDecoration(gradient: AGLDemoColors.gradientBackgroundColor), + // child: SvgPicture.asset( + // 'assets/HVACBackground.svg', + // alignment: Alignment.center, + // fit: BoxFit.cover, + // //width: 200, + // //height: 200, + // ), + // ), + SizedBox( + width: double.infinity, + height: double.infinity, + // color: Colors.black, + child: SvgPicture.asset( + 'assets/backgroundTextures.svg', + alignment: Alignment.center, + //width: 200, + //height: 200, + ), + ), + const SingleChildScrollView(child: HVAC()), + ], + ); + } +} diff --git a/lib/presentation/screens/hvac/hvac_content.dart b/lib/presentation/screens/hvac/hvac_content.dart new file mode 100644 index 0000000..f79ec14 --- /dev/null +++ b/lib/presentation/screens/hvac/hvac_content.dart @@ -0,0 +1,249 @@ +import 'package:flutter_ics_homescreen/export.dart'; + +class HVAC extends ConsumerStatefulWidget { + const HVAC({super.key}); + + @override + HVACState createState() => HVACState(); +} + +class HVACState extends ConsumerState { + bool isFanFocusLeftTopSelected = false; + bool isFanFocusRightTopSelected = true; + bool isFanFocusLeftBottomSelected = true; + bool isFanFocusRightBottomSelected = false; + + late bool isACSelected; + bool isSYNCSelected = true; + late bool isFrontDefrostSelected; + bool isAutoSelected = true; + late bool isRecirculationSelected; + late bool isRearDefrostSelected; + + int temperatureLeft = 26; + int temperatureRight = 26; + @override + void initState() { + super.initState(); + } + + TextStyle climateControlTextStyle = GoogleFonts.raleway( + color: AGLDemoColors.periwinkleColor, + fontSize: 44, + height: 1.25, + fontWeight: FontWeight.w500, + shadows: [ + Shadow( + offset: const Offset(1, 2), + blurRadius: 3, + color: Colors.black.withOpacity(0.7)) + ]); + TextStyle climateControlSelectedTextStyle = GoogleFonts.raleway( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 44, + height: 1.25, + shadows: [ + Shadow( + offset: const Offset(1, 2), + blurRadius: 3, + color: Colors.black.withOpacity(0.7)) + ]); + + @override + Widget build(BuildContext context) { + final vehicle = ref.watch(vehicleProvider.select((vehicle) => vehicle)); + isACSelected = vehicle.isAirConditioningActive; + isFrontDefrostSelected = vehicle.isFrontDefrosterActive; + isRearDefrostSelected = vehicle.isRearDefrosterActive; + isRecirculationSelected = vehicle.isRecirculationActive; + Size size = MediaQuery.sizeOf(context); + + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox( + height: 83, + ), + Row( + children: [ + SizedBox( + width: size.width * 0.125, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Padding( + padding: EdgeInsets.symmetric(vertical: 20), + child: Text( + "Left", + style: TextStyle(color: Colors.white, fontSize: 40), + textAlign: TextAlign.center, + ), + ), + FanFocus( + onPressed: () { + setState(() { + isFanFocusLeftTopSelected = !isFanFocusLeftTopSelected; + }); + }, + isSelected: isFanFocusLeftTopSelected, + focusType: "top_half"), + const SizedBox( + height: 12, + ), + FanFocus( + onPressed: () { + setState(() { + isFanFocusLeftBottomSelected = + !isFanFocusLeftBottomSelected; + }); + }, + isSelected: isFanFocusLeftBottomSelected, + focusType: "bottom_half") + ], + )), + SizedBox( + width: size.width * 0.05, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Padding( + padding: EdgeInsets.symmetric(vertical: 20), + child: Text( + "Right", + style: TextStyle(color: Colors.white, fontSize: 40), + textAlign: TextAlign.center, + ), + ), + FanFocus( + onPressed: () { + setState(() { + isFanFocusRightTopSelected = + !isFanFocusRightTopSelected; + }); + }, + isSelected: isFanFocusRightTopSelected, + focusType: "top_half"), + const SizedBox( + height: 12, + ), + FanFocus( + onPressed: () { + setState(() { + isFanFocusRightBottomSelected = + !isFanFocusRightBottomSelected; + }); + }, + isSelected: isFanFocusRightBottomSelected, + focusType: "bottom_half") + ], + )), + SizedBox( + width: size.width * 0.1, + ), + ], + ), + const SizedBox( + height: 80, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + TemperatureControl(temperature: temperatureLeft), + TemperatureControl(temperature: temperatureRight) + ], + ), + const SizedBox( + height: 170, + ), + const FanSpeedControls(), + const SizedBox( + height: 70, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ClimateControls( + isSelected: isACSelected, + onPressed: () { + ref + .read(vehicleProvider.notifier) + .setHVACMode(mode: 'airCondition'); + }, + child: Text( + "A/C", + style: isACSelected + ? climateControlSelectedTextStyle + : climateControlTextStyle, + )), + ClimateControls( + onPressed: () { + setState(() { + isSYNCSelected = !isSYNCSelected; + }); + }, + isSelected: isSYNCSelected, + child: Text( + "SYNC", + style: isSYNCSelected + ? climateControlSelectedTextStyle + : climateControlTextStyle, + )), + ClimateControls( + onPressed: () { + ref + .read(vehicleProvider.notifier) + .setHVACMode(mode: 'frontDefrost'); + }, + isSelected: isFrontDefrostSelected, + child: SvgPicture.asset( + "assets/${isFrontDefrostSelected ? "FrontDefrostFilled.svg" : "FrontDefrost.svg"}", + )) + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ClimateControls( + isSelected: isAutoSelected, + onPressed: () { + setState(() { + isAutoSelected = !isAutoSelected; + }); + }, + child: Text( + "AUTO", + style: isAutoSelected + ? climateControlSelectedTextStyle + : climateControlTextStyle, + )), + ClimateControls( + onPressed: () { + ref + .read(vehicleProvider.notifier) + .setHVACMode(mode: 'recirculation'); + }, + isSelected: isRecirculationSelected, + child: SvgPicture.asset( + "assets/${isRecirculationSelected ? "RecirculationFilled.svg" : "Recirculation.svg"}", + )), + ClimateControls( + onPressed: () { + ref + .read(vehicleProvider.notifier) + .setHVACMode(mode: 'rearDefrost'); + }, + isSelected: isRearDefrostSelected, + child: SvgPicture.asset( + "assets/${isRearDefrostSelected ? "BackDefrostFilled.svg" : "BackDefrost.svg"}", + )) + ], + ) + ], + ); + } +} diff --git a/lib/presentation/screens/hvac/widgets/climate_controls.dart b/lib/presentation/screens/hvac/widgets/climate_controls.dart new file mode 100644 index 0000000..c7dcd52 --- /dev/null +++ b/lib/presentation/screens/hvac/widgets/climate_controls.dart @@ -0,0 +1,80 @@ +import 'package:flutter_ics_homescreen/export.dart'; + +class ClimateControls extends StatefulWidget { + const ClimateControls( + {super.key, + required this.child, + required this.isSelected, + required this.onPressed}); + final Widget child; + final bool isSelected; + final VoidCallback onPressed; + + @override + State createState() => _ClimateControlsState(); +} + +class _ClimateControlsState extends State { + @override + Widget build(BuildContext context) { + Size size = MediaQuery.sizeOf(context); + + return Container( + margin: const EdgeInsets.all(8), + width: size.width * 0.23, + height: size.height * 0.07, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(2), + boxShadow: [ + BoxShadow( + offset: Offset( + widget.isSelected ? 0 : 1, widget.isSelected ? 4 : 2), + blurRadius: widget.isSelected ? 4 : 3, + spreadRadius: 0, + color: Colors.black.withOpacity(widget.isSelected ? 0.25 : 0.7)) + ], + gradient: LinearGradient( + colors: widget.isSelected + ? [ + AGLDemoColors.periwinkleColor, + AGLDemoColors.periwinkleColor.withOpacity(0.25) + ] + : [ + AGLDemoColors.neonBlueColor, + AGLDemoColors.neonBlueColor.withOpacity(0.2) + ], + begin: Alignment.centerLeft, + end: Alignment.centerRight), + border: Border.all(color: Colors.white12)), + child: Container( + padding: const EdgeInsets.all(2), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(2), + boxShadow: [ + BoxShadow( + offset: Offset( + widget.isSelected ? 0 : 1, widget.isSelected ? 4 : 2), + blurRadius: widget.isSelected ? 4 : 3, + spreadRadius: 0, + color: + Colors.black.withOpacity(widget.isSelected ? 0.25 : 0.7)) + ], + color: widget.isSelected + ? AGLDemoColors.neonBlueColor + : AGLDemoColors.buttonFillEnabledColor, + border: Border.all(color: Colors.white12)), + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: widget.onPressed, + child: Center( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: widget.child, + )), + ), + ), + ), + ); + } +} diff --git a/lib/presentation/screens/hvac/widgets/fan_focus.dart b/lib/presentation/screens/hvac/widgets/fan_focus.dart new file mode 100644 index 0000000..556c2c7 --- /dev/null +++ b/lib/presentation/screens/hvac/widgets/fan_focus.dart @@ -0,0 +1,116 @@ +import 'package:flutter_ics_homescreen/export.dart'; + +class FanFocus extends StatefulWidget { + const FanFocus( + {super.key, + required this.isSelected, + required this.focusType, + required this.onPressed}); + final bool isSelected; + final String focusType; + final VoidCallback onPressed; + @override + State createState() => _FanFocusState(); +} + +class _FanFocusState extends State { + @override + Widget build(BuildContext context) { + double height = MediaQuery.sizeOf(context).height * 0.10; + double iconSize = 32; + + return Container( + height: height, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: widget.isSelected + ? [ + AGLDemoColors.periwinkleColor, + AGLDemoColors.periwinkleColor.withOpacity(0.25) + ] + : [ + AGLDemoColors.jordyBlueColor, + AGLDemoColors.jordyBlueColor.withOpacity(0.2) + ]), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(widget.focusType == "top_half" ? 16 : 0), + topRight: Radius.circular(widget.focusType == "top_half" ? 16 : 0), + bottomLeft: + Radius.circular(widget.focusType == "bottom_half" ? 16 : 0), + bottomRight: + Radius.circular(widget.focusType == "bottom_half" ? 16 : 0)), + ), + child: Container( + margin: const EdgeInsets.all(1), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(widget.focusType == "top_half" ? 16 : 0), + topRight: + Radius.circular(widget.focusType == "top_half" ? 16 : 0), + bottomLeft: + Radius.circular(widget.focusType == "bottom_half" ? 16 : 0), + bottomRight: + Radius.circular(widget.focusType == "bottom_half" ? 16 : 0)), + color: widget.isSelected + ? AGLDemoColors.neonBlueColor + : AGLDemoColors.buttonFillEnabledColor, + image: const DecorationImage( + image: AssetImage("assets/PlusVector.png"), + opacity: 0.5, + fit: BoxFit.cover), + ), + child: Material( + color: Colors.transparent, + child: InkWell( + borderRadius: BorderRadius.only( + topLeft: + Radius.circular(widget.focusType == "top_half" ? 16 : 0), + topRight: + Radius.circular(widget.focusType == "top_half" ? 16 : 0), + bottomLeft: + Radius.circular(widget.focusType == "bottom_half" ? 16 : 0), + bottomRight: Radius.circular( + widget.focusType == "bottom_half" ? 16 : 0)), + onTap: widget.onPressed, + child: Row( + crossAxisAlignment: widget.focusType == "top_half" + ? CrossAxisAlignment.end + : CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(top: 38), + child: Icon( + Icons.arrow_forward, + color: Colors.white, + size: iconSize, + shadows: [ + Shadow( + offset: Offset(1, widget.isSelected ? 2 : 4), + blurRadius: widget.isSelected ? 3 : 4, + color: Colors.black.withOpacity(0.7)) + ], + ), + ), + Image.asset( + "assets/${widget.focusType == "top_half" ? widget.isSelected ? "head_selected" : "head" : widget.isSelected ? "legs_selected" : "legs"}.png", + //fit: BoxFit.contain, + // alignment: Alignment.bottomRight, + // width: widget.focusType == "top_half" ? 108 : 250, + // height: 180, + ), + ], + ), + SizedBox( + width: widget.focusType == "top_half" ? 5 : 40, + ) + ]), + ), + ), + ), + ); + } +} diff --git a/lib/presentation/screens/hvac/widgets/fan_speed_controls.dart b/lib/presentation/screens/hvac/widgets/fan_speed_controls.dart new file mode 100644 index 0000000..00f1181 --- /dev/null +++ b/lib/presentation/screens/hvac/widgets/fan_speed_controls.dart @@ -0,0 +1,251 @@ +import 'package:flutter_ics_homescreen/export.dart'; +import 'package:gradient_borders/gradient_borders.dart'; +import 'package:rive/rive.dart' as rive; + +class FanSpeedControls extends ConsumerStatefulWidget { + const FanSpeedControls({super.key}); + + @override + FanSpeedControlsState createState() => FanSpeedControlsState(); +} + +class FanSpeedControlsState extends ConsumerState + with TickerProviderStateMixin { + bool isPressed = false; + LinearGradient gradientEnable1 = const LinearGradient(colors: [ + Color(0xFF2962FF), + Color(0x802962FF), + ]); + LinearGradient gradientEnable2 = const LinearGradient(colors: [ + Color(0xFF1A237E), + Color(0xFF141F64), + ]); + bool isMainACSelected = false; + late AnimationController animationController; + double controlProgress = 0.0; + int selectedFanSpeed = 0; + late rive.RiveAnimationController _controller; + + bool _isPlaying = false; + + /// Tracks if the animation is playing by whether controller is running + bool get isPlaying => _controller.isActive; + + @override + void initState() { + super.initState(); + _controller = rive.OneShotAnimation( + 'Fan Spin', + autoplay: false, + onStop: () => setState(() => _isPlaying = false), + onStart: () => setState(() => _isPlaying = true), + ); + animationController = AnimationController( + vsync: this, + duration: const Duration(seconds: 1), + ); + + animationController.addListener(() { + setState(() { + // _currentColorIndex = (_currentColorIndex + 1) % colorsList.length; + }); // Trigger a rebuild to repaint the CustomPaint + }); + animationController.forward(); + } + + @override + void dispose() { + animationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + double size = MediaQuery.sizeOf(context).height * 0.13021; + double fanSpeedWidth = MediaQuery.sizeOf(context).width * 0.35; + double fanSpeedHeight = MediaQuery.sizeOf(context).height * 0.15; + double strokeWidth = MediaQuery.sizeOf(context).height * 0.03; + + double iconSize = 80; + + final vehicle = ref.watch(vehicleProvider.select((vehicle) => vehicle)); + selectedFanSpeed = vehicle.fanSpeed; + controlProgress = selectedFanSpeed * 0.3; + + return Stack( + children: [ + Center( + child: CustomPaint( + size: Size( + fanSpeedWidth, fanSpeedHeight), // Set the desired size here + painter: AnimatedColorPainter( + animationController, + controlProgress, + AGLDemoColors.blueGlowFillColor, + AGLDemoColors.backgroundInsetColor, + strokeWidth, + ), + ), + ), + Center( + child: Container( + margin: const EdgeInsets.only(top: 3), + // decoration: BoxDecoration( + // shape: BoxShape.circle, + // gradient: LinearGradient( + // colors: !isMainACSelected + // ? [ + // AGLDemoColors.neonBlueColor, + // AGLDemoColors.neonBlueColor.withOpacity(0.2) + // ] + // : [ + // const Color.fromARGB(255, 255, 193, 193) + // .withOpacity(0.2), + // const Color.fromARGB(255, 255, 193, 193) + // ]), + // boxShadow: isMainACSelected + // ? [ + // BoxShadow( + // offset: Offset( + // isMainACSelected ? 1 : 1, isMainACSelected ? 2 : 2), + // blurRadius: isMainACSelected ? 16 : 16, + // spreadRadius: 0, + // color: isMainACSelected + // ? Colors.black.withOpacity(0.5) + // : Colors.black) + // ] + // : [], + // ), + //border: Border.all(color: Colors.white12, width: 1)), + //width: 90, + //height: 90, + child: Container( + margin: const EdgeInsets.all(1), + decoration: BoxDecoration( + shape: BoxShape.circle, + image: const DecorationImage( + image: AssetImage("assets/PlusVector.png"), + ), + gradient: Gradient.lerp(gradientEnable1, gradientEnable2, 0.5), + // border: Border.all( + // color: isMainACSelected + // ? AGLDemoColors.buttonFillEnabledColor + // : Colors.white12, + // width: isMainACSelected ? 3 : 1), + border: const GradientBoxBorder( + width: 2, + gradient: LinearGradient( + colors: [ + Color(0x30C1D8FF), + Color(0xFFC1D8FF), + ], + ), + ), + ), + alignment: Alignment.center, + child: Material( + color: Colors.transparent, + child: InkWell( + splashColor: Colors.transparent, + hoverColor: Colors.transparent, + highlightColor: Colors.transparent, + customBorder: const CircleBorder(), + onTap: () { + _isPlaying ? null : _controller.isActive = true; + setState(() { + if (controlProgress >= 0.80) { + controlProgress = 0.0; + isMainACSelected = false; + animationController.reverse(); + } else { + isMainACSelected = true; + _controller.isActive = true; + _isPlaying = true; + controlProgress += 0.30; + animationController.forward(); + } + ref + .read(vehicleProvider.notifier) + .updateFanSpeed(controlProgress ~/ 0.3); + + // isMainACSelected = !isMainACSelected; + // if (controlProgress != 0.0) { + // previousProgress = controlProgress; + // } + // if (isMainACSelected) { + // controlProgress = previousProgress; + // animationController.forward(); + // } else { + // controlProgress = 0.0; + // animationController.reverse(); + // } + }); + }, + onTapDown: (details) { + setState(() { + gradientEnable1 = LinearGradient(colors: [ + const Color(0xFF2962FF).withOpacity(0.15), + const Color(0x802962FF).withOpacity(0.15), + ]); + gradientEnable2 = const LinearGradient(colors: [ + Color(0xFF1A237E), + Color(0xFF1C2D92), + ]); + }); + //change style + }, + onTapUp: (details) { + setState(() { + gradientEnable1 = const LinearGradient(colors: [ + Color(0xFF2962FF), + Color(0x802962FF), + ]); + gradientEnable2 = const LinearGradient(colors: [ + Color(0xFF1A237E), + Color(0xFF141F64), + ]); + }); + }, + child: Container( + width: size, + height: size, + alignment: Alignment.center, + child: !_isPlaying && controlProgress == 0.0 + ? SvgPicture.asset( + "assets/ACMainButtonOff.svg", + width: iconSize, + height: iconSize, + ) + // : !_isPlaying && controlProgress > 0.8 + // ? SvgPicture.asset( + // "assets/ACMainButton.svg", + // width: iconSize, + // height: iconSize, + // ) + : SizedBox( + width: iconSize, + height: iconSize, + child: rive.RiveAnimation.asset( + 'assets/new_file.riv', + controllers: [_controller], + onInit: (_) => setState(() { + _controller.isActive = true; + })))) + // Container( + // width: size, + // height: size, + // alignment: Alignment.center, + // child: SvgPicture.asset( + // "assets/ACMainButton.svg", + // width: iconSize, + // height: iconSize, + // ), + // ), + ), + ), + ), + )) + ], + ); + } +} diff --git a/lib/presentation/screens/hvac/widgets/semi_circle_painter.dart b/lib/presentation/screens/hvac/widgets/semi_circle_painter.dart new file mode 100644 index 0000000..e2003c5 --- /dev/null +++ b/lib/presentation/screens/hvac/widgets/semi_circle_painter.dart @@ -0,0 +1,109 @@ +import 'package:flutter_ics_homescreen/export.dart'; +import 'dart:math' as math; + +class AnimatedColorPainter extends CustomPainter { + final AnimationController animationController; + final double progress; + final Color progressColor; // New parameter for progress color + final Color backgroundColor; + final double strokeWidth; + + AnimatedColorPainter(this.animationController, this.progress, + this.progressColor, this.backgroundColor, this.strokeWidth); + + @override + void paint(Canvas canvas, Size size) { + // const strokeWidth = 25.0; + const borderWidth = 2.0; + + // Divide the arc into equal parts based on the number of colors + const arcAngle = math.pi; + const arcPart = arcAngle / 3; + const gapAngle = arcAngle / 150; + + // Calculate the current color index based on animation progress and progress value + final double normalizedProgress = progress * 3; + int currentColorIndex = + (animationController.value * normalizedProgress).floor(); + if (progress == 0.0) { + currentColorIndex = -1; // Force background color when progress is 0 + } + // Draw each part with a border and inner color + double startAngle = -math.pi; // Start from left + for (int i = 0; i < 3; i++) { + Color? currentColor = backgroundColor; + if (i <= currentColorIndex) { + // Use progress color if within progress range + currentColor = progressColor; + } else { + // Use background color if outside progress range + currentColor = backgroundColor; + } + + // Draw border + final borderPaint = Paint() + ..strokeWidth = strokeWidth + borderWidth + ..style = PaintingStyle.stroke + ..color = Colors.white12; + canvas.drawArc( + Rect.fromCircle( + center: Offset(size.width / 2, size.height / 2), + radius: size.width / 2, + ), + startAngle, + arcPart - 2 * gapAngle, + false, // Draw clockwise + borderPaint, + ); + + // Draw inner color + final colorPaint = Paint() + ..strokeWidth = strokeWidth + ..style = PaintingStyle.stroke + ..shader = _createColorShader(currentColor, size); + canvas.drawArc( + Rect.fromCircle( + center: Offset(size.width / 2, size.height / 2), + radius: size.width / 2, + ), + startAngle, + arcPart - 2 * gapAngle, + false, // Draw clockwise + colorPaint, + ); + + startAngle += arcPart + gapAngle; + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => true; + + Shader _createColorShader(Color color, Size size) { + if (color == progressColor) { + return const RadialGradient( + center: Alignment.center, + radius: 2, + tileMode: TileMode.repeated, + focal: Alignment.center, + focalRadius: 8, + colors: [ + AGLDemoColors.blueGlowFillColor, + AGLDemoColors.jordyBlueColor, + AGLDemoColors.neonBlueColor + ], + ).createShader( + Rect.fromCircle( + center: Offset(size.width / 2, size.height / 2), + radius: size.width / 2, + ), + ); + } + return LinearGradient(colors: [color, color]).createShader( + Rect.fromCircle( + center: Offset(size.width / 2, size.height / 2), + radius: size.width / 2, + ), + ); + } +} diff --git a/lib/presentation/screens/hvac/widgets/temperature_control.dart b/lib/presentation/screens/hvac/widgets/temperature_control.dart new file mode 100644 index 0000000..df83840 --- /dev/null +++ b/lib/presentation/screens/hvac/widgets/temperature_control.dart @@ -0,0 +1,255 @@ +import 'package:flutter_ics_homescreen/export.dart'; + +class TemperatureControl extends StatefulWidget { + const TemperatureControl({super.key, required this.temperature}); + final int temperature; + + @override + State createState() => _TemperatureControlState(); +} + +class _TemperatureControlState extends State { + int temperature = 0; + bool isUpButtonHighlighted = false; + bool isDownButtonHighlighted = false; + + @override + void initState() { + super.initState(); + setState(() { + temperature = widget.temperature; + }); + } + + onPressed({required String type}) { + setState(() { + if (type == "add") { + temperature = temperature + 1; + } else if (type == "subtract") { + temperature = temperature - 1; + } + }); + } + + @override + Widget build(BuildContext context) { + double iconSize = 32; + double height = MediaQuery.sizeOf(context).height * 0.0417; + double width = MediaQuery.sizeOf(context).width * 0.2112; + + return Column( + children: [ + Material( + color: Colors.transparent, + child: InkWell( + onHighlightChanged: (value) { + setState(() { + isUpButtonHighlighted = value; + }); + }, + onTap: () { + onPressed(type: "add"); + }, + child: SizedBox( + height: height, + width: width, + child: Image.asset( + "assets/${isUpButtonHighlighted ? 'UpPressed' : 'Up'}.png")), + ), + ), + // ClipRect( + // clipper: MyCustomClipper(type: "top"), + // child: ClipRRect( + // borderRadius: const BorderRadius.only( + // bottomLeft: Radius.circular(22), + // bottomRight: Radius.circular(22)), + // child: Container( + // height: height, + // width: width, + // decoration: BoxDecoration( + // boxShadow: [ + // BoxShadow( + // offset: const Offset(1, 2), + // blurRadius: 3, + // color: Colors.black.withOpacity(0.7)), + // ], + // gradient: LinearGradient(colors: [ + // AGLDemoColors.neonBlueColor, + // AGLDemoColors.neonBlueColor.withOpacity(0.20) + // ]), + // borderRadius: const BorderRadius.only( + // topLeft: Radius.circular(100), + // topRight: Radius.circular(100), + // bottomLeft: Radius.circular(10), + // bottomRight: Radius.circular(10))), + // child: Container( + // margin: const EdgeInsets.all(1), + // decoration: const BoxDecoration( + // color: AGLDemoColors.buttonFillEnabledColor, + // borderRadius: BorderRadius.only( + // topLeft: Radius.circular(100), + // topRight: Radius.circular(100), + // bottomLeft: Radius.circular(10), + // bottomRight: Radius.circular(10))), + // child: Material( + // color: Colors.transparent, + // child: InkWell( + // onTap: () { + // onPressed(type: "add"); + // }, + // child: Padding( + // padding: const EdgeInsets.only(bottom: 10), + // child: Icon( + // Icons.arrow_upward, + // color: Colors.white, + // size: iconSize, + // shadows: [ + // BoxShadow( + // offset: const Offset(1, 2), + // blurRadius: 3, + // color: Colors.black.withOpacity(0.7)), + // ], + // ), + // ), + // ), + // ), + // ), + // ), + // ), + // ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Text( + "$temperature°C", + style: GoogleFonts.brunoAce(fontSize: 44, height: 1.25), + ), + ), + Material( + color: Colors.transparent, + child: InkWell( + onHighlightChanged: (value) { + setState(() { + isDownButtonHighlighted = value; + }); + }, + onTap: () { + onPressed(type: "subtract"); + }, + child: SizedBox( + height: height, + width: width, + child: Image.asset( + "assets/${isDownButtonHighlighted ? 'DownPressed' : 'Down'}.png")), + ), + ), + // ClipRect( + // clipper: MyCustomClipper(type: "bottom"), + // child: ClipRRect( + // borderRadius: const BorderRadius.only( + // topLeft: Radius.circular(20), topRight: Radius.circular(20)), + // child: Container( + // height: height, + // width: width, + // decoration: BoxDecoration( + // boxShadow: [ + // BoxShadow( + // offset: const Offset(1, 2), + // blurRadius: 3, + // color: Colors.black.withOpacity(0.7)), + // ], + // gradient: LinearGradient(colors: [ + // AGLDemoColors.neonBlueColor, + // AGLDemoColors.neonBlueColor.withOpacity(0.20) + // ]), + // border: Border.all(color: Colors.white12), + // borderRadius: const BorderRadius.only( + // bottomLeft: Radius.circular(100), + // bottomRight: Radius.circular(100), + // topLeft: Radius.circular(10), + // topRight: Radius.circular(10))), + // child: Container( + // margin: const EdgeInsets.all(1), + // decoration: const BoxDecoration( + // color: AGLDemoColors.buttonFillEnabledColor, + // borderRadius: BorderRadius.only( + // bottomLeft: Radius.circular(100), + // bottomRight: Radius.circular(100), + // topLeft: Radius.circular(10), + // topRight: Radius.circular(10))), + // child: Material( + // color: Colors.transparent, + // child: InkWell( + // onTap: () { + // onPressed(type: "subtract"); + // }, + // child: Padding( + // padding: const EdgeInsets.only(top: 10), + // child: Icon( + // Icons.arrow_downward, + // color: Colors.white, + // size: iconSize, + // shadows: [ + // BoxShadow( + // offset: const Offset(1, 2), + // blurRadius: 3, + // color: Colors.black.withOpacity(0.7)), + // ], + // ), + // ), + // ), + // ), + // )), + // )), + ], + ); + } +} + +class MyCustomClipper extends CustomClipper { + final String type; + + MyCustomClipper({super.reclip, required this.type}); + @override + Rect getClip(Size size) { + // Clip 10 pixels from the top of the container + return Rect.fromPoints( + Offset(0, type == "top" ? 0 : 10), + Offset(size.width, type == "top" ? size.height - 10 : size.height), + ); + } + + @override + bool shouldReclip(CustomClipper oldClipper) { + return false; + } +} + +class CustomShapePainter extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = Colors.blue + ..strokeWidth = 5.0; + + final path = Path(); + + // Draw the top part of the oval + path.moveTo(0.0, size.height / 2.0); + path.quadraticBezierTo( + size.width / 3.0, size.height / 2.0, size.width / 2.0, size.height); + + // Draw the straight line for the bottom part + path.lineTo(size.width / 2.0, size.height); + + // Draw the left part of the oval + path.quadraticBezierTo(size.width / 3.0, 0.0, 0.0, 0.0); + + // Close the path + path.close(); + + canvas.drawPath(path, paint); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; +} -- cgit 1.2.3-korg