diff --git a/assets/functions_icons/ac_air.svg b/assets/functions_icons/ac_air.svg new file mode 100644 index 00000000..3c4fff0d --- /dev/null +++ b/assets/functions_icons/ac_air.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/functions_icons/ac_device.svg b/assets/functions_icons/ac_device.svg new file mode 100644 index 00000000..d5fbe2a6 --- /dev/null +++ b/assets/functions_icons/ac_device.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/assets/functions_icons/ac_sun.svg b/assets/functions_icons/ac_sun.svg new file mode 100644 index 00000000..33c7e01a --- /dev/null +++ b/assets/functions_icons/ac_sun.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/lib/pages/device_managment/ac/bloc/ac_bloc.dart b/lib/pages/device_managment/ac/bloc/ac_bloc.dart index 86b48d52..e36043f1 100644 --- a/lib/pages/device_managment/ac/bloc/ac_bloc.dart +++ b/lib/pages/device_managment/ac/bloc/ac_bloc.dart @@ -1,16 +1,31 @@ - import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.dart'; import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_state.dart'; +import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/services/devices_mang_api.dart'; - class AcBloc extends Bloc { + AcStatusModel deviceStatus = AcStatusModel( + uuid: '', + acSwitch: true, + modeString: 'hot', + tempSet: 300, + currentTemp: 315, + fanSpeedsString: 'low', + childLock: false, + ); AcBloc() : super(AcsInitialState()) { on(_onFetchAcStatus); - + on(_onAcControl); + on(_changeAcSwitch); + on(_increaseCoolTo); + on(_decreaseCoolTo); + on(_changeLockValue); + on(_changeAcMode); + on(_changeFanSpeed); } FutureOr _onFetchAcStatus( @@ -18,11 +33,121 @@ class AcBloc extends Bloc { emit(AcsLoadingState()); try { final status = - await DevicesManagementApi().getDeviceStatus(event.deviceId); - emit(ACStatusLoaded(status)); + await DevicesManagementApi().getDeviceStatus(event.deviceId); + deviceStatus = AcStatusModel.fromJson(status.productUuid, status.status); + emit(ACStatusLoaded(deviceStatus)); } catch (e) { - emit(AcsFailedState( error: e.toString())); + emit(AcsFailedState(error: e.toString())); } } + FutureOr _onAcControl(AcControl event, Emitter emit) async { + final oldValue = _getValueByCode(event.code); + + _updateLocalValue(event.code, event.value); + emit(ACStatusLoaded(deviceStatus)); + + try { + final status = Status(code: event.code, value: event.value); + final response = + await DevicesManagementApi().deviceControl(event.deviceId, status); + + if (!response) { + _updateLocalValue(event.code, oldValue); + emit(ACStatusLoaded(deviceStatus)); + emit(AcsFailedState(error: 'Failed to control the device.')); + } + } catch (e) { + _updateLocalValue(event.code, oldValue); + emit(ACStatusLoaded(deviceStatus)); + emit(AcsFailedState(error: 'Error controlling the device: $e')); + } + } + + void _updateLocalValue(String code, dynamic value) { + switch (code) { + case 'switch': + deviceStatus.acSwitch = value; + break; + case 'temp_set': + deviceStatus.tempSet = value; + break; + case 'mode': + deviceStatus.modeString = value; + deviceStatus.acMode = AcStatusModel.getACMode(value); + break; + case 'level': + deviceStatus.fanSpeedsString = value; + deviceStatus.acFanSpeed = AcStatusModel.getFanSpeed(value); + break; + case 'child_lock': + deviceStatus.childLock = value; + break; + default: + break; + } + } + + dynamic _getValueByCode(String code) { + switch (code) { + case 'switch': + return deviceStatus.acSwitch; + case 'temp_set': + return deviceStatus.tempSet; + case 'mode': + return deviceStatus.modeString; + case 'level': + return deviceStatus.fanSpeedsString; + case 'child_lock': + return deviceStatus.childLock; + default: + return null; + } + } + + FutureOr _changeAcSwitch(AcSwitch event, Emitter emit) async { + final newValue = !event.acSwitch; + add(AcControl(deviceId: event.deviceId, code: 'switch', value: newValue)); + } + + FutureOr _increaseCoolTo( + IncreaseCoolToTemp event, Emitter emit) async { + final newValue = (event.value * 10).toInt() + 5; + if (_isValidTemperature(newValue)) { + add(AcControl( + deviceId: event.deviceId, code: 'temp_set', value: newValue)); + } + } + + FutureOr _decreaseCoolTo( + DecreaseCoolToTemp event, Emitter emit) async { + final newValue = (event.value * 10).toInt() - 5; + if (_isValidTemperature(newValue)) { + add(AcControl( + deviceId: event.deviceId, code: 'temp_set', value: newValue)); + } + } + + FutureOr _changeLockValue( + ChangeLock event, Emitter emit) async { + final newValue = !event.lockBool; + add(AcControl( + deviceId: event.deviceId, code: 'child_lock', value: newValue)); + } + + FutureOr _changeAcMode( + ChangeAcMode event, Emitter emit) async { + final newValue = AcStatusModel.getACMode(event.tempModes.name); + add(AcControl(deviceId: event.deviceId, code: 'mode', value: newValue)); + } + + FutureOr _changeFanSpeed( + ChangeFanSpeed event, Emitter emit) async { + final newValue = AcStatusModel.getFanSpeed(event.fanSpeeds.name); + add(AcControl(deviceId: event.deviceId, code: 'level', value: newValue)); + } + + bool _isValidTemperature(int value) { + return value >= 200 && value <= 300; + } } diff --git a/lib/pages/device_managment/ac/bloc/ac_event.dart b/lib/pages/device_managment/ac/bloc/ac_event.dart index cf1a1312..026bba8f 100644 --- a/lib/pages/device_managment/ac/bloc/ac_event.dart +++ b/lib/pages/device_managment/ac/bloc/ac_event.dart @@ -1,6 +1,5 @@ - - import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart'; sealed class AcsEvent extends Equatable { const AcsEvent(); @@ -21,12 +20,74 @@ class AcFetchDeviceStatus extends AcsEvent { class AcControl extends AcsEvent { final String deviceId; final String code; - final bool value; + final dynamic value; - const AcControl( - {required this.deviceId, required this.code, required this.value}); + const AcControl({ + required this.deviceId, + required this.code, + required this.value, + }); @override List get props => [deviceId, code, value]; } +class AcSwitch extends AcsEvent { + final bool acSwitch; + final String deviceId; + + const AcSwitch({required this.acSwitch, required this.deviceId}); + + @override + List get props => [acSwitch, deviceId]; +} + +class IncreaseCoolToTemp extends AcsEvent { + final double value; + final String deviceId; + + const IncreaseCoolToTemp({required this.value, required this.deviceId}); + + @override + List get props => [value, deviceId]; +} + +class DecreaseCoolToTemp extends AcsEvent { + final double value; + final String deviceId; + + const DecreaseCoolToTemp({required this.value, required this.deviceId}); + + @override + List get props => [value, deviceId]; +} + +class ChangeLock extends AcsEvent { + final bool lockBool; + final String deviceId; + + const ChangeLock({required this.lockBool, required this.deviceId}); + + @override + List get props => [lockBool, deviceId]; +} + +class ChangeAcMode extends AcsEvent { + final TempModes tempModes; + final String deviceId; + + const ChangeAcMode({required this.tempModes, required this.deviceId}); + + @override + List get props => [tempModes, deviceId]; +} + +class ChangeFanSpeed extends AcsEvent { + final FanSpeeds fanSpeeds; + final String deviceId; + + const ChangeFanSpeed({required this.fanSpeeds, required this.deviceId}); + + @override + List get props => [fanSpeeds, deviceId]; +} diff --git a/lib/pages/device_managment/ac/bloc/ac_state.dart b/lib/pages/device_managment/ac/bloc/ac_state.dart index 0ebc523b..4a3a7e80 100644 --- a/lib/pages/device_managment/ac/bloc/ac_state.dart +++ b/lib/pages/device_managment/ac/bloc/ac_state.dart @@ -1,7 +1,5 @@ import 'package:equatable/equatable.dart'; import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart'; -import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; -import 'package:syncrow_web/pages/visitor_password/model/device_model.dart'; abstract class AcsState extends Equatable { const AcsState(); @@ -14,9 +12,8 @@ class AcsInitialState extends AcsState {} class AcsLoadingState extends AcsState {} - class ACStatusLoaded extends AcsState { - final DeviceStatus status; + final AcStatusModel status; const ACStatusLoaded(this.status); @@ -24,46 +21,49 @@ class ACStatusLoaded extends AcsState { List get props => [status]; } -class AcChangeLoading extends AcsState { - final AcStatusModel acStatusModel; - const AcChangeLoading({required this.acStatusModel}); +class AcSwitchChanged extends AcsState { + final bool acSwitch; + + const AcSwitchChanged(this.acSwitch); @override - List get props => [acStatusModel]; + List get props => [acSwitch]; } -class AcModifyingState extends AcsState { - final AcStatusModel acStatusModel; - const AcModifyingState({required this.acStatusModel}); +class AcTempChanged extends AcsState { + final double tempSet; + + const AcTempChanged(this.tempSet); @override - List get props => [acStatusModel]; + List get props => [tempSet]; } -class GetAcStatusState extends AcsState { - final AcStatusModel acStatusModel; - const GetAcStatusState({required this.acStatusModel}); +class AcModeChanged extends AcsState { + final TempModes mode; + + const AcModeChanged(this.mode); @override - List get props => [acStatusModel]; + List get props => [mode]; } -class GetAllAcsStatusState extends AcsState { - final List allAcsStatues; - final List allAcs; - final bool allOn; - final bool allTempSame; - final int temp; +class AcFanSpeedChanged extends AcsState { + final FanSpeeds fanSpeed; - const GetAllAcsStatusState( - {required this.allAcsStatues, - required this.allAcs, - required this.allOn, - required this.allTempSame, - required this.temp}); + const AcFanSpeedChanged(this.fanSpeed); @override - List get props => [allAcsStatues, allAcs, allAcs, allTempSame, temp]; + List get props => [fanSpeed]; +} + +class AcLockChanged extends AcsState { + final bool lock; + + const AcLockChanged(this.lock); + + @override + List get props => [lock]; } class AcsFailedState extends AcsState { diff --git a/lib/pages/device_managment/ac/control_list/ac_mode.dart b/lib/pages/device_managment/ac/control_list/ac_mode.dart new file mode 100644 index 00000000..8c13e1fb --- /dev/null +++ b/lib/pages/device_managment/ac/control_list/ac_mode.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.dart'; + +class AcMode extends StatelessWidget { + const AcMode({ + super.key, + required this.value, + required this.code, + required this.deviceId, + }); + + final TempModes value; + final String code; + final String deviceId; + + void _onModeSelected(BuildContext context, TempModes mode) { + context.read().add( + ChangeAcMode(tempModes: mode, deviceId: deviceId), + ); + } + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: ColorsManager.greyColor.withOpacity(0.2), + border: Border.all(color: ColorsManager.boxDivider), + ), + padding: const EdgeInsets.all(16), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _buildIconContainer(context, TempModes.cold, Assets.freezing, + value == TempModes.cold), + _buildIconContainer( + context, TempModes.hot, Assets.acSun, value == TempModes.hot), + _buildIconContainer(context, TempModes.wind, Assets.acAirConditioner, + value == TempModes.wind), + ], + ), + ); + } + + Widget _buildIconContainer( + BuildContext context, TempModes mode, String assetPath, bool isSelected) { + return Flexible( + child: GestureDetector( + onTap: () { + _onModeSelected(context, mode); + }, + child: Container( + width: 50, + height: 50, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.whiteColors, + border: Border.all( + color: isSelected ? Colors.blue : Colors.transparent, + width: 2.0, + ), + ), + margin: const EdgeInsets.symmetric(horizontal: 4), + padding: const EdgeInsets.all(4), + child: ClipOval( + child: SvgPicture.asset( + assetPath, + fit: BoxFit.contain, + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/device_managment/ac/control_list/ac_toggle.dart b/lib/pages/device_managment/ac/control_list/ac_toggle.dart index ffeba102..53f79761 100644 --- a/lib/pages/device_managment/ac/control_list/ac_toggle.dart +++ b/lib/pages/device_managment/ac/control_list/ac_toggle.dart @@ -1,43 +1,85 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/device_managment/living_room_switch/bloc/living_room_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; class AcToggle extends StatelessWidget { - const AcToggle( - {super.key, - required this.value, - required this.code, - required this.deviceId}); + const AcToggle({ + super.key, + required this.value, + required this.code, + required this.deviceId, + this.icon, + this.description, + }); final bool value; final String code; final String deviceId; + final String? icon; + final String? description; @override Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - ClipOval( - child: Transform.scale( - scale: .8, - child: CupertinoSwitch( - value:true, - onChanged: (value) { - - }, - applyTheme: true, - ), - ),), - ], - ), - - ], + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: ColorsManager.greyColor.withOpacity(0.2), + border: Border.all(color: ColorsManager.boxDivider), + ), + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipOval( + child: Container( + color: ColorsManager.whiteColors, + child: SvgPicture.asset( + icon ?? Assets.acDevice, + width: 60, + height: 60, + fit: BoxFit.cover, + ), + )), + SizedBox( + height: 20, + width: 35, + child: CupertinoSwitch( + activeColor: ColorsManager.dialogBlueTitle, + value: value, + onChanged: (newValue) { + context.read().add( + AcControl( + deviceId: deviceId, + code: code, + value: newValue, + ), + ); + }, + ), + ), + ], + ), + const Spacer(), + Center( + child: Text( + description ?? 'ThermoState', + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 14, + ), + ), + ), + ], + ), ); } } diff --git a/lib/pages/device_managment/ac/control_list/current_temp.dart b/lib/pages/device_managment/ac/control_list/current_temp.dart new file mode 100644 index 00000000..bb3cfbfa --- /dev/null +++ b/lib/pages/device_managment/ac/control_list/current_temp.dart @@ -0,0 +1,134 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.dart'; +import 'package:syncrow_web/pages/device_managment/shared/celciuse_symbol.dart'; +import 'package:syncrow_web/pages/device_managment/shared/increament_decreament.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart'; + +class CurrentTemp extends StatefulWidget { + const CurrentTemp({ + super.key, + required this.code, + required this.deviceId, + required this.currentTemp, + required this.tempSet, + }); + + final String code; + final String deviceId; + final int currentTemp; + final int tempSet; + + @override + State createState() => _CurrentTempState(); +} + +class _CurrentTempState extends State { + late double _adjustedValue; + Timer? _debounce; + + @override + void initState() { + super.initState(); + _adjustedValue = _initialAdjustedValue(widget.tempSet); + } + + double _initialAdjustedValue(dynamic value) { + if (value is int || value is double) { + double doubleValue = value.toDouble(); + return doubleValue > 99 ? doubleValue / 10 : doubleValue; + } else { + throw ArgumentError('Invalid value type: Expected int or double'); + } + } + + void _onValueChanged(double newValue) { + if (_debounce?.isActive ?? false) { + _debounce?.cancel(); + } + _debounce = Timer(const Duration(milliseconds: 500), () { + context.read().add( + AcControl( + deviceId: widget.deviceId, + code: widget.code, + value: newValue * 10, + ), + ); + }); + } + + @override + void dispose() { + _debounce?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: ColorsManager.greyColor.withOpacity(0.2), + border: Border.all(color: ColorsManager.boxDivider), + ), + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Current Temperature', + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith(color: Colors.grey), + ), + const SizedBox( + height: 5, + ), + Row( + children: [ + Text( + (widget.currentTemp > 99 + ? widget.currentTemp / 10 + : widget.currentTemp) + .toString(), + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith(color: Colors.grey), + ), + const CelsiusSymbol( + color: Colors.grey, + ) + ], + ), + ], + ), + const Spacer(), + IncrementDecrementWidget( + value: _adjustedValue.toString(), + description: '°C', + descriptionColor: ColorsManager.dialogBlueTitle, + onIncrement: () { + setState(() { + _adjustedValue++; + }); + _onValueChanged(_adjustedValue); + }, + onDecrement: () { + setState(() { + _adjustedValue--; + }); + _onValueChanged(_adjustedValue); + }), + ], + ), + ); + } +} diff --git a/lib/pages/device_managment/ac/helper/ac_helper.dart b/lib/pages/device_managment/ac/helper/ac_helper.dart new file mode 100644 index 00000000..1614f2b2 --- /dev/null +++ b/lib/pages/device_managment/ac/helper/ac_helper.dart @@ -0,0 +1,35 @@ +// import 'package:flutter/cupertino.dart'; +// import 'package:syncrow_web/pages/device_managment/ac/control_list/ac_mode.dart'; +// import 'package:syncrow_web/pages/device_managment/ac/control_list/ac_toggle.dart'; +// import 'package:syncrow_web/pages/device_managment/ac/control_list/current_temp.dart'; +// import 'package:syncrow_web/utils/constants/assets.dart'; + +// mixin ACHelper { +// Widget acHelperControlWidgets({ +// required dynamic value, +// required String code, +// required String deviceId, +// }) { +// switch (code) { +// case 'switch': +// return AcToggle(value: value, code: code, deviceId: deviceId); +// case 'temp_current': +// return CurrentTemp(value: value, code: 'temp_set', deviceId: deviceId); +// case 'temp_set': +// return SizedBox(); +// case 'mode': +// return AcMode(value: value, code: code, deviceId: deviceId); +// case 'level': +// return SizedBox(); +// case 'child_lock': +// return AcToggle( +// value: value, +// code: code, +// deviceId: deviceId, +// icon: Assets.childLock, +// description: 'Child Lock'); +// default: +// return const SizedBox(); +// } +// } +// } diff --git a/lib/pages/device_managment/ac/model/ac_model.dart b/lib/pages/device_managment/ac/model/ac_model.dart index 566300ec..5626c720 100644 --- a/lib/pages/device_managment/ac/model/ac_model.dart +++ b/lib/pages/device_managment/ac/model/ac_model.dart @@ -1,28 +1,33 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; -import 'package:syncrow_web/utils/constants/const.dart'; + +enum TempModes { hot, cold, wind } + +enum FanSpeeds { auto, low, middle, high } class AcStatusModel { + String uuid; bool acSwitch; String modeString; int tempSet; int currentTemp; String fanSpeedsString; bool childLock; - // late TempModes acMode; - // late FanSpeeds acFanSpeed; + late TempModes acMode; + late FanSpeeds acFanSpeed; AcStatusModel( - {required this.acSwitch, + {required this.uuid, + required this.acSwitch, required this.modeString, required this.tempSet, required this.currentTemp, required this.fanSpeedsString, required this.childLock}) { - // acMode = getACMode(modeString); - // acFanSpeed = getFanSpeed(fanSpeedsString); + acMode = getACMode(modeString); + acFanSpeed = getFanSpeed(fanSpeedsString); } - factory AcStatusModel.fromJson(List jsonList) { + factory AcStatusModel.fromJson(String id, List jsonList) { late bool _acSwitch; late String _mode; late int _tempSet; @@ -45,6 +50,7 @@ class AcStatusModel { } } return AcStatusModel( + uuid: id, acSwitch: _acSwitch, modeString: _mode, tempSet: _tempSet, diff --git a/lib/pages/device_managment/ac/view/ac_device_control.dart b/lib/pages/device_managment/ac/view/ac_device_control.dart index b5be0d74..a4f4b524 100644 --- a/lib/pages/device_managment/ac/view/ac_device_control.dart +++ b/lib/pages/device_managment/ac/view/ac_device_control.dart @@ -3,68 +3,72 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart'; import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.dart'; import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_state.dart'; -import 'package:syncrow_web/pages/device_managment/all_devices/helper/ac_helper.dart'; -import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; +import 'package:syncrow_web/pages/device_managment/ac/control_list/ac_mode.dart'; +import 'package:syncrow_web/pages/device_managment/ac/control_list/ac_toggle.dart'; +import 'package:syncrow_web/pages/device_managment/ac/control_list/current_temp.dart'; +import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; -class AcDeviceControl extends StatelessWidget with ACHelper { +class AcDeviceControl extends StatelessWidget with HelperResponsiveLayout { const AcDeviceControl({super.key, required this.device}); final AllDevicesModel device; @override Widget build(BuildContext context) { + final isLarge = isLargeScreenSize(context); + final isMedium = isMediumScreenSize(context); return BlocProvider( - create: (context) => - AcBloc()..add(AcFetchDeviceStatus(device.uuid!)), - child: BlocListener( - listener: (context, state) { - + create: (context) => AcBloc()..add(AcFetchDeviceStatus(device.uuid!)), + child: BlocBuilder( + builder: (context, state) { + if (state is AcsLoadingState) { + return const Center(child: CircularProgressIndicator()); + } else if (state is ACStatusLoaded) { + return _buildStatusControls(state.status, isLarge, isMedium); + } else { + return const Center(child: Text('Error fetching status')); + } }, - child: BlocBuilder( - builder: (context, state) { - if (state is AcsLoadingState) { - return const Center(child: CircularProgressIndicator()); - } else if (state is ACStatusLoaded) { - return _buildStatusControls(state.status.status); - } else { - return const Center(child: Text('Error fetching status')); - } - }, - ), ), ); } - Widget _buildStatusControls(List statuses) { - return GridView.builder( - padding: const EdgeInsets.symmetric(horizontal: 40), + Widget _buildStatusControls( + AcStatusModel statuses, bool isLarge, bool isMedium) { + return GridView( + padding: const EdgeInsets.symmetric(horizontal: 50), shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: isLarge + ? 3 + : isMedium + ? 2 + : 1, mainAxisExtent: 133, crossAxisSpacing: 12, mainAxisSpacing: 12, ), - itemCount: statuses.length, - itemBuilder: (context, index) { - final status = statuses[index]; - - return Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - color: ColorsManager.greyColor.withOpacity(0.2), - border: Border.all(color: ColorsManager.boxDivider), - ), - padding: const EdgeInsets.all(16), - child: ACHelperControlWidgets( - value: status.value, - code: status.code, - deviceId: device.uuid!), - ); - }, + children: [ + AcToggle( + value: statuses.acSwitch, + code: 'switch', + deviceId: statuses.uuid, + ), + AcMode( + value: statuses.acMode, + code: 'mode', + deviceId: statuses.uuid, + ), + CurrentTemp( + currentTemp: statuses.currentTemp, + tempSet: statuses.tempSet, + code: 'temp_set', + deviceId: statuses.uuid, + ), + ], ); } } diff --git a/lib/pages/device_managment/all_devices/helper/ac_helper.dart b/lib/pages/device_managment/all_devices/helper/ac_helper.dart deleted file mode 100644 index 766f9c5a..00000000 --- a/lib/pages/device_managment/all_devices/helper/ac_helper.dart +++ /dev/null @@ -1,19 +0,0 @@ - -import 'package:flutter/cupertino.dart'; -import 'package:syncrow_web/pages/device_managment/ac/control_list/ac_toggle.dart'; - -mixin ACHelper { - Widget ACHelperControlWidgets( - {required bool value, required String code, required String deviceId}) { - switch (code) { - case 'switch_1': - return AcToggle(value: value, code: code, deviceId: deviceId); - case 'switch_2': - return SizedBox(); - case 'switch_3': - return SizedBox(); - default: - return const SizedBox(); - } - } -} diff --git a/lib/pages/device_managment/living_room_switch/bloc/living_room_bloc.dart b/lib/pages/device_managment/living_room_switch/bloc/living_room_bloc.dart index d13d9c0c..55c8ad84 100644 --- a/lib/pages/device_managment/living_room_switch/bloc/living_room_bloc.dart +++ b/lib/pages/device_managment/living_room_switch/bloc/living_room_bloc.dart @@ -35,9 +35,12 @@ class LivingRoomBloc extends Bloc { await DevicesManagementApi().deviceControl(event.deviceId, status); if (response) { - emit(LivingRoomControlSuccess()); await Future.delayed(const Duration(milliseconds: 500)); - add(LivingRoomFetchDeviceStatus(event.deviceId)); + final newStatus = + await DevicesManagementApi().getDeviceStatus(event.deviceId); + emit(LivingRoomDeviceStatusLoaded(newStatus)); + } else { + emit(const LivingRoomControlError('Failed to control the device.')); } } catch (e) { emit(LivingRoomControlError('Error controlling the device: $e')); diff --git a/lib/pages/device_managment/living_room_switch/control_list/cieling_light.dart b/lib/pages/device_managment/living_room_switch/control_list/cieling_light.dart index d8479f3e..b529e6e6 100644 --- a/lib/pages/device_managment/living_room_switch/control_list/cieling_light.dart +++ b/lib/pages/device_managment/living_room_switch/control_list/cieling_light.dart @@ -41,6 +41,7 @@ class CeilingLight extends StatelessWidget { width: 35, child: CupertinoSwitch( value: value, + activeColor: ColorsManager.dialogBlueTitle, onChanged: (newValue) { context.read().add( LivingRoomControl( diff --git a/lib/pages/device_managment/living_room_switch/control_list/spot_light.dart b/lib/pages/device_managment/living_room_switch/control_list/spot_light.dart index e3717090..14c4fe5e 100644 --- a/lib/pages/device_managment/living_room_switch/control_list/spot_light.dart +++ b/lib/pages/device_managment/living_room_switch/control_list/spot_light.dart @@ -41,6 +41,7 @@ class SpotLight extends StatelessWidget { width: 35, child: CupertinoSwitch( value: value, + activeColor: ColorsManager.dialogBlueTitle, onChanged: (newValue) { context.read().add( LivingRoomControl( diff --git a/lib/pages/device_managment/living_room_switch/control_list/wall_light.dart b/lib/pages/device_managment/living_room_switch/control_list/wall_light.dart index c82dd89d..8e168ec0 100644 --- a/lib/pages/device_managment/living_room_switch/control_list/wall_light.dart +++ b/lib/pages/device_managment/living_room_switch/control_list/wall_light.dart @@ -41,6 +41,7 @@ class WallLight extends StatelessWidget { width: 35, child: CupertinoSwitch( value: value, + activeColor: ColorsManager.dialogBlueTitle, onChanged: (newValue) { context.read().add( LivingRoomControl( diff --git a/lib/pages/device_managment/living_room_switch/helper/living_room_helper.dart b/lib/pages/device_managment/living_room_switch/helper/living_room_helper.dart index c97712cf..2f978315 100644 --- a/lib/pages/device_managment/living_room_switch/helper/living_room_helper.dart +++ b/lib/pages/device_managment/living_room_switch/helper/living_room_helper.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:syncrow_web/pages/device_managment/ac/control_list/ac_toggle.dart'; import 'package:syncrow_web/pages/device_managment/living_room_switch/control_list/cieling_light.dart'; import 'package:syncrow_web/pages/device_managment/living_room_switch/control_list/spot_light.dart'; import 'package:syncrow_web/pages/device_managment/living_room_switch/control_list/wall_light.dart'; diff --git a/lib/pages/device_managment/living_room_switch/view/living_room_device_control.dart b/lib/pages/device_managment/living_room_switch/view/living_room_device_control.dart index 71d6062b..d9f9b417 100644 --- a/lib/pages/device_managment/living_room_switch/view/living_room_device_control.dart +++ b/lib/pages/device_managment/living_room_switch/view/living_room_device_control.dart @@ -16,25 +16,19 @@ class LivingRoomDeviceControl extends StatelessWidget with LivingRoomHelper { return BlocProvider( create: (context) => LivingRoomBloc()..add(LivingRoomFetchDeviceStatus(device.uuid!)), - child: BlocListener( - listener: (context, state) { - if (state is LivingRoomControlSuccess) { - context - .read() - .add(LivingRoomFetchDeviceStatus(device.uuid!)); + child: BlocBuilder( + builder: (context, state) { + if (state is LivingRoomDeviceStatusLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is LivingRoomDeviceStatusLoaded) { + return _buildStatusControls(state.status.status); + } else if ((state is LivingRoomDeviceManagementError) || + (state is LivingRoomControlError)) { + return const Center(child: Text('Error fetching status')); + } else { + return const Center(child: CircularProgressIndicator()); } }, - child: BlocBuilder( - builder: (context, state) { - if (state is LivingRoomDeviceStatusLoading) { - return const Center(child: CircularProgressIndicator()); - } else if (state is LivingRoomDeviceStatusLoaded) { - return _buildStatusControls(state.status.status); - } else { - return const Center(child: Text('Error fetching status')); - } - }, - ), ), ); } @@ -50,7 +44,7 @@ class LivingRoomDeviceControl extends StatelessWidget with LivingRoomHelper { crossAxisSpacing: 12, mainAxisSpacing: 12, ), - itemCount: statuses.length, + itemCount: 3, itemBuilder: (context, index) { final status = statuses[index]; diff --git a/lib/pages/device_managment/shared/celciuse_symbol.dart b/lib/pages/device_managment/shared/celciuse_symbol.dart new file mode 100644 index 00000000..b8db1439 --- /dev/null +++ b/lib/pages/device_managment/shared/celciuse_symbol.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +class CelsiusSymbol extends StatelessWidget { + const CelsiusSymbol({this.color, super.key}); + + final Color? color; + + @override + Widget build(BuildContext context) { + return Text( + '°C', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: color, + ), + ); + } +} diff --git a/lib/pages/device_managment/shared/device_control_dialog.dart b/lib/pages/device_managment/shared/device_control_dialog.dart index 190ebd52..ab0e0305 100644 --- a/lib/pages/device_managment/shared/device_control_dialog.dart +++ b/lib/pages/device_managment/shared/device_control_dialog.dart @@ -44,7 +44,7 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode { const SizedBox(height: 20), //// BUILD DEVICE CONTROLS /// - //// ROUTE TO SPECIFIC CONTROL VIEW BASED ON DEVICE CATEGORY + //// ROUTE TO SPECIFIC CONTROL VIEW BASED ON DEVICE CATEGORY routeControlsWidgets(device: device), ], ), @@ -56,7 +56,7 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode { Widget _buildDeviceInfoSection() { return Padding( - padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 50), + padding: const EdgeInsets.symmetric(vertical: 50, horizontal: 50), child: Table( children: [ TableRow( diff --git a/lib/pages/device_managment/shared/increament_decreament.dart b/lib/pages/device_managment/shared/increament_decreament.dart index 3e3c8c1d..bb5fd175 100644 --- a/lib/pages/device_managment/shared/increament_decreament.dart +++ b/lib/pages/device_managment/shared/increament_decreament.dart @@ -6,6 +6,7 @@ class IncrementDecrementWidget extends StatelessWidget { final String description; final VoidCallback onIncrement; final VoidCallback onDecrement; + final Color? descriptionColor; const IncrementDecrementWidget({ super.key, @@ -13,6 +14,7 @@ class IncrementDecrementWidget extends StatelessWidget { required this.description, required this.onIncrement, required this.onDecrement, + this.descriptionColor, }); @override @@ -22,18 +24,20 @@ class IncrementDecrementWidget extends StatelessWidget { children: [ Material( type: MaterialType.transparency, - child: ClipRRect( - borderRadius: BorderRadius.circular(100), - child: InkWell( - splashColor: Colors.transparent, - highlightColor: Colors.transparent, - onTap: onDecrement, - child: const Padding( - padding: EdgeInsets.all(8.0), - child: Icon( - Icons.remove, - color: ColorsManager.greyColor, - size: 32, + child: Flexible( + child: ClipRRect( + borderRadius: BorderRadius.circular(100), + child: InkWell( + splashColor: Colors.transparent, + highlightColor: Colors.transparent, + onTap: onDecrement, + child: const Padding( + padding: EdgeInsets.all(8.0), + child: Icon( + Icons.remove, + color: ColorsManager.greyColor, + size: 28, + ), ), ), ), @@ -53,9 +57,9 @@ class IncrementDecrementWidget extends StatelessWidget { ), TextSpan( text: description, - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: ColorsManager.blackColor, + color: descriptionColor ?? ColorsManager.blackColor, fontWeight: FontWeight.bold, ), ), @@ -64,18 +68,20 @@ class IncrementDecrementWidget extends StatelessWidget { ), Material( type: MaterialType.transparency, - child: ClipRRect( - borderRadius: BorderRadius.circular(100), - child: InkWell( - splashColor: Colors.transparent, - highlightColor: Colors.transparent, - onTap: onIncrement, - child: const Padding( - padding: EdgeInsets.all(8.0), - child: Icon( - Icons.add, - color: ColorsManager.greyColor, - size: 32, + child: Flexible( + child: ClipRRect( + borderRadius: BorderRadius.circular(100), + child: InkWell( + splashColor: Colors.transparent, + highlightColor: Colors.transparent, + onTap: onIncrement, + child: const Padding( + padding: EdgeInsets.all(8.0), + child: Icon( + Icons.add, + color: ColorsManager.greyColor, + size: 28, + ), ), ), ), diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index 8a89358c..17f759e0 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -139,4 +139,9 @@ class Assets { "presence_sensor_assets/presence-sensor-assets/presence-sensor-motion.svg"; static const String lightPulp = "functions_icons/light_pulb.svg"; + + static const String acDevice = "functions_icons/ac_device.svg"; + + static const String acAirConditioner = "functions_icons/ac_air.svg"; + static const String acSun = "functions_icons/ac_sun.svg"; }