From 65776527027c18d911e9bfad3f728d293a66882f Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Wed, 3 Apr 2024 18:53:54 +0300 Subject: [PATCH] Refactor AC mode control unit widget Move fan speed and temperature mode logic to the DevicesCubit for better separation of concerns and improved code readability. Update widget to use BlocBuilder for state management and simplify control logic. --- lib/features/devices/bloc/devices_cubit.dart | 51 ++++--- lib/features/devices/bloc/devices_state.dart | 24 +++- .../devices/model/device_control_model.dart | 2 +- .../view/widgets/ACs/ac_interface.dart | 125 ++++++++++-------- .../widgets/ACs/ac_mode_control_unit.dart | 125 ++++++++++-------- 5 files changed, 197 insertions(+), 130 deletions(-) diff --git a/lib/features/devices/bloc/devices_cubit.dart b/lib/features/devices/bloc/devices_cubit.dart index db7794b..6449d93 100644 --- a/lib/features/devices/bloc/devices_cubit.dart +++ b/lib/features/devices/bloc/devices_cubit.dart @@ -23,7 +23,7 @@ import 'package:syncrow_app/utils/resource_manager/constants.dart'; part 'devices_state.dart'; class DevicesCubit extends Cubit { - DevicesCubit() : super(DevicesInitial()) { + DevicesCubit._() : super(DevicesInitial()) { if (HomeCubit.getInstance().selectedSpace != null) { fetchGroups(HomeCubit.getInstance().selectedSpace!.id!); for (var room in HomeCubit.getInstance().selectedSpace!.rooms!) { @@ -33,16 +33,19 @@ class DevicesCubit extends Cubit { } bool _isClosed = false; - // static DevicesCubit? _instance; - // static DevicesCubit.get(context) { - // // If an instance already exists, return it - // _instance ??= DevicesCubit._(); - // return _instance!; - // } + static DevicesCubit? _instance; + static DevicesCubit getInstance() { + // If an instance already exists, return it + _instance ??= DevicesCubit._(); + return _instance!; + } + + DeviceModel? selectedDevice; @override Future close() { _isClosed = true; + _instance = null; return super.close(); } @@ -246,11 +249,28 @@ class DevicesCubit extends Cubit { emitSafe(DevicesCategoryChanged()); } - deviceControl(DeviceControlModel control) async { - emitSafe(DeviceControlLoading()); +///////////////////////// API CALLS ////////////////////////// + deviceControl(DeviceControlModel control, String deviceId) async { + emitSafe(DeviceControlLoading( + code: control.code, + )); try { - await DevicesAPI.controlDevice(control); - emitSafe(DeviceControlSuccess()); + await DevicesAPI.controlDevice(control).then((value) { + emitSafe(DeviceControlSuccess( + code: control.code, + )); + if (value['result'] ?? false) { + Future.delayed(const Duration(milliseconds: 400), () { + getDevicesStatues( + deviceId, + HomeCubit.getInstance().selectedSpace!.rooms!.indexOf( + HomeCubit.getInstance().selectedRoom!, + ), + code: control.code, + ); + }); + } + }); } on DioException catch (e) { emitSafe(DeviceControlError(ServerFailure.fromDioError(e).errMessage)); } @@ -297,11 +317,11 @@ class DevicesCubit extends Cubit { } } - getDevicesStatues(String deviceId, int roomIndex) async { + getDevicesStatues(String deviceId, int roomIndex, {String? code}) async { if (_isClosed) return; try { - emitSafe(GetDeviceStatusLoading()); + emitSafe(GetDeviceStatusLoading(code: code)); int deviceIndex = HomeCubit.getInstance() .selectedSpace! .rooms![roomIndex] @@ -310,7 +330,8 @@ class DevicesCubit extends Cubit { List statuses = []; var response = await DevicesAPI.getDeviceStatus(deviceId); - print('response : $response'); + // if (response['result']['status'].length > 4) + // print('response : ${response['result']['status'][4]}'); for (var status in response['result']['status']) { statuses.add(StatusModel.fromJson(status)); @@ -321,7 +342,7 @@ class DevicesCubit extends Cubit { .rooms![roomIndex] .devices![deviceIndex] .status = statuses; - emitSafe(GetDeviceStatusSuccess()); + emitSafe(GetDeviceStatusSuccess(code: code)); } on DioException catch (error) { emitSafe( GetDeviceStatusError(ServerFailure.fromDioError(error).errMessage), diff --git a/lib/features/devices/bloc/devices_state.dart b/lib/features/devices/bloc/devices_state.dart index 3375450..4f89268 100644 --- a/lib/features/devices/bloc/devices_state.dart +++ b/lib/features/devices/bloc/devices_state.dart @@ -15,9 +15,17 @@ class ChangeIndex extends DevicesState {} // Devices -class GetDeviceStatusLoading extends DevicesState {} +class GetDeviceStatusLoading extends DevicesState { + final String? code; -class GetDeviceStatusSuccess extends DevicesState {} + GetDeviceStatusLoading({this.code}); +} + +class GetDeviceStatusSuccess extends DevicesState { + final String? code; + + GetDeviceStatusSuccess({this.code}); +} class GetDeviceStatusError extends DevicesState { final String errorMsg; @@ -44,9 +52,17 @@ class DeviceSwitchChanged extends DevicesState {} class DeviceSelected extends DevicesState {} // Device Control -class DeviceControlLoading extends DevicesState {} +class DeviceControlLoading extends DevicesState { + final String? code; -class DeviceControlSuccess extends DevicesState {} + DeviceControlLoading({this.code}); +} + +class DeviceControlSuccess extends DevicesState { + final String? code; + + DeviceControlSuccess({this.code}); +} class DeviceControlError extends DevicesState { final String errorMsg; diff --git a/lib/features/devices/model/device_control_model.dart b/lib/features/devices/model/device_control_model.dart index 8f9edf7..58a6476 100644 --- a/lib/features/devices/model/device_control_model.dart +++ b/lib/features/devices/model/device_control_model.dart @@ -1,7 +1,7 @@ class DeviceControlModel { String? deviceId; String? code; - bool? value; + dynamic value; DeviceControlModel({ required this.deviceId, diff --git a/lib/features/devices/view/widgets/ACs/ac_interface.dart b/lib/features/devices/view/widgets/ACs/ac_interface.dart index 0bf1fb1..c0ac062 100644 --- a/lib/features/devices/view/widgets/ACs/ac_interface.dart +++ b/lib/features/devices/view/widgets/ACs/ac_interface.dart @@ -24,65 +24,78 @@ class AcInterface extends StatelessWidget { statusBarIconBrightness: Brightness.light, ), child: SafeArea( - child: Scaffold( - backgroundColor: ColorsManager.backgroundColor, - extendBodyBehindAppBar: true, - extendBody: true, - appBar: AppBar( - backgroundColor: Colors.transparent, - centerTitle: true, - title: BodyLarge( - text: deviceModel.name ?? "", - fontColor: ColorsManager.primaryColor, - fontWeight: FontsManager.bold, - ), - ), - body: Container( - width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height, - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage( - Assets.imagesBackground, + child: BlocConsumer( + listener: (context, state) { + if (state is DeviceControlError) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(state.errorMsg), ), - fit: BoxFit.cover, - opacity: 0.4, - ), - ), - child: Padding( - padding: EdgeInsets.only( - top: Constants.appBarHeight, - left: Constants.defaultPadding, - right: Constants.defaultPadding, - ), - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 380, - ), - child: AcInterfaceTempUnit( - deviceModel: deviceModel, - ), - ), - const SizedBox( - height: 10, - ), - ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 120, - ), - child: AcInterfaceControls( - deviceModel: deviceModel, - ), - ), - ], + ); + } + }, + builder: (context, state) { + return Scaffold( + backgroundColor: ColorsManager.backgroundColor, + extendBodyBehindAppBar: true, + extendBody: true, + appBar: AppBar( + backgroundColor: Colors.transparent, + centerTitle: true, + title: BodyLarge( + text: deviceModel.name ?? "", + fontColor: ColorsManager.primaryColor, + fontWeight: FontsManager.bold, ), ), - ), - ), + body: Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage( + Assets.imagesBackground, + ), + fit: BoxFit.cover, + opacity: 0.4, + ), + ), + child: Padding( + padding: EdgeInsets.only( + top: Constants.appBarHeight, + left: Constants.defaultPadding, + right: Constants.defaultPadding, + ), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ConstrainedBox( + constraints: const BoxConstraints( + maxHeight: 380, + ), + child: AcInterfaceTempUnit( + deviceModel: deviceModel, + ), + ), + const SizedBox( + height: 10, + ), + ConstrainedBox( + constraints: const BoxConstraints( + maxHeight: 120, + ), + child: AcInterfaceControls( + deviceModel: deviceModel, + ), + ), + ], + ), + ), + ), + ), + ); + }, ), ), ); diff --git a/lib/features/devices/view/widgets/ACs/ac_mode_control_unit.dart b/lib/features/devices/view/widgets/ACs/ac_mode_control_unit.dart index b82e685..1260eec 100644 --- a/lib/features/devices/view/widgets/ACs/ac_mode_control_unit.dart +++ b/lib/features/devices/view/widgets/ACs/ac_mode_control_unit.dart @@ -1,79 +1,96 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; +import 'package:syncrow_app/features/devices/model/device_control_model.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart'; - -import '../../../../../generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; class ACModeControlUnit extends StatefulWidget { const ACModeControlUnit({ super.key, - required this.model, + required this.acDevice, }); - final DeviceModel model; + final DeviceModel acDevice; @override State createState() => _ACModeControlUnitState(); } class _ACModeControlUnitState extends State { - Map fanSpeedsMap = { - 'auto': Assets.iconsFan0, - 'iconsFan1': Assets.iconsFan1, - 'iconsFan2': Assets.iconsFan2, - 'iconsFan3': Assets.iconsFan3, - }; - - Map tempModesMap = { - 'sunny': Assets.iconsSunnyMode, - 'cold': Assets.iconsColdMode, - 'windy': Assets.iconsWindyMode, - }; - @override Widget build(BuildContext context) { - //TODO Move the fanSpeeds and tempModes to the Cubit - return Row( - children: [ - Flexible( - child: InkWell( - onTap: () { - setState(() { - // widget.model.fanSpeed = - // widget.model.fanSpeed == 3 ? 0 : widget.model.fanSpeed + 1; - }); - }, - child: DefaultContainer( - height: 55, - child: Center( - child: SvgPicture.asset(fanSpeedsMap[widget.model.status - .firstWhere((element) => element.code == 'level') - .value]!), + return BlocBuilder( + builder: (context, state) { + FanSpeeds fanSpeed = fanSpeedsMap[widget.acDevice.status + .firstWhere((element) => element.code == 'level') + .value]!; + TempModes tempMode = tempModesMap[widget.acDevice.status + .firstWhere((element) => element.code == 'mode') + .value]!; + return Row( + children: [ + Flexible( + child: InkWell( + onTap: () { + // print( + // '\n\ncurrentFanSpeed:$fanSpeed \nchanged to:\t${fanSpeedsMap[getNextFanSpeedKey(fanSpeed)]!}\nKey:\t\t\"${reversedFanSpeedsMap[fanSpeedsMap[getNextFanSpeedKey(fanSpeed)]!]!}\"'); + + fanSpeed = fanSpeedsMap[getNextFanSpeedKey(fanSpeed)]!; + + DevicesCubit.getInstance().deviceControl( + DeviceControlModel( + deviceId: widget.acDevice.id, + code: 'level', + value: reversedFanSpeedsMap[fanSpeed]!), + widget.acDevice.id!); + }, + child: DefaultContainer( + height: 55, + child: Center( + child: state is GetDeviceStatusLoading && + state.code == 'level' || + state is DeviceControlSuccess && + state.code == 'level' || + state is DeviceControlLoading && + state.code == 'level' + ? const CircularProgressIndicator() + : SvgPicture.asset(fanSpeedsIconMap[fanSpeed]!)), + ), ), ), - ), - ), - const SizedBox(width: 10), - Flexible( - child: InkWell( - onTap: () { - setState(() { - // widget.model.tempMode = - // widget.model.tempMode == 2 ? 0 : widget.model.tempMode + 1; - }); - }, - child: DefaultContainer( - height: 55, - child: Center( - child: SvgPicture.asset(tempModesMap[widget.model.status - .firstWhere((element) => element.code == 'mode') - .value]!), + const SizedBox(width: 10), + Flexible( + child: InkWell( + onTap: () { + tempMode = tempModesMap[getNextItem(tempModesMap, tempMode)]!; + DevicesCubit.getInstance().deviceControl( + DeviceControlModel( + deviceId: widget.acDevice.id, + code: 'mode', + value: reversedTempModesMap[tempMode]!), + widget.acDevice.id!); + }, + child: DefaultContainer( + height: 55, + child: Center( + child: state is GetDeviceStatusLoading && + state.code == 'mode' || + state is DeviceControlSuccess && + state.code == 'mode' || + state is DeviceControlLoading && + state.code == 'mode' + ? const CircularProgressIndicator() + : SvgPicture.asset(tempModesIconMap[tempMode]!), + ), + ), ), ), - ), - ), - ], + ], + ); + }, ); } }