diff --git a/lib/pages/routiens/helper/one_gang_switch_helper.dart b/lib/pages/routiens/helper/one_gang_switch_helper.dart index 0837829b..014e41f9 100644 --- a/lib/pages/routiens/helper/one_gang_switch_helper.dart +++ b/lib/pages/routiens/helper/one_gang_switch_helper.dart @@ -151,9 +151,7 @@ class OneGangSwitchHelper { context, {selectedFn.code: functionData}); } : null, - isConfirmEnabled: - selectedFunctionNotifier.value != null && - selectedValueNotifier.value != null, + isConfirmEnabled: selectedFunctionNotifier.value != null, ), ], ), @@ -162,7 +160,13 @@ class OneGangSwitchHelper { }, ); }, - ); + ).then((value) { + selectedFunctionNotifier.dispose(); + selectedValueNotifier.dispose(); + selectedConditionNotifier.dispose(); + selectedConditionsNotifier.dispose(); + return value; + }); } static Widget _buildCountDownSelector( diff --git a/lib/pages/routiens/helper/three_gang_switch_helper.dart b/lib/pages/routiens/helper/three_gang_switch_helper.dart index 56d1f3bb..1e62f5c4 100644 --- a/lib/pages/routiens/helper/three_gang_switch_helper.dart +++ b/lib/pages/routiens/helper/three_gang_switch_helper.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:syncrow_web/pages/routiens/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/widgets/dialog_footer.dart'; +import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; @@ -20,20 +20,23 @@ class ThreeGangSwitchHelper { f is ThreeGangCountdown2Function || f is ThreeGangCountdown3Function) .toList(); - Map selectedValues = {}; - List selectedFunctions = []; - String? selectedCondition = "<"; - List selectedConditions = [true, false, false]; + + final selectedFunctionNotifier = ValueNotifier(null); + final selectedValueNotifier = ValueNotifier(null); + final selectedConditionNotifier = ValueNotifier('<'); + final selectedConditionsNotifier = + ValueNotifier>([true, false, false]); await showDialog( context: context, builder: (BuildContext context) { - return StatefulBuilder( - builder: (context, setState) { + return ValueListenableBuilder( + valueListenable: selectedFunctionNotifier, + builder: (context, selectedFunction, _) { return AlertDialog( contentPadding: EdgeInsets.zero, content: Container( - width: 600, + width: selectedFunction != null ? 600 : 300, height: 450, decoration: BoxDecoration( color: Colors.white, @@ -43,22 +46,7 @@ class ThreeGangSwitchHelper { child: Column( mainAxisSize: MainAxisSize.min, children: [ - Text( - '3 Gangs Light Switch Condition', - style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: ColorsManager.primaryColorWithOpacity, - fontWeight: FontWeight.bold, - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 15, horizontal: 50), - child: Container( - height: 1, - width: double.infinity, - color: ColorsManager.greyColor, - ), - ), + const DialogHeader('3 Gangs Light Switch Condition'), Expanded( child: Row( children: [ @@ -71,188 +59,111 @@ class ThreeGangSwitchHelper { ), itemBuilder: (context, index) { final function = switchFunctions[index]; - final isSelected = - selectedValues.containsKey(function.code); - return ListTile( - tileColor: - isSelected ? Colors.grey.shade100 : null, - leading: SvgPicture.asset( - function.icon, - width: 24, - height: 24, - ), - title: Text( - function.operationName, - style: context.textTheme.bodyMedium, - ), - trailing: isSelected - ? Icon( - Icons.check_circle, - color: ColorsManager - .primaryColorWithOpacity, - size: 20, - ) - : const Icon( - Icons.arrow_forward_ios, - size: 16, - color: ColorsManager.textGray, - ), - onTap: () { - if (isSelected) { - selectedValues.remove(function.code); - selectedFunctions.removeWhere( - (f) => f.function == function.code); - } - (context as Element).markNeedsBuild(); - }, - ); - }, - ), - ), - // Right side: Value selector - Expanded( - child: Builder( - builder: (context) { - final selectedFn = switchFunctions.firstWhere( - (f) => selectedValues.containsKey(f.code), - orElse: () => switchFunctions.first, - ) as BaseSwitchFunction; - - if (selectedFn is ThreeGangCountdown1Function || - selectedFn is ThreeGangCountdown2Function || - selectedFn is ThreeGangCountdown3Function) { - return _buildCountDownSelector( - context, - setState, - selectedValues[selectedFn.code] ?? 0, - selectedCondition, - selectedConditions, - (value) { - selectedValues[selectedFn.code] = value; - final functionData = DeviceFunctionData( - entityId: selectedFn.deviceId, - function: selectedFn.code, - operationName: selectedFn.operationName, - value: value, - condition: selectedCondition, - valueDescription: '${value} sec', - ); - - final existingIndex = - selectedFunctions.indexWhere((f) => - f.function == selectedFn.code); - if (existingIndex != -1) { - selectedFunctions[existingIndex] = - functionData; - } else { - selectedFunctions.add(functionData); - } - (context as Element).markNeedsBuild(); - }, - (condition) { - setState(() { - selectedCondition = condition; - }); - }, - ); - } - - final values = - selectedFn.getOperationalValues(); - return ListView.builder( - itemCount: values.length, - itemBuilder: (context, index) { - final value = values[index]; + return ValueListenableBuilder( + valueListenable: selectedFunctionNotifier, + builder: (context, selectedFunction, _) { + final isSelected = + selectedFunction == function.code; return ListTile( + tileColor: isSelected + ? Colors.grey.shade100 + : null, leading: SvgPicture.asset( - value.icon, + function.icon, width: 24, height: 24, ), title: Text( - value.description, + function.operationName, style: context.textTheme.bodyMedium, ), - trailing: Radio( - value: value.value, - groupValue: - selectedValues[selectedFn.code], - onChanged: (newValue) { - selectedValues[selectedFn.code] = - newValue; - final functionData = - DeviceFunctionData( - entityId: selectedFn.deviceId, - function: selectedFn.code, - operationName: - selectedFn.operationName, - value: newValue, - valueDescription: value.description, - ); - - final existingIndex = - selectedFunctions.indexWhere( - (f) => - f.function == - selectedFn.code); - if (existingIndex != -1) { - selectedFunctions[existingIndex] = - functionData; - } else { - selectedFunctions.add(functionData); - } - (context as Element).markNeedsBuild(); - }, + trailing: const Icon( + Icons.arrow_forward_ios, + size: 16, + color: ColorsManager.textGray, ), + onTap: () { + selectedFunctionNotifier.value = + function.code; + selectedValueNotifier.value = function + is ThreeGangCountdown1Function || + function + is ThreeGangCountdown2Function || + function + is ThreeGangCountdown3Function + ? 0 + : null; + }, ); }, ); }, ), ), + // Right side: Value selector + if (selectedFunction != null) + Expanded( + child: ValueListenableBuilder( + 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, + ); + }, + ), + ), ], ), ), - Container( - height: 1, - width: double.infinity, - color: ColorsManager.greyColor, - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: Text( - 'Cancel', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(color: ColorsManager.greyColor), - ), - ), - TextButton( - onPressed: selectedFunctions.isNotEmpty - ? () { - for (final function in selectedFunctions) { - context - .read() - .add(AddFunction(function)); - } - Navigator.pop(context, true); - } - : null, - child: Text( - 'Confirm', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith( - color: ColorsManager.primaryColorWithOpacity, - ), - ), - ), - ], + 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, + function: 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, ), ], ), @@ -261,80 +172,110 @@ class ThreeGangSwitchHelper { }, ); }, - ); + ).then((value) { + selectedFunctionNotifier.dispose(); + selectedValueNotifier.dispose(); + selectedConditionNotifier.dispose(); + selectedConditionsNotifier.dispose(); + return value; + }); } - /// Build countdown selector for switch functions dialog static Widget _buildCountDownSelector( BuildContext context, - StateSetter setState, - dynamic selectedValue, - String? selectedCondition, - List selectedConditions, - Function(dynamic) onValueSelected, - Function(String?) onConditionSelected, + ValueNotifier valueNotifier, + ValueNotifier conditionNotifier, + ValueNotifier> conditionsNotifier, ) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _buildConditionToggle( - context, - setState, - selectedConditions, - onConditionSelected, - ), - const SizedBox(height: 20), - Text( - '${selectedValue.toString()} sec', - style: Theme.of(context).textTheme.headlineMedium, - ), - const SizedBox(height: 20), - Slider( - value: selectedValue.toDouble(), - min: 0, - max: 300, // 5 minutes in seconds - divisions: 300, - onChanged: (value) { - setState(() { - onValueSelected(value.toInt()); - }); - }, - ), - ], + return ValueListenableBuilder( + valueListenable: valueNotifier, + builder: (context, value, _) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ValueListenableBuilder>( + valueListenable: conditionsNotifier, + builder: (context, selectedConditions, _) { + return ToggleButtons( + onPressed: (int index) { + final newConditions = List.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(); + }, + ), + ], + ); + }, ); } - /// Build condition toggle for AC functions dialog - static Widget _buildConditionToggle( + static Widget _buildOperationalValuesList( BuildContext context, - StateSetter setState, - List selectedConditions, - Function(String?) onConditionSelected, + BaseSwitchFunction function, + ValueNotifier valueNotifier, ) { - return ToggleButtons( - onPressed: (int index) { - setState(() { - for (int i = 0; i < selectedConditions.length; i++) { - selectedConditions[i] = i == index; - } - onConditionSelected(index == 0 - ? "<" - : index == 1 - ? "==" - : ">"); - }); + final values = function.getOperationalValues(); + return ValueListenableBuilder( + 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( + value: value.value, + groupValue: selectedValue, + onChanged: (newValue) { + valueNotifier.value = newValue; + }, + ), + ); + }, + ); }, - 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(">")], ); } } diff --git a/lib/pages/routiens/helper/two_gang_switch_helper.dart b/lib/pages/routiens/helper/two_gang_switch_helper.dart index ae6abac4..8a7e5580 100644 --- a/lib/pages/routiens/helper/two_gang_switch_helper.dart +++ b/lib/pages/routiens/helper/two_gang_switch_helper.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:syncrow_web/pages/routiens/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'; @@ -166,7 +164,13 @@ class TwoGangSwitchHelper { }, ); }, - ); + ).then((value) { + selectedFunctionNotifier.dispose(); + selectedValueNotifier.dispose(); + selectedConditionNotifier.dispose(); + selectedConditionsNotifier.dispose(); + return value; + }); } static Widget _buildOperationalValuesList( diff --git a/lib/pages/routiens/view/create_new_routine_view.dart b/lib/pages/routiens/view/create_new_routine_view.dart index c81e878f..38750991 100644 --- a/lib/pages/routiens/view/create_new_routine_view.dart +++ b/lib/pages/routiens/view/create_new_routine_view.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/routiens/bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/widgets/conditions_routines_devices_view.dart'; import 'package:syncrow_web/pages/routiens/widgets/if_container.dart'; import 'package:syncrow_web/pages/routiens/widgets/routine_search_and_buttons.dart'; diff --git a/lib/pages/routiens/view/routines_view.dart b/lib/pages/routiens/view/routines_view.dart index b229dd62..27690a9c 100644 --- a/lib/pages/routiens/view/routines_view.dart +++ b/lib/pages/routiens/view/routines_view.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart'; -import 'package:syncrow_web/pages/routiens/bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routiens/view/create_new_routine_view.dart'; import 'package:syncrow_web/utils/color_manager.dart';