Files
syncrow-web/lib/pages/routiens/helper/one_gang_switch_helper.dart
2024-11-22 16:55:30 +03:00

334 lines
15 KiB
Dart

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/one_gang_switch/one_gang_switch.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class OneGangSwitchHelper {
static Future<void> showSwitchFunctionsDialog(
BuildContext context, List<DeviceFunction<dynamic>> functions) async {
List<DeviceFunction<dynamic>> switchFunctions = functions
.where(
(f) => f is OneGangSwitchFunction || f is OneGangCountdownFunction)
.toList();
Map<String, dynamic> selectedValues = {};
List<DeviceFunctionData> selectedFunctions = [];
String? selectedCondition = "<";
List<bool> selectedConditions = [true, false, false];
await showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: Container(
width: 600,
height: 450,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.only(top: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'1 Gang 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,
),
),
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];
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 OneGangCountdownFunction) {
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 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:
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();
},
),
);
},
);
},
),
),
],
),
),
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<RoutineBloc>()
.add(AddFunction(function));
}
Navigator.pop(context, true);
}
: null,
child: Text(
'Confirm',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ColorsManager.primaryColorWithOpacity,
),
),
),
],
),
],
),
),
);
},
);
},
);
}
/// Build countdown selector for switch functions dialog
static Widget _buildCountDownSelector(
BuildContext context,
StateSetter setState,
dynamic selectedValue,
String? selectedCondition,
List<bool> selectedConditions,
Function(dynamic) onValueSelected,
Function(String?) onConditionSelected,
) {
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());
});
},
),
],
);
}
/// Build condition toggle for AC functions dialog
static Widget _buildConditionToggle(
BuildContext context,
StateSetter setState,
List<bool> selectedConditions,
Function(String?) onConditionSelected,
) {
return ToggleButtons(
onPressed: (int index) {
setState(() {
for (int i = 0; i < selectedConditions.length; i++) {
selectedConditions[i] = i == index;
}
onConditionSelected(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(">")],
);
}
}