diff --git a/lib/pages/routiens/helper/ac_helper.dart b/lib/pages/routiens/helper/ac_helper.dart index 9ba0b224..d940f4e4 100644 --- a/lib/pages/routiens/helper/ac_helper.dart +++ b/lib/pages/routiens/helper/ac_helper.dart @@ -13,210 +13,203 @@ class ACHelper { List> functions, ) async { List acFunctions = functions.whereType().toList(); - String? selectedFunction; - dynamic selectedValue = 20; - String? selectedCondition = "=="; - List _selectedConditions = [false, true, false]; + final selectedFunctionNotifier = ValueNotifier(null); + final selectedValueNotifier = ValueNotifier(null); + final selectedConditionNotifier = ValueNotifier('=='); + final selectedConditionsNotifier = + ValueNotifier>([false, true, false]); return showDialog?>( context: context, builder: (BuildContext context) { - return StatefulBuilder( - builder: (context, setState) { + return ValueListenableBuilder( + valueListenable: selectedFunctionNotifier, + builder: (context, selectedFunction, _) { return AlertDialog( contentPadding: EdgeInsets.zero, - content: _buildDialogContent( - context, - setState, - acFunctions, - selectedFunction, - selectedValue, - selectedCondition, - _selectedConditions, - (fn) => selectedFunction = fn, - (val) => selectedValue = val, - (cond) => selectedCondition = cond, + content: 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('AC Functions'), + Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Function list + SizedBox( + width: selectedFunction != null ? 320 : 360, + child: _buildFunctionsList( + context, + acFunctions, + selectedFunctionNotifier, + ), + ), + // Value selector + if (selectedFunction != null) + Expanded( + child: _buildValueSelector( + context, + selectedFunction, + selectedValueNotifier, + selectedConditionNotifier, + selectedConditionsNotifier, + acFunctions, + ), + ), + ], + ), + ), + DialogFooter( + onCancel: () { + selectedFunctionNotifier.dispose(); + selectedValueNotifier.dispose(); + selectedConditionNotifier.dispose(); + selectedConditionsNotifier.dispose(); + Navigator.pop(context); + }, + onConfirm: selectedFunctionNotifier.value != null && + selectedValueNotifier.value != null + ? () { + selectedFunctionNotifier.dispose(); + selectedValueNotifier.dispose(); + selectedConditionNotifier.dispose(); + selectedConditionsNotifier.dispose(); + Navigator.pop(context, { + 'function': selectedFunctionNotifier.value, + 'value': selectedValueNotifier.value, + 'condition': + selectedConditionNotifier.value ?? "==", + }); + } + : null, + isConfirmEnabled: selectedFunction != null, + ), + ], + ), ), ); }, ); }, - ); - } - - /// Build dialog content for AC functions dialog - static Widget _buildDialogContent( - BuildContext context, - StateSetter setState, - List acFunctions, - String? selectedFunction, - dynamic selectedValue, - String? selectedCondition, - List selectedConditions, - Function(String?) onFunctionSelected, - Function(dynamic) onValueSelected, - Function(String?) onConditionSelected, - ) { - 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('AC Functions'), - Flexible( - child: Row( - children: [ - _buildFunctionsList( - context, - setState, - acFunctions, - selectedFunction, - onFunctionSelected, - ), - if (selectedFunction != null) - _buildValueSelector( - context, - setState, - selectedFunction, - selectedValue, - selectedCondition, - selectedConditions, - onValueSelected, - onConditionSelected, - acFunctions, - ), - ], - ), - ), - DialogFooter( - onCancel: () => Navigator.pop(context), - onConfirm: selectedFunction != null && selectedValue != null - ? () => Navigator.pop(context, { - 'function': selectedFunction, - 'value': selectedValue, - 'condition': selectedCondition ?? "==", - }) - : null, - isConfirmEnabled: selectedFunction != null && selectedValue != null, - ), - ], - ), - ); + ).then((value) { + selectedFunctionNotifier.dispose(); + selectedValueNotifier.dispose(); + selectedConditionNotifier.dispose(); + selectedConditionsNotifier.dispose(); + return value; + }); } /// Build functions list for AC functions dialog static Widget _buildFunctionsList( BuildContext context, - StateSetter setState, List acFunctions, - String? selectedFunction, - Function(String?) onFunctionSelected, + ValueNotifier selectedFunctionNotifier, ) { - return Expanded( - child: ListView.separated( - shrinkWrap: false, - physics: const AlwaysScrollableScrollPhysics(), - itemCount: acFunctions.length, - separatorBuilder: (context, index) => const Divider( + return ListView.separated( + shrinkWrap: false, + physics: const AlwaysScrollableScrollPhysics(), + itemCount: acFunctions.length, + separatorBuilder: (context, index) => const Padding( + padding: EdgeInsets.symmetric(horizontal: 40.0), + child: Divider( color: ColorsManager.dividerColor, ), - itemBuilder: (context, index) { - final function = acFunctions[index]; - return ListTile( - leading: SvgPicture.asset( - function.icon, + ), + itemBuilder: (context, index) { + final function = acFunctions[index]; + return ListTile( + leading: SvgPicture.asset( + function.icon, + width: 24, + height: 24, + placeholderBuilder: (BuildContext context) => Container( width: 24, height: 24, + color: Colors.transparent, ), - title: Text( - function.operationName, - style: context.textTheme.bodyMedium, - ), - trailing: const Icon( - Icons.arrow_forward_ios, - size: 16, - color: ColorsManager.textGray, - ), - onTap: () => setState(() => onFunctionSelected(function.code)), - ); - }, - ), + ), + title: Text( + function.operationName, + style: context.textTheme.bodyMedium, + ), + trailing: const Icon( + Icons.arrow_forward_ios, + size: 16, + color: ColorsManager.textGray, + ), + onTap: () => selectedFunctionNotifier.value = function.code, + ); + }, ); } /// Build value selector for AC functions dialog static Widget _buildValueSelector( BuildContext context, - StateSetter setState, String selectedFunction, - dynamic selectedValue, - String? selectedCondition, - List selectedConditions, - Function(dynamic) onValueSelected, - Function(String?) onConditionSelected, + ValueNotifier selectedValueNotifier, + ValueNotifier selectedConditionNotifier, + ValueNotifier> selectedConditionsNotifier, List acFunctions, ) { + // Handle temperature functions if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') { - return Expanded( - child: _buildTemperatureSelector( - context, - setState, - selectedValue, - selectedCondition, - selectedConditions, - onValueSelected, - onConditionSelected, - ), + // Initialize with 20°C (200 in internal representation) + if (selectedValueNotifier.value == null || + selectedValueNotifier.value is! int) { + selectedValueNotifier.value = 200; + } + return _buildTemperatureSelector( + context, + selectedValueNotifier, + selectedConditionNotifier, + selectedConditionsNotifier, ); } + // Handle other functions final selectedFn = acFunctions.firstWhere((f) => f.code == selectedFunction); final values = selectedFn.getOperationalValues(); - return Expanded( - child: _buildOperationalValuesList( - context, - setState, - values, - selectedValue, - onValueSelected, - ), + + // Don't set any default value for non-temperature functions + return _buildOperationalValuesList( + context, + values, + selectedValueNotifier, ); } /// Build temperature selector for AC functions dialog static Widget _buildTemperatureSelector( BuildContext context, - StateSetter setState, - dynamic selectedValue, - String? selectedCondition, - List selectedConditions, - Function(dynamic) onValueSelected, - Function(String?) onConditionSelected, + ValueNotifier selectedValueNotifier, + ValueNotifier selectedConditionNotifier, + ValueNotifier> selectedConditionsNotifier, ) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ _buildConditionToggle( context, - setState, - selectedConditions, - onConditionSelected, + selectedConditionNotifier, + selectedConditionsNotifier, ), const SizedBox(height: 20), - _buildTemperatureDisplay(context, selectedValue), + _buildTemperatureDisplay(context, selectedValueNotifier), const SizedBox(height: 20), _buildTemperatureSlider( context, - setState, - selectedValue, - onValueSelected, + selectedValueNotifier, ), ], ); @@ -225,104 +218,121 @@ class ACHelper { /// Build condition toggle for AC functions dialog static Widget _buildConditionToggle( BuildContext context, - StateSetter setState, - List selectedConditions, - Function(String?) onConditionSelected, + ValueNotifier selectedConditionNotifier, + ValueNotifier> selectedConditionsNotifier, ) { - return ToggleButtons( - onPressed: (int index) { - setState(() { - for (int i = 0; i < selectedConditions.length; i++) { - selectedConditions[i] = i == index; - } - onConditionSelected(index == 0 - ? "<" - : index == 1 - ? "==" - : ">"); - }); + return ValueListenableBuilder>( + valueListenable: selectedConditionsNotifier, + builder: (context, selectedConditions, _) { + return ToggleButtons( + onPressed: (int index) { + final newConditions = [false, false, false]; + newConditions[index] = true; + selectedConditionsNotifier.value = newConditions; + selectedConditionNotifier.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(">")], + ); }, - 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(">")], ); } /// Build temperature display for AC functions dialog static Widget _buildTemperatureDisplay( - BuildContext context, dynamic selectedValue) { + BuildContext context, ValueNotifier selectedValueNotifier) { return Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), decoration: BoxDecoration( color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1), borderRadius: BorderRadius.circular(10), ), - child: Text( - '${selectedValue ?? 20}°C', - style: context.textTheme.headlineMedium!.copyWith( - color: ColorsManager.primaryColorWithOpacity, - ), + child: ValueListenableBuilder( + valueListenable: selectedValueNotifier, + builder: (context, selectedValue, child) { + return Text( + '${(selectedValue ?? 200) / 10}°C', + style: context.textTheme.headlineMedium!.copyWith( + color: ColorsManager.primaryColorWithOpacity, + ), + ); + }, ), ); } static Widget _buildTemperatureSlider( BuildContext context, - StateSetter setState, - dynamic selectedValue, - Function(dynamic) onValueSelected, + ValueNotifier selectedValueNotifier, ) { - final currentValue = selectedValue is int ? selectedValue.toDouble() : 20.0; - return Slider( - value: currentValue, - min: 16, - max: 30, - divisions: 14, - label: '${currentValue.toInt()}°C', - onChanged: (value) { - setState(() => onValueSelected(value.toInt())); + return ValueListenableBuilder( + valueListenable: selectedValueNotifier, + builder: (context, selectedValue, child) { + final currentValue = + selectedValue is int ? selectedValue.toDouble() : 200.0; + return Slider( + value: currentValue, + min: 160, + max: 300, + divisions: 14, + label: '${(currentValue / 10).toInt()}°C', + onChanged: (value) => selectedValueNotifier.value = value.toInt(), + ); }, ); } static Widget _buildOperationalValuesList( BuildContext context, - StateSetter setState, List values, - dynamic selectedValue, - Function(dynamic) onValueSelected, + ValueNotifier selectedValueNotifier, ) { - return ListView.builder( - shrinkWrap: false, - physics: const AlwaysScrollableScrollPhysics(), - 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) { - setState(() => onValueSelected(newValue)); - }, - ), + return ValueListenableBuilder( + valueListenable: selectedValueNotifier, + builder: (context, selectedValue, _) { + return ListView.builder( + shrinkWrap: false, + physics: const AlwaysScrollableScrollPhysics(), + itemCount: values.length, + itemBuilder: (context, index) { + final value = values[index]; + 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: Radio( + value: value.value, + groupValue: selectedValue, + onChanged: (_) => selectedValueNotifier.value = value.value, + activeColor: ColorsManager.primaryColorWithOpacity, + ), + onTap: () => selectedValueNotifier.value = value.value, + ); + }, ); }, );