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.
This commit is contained in:
Mohammad Salameh
2024-04-03 18:53:54 +03:00
parent e49627d3e1
commit 6577652702
5 changed files with 197 additions and 130 deletions
+36 -15
View File
@@ -23,7 +23,7 @@ import 'package:syncrow_app/utils/resource_manager/constants.dart';
part 'devices_state.dart';
class DevicesCubit extends Cubit<DevicesState> {
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<DevicesState> {
}
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<void> close() {
_isClosed = true;
_instance = null;
return super.close();
}
@@ -246,11 +249,28 @@ class DevicesCubit extends Cubit<DevicesState> {
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<DevicesState> {
}
}
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<DevicesState> {
List<StatusModel> 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<DevicesState> {
.rooms![roomIndex]
.devices![deviceIndex]
.status = statuses;
emitSafe(GetDeviceStatusSuccess());
emitSafe(GetDeviceStatusSuccess(code: code));
} on DioException catch (error) {
emitSafe(
GetDeviceStatusError(ServerFailure.fromDioError(error).errMessage),
+20 -4
View File
@@ -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;
@@ -1,7 +1,7 @@
class DeviceControlModel {
String? deviceId;
String? code;
bool? value;
dynamic value;
DeviceControlModel({
required this.deviceId,
@@ -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<DevicesCubit, DevicesState>(
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,
),
),
],
),
),
),
),
);
},
),
),
);
@@ -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<ACModeControlUnit> createState() => _ACModeControlUnitState();
}
class _ACModeControlUnitState extends State<ACModeControlUnit> {
Map<String, String> fanSpeedsMap = {
'auto': Assets.iconsFan0,
'iconsFan1': Assets.iconsFan1,
'iconsFan2': Assets.iconsFan2,
'iconsFan3': Assets.iconsFan3,
};
Map<String, String> 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<DevicesCubit, DevicesState>(
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]!),
),
),
),
),
),
),
],
],
);
},
);
}
}