fixes unique id for card drag

This commit is contained in:
ashrafzarkanisala
2024-11-24 01:10:31 +03:00
parent 590a41ff83
commit 5e94de5d78
17 changed files with 905 additions and 712 deletions
+34 -17
View File
@@ -18,6 +18,7 @@ class ACHelper {
List<DeviceFunction> functions,
AllDevicesModel? device,
List<DeviceFunctionData>? deviceSelectedFunctions,
String uniqueCustomId,
) async {
List<ACFunction> acFunctions = functions.whereType<ACFunction>().toList();
@@ -95,13 +96,14 @@ class ACHelper {
onConfirm: state.addedFunctions.isNotEmpty
? () {
/// add the functions to the routine bloc
for (var function in state.addedFunctions) {
context.read<RoutineBloc>().add(
AddFunctionToRoutine(
function,
),
);
}
// for (var function in state.addedFunctions) {
context.read<RoutineBloc>().add(
AddFunctionToRoutine(
state.addedFunctions,
uniqueCustomId,
),
);
//}
// Return the device data to be added to the container
Navigator.pop(context, {
'deviceId': functions.first.deviceId,
@@ -229,11 +231,23 @@ class ACHelper {
selectedFunctionData,
),
const SizedBox(height: 20),
_buildTemperatureDisplay(context, initialValue, device, operationName,
selectedFunctionData, selectCode),
_buildTemperatureDisplay(
context,
initialValue,
device,
operationName,
selectedFunctionData,
selectCode,
),
const SizedBox(height: 20),
_buildTemperatureSlider(context, initialValue, device, operationName,
selectedFunctionData, selectCode),
_buildTemperatureSlider(
context,
initialValue,
device,
operationName,
selectedFunctionData,
selectCode,
),
],
);
}
@@ -246,6 +260,7 @@ class ACHelper {
AllDevicesModel? device,
String operationName,
DeviceFunctionData? selectedFunctionData,
// Function(String) onConditionChanged,
) {
final conditions = ["<", "==", ">"];
@@ -282,12 +297,13 @@ class ACHelper {
/// Build temperature display for AC functions dialog
static Widget _buildTemperatureDisplay(
BuildContext context,
dynamic initialValue,
AllDevicesModel? device,
String operationName,
DeviceFunctionData? selectedFunctionData,
String selectCode) {
BuildContext context,
dynamic initialValue,
AllDevicesModel? device,
String operationName,
DeviceFunctionData? selectedFunctionData,
String selectCode,
) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
@@ -342,6 +358,7 @@ class ACHelper {
required String operationName,
required String selectCode,
DeviceFunctionData? selectedFunctionData,
// required Function(dynamic) onValueChanged,
}) {
return ListView.builder(
@@ -39,24 +39,28 @@ class DeviceDialogHelper {
List<DeviceFunction> functions,
) async {
final routineBloc = context.read<RoutineBloc>();
final deviceSelectedFunctions = routineBloc.state.selectedFunctions
.where((f) => f.entityId == data['deviceId'])
.toList();
final deviceSelectedFunctions =
routineBloc.state.selectedFunctions[data['uniqueCustomId']] ?? [];
switch (productType) {
case 'AC':
return ACHelper.showACFunctionsDialog(
context, functions, data['device'], deviceSelectedFunctions);
return ACHelper.showACFunctionsDialog(context, functions,
data['device'], deviceSelectedFunctions, data['uniqueCustomId']);
case '1G':
return OneGangSwitchHelper.showSwitchFunctionsDialog(
context, functions, data['device'], deviceSelectedFunctions);
return OneGangSwitchHelper.showSwitchFunctionsDialog(context, functions,
data['device'], deviceSelectedFunctions, data['uniqueCustomId']);
case '2G':
return TwoGangSwitchHelper.showSwitchFunctionsDialog(
context, functions);
return TwoGangSwitchHelper.showSwitchFunctionsDialog(context, functions,
data['device'], deviceSelectedFunctions, data['uniqueCustomId']);
case '3G':
return ThreeGangSwitchHelper.showSwitchFunctionsDialog(
context, functions);
context,
functions,
data['device'],
deviceSelectedFunctions,
data['uniqueCustomId'],
);
default:
return null;
}
@@ -4,11 +4,9 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routiens/bloc/functions_bloc/functions_bloc_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/models/ac/ac_function.dart';
import 'package:syncrow_web/pages/routiens/models/ac/ac_operational_value.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/one_gang_switch/one_gang_switch.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart';
import 'package:syncrow_web/utils/color_manager.dart';
@@ -20,8 +18,10 @@ class OneGangSwitchHelper {
List<DeviceFunction> functions,
AllDevicesModel? device,
List<DeviceFunctionData>? deviceSelectedFunctions,
String uniqueCustomId,
) async {
List<ACFunction> acFunctions = functions.whereType<ACFunction>().toList();
List<BaseSwitchFunction> acFunctions =
functions.whereType<BaseSwitchFunction>().toList();
return showDialog<Map<String, dynamic>?>(
context: context,
@@ -107,30 +107,6 @@ class OneGangSwitchHelper {
operationName: selectedOperationName ?? '',
),
),
// ValueListenableBuilder<String?>(
// valueListenable: selectedFunctionNotifier,
// builder: (context, selectedFunction, _) {
// final selectedFn =
// switchFunctions.firstWhere(
// (f) => f.code == selectedFunction,
// );
// return Expanded(
// child: selectedFn
// is OneGangCountdownFunction
// ? _buildCountDownSelector(
// context,
// selectedValueNotifier,
// selectedConditionNotifier,
// selectedConditionsNotifier,
// )
// : _buildOperationalValuesList(
// context,
// selectedFn as BaseSwitchFunction,
// selectedValueNotifier,
// ),
// );
// },
// ),
],
),
),
@@ -146,13 +122,20 @@ class OneGangSwitchHelper {
onConfirm: state.addedFunctions.isNotEmpty
? () {
/// add the functions to the routine bloc
for (var function in state.addedFunctions) {
context.read<RoutineBloc>().add(
AddFunctionToRoutine(
function,
),
);
}
// for (var function in state.addedFunctions) {
// context.read<RoutineBloc>().add(
// AddFunctionToRoutine(
// function,
// uniqueCustomId,
// ),
// );
// }
context.read<RoutineBloc>().add(
AddFunctionToRoutine(
state.addedFunctions,
uniqueCustomId,
),
);
// Return the device data to be added to the container
Navigator.pop(context, {
'deviceId': functions.first.deviceId,
@@ -175,13 +158,13 @@ class OneGangSwitchHelper {
required BuildContext context,
required String selectedFunction,
required DeviceFunctionData? selectedFunctionData,
required List<ACFunction> acFunctions,
required List<BaseSwitchFunction> acFunctions,
AllDevicesModel? device,
required String operationName,
}) {
if (selectedFunction == 'countdown_1') {
final initialValue = selectedFunctionData?.value ?? 200;
return _buildTemperatureSelector(
return _buildCountDownSelector(
context: context,
initialValue: initialValue,
selectCode: selectedFunction,
@@ -207,7 +190,7 @@ class OneGangSwitchHelper {
);
}
static Widget _buildTemperatureSelector({
static Widget _buildCountDownSelector({
required BuildContext context,
required dynamic initialValue,
required String? currentCondition,
@@ -228,10 +211,10 @@ class OneGangSwitchHelper {
selectedFunctionData,
),
const SizedBox(height: 20),
_buildTemperatureDisplay(context, initialValue, device, operationName,
_buildCountDownDisplay(context, initialValue, device, operationName,
selectedFunctionData, selectCode),
const SizedBox(height: 20),
_buildTemperatureSlider(context, initialValue, device, operationName,
_buildCountDownSlider(context, initialValue, device, operationName,
selectedFunctionData, selectCode),
],
);
@@ -280,7 +263,7 @@ class OneGangSwitchHelper {
}
/// Build temperature display for AC functions dialog
static Widget _buildTemperatureDisplay(
static Widget _buildCountDownDisplay(
BuildContext context,
dynamic initialValue,
AllDevicesModel? device,
@@ -302,7 +285,7 @@ class OneGangSwitchHelper {
);
}
static Widget _buildTemperatureSlider(
static Widget _buildCountDownSlider(
BuildContext context,
dynamic initialValue,
AllDevicesModel? device,
@@ -310,11 +293,22 @@ class OneGangSwitchHelper {
DeviceFunctionData? selectedFunctionData,
String selectCode,
) {
final operationalValues = SwitchOperationalValue(
icon: '',
description: "sec",
value: 0.0,
minValue: 0,
maxValue: 43200,
stepValue: 1,
);
return Slider(
value: (initialValue ?? 0).toDouble(),
min: 0,
max: 300,
divisions: 300,
min: operationalValues.minValue?.toDouble() ?? 0.0,
max: operationalValues.maxValue?.toDouble() ?? 0.0,
divisions: (((operationalValues.maxValue ?? 0) -
(operationalValues.minValue ?? 0)) /
(operationalValues.stepValue ?? 1))
.round(),
onChanged: (value) {
context.read<FunctionBloc>().add(
AddFunction(
@@ -334,13 +328,12 @@ class OneGangSwitchHelper {
static Widget _buildOperationalValuesList({
required BuildContext context,
required List<ACOperationalValue> values,
required List<SwitchOperationalValue> values,
required dynamic selectedValue,
AllDevicesModel? device,
required String operationName,
required String selectCode,
DeviceFunctionData? selectedFunctionData,
// required Function(dynamic) onValueChanged,
}) {
return ListView.builder(
shrinkWrap: false,
@@ -394,102 +387,4 @@ class OneGangSwitchHelper {
},
);
}
// static Widget _buildCountDownSelector(
// BuildContext context,
// ValueNotifier<dynamic> valueNotifier,
// ValueNotifier<String?> conditionNotifier,
// ValueNotifier<List<bool>> conditionsNotifier,
// ) {
// return ValueListenableBuilder<dynamic>(
// valueListenable: valueNotifier,
// builder: (context, value, _) {
// return Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// ValueListenableBuilder<List<bool>>(
// valueListenable: conditionsNotifier,
// builder: (context, selectedConditions, _) {
// return ToggleButtons(
// onPressed: (int index) {
// final newConditions = List<bool>.filled(3, false);
// newConditions[index] = true;
// conditionsNotifier.value = newConditions;
// conditionNotifier.value = index == 0
// ? "<"
// : index == 1
// ? "=="
// : ">";
// },
// borderRadius: const BorderRadius.all(Radius.circular(8)),
// selectedBorderColor: ColorsManager.primaryColorWithOpacity,
// selectedColor: Colors.white,
// fillColor: ColorsManager.primaryColorWithOpacity,
// color: ColorsManager.primaryColorWithOpacity,
// constraints: const BoxConstraints(
// minHeight: 40.0,
// minWidth: 40.0,
// ),
// isSelected: selectedConditions,
// children: const [Text("<"), Text("="), Text(">")],
// );
// },
// ),
// const SizedBox(height: 20),
// Text(
// '${value ?? 0} sec',
// style: Theme.of(context).textTheme.headlineMedium,
// ),
// const SizedBox(height: 20),
// Slider(
// value: (value ?? 0).toDouble(),
// min: 0,
// max: 300,
// divisions: 300,
// onChanged: (newValue) {
// valueNotifier.value = newValue.toInt();
// },
// ),
// ],
// );
// },
// );
// }
// static Widget _buildOperationalValuesList(
// BuildContext context,
// BaseSwitchFunction function,
// ValueNotifier<dynamic> valueNotifier,
// ) {
// final values = function.getOperationalValues();
// return ValueListenableBuilder<dynamic>(
// valueListenable: valueNotifier,
// builder: (context, selectedValue, _) {
// return ListView.builder(
// itemCount: values.length,
// itemBuilder: (context, index) {
// final value = values[index];
// return ListTile(
// leading: SvgPicture.asset(
// value.icon,
// width: 24,
// height: 24,
// ),
// title: Text(
// value.description,
// style: context.textTheme.bodyMedium,
// ),
// trailing: Radio<dynamic>(
// value: value.value,
// groupValue: selectedValue,
// onChanged: (newValue) {
// valueNotifier.value = newValue;
// },
// ),
// );
// },
// );
// },
// );
// }
}
@@ -1,8 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routiens/bloc/functions_bloc/functions_bloc_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/three_gang_switch/three_gang_switch.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart';
import 'package:syncrow_web/utils/color_manager.dart';
@@ -10,64 +14,60 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart';
class ThreeGangSwitchHelper {
static Future<Map<String, dynamic>?> showSwitchFunctionsDialog(
BuildContext context, List<DeviceFunction<dynamic>> functions) async {
List<DeviceFunction<dynamic>> switchFunctions = functions
.where((f) =>
f is ThreeGangSwitch1Function ||
f is ThreeGangSwitch2Function ||
f is ThreeGangSwitch3Function ||
f is ThreeGangCountdown1Function ||
f is ThreeGangCountdown2Function ||
f is ThreeGangCountdown3Function)
.toList();
BuildContext context,
List<DeviceFunction> functions,
AllDevicesModel? device,
List<DeviceFunctionData>? deviceSelectedFunctions,
String uniqueCustomId,
) async {
List<BaseSwitchFunction> switchFunctions =
functions.whereType<BaseSwitchFunction>().toList();
final selectedFunctionNotifier = ValueNotifier<String?>(null);
final selectedValueNotifier = ValueNotifier<dynamic>(null);
final selectedConditionNotifier = ValueNotifier<String?>('<');
final selectedConditionsNotifier =
ValueNotifier<List<bool>>([true, false, false]);
await showDialog(
return showDialog<Map<String, dynamic>?>(
context: context,
builder: (BuildContext context) {
return ValueListenableBuilder<String?>(
valueListenable: selectedFunctionNotifier,
builder: (context, selectedFunction, _) {
return AlertDialog(
return BlocProvider(
create: (_) => FunctionBloc()
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
child: AlertDialog(
contentPadding: EdgeInsets.zero,
content: Container(
width: selectedFunction != null ? 600 : 300,
height: 450,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.only(top: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const DialogHeader('3 Gangs Light Switch Condition'),
Expanded(
child: Row(
children: [
// Left side: Function list
Expanded(
child: ListView.separated(
itemCount: switchFunctions.length,
separatorBuilder: (_, __) => const Divider(
color: ColorsManager.dividerColor,
),
itemBuilder: (context, index) {
final function = switchFunctions[index];
return ValueListenableBuilder<String?>(
valueListenable: selectedFunctionNotifier,
builder: (context, selectedFunction, _) {
final isSelected =
selectedFunction == function.code;
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) {
final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = state.addedFunctions
.firstWhere((f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData(
entityId: '',
functionCode: selectedFunction ?? '',
operationName: '',
value: null,
));
return Container(
width: selectedFunction != null ? 600 : 360,
height: 450,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.only(top: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const DialogHeader('3 Gangs Light Switch Condition'),
Expanded(
child: Row(
children: [
// Left side: Function list
Expanded(
child: ListView.separated(
itemCount: switchFunctions.length,
separatorBuilder: (_, __) => const Divider(
color: ColorsManager.dividerColor,
),
itemBuilder: (context, index) {
final function = switchFunctions[index];
return ListTile(
tileColor: isSelected
? Colors.grey.shade100
: null,
leading: SvgPicture.asset(
function.icon,
width: 24,
@@ -83,196 +83,307 @@ class ThreeGangSwitchHelper {
color: ColorsManager.textGray,
),
onTap: () {
selectedFunctionNotifier.value =
function.code;
selectedValueNotifier.value = function
is ThreeGangCountdown1Function ||
function
is ThreeGangCountdown2Function ||
function
is ThreeGangCountdown3Function
? 0
: null;
context
.read<FunctionBloc>()
.add(SelectFunction(
functionCode: function.code,
operationName:
function.operationName,
));
},
);
},
);
},
),
),
// Right side: Value selector
if (selectedFunction != null)
Expanded(
child: ValueListenableBuilder<dynamic>(
valueListenable: selectedValueNotifier,
builder: (context, selectedValue, _) {
final selectedFn = switchFunctions.firstWhere(
(f) => f.code == selectedFunction,
);
return selectedFn
is ThreeGangCountdown1Function ||
selectedFn
is ThreeGangCountdown2Function ||
selectedFn
is ThreeGangCountdown3Function
? _buildCountDownSelector(
context,
selectedValueNotifier,
selectedConditionNotifier,
selectedConditionsNotifier,
)
: _buildOperationalValuesList(
context,
selectedFn as BaseSwitchFunction,
selectedValueNotifier,
);
},
),
),
),
],
),
// Right side: Value selector
if (selectedFunction != null)
Expanded(
child: _buildValueSelector(
context: context,
selectedFunction: selectedFunction,
selectedFunctionData: selectedFunctionData,
switchFunctions: switchFunctions,
device: device,
operationName: selectedOperationName ?? '',
),
),
],
),
),
Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
DialogFooter(
onCancel: () {
Navigator.pop(context);
},
onConfirm: state.addedFunctions.isNotEmpty
? () {
/// add the functions to the routine bloc
// for (var function in state.addedFunctions) {
// context.read<RoutineBloc>().add(
// AddFunctionToRoutine(
// function,
// uniqueCustomId,
// ),
// );
// }
context.read<RoutineBloc>().add(
AddFunctionToRoutine(
state.addedFunctions,
uniqueCustomId,
),
);
// Return the device data to be added to the container
Navigator.pop(context, {
'deviceId': functions.first.deviceId,
});
}
: null,
isConfirmEnabled: selectedFunction != null,
),
],
),
DialogFooter(
onCancel: () => Navigator.pop(context),
onConfirm: selectedFunctionNotifier.value != null &&
selectedValueNotifier.value != null
? () {
final selectedFn = switchFunctions.firstWhere(
(f) => f.code == selectedFunctionNotifier.value,
);
final value = selectedValueNotifier.value;
final functionData = DeviceFunctionData(
entityId: selectedFn.deviceId,
functionCode: selectedFn.code,
operationName: selectedFn.operationName,
value: value,
condition: selectedConditionNotifier.value,
valueDescription: selectedFn
is ThreeGangCountdown1Function ||
selectedFn
is ThreeGangCountdown2Function ||
selectedFn
is ThreeGangCountdown3Function
? '${value} sec'
: ((selectedFn as BaseSwitchFunction)
.getOperationalValues()
.firstWhere((v) => v.value == value)
.description),
);
Navigator.pop(
context, {selectedFn.code: functionData});
}
: null,
isConfirmEnabled: selectedFunctionNotifier.value != null,
),
],
),
);
},
),
);
},
);
},
).then((value) {
selectedFunctionNotifier.dispose();
selectedValueNotifier.dispose();
selectedConditionNotifier.dispose();
selectedConditionsNotifier.dispose();
return value;
});
}
static Widget _buildCountDownSelector(
BuildContext context,
ValueNotifier<dynamic> valueNotifier,
ValueNotifier<String?> conditionNotifier,
ValueNotifier<List<bool>> conditionsNotifier,
) {
return ValueListenableBuilder<dynamic>(
valueListenable: valueNotifier,
builder: (context, value, _) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ValueListenableBuilder<List<bool>>(
valueListenable: conditionsNotifier,
builder: (context, selectedConditions, _) {
return ToggleButtons(
onPressed: (int index) {
final newConditions = List<bool>.filled(3, false);
newConditions[index] = true;
conditionsNotifier.value = newConditions;
conditionNotifier.value = index == 0
? "<"
: index == 1
? "=="
: ">";
},
borderRadius: const BorderRadius.all(Radius.circular(8)),
selectedBorderColor: ColorsManager.primaryColorWithOpacity,
selectedColor: Colors.white,
fillColor: ColorsManager.primaryColorWithOpacity,
color: ColorsManager.primaryColorWithOpacity,
constraints: const BoxConstraints(
minHeight: 40.0,
minWidth: 40.0,
),
isSelected: selectedConditions,
children: const [Text("<"), Text("="), Text(">")],
);
},
),
const SizedBox(height: 20),
Text(
'${value ?? 0} sec',
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 20),
Slider(
value: (value ?? 0).toDouble(),
min: 0,
max: 300,
divisions: 300,
onChanged: (newValue) {
valueNotifier.value = newValue.toInt();
},
),
],
);
));
},
);
}
static Widget _buildOperationalValuesList(
static Widget _buildValueSelector({
required BuildContext context,
required String selectedFunction,
required DeviceFunctionData? selectedFunctionData,
required List<BaseSwitchFunction> switchFunctions,
AllDevicesModel? device,
required String operationName,
}) {
if (selectedFunction == 'countdown_1' ||
selectedFunction == 'countdown_2' ||
selectedFunction == 'countdown_3') {
final initialValue = selectedFunctionData?.value ?? 200;
return _buildTemperatureSelector(
context: context,
initialValue: initialValue,
selectCode: selectedFunction,
currentCondition: selectedFunctionData?.condition,
device: device,
operationName: operationName,
selectedFunctionData: selectedFunctionData,
);
}
final selectedFn =
switchFunctions.firstWhere((f) => f.code == selectedFunction);
final values = selectedFn.getOperationalValues();
return _buildOperationalValuesList(
context: context,
values: values,
selectedValue: selectedFunctionData?.value,
device: device,
operationName: operationName,
selectCode: selectedFunction,
selectedFunctionData: selectedFunctionData,
);
}
static Widget _buildTemperatureSelector({
required BuildContext context,
required dynamic initialValue,
required String? currentCondition,
required String selectCode,
AllDevicesModel? device,
required String operationName,
DeviceFunctionData? selectedFunctionData,
}) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildConditionToggle(
context,
currentCondition,
selectCode,
device,
operationName,
selectedFunctionData,
),
const SizedBox(height: 20),
_buildCountDownDisplay(context, initialValue, device, operationName,
selectedFunctionData, selectCode),
const SizedBox(height: 20),
_buildCountDownSlider(context, initialValue, device, operationName,
selectedFunctionData, selectCode),
],
);
}
/// Build condition toggle for AC functions dialog
static Widget _buildConditionToggle(
BuildContext context,
BaseSwitchFunction function,
ValueNotifier<dynamic> valueNotifier,
String? currentCondition,
String selectCode,
AllDevicesModel? device,
String operationName,
DeviceFunctionData? selectedFunctionData,
// Function(String) onConditionChanged,
) {
final values = function.getOperationalValues();
return ValueListenableBuilder<dynamic>(
valueListenable: valueNotifier,
builder: (context, selectedValue, _) {
return ListView.builder(
itemCount: values.length,
itemBuilder: (context, index) {
final value = values[index];
return ListTile(
leading: SvgPicture.asset(
value.icon,
width: 24,
height: 24,
),
title: Text(
value.description,
style: context.textTheme.bodyMedium,
),
trailing: Radio<dynamic>(
value: value.value,
groupValue: selectedValue,
onChanged: (newValue) {
valueNotifier.value = newValue;
},
final conditions = ["<", "==", ">"];
return ToggleButtons(
onPressed: (int index) {
context.read<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: device?.uuid ?? '',
functionCode: selectCode,
operationName: operationName,
condition: conditions[index],
value: selectedFunctionData?.value,
valueDescription: selectedFunctionData?.valueDescription,
),
),
);
},
borderRadius: const BorderRadius.all(Radius.circular(8)),
selectedBorderColor: ColorsManager.primaryColorWithOpacity,
selectedColor: Colors.white,
fillColor: ColorsManager.primaryColorWithOpacity,
color: ColorsManager.primaryColorWithOpacity,
constraints: const BoxConstraints(
minHeight: 40.0,
minWidth: 40.0,
),
isSelected:
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
children: conditions.map((c) => Text(c)).toList(),
);
}
/// Build temperature display for AC functions dialog
static Widget _buildCountDownDisplay(
BuildContext context,
dynamic initialValue,
AllDevicesModel? device,
String operationName,
DeviceFunctionData? selectedFunctionData,
String selectCode) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Text(
'${initialValue ?? 0} sec',
style: context.textTheme.headlineMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
),
),
);
}
static Widget _buildCountDownSlider(
BuildContext context,
dynamic initialValue,
AllDevicesModel? device,
String operationName,
DeviceFunctionData? selectedFunctionData,
String selectCode,
) {
final operationalValues = SwitchOperationalValue(
icon: '',
description: "sec",
value: 0.0,
minValue: 0,
maxValue: 43200,
stepValue: 1,
);
return Slider(
value: (initialValue ?? 0).toDouble(),
min: operationalValues.minValue?.toDouble() ?? 0.0,
max: operationalValues.maxValue?.toDouble() ?? 0.0,
divisions: (((operationalValues.maxValue ?? 0) -
(operationalValues.minValue ?? 0)) /
(operationalValues.stepValue ?? 1))
.round(),
onChanged: (value) {
context.read<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: device?.uuid ?? '',
functionCode: selectCode,
operationName: operationName,
value: value,
condition: selectedFunctionData?.condition,
valueDescription: selectedFunctionData?.valueDescription,
),
),
);
},
);
}
static Widget _buildOperationalValuesList({
required BuildContext context,
required List<SwitchOperationalValue> values,
required dynamic selectedValue,
AllDevicesModel? device,
required String operationName,
required String selectCode,
DeviceFunctionData? selectedFunctionData,
}) {
return ListView.builder(
shrinkWrap: false,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: values.length,
itemBuilder: (context, index) {
final value = values[index];
final isSelected = selectedValue == value.value;
return ListTile(
leading: SvgPicture.asset(
value.icon,
width: 24,
height: 24,
placeholderBuilder: (BuildContext context) => Container(
width: 24,
height: 24,
color: Colors.transparent,
),
),
title: Text(
value.description,
style: context.textTheme.bodyMedium,
),
trailing: Icon(
isSelected
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
size: 24,
color: isSelected
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
),
onTap: () {
if (!isSelected) {
context.read<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: device?.uuid ?? '',
functionCode: selectCode,
operationName: operationName,
value: value.value,
condition: selectedFunctionData?.condition,
valueDescription:
selectedFunctionData?.valueDescription,
),
),
);
}
},
);
},
@@ -1,8 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routiens/bloc/functions_bloc/functions_bloc_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/two_gang_switch/two_gang_switch.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart';
import 'package:syncrow_web/utils/color_manager.dart';
@@ -10,62 +14,60 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart';
class TwoGangSwitchHelper {
static Future<Map<String, dynamic>?> showSwitchFunctionsDialog(
BuildContext context, List<DeviceFunction<dynamic>> functions) async {
List<DeviceFunction<dynamic>> switchFunctions = functions
.where((f) =>
f is TwoGangSwitch1Function ||
f is TwoGangSwitch2Function ||
f is TwoGangCountdown1Function ||
f is TwoGangCountdown2Function)
.toList();
BuildContext context,
List<DeviceFunction> functions,
AllDevicesModel? device,
List<DeviceFunctionData>? deviceSelectedFunctions,
String uniqueCustomId,
) async {
List<BaseSwitchFunction> switchFunctions =
functions.whereType<BaseSwitchFunction>().toList();
final selectedFunctionNotifier = ValueNotifier<String?>(null);
final selectedValueNotifier = ValueNotifier<dynamic>(null);
final selectedConditionNotifier = ValueNotifier<String?>('<');
final selectedConditionsNotifier =
ValueNotifier<List<bool>>([true, false, false]);
await showDialog(
return showDialog<Map<String, dynamic>?>(
context: context,
builder: (BuildContext context) {
return ValueListenableBuilder<String?>(
valueListenable: selectedFunctionNotifier,
builder: (context, selectedFunction, _) {
return AlertDialog(
return BlocProvider(
create: (_) => FunctionBloc()
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
child: AlertDialog(
contentPadding: EdgeInsets.zero,
content: Container(
width: selectedFunction != null ? 600 : 300,
height: 450,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.only(top: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const DialogHeader('2 Gangs Light Switch Condition'),
Expanded(
child: Row(
children: [
// Left side: Function list
Expanded(
child: ListView.separated(
itemCount: switchFunctions.length,
separatorBuilder: (_, __) => const Divider(
color: ColorsManager.dividerColor,
),
itemBuilder: (context, index) {
final function = switchFunctions[index];
return ValueListenableBuilder<String?>(
valueListenable: selectedFunctionNotifier,
builder: (context, selectedFunction, _) {
final isSelected =
selectedFunction == function.code;
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) {
final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = state.addedFunctions
.firstWhere((f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData(
entityId: '',
functionCode: selectedFunction ?? '',
operationName: '',
value: null,
));
return Container(
width: selectedFunction != null ? 600 : 360,
height: 450,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.only(top: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const DialogHeader('2 Gangs Light Switch Condition'),
Expanded(
child: Row(
children: [
// Left side: Function list
Expanded(
child: ListView.separated(
itemCount: switchFunctions.length,
separatorBuilder: (_, __) => const Divider(
color: ColorsManager.dividerColor,
),
itemBuilder: (context, index) {
final function = switchFunctions[index];
return ListTile(
tileColor: isSelected
? Colors.grey.shade100
: null,
leading: SvgPicture.asset(
function.icon,
width: 24,
@@ -81,191 +83,307 @@ class TwoGangSwitchHelper {
color: ColorsManager.textGray,
),
onTap: () {
selectedFunctionNotifier.value =
function.code;
selectedValueNotifier.value = function
is TwoGangCountdown1Function ||
function
is TwoGangCountdown2Function
? 0
: null;
context
.read<FunctionBloc>()
.add(SelectFunction(
functionCode: function.code,
operationName:
function.operationName,
));
},
);
},
);
},
),
),
// Right side: Value selector
if (selectedFunction != null)
Expanded(
child: ValueListenableBuilder<dynamic>(
valueListenable: selectedValueNotifier,
builder: (context, selectedValue, _) {
final selectedFn = switchFunctions.firstWhere(
(f) => f.code == selectedFunction,
);
if (selectedFn is TwoGangCountdown1Function ||
selectedFn is TwoGangCountdown2Function) {
return _buildCountDownSelector(
context,
selectedValueNotifier,
selectedConditionNotifier,
selectedConditionsNotifier,
);
}
return _buildOperationalValuesList(
context,
selectedFn as BaseSwitchFunction,
selectedValueNotifier,
);
},
),
),
),
],
),
// Right side: Value selector
if (selectedFunction != null)
Expanded(
child: _buildValueSelector(
context: context,
selectedFunction: selectedFunction,
selectedFunctionData: selectedFunctionData,
switchFunctions: switchFunctions,
device: device,
operationName: selectedOperationName ?? '',
),
),
],
),
),
Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
DialogFooter(
onCancel: () {
Navigator.pop(context);
},
onConfirm: state.addedFunctions.isNotEmpty
? () {
/// add the functions to the routine bloc
// for (var function in state.addedFunctions) {
// context.read<RoutineBloc>().add(
// AddFunctionToRoutine(
// function,
// uniqueCustomId
// ),
// );
// }
context.read<RoutineBloc>().add(
AddFunctionToRoutine(
state.addedFunctions,
uniqueCustomId,
),
);
// Return the device data to be added to the container
Navigator.pop(context, {
'deviceId': functions.first.deviceId,
});
}
: null,
isConfirmEnabled: selectedFunction != null,
),
],
),
DialogFooter(
onCancel: () => Navigator.pop(context),
onConfirm: selectedFunction != null &&
selectedValueNotifier.value != null
? () {
final selectedFn = switchFunctions.firstWhere(
(f) => f.code == selectedFunction,
);
final value = selectedValueNotifier.value;
final functionData = DeviceFunctionData(
entityId: selectedFn.deviceId,
functionCode: selectedFn.code,
operationName: selectedFn.operationName,
value: value,
condition: selectedConditionNotifier.value,
valueDescription: selectedFn
is TwoGangCountdown1Function ||
selectedFn is TwoGangCountdown2Function
? '${value} sec'
: ((selectedFn as BaseSwitchFunction)
.getOperationalValues()
.firstWhere((v) => v.value == value)
.description),
);
Navigator.pop(
context, {selectedFn.code: functionData});
}
: null,
isConfirmEnabled: selectedFunction != null,
),
],
),
),
);
},
);
},
).then((value) {
selectedFunctionNotifier.dispose();
selectedValueNotifier.dispose();
selectedConditionNotifier.dispose();
selectedConditionsNotifier.dispose();
return value;
});
}
static Widget _buildOperationalValuesList(
BuildContext context,
BaseSwitchFunction function,
ValueNotifier<dynamic> valueNotifier,
) {
final values = function.getOperationalValues();
return ValueListenableBuilder<dynamic>(
valueListenable: valueNotifier,
builder: (context, selectedValue, _) {
return ListView.builder(
itemCount: values.length,
itemBuilder: (context, index) {
final value = values[index];
return ListTile(
leading: SvgPicture.asset(
value.icon,
width: 24,
height: 24,
),
title: Text(
value.description,
style: context.textTheme.bodyMedium,
),
trailing: Radio<dynamic>(
value: value.value,
groupValue: selectedValue,
onChanged: (newValue) {
valueNotifier.value = newValue;
);
},
),
);
},
);
));
},
);
}
static Widget _buildCountDownSelector(
static Widget _buildValueSelector({
required BuildContext context,
required String selectedFunction,
required DeviceFunctionData? selectedFunctionData,
required List<BaseSwitchFunction> switchFunctions,
AllDevicesModel? device,
required String operationName,
}) {
if (selectedFunction == 'countdown_1' ||
selectedFunction == 'countdown_2') {
final initialValue = selectedFunctionData?.value ?? 200;
return _buildTemperatureSelector(
context: context,
initialValue: initialValue,
selectCode: selectedFunction,
currentCondition: selectedFunctionData?.condition,
device: device,
operationName: operationName,
selectedFunctionData: selectedFunctionData,
);
}
final selectedFn =
switchFunctions.firstWhere((f) => f.code == selectedFunction);
final values = selectedFn.getOperationalValues();
return _buildOperationalValuesList(
context: context,
values: values,
selectedValue: selectedFunctionData?.value,
device: device,
operationName: operationName,
selectCode: selectedFunction,
selectedFunctionData: selectedFunctionData,
);
}
static Widget _buildTemperatureSelector({
required BuildContext context,
required dynamic initialValue,
required String? currentCondition,
required String selectCode,
AllDevicesModel? device,
required String operationName,
DeviceFunctionData? selectedFunctionData,
}) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildConditionToggle(
context,
currentCondition,
selectCode,
device,
operationName,
selectedFunctionData,
),
const SizedBox(height: 20),
_buildCountDownDisplay(context, initialValue, device, operationName,
selectedFunctionData, selectCode),
const SizedBox(height: 20),
_buildCountDownSlider(context, initialValue, device, operationName,
selectedFunctionData, selectCode),
],
);
}
/// Build condition toggle for AC functions dialog
static Widget _buildConditionToggle(
BuildContext context,
ValueNotifier<dynamic> valueNotifier,
ValueNotifier<String?> conditionNotifier,
ValueNotifier<List<bool>> conditionsNotifier,
String? currentCondition,
String selectCode,
AllDevicesModel? device,
String operationName,
DeviceFunctionData? selectedFunctionData,
// Function(String) onConditionChanged,
) {
return ValueListenableBuilder<dynamic>(
valueListenable: valueNotifier,
builder: (context, value, _) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ValueListenableBuilder<List<bool>>(
valueListenable: conditionsNotifier,
builder: (context, selectedConditions, _) {
return ToggleButtons(
onPressed: (int index) {
final newConditions = List<bool>.filled(3, false);
newConditions[index] = true;
conditionsNotifier.value = newConditions;
conditionNotifier.value = index == 0
? "<"
: index == 1
? "=="
: ">";
},
borderRadius: const BorderRadius.all(Radius.circular(8)),
selectedBorderColor: ColorsManager.primaryColorWithOpacity,
selectedColor: Colors.white,
fillColor: ColorsManager.primaryColorWithOpacity,
color: ColorsManager.primaryColorWithOpacity,
constraints: const BoxConstraints(
minHeight: 40.0,
minWidth: 40.0,
),
isSelected: selectedConditions,
children: const [Text("<"), Text("="), Text(">")],
);
},
final conditions = ["<", "==", ">"];
return ToggleButtons(
onPressed: (int index) {
context.read<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: device?.uuid ?? '',
functionCode: selectCode,
operationName: operationName,
condition: conditions[index],
value: selectedFunctionData?.value,
valueDescription: selectedFunctionData?.valueDescription,
),
),
);
},
borderRadius: const BorderRadius.all(Radius.circular(8)),
selectedBorderColor: ColorsManager.primaryColorWithOpacity,
selectedColor: Colors.white,
fillColor: ColorsManager.primaryColorWithOpacity,
color: ColorsManager.primaryColorWithOpacity,
constraints: const BoxConstraints(
minHeight: 40.0,
minWidth: 40.0,
),
isSelected:
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
children: conditions.map((c) => Text(c)).toList(),
);
}
/// Build temperature display for AC functions dialog
static Widget _buildCountDownDisplay(
BuildContext context,
dynamic initialValue,
AllDevicesModel? device,
String operationName,
DeviceFunctionData? selectedFunctionData,
String selectCode) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Text(
'${initialValue ?? 0} sec',
style: context.textTheme.headlineMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
),
),
);
}
static Widget _buildCountDownSlider(
BuildContext context,
dynamic initialValue,
AllDevicesModel? device,
String operationName,
DeviceFunctionData? selectedFunctionData,
String selectCode,
) {
final operationalValues = SwitchOperationalValue(
icon: '',
description: "sec",
value: 0.0,
minValue: 0,
maxValue: 43200,
stepValue: 1,
);
return Slider(
value: (initialValue ?? 0).toDouble(),
min: operationalValues.minValue?.toDouble() ?? 0.0,
max: operationalValues.maxValue?.toDouble() ?? 0.0,
divisions: (((operationalValues.maxValue ?? 0) -
(operationalValues.minValue ?? 0)) /
(operationalValues.stepValue ?? 1))
.round(),
onChanged: (value) {
context.read<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: device?.uuid ?? '',
functionCode: selectCode,
operationName: operationName,
value: value,
condition: selectedFunctionData?.condition,
valueDescription: selectedFunctionData?.valueDescription,
),
),
);
},
);
}
static Widget _buildOperationalValuesList({
required BuildContext context,
required List<SwitchOperationalValue> values,
required dynamic selectedValue,
AllDevicesModel? device,
required String operationName,
required String selectCode,
DeviceFunctionData? selectedFunctionData,
}) {
return ListView.builder(
shrinkWrap: false,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: values.length,
itemBuilder: (context, index) {
final value = values[index];
final isSelected = selectedValue == value.value;
return ListTile(
leading: SvgPicture.asset(
value.icon,
width: 24,
height: 24,
placeholderBuilder: (BuildContext context) => Container(
width: 24,
height: 24,
color: Colors.transparent,
),
const SizedBox(height: 20),
Text(
'${value ?? 0} sec',
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 20),
Slider(
value: (value ?? 0).toDouble(),
min: 0,
max: 300,
divisions: 300,
onChanged: (newValue) {
valueNotifier.value = newValue.toInt();
},
),
],
),
title: Text(
value.description,
style: context.textTheme.bodyMedium,
),
trailing: Icon(
isSelected
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
size: 24,
color: isSelected
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
),
onTap: () {
if (!isSelected) {
context.read<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: device?.uuid ?? '',
functionCode: selectCode,
operationName: operationName,
value: value.value,
condition: selectedFunctionData?.condition,
valueDescription:
selectedFunctionData?.valueDescription,
),
),
);
}
},
);
},
);