mirror of
https://github.com/SyncrowIOT/web.git
synced 2026-10-26 01:37:57 +00:00
push design and missing view and folder structure
This commit is contained in:
@@ -0,0 +1,415 @@
|
||||
import 'package:flutter/material.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/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/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';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routiens/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
|
||||
class ACHelper {
|
||||
static Future<Map<String, dynamic>?> showACFunctionsDialog(
|
||||
BuildContext context,
|
||||
List<DeviceFunction> functions,
|
||||
AllDevicesModel? device,
|
||||
List<DeviceFunctionData>? deviceSelectedFunctions,
|
||||
String uniqueCustomId,
|
||||
) async {
|
||||
List<ACFunction> acFunctions = functions.whereType<ACFunction>().toList();
|
||||
|
||||
return showDialog<Map<String, dynamic>?>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (_) => FunctionBloc()
|
||||
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
|
||||
child: AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
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('AC Functions'),
|
||||
Expanded(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// Function list
|
||||
SizedBox(
|
||||
width: selectedFunction != null ? 320 : 360,
|
||||
child: _buildFunctionsList(
|
||||
context: context,
|
||||
acFunctions: acFunctions,
|
||||
onFunctionSelected:
|
||||
(functionCode, operationName) => context
|
||||
.read<FunctionBloc>()
|
||||
.add(SelectFunction(
|
||||
functionCode: functionCode,
|
||||
operationName: operationName,
|
||||
)),
|
||||
),
|
||||
),
|
||||
// Value selector
|
||||
if (selectedFunction != null)
|
||||
Expanded(
|
||||
child: _buildValueSelector(
|
||||
context: context,
|
||||
selectedFunction: selectedFunction,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
acFunctions: acFunctions,
|
||||
device: device,
|
||||
operationName: selectedOperationName ?? '',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
DialogFooter(
|
||||
onCancel: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
onConfirm: state.addedFunctions.isNotEmpty
|
||||
? () {
|
||||
/// add the functions to the routine bloc
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
).then((value) {
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
/// Build functions list for AC functions dialog
|
||||
static Widget _buildFunctionsList({
|
||||
required BuildContext context,
|
||||
required List<ACFunction> acFunctions,
|
||||
required Function(String, String) onFunctionSelected,
|
||||
}) {
|
||||
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,
|
||||
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: () => onFunctionSelected(
|
||||
function.code,
|
||||
function.operationName,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Build value selector for AC functions dialog
|
||||
static Widget _buildValueSelector({
|
||||
required BuildContext context,
|
||||
required String selectedFunction,
|
||||
required DeviceFunctionData? selectedFunctionData,
|
||||
required List<ACFunction> acFunctions,
|
||||
AllDevicesModel? device,
|
||||
required String operationName,
|
||||
}) {
|
||||
if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') {
|
||||
final initialValue = selectedFunctionData?.value ?? 200;
|
||||
return _buildTemperatureSelector(
|
||||
context: context,
|
||||
initialValue: initialValue,
|
||||
selectCode: selectedFunction,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
device: device,
|
||||
operationName: operationName,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
);
|
||||
}
|
||||
|
||||
final selectedFn =
|
||||
acFunctions.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,
|
||||
);
|
||||
}
|
||||
|
||||
/// Build temperature selector for AC functions dialog
|
||||
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),
|
||||
_buildTemperatureDisplay(
|
||||
context,
|
||||
initialValue,
|
||||
device,
|
||||
operationName,
|
||||
selectedFunctionData,
|
||||
selectCode,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildTemperatureSlider(
|
||||
context,
|
||||
initialValue,
|
||||
device,
|
||||
operationName,
|
||||
selectedFunctionData,
|
||||
selectCode,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Build condition toggle for AC functions dialog
|
||||
static Widget _buildConditionToggle(
|
||||
BuildContext context,
|
||||
String? currentCondition,
|
||||
String selectCode,
|
||||
AllDevicesModel? device,
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
|
||||
// Function(String) onConditionChanged,
|
||||
) {
|
||||
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 _buildTemperatureDisplay(
|
||||
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 ?? 200) / 10}°C',
|
||||
style: context.textTheme.headlineMedium!.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildTemperatureSlider(
|
||||
BuildContext context,
|
||||
dynamic initialValue,
|
||||
AllDevicesModel? device,
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode,
|
||||
) {
|
||||
return Slider(
|
||||
value: initialValue is int ? initialValue.toDouble() : 160.0,
|
||||
min: 160,
|
||||
max: 300,
|
||||
divisions: 14,
|
||||
label: '${((initialValue ?? 160) / 10).toInt()}°C',
|
||||
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<ACOperationalValue> values,
|
||||
required dynamic selectedValue,
|
||||
AllDevicesModel? device,
|
||||
required String operationName,
|
||||
required String selectCode,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
|
||||
// required Function(dynamic) onValueChanged,
|
||||
}) {
|
||||
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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_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/widgets/dialog_footer.dart';
|
||||
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart';
|
||||
|
||||
class DelayHelper {
|
||||
static Future<Map<String, dynamic>?> showDelayPickerDialog(
|
||||
BuildContext context, String uniqueCustomId) async {
|
||||
int hours = 0;
|
||||
int minutes = 0;
|
||||
|
||||
return showDialog<Map<String, dynamic>?>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
content: Container(
|
||||
width: 600,
|
||||
height: 300,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const DialogHeader('Select Delay Duration'),
|
||||
Expanded(
|
||||
child: CupertinoTimerPicker(
|
||||
mode: CupertinoTimerPickerMode.hm,
|
||||
initialTimerDuration:
|
||||
Duration(hours: hours, minutes: minutes),
|
||||
onTimerDurationChanged: (Duration newDuration) {
|
||||
hours = newDuration.inHours;
|
||||
minutes = newDuration.inMinutes % 60;
|
||||
},
|
||||
),
|
||||
),
|
||||
DialogFooter(
|
||||
onCancel: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
onConfirm: () {
|
||||
int totalSeconds = (hours * 3600) + (minutes * 60);
|
||||
context.read<RoutineBloc>().add(AddFunctionToRoutine(
|
||||
[
|
||||
DeviceFunctionData(
|
||||
entityId: 'delay',
|
||||
functionCode: 'delay',
|
||||
operationName: 'Delay',
|
||||
value: totalSeconds,
|
||||
)
|
||||
],
|
||||
uniqueCustomId,
|
||||
));
|
||||
|
||||
Navigator.pop(context, {
|
||||
'deviceId': 'delay',
|
||||
'value': totalSeconds,
|
||||
});
|
||||
},
|
||||
isConfirmEnabled: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_event.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/app_enum.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:time_picker_spinner/time_picker_spinner.dart';
|
||||
|
||||
class EffectPeriodHelper {
|
||||
static Future<List<String>?> showCustomTimePicker(BuildContext context) async {
|
||||
String selectedStartTime = "00:00";
|
||||
String selectedEndTime = "23:59";
|
||||
PageController pageController = PageController(initialPage: 0);
|
||||
|
||||
DateTime startDateTime = DateTime(2022, 1, 1, 0, 0);
|
||||
DateTime endDateTime = DateTime(2022, 1, 1, 23, 59);
|
||||
|
||||
context.customAlertDialog(
|
||||
alertBody: SizedBox(
|
||||
height: 250,
|
||||
child: PageView(
|
||||
controller: pageController,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
children: [
|
||||
_buildTimePickerPage(
|
||||
context: context,
|
||||
pageController: pageController,
|
||||
isStartTime: true,
|
||||
time: startDateTime,
|
||||
onTimeChange: (time) {
|
||||
selectedStartTime =
|
||||
"${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}";
|
||||
},
|
||||
),
|
||||
_buildTimePickerPage(
|
||||
context: context,
|
||||
pageController: pageController,
|
||||
isStartTime: false,
|
||||
time: endDateTime,
|
||||
onTimeChange: (time) {
|
||||
selectedEndTime =
|
||||
"${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}";
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
title: "Custom",
|
||||
onConfirm: () {
|
||||
context.read<EffectPeriodBloc>().add(
|
||||
SetCustomTime(selectedStartTime, selectedEndTime),
|
||||
);
|
||||
context.read<EffectPeriodBloc>().add(
|
||||
const SetPeriod(EnumEffectivePeriodOptions.custom),
|
||||
);
|
||||
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
static Widget _buildTimePickerPage({
|
||||
required BuildContext context,
|
||||
required PageController pageController,
|
||||
required bool isStartTime,
|
||||
required DateTime time,
|
||||
required Function(DateTime) onTimeChange,
|
||||
}) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
if (!isStartTime)
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
pageController.previousPage(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeIn,
|
||||
);
|
||||
},
|
||||
child: const Text('Start'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
child: Text(isStartTime ? "Start" : "End",
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: ColorsManager.textPrimaryColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 10)),
|
||||
),
|
||||
if (isStartTime)
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
pageController.nextPage(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeIn,
|
||||
);
|
||||
},
|
||||
child: Text('End',
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: ColorsManager.textPrimaryColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 10)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
TimePickerSpinner(
|
||||
is24HourMode: false,
|
||||
normalTextStyle: const TextStyle(
|
||||
fontSize: 24,
|
||||
color: Colors.grey,
|
||||
),
|
||||
highlightedTextStyle: const TextStyle(
|
||||
fontSize: 24,
|
||||
color: ColorsManager.primaryColor,
|
||||
),
|
||||
spacing: 20,
|
||||
itemHeight: 50,
|
||||
isForce2Digits: true,
|
||||
time: time,
|
||||
onTimeChange: onTimeChange,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static String formatEnumValue(EnumEffectivePeriodOptions value) {
|
||||
switch (value) {
|
||||
case EnumEffectivePeriodOptions.allDay:
|
||||
return "All Day";
|
||||
case EnumEffectivePeriodOptions.daytime:
|
||||
return "Daytime";
|
||||
case EnumEffectivePeriodOptions.night:
|
||||
return "Night";
|
||||
case EnumEffectivePeriodOptions.custom:
|
||||
return "Custom";
|
||||
case EnumEffectivePeriodOptions.none:
|
||||
return "None";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
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/helper/duration_format_helper.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/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';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class OneGangSwitchHelper {
|
||||
static Future<Map<String, dynamic>?> showSwitchFunctionsDialog(
|
||||
BuildContext context,
|
||||
List<DeviceFunction> functions,
|
||||
AllDevicesModel? device,
|
||||
List<DeviceFunctionData>? deviceSelectedFunctions,
|
||||
String uniqueCustomId,
|
||||
) async {
|
||||
List<BaseSwitchFunction> acFunctions =
|
||||
functions.whereType<BaseSwitchFunction>().toList();
|
||||
|
||||
return showDialog<Map<String, dynamic>?>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (_) => FunctionBloc()
|
||||
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
|
||||
child: AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
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('1 Gang Light Switch Condition'),
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
// Left side: Function list
|
||||
Expanded(
|
||||
child: ListView.separated(
|
||||
itemCount: acFunctions.length,
|
||||
separatorBuilder: (_, __) => const Divider(
|
||||
color: ColorsManager.dividerColor,
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final function = acFunctions[index];
|
||||
return ListTile(
|
||||
leading: SvgPicture.asset(
|
||||
function.icon,
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
title: Text(
|
||||
function.operationName,
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
trailing: const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 16,
|
||||
color: ColorsManager.textGray,
|
||||
),
|
||||
onTap: () {
|
||||
context
|
||||
.read<FunctionBloc>()
|
||||
.add(SelectFunction(
|
||||
functionCode: function.code,
|
||||
operationName:
|
||||
function.operationName,
|
||||
));
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
// Right side: Value selector
|
||||
if (selectedFunction != null)
|
||||
Expanded(
|
||||
child: _buildValueSelector(
|
||||
context: context,
|
||||
selectedFunction: selectedFunction,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
acFunctions: acFunctions,
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildValueSelector({
|
||||
required BuildContext context,
|
||||
required String selectedFunction,
|
||||
required DeviceFunctionData? selectedFunctionData,
|
||||
required List<BaseSwitchFunction> acFunctions,
|
||||
AllDevicesModel? device,
|
||||
required String operationName,
|
||||
}) {
|
||||
if (selectedFunction == 'countdown_1') {
|
||||
final initialValue = selectedFunctionData?.value ?? 200;
|
||||
return _buildCountDownSelector(
|
||||
context: context,
|
||||
initialValue: initialValue,
|
||||
selectCode: selectedFunction,
|
||||
currentCondition: selectedFunctionData?.condition,
|
||||
device: device,
|
||||
operationName: operationName,
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
);
|
||||
}
|
||||
|
||||
final selectedFn =
|
||||
acFunctions.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 _buildCountDownSelector({
|
||||
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,
|
||||
String? currentCondition,
|
||||
String selectCode,
|
||||
AllDevicesModel? device,
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
// Function(String) onConditionChanged,
|
||||
) {
|
||||
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(
|
||||
DurationFormatMixin.formatDuration(initialValue?.toInt() ?? 0),
|
||||
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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_event.dart';
|
||||
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_state.dart';
|
||||
import 'package:syncrow_web/pages/routiens/models/icon_model.dart';
|
||||
import 'package:syncrow_web/pages/routiens/view/effective_period_view.dart';
|
||||
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class SettingHelper {
|
||||
static Future<String?> showSettingDialog({
|
||||
required BuildContext context,
|
||||
String? iconId,
|
||||
}) async {
|
||||
return showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
final isAutomation = context.read<RoutineBloc>().isAutomation;
|
||||
return BlocProvider(
|
||||
create: (_) =>
|
||||
SettingBloc()..add(InitialEvent(selectedIcon: iconId ?? '')),
|
||||
child: AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
content: BlocBuilder<SettingBloc, SettingState>(
|
||||
builder: (context, state) {
|
||||
String selectedIcon = '';
|
||||
List<IconModel> list = [];
|
||||
if (state is TabToRunSettingLoaded) {
|
||||
selectedIcon = state.selectedIcon;
|
||||
list = state.iconList;
|
||||
}
|
||||
return Container(
|
||||
width: context.read<SettingBloc>().isExpanded ? 800 : 400,
|
||||
height: context.read<SettingBloc>().isExpanded && isAutomation
|
||||
? 500
|
||||
: 300,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const DialogHeader('Settings'),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 400,
|
||||
child: isAutomation
|
||||
? Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 10,
|
||||
left: 10,
|
||||
right: 10,
|
||||
bottom: 10),
|
||||
child: Column(
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {},
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Validity',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(
|
||||
color: ColorsManager
|
||||
.textPrimaryColor,
|
||||
fontWeight:
|
||||
FontWeight
|
||||
.w400,
|
||||
fontSize: 14),
|
||||
),
|
||||
const Icon(
|
||||
Icons
|
||||
.arrow_forward_ios_outlined,
|
||||
color: ColorsManager
|
||||
.textGray,
|
||||
size: 15,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
const Divider(
|
||||
color: ColorsManager.graysColor,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
BlocProvider.of<SettingBloc>(
|
||||
context)
|
||||
.add(FetchIcons(
|
||||
expanded: !context
|
||||
.read<
|
||||
SettingBloc>()
|
||||
.isExpanded));
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Effective Period',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(
|
||||
color: ColorsManager
|
||||
.textPrimaryColor,
|
||||
fontWeight:
|
||||
FontWeight
|
||||
.w400,
|
||||
fontSize: 14),
|
||||
),
|
||||
const Icon(
|
||||
Icons
|
||||
.arrow_forward_ios_outlined,
|
||||
color: ColorsManager
|
||||
.textGray,
|
||||
size: 15,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
const Divider(
|
||||
color: ColorsManager.graysColor,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Executed by',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(
|
||||
color: ColorsManager
|
||||
.textPrimaryColor,
|
||||
fontWeight:
|
||||
FontWeight.w400,
|
||||
fontSize: 14),
|
||||
),
|
||||
Text('Cloud',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(
|
||||
color:
|
||||
ColorsManager
|
||||
.textGray,
|
||||
fontWeight:
|
||||
FontWeight
|
||||
.w400,
|
||||
fontSize: 14)),
|
||||
],
|
||||
),
|
||||
],
|
||||
)),
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 10,
|
||||
left: 10,
|
||||
right: 10,
|
||||
bottom: 10),
|
||||
child: Column(
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
BlocProvider.of<SettingBloc>(
|
||||
context)
|
||||
.add(FetchIcons(
|
||||
expanded: !context
|
||||
.read<
|
||||
SettingBloc>()
|
||||
.isExpanded));
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Icons',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(
|
||||
color: ColorsManager
|
||||
.textPrimaryColor,
|
||||
fontWeight:
|
||||
FontWeight
|
||||
.w400,
|
||||
fontSize: 14),
|
||||
),
|
||||
const Icon(
|
||||
Icons
|
||||
.arrow_forward_ios_outlined,
|
||||
color: ColorsManager
|
||||
.textGray,
|
||||
size: 15,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
const Divider(
|
||||
color: ColorsManager.graysColor,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Show on devices page',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(
|
||||
color: ColorsManager
|
||||
.textPrimaryColor,
|
||||
fontWeight:
|
||||
FontWeight.w400,
|
||||
fontSize: 14),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.end,
|
||||
children: [
|
||||
Container(
|
||||
height: 30,
|
||||
width: 1,
|
||||
color: ColorsManager
|
||||
.graysColor,
|
||||
),
|
||||
Transform.scale(
|
||||
scale: .8,
|
||||
child: CupertinoSwitch(
|
||||
value: true,
|
||||
onChanged: (value) {},
|
||||
applyTheme: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
const Divider(
|
||||
color: ColorsManager.graysColor,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Executed by',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(
|
||||
color: ColorsManager
|
||||
.textPrimaryColor,
|
||||
fontWeight:
|
||||
FontWeight.w400,
|
||||
fontSize: 14),
|
||||
),
|
||||
Text('Cloud',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(
|
||||
color:
|
||||
ColorsManager
|
||||
.textGray,
|
||||
fontWeight:
|
||||
FontWeight
|
||||
.w400,
|
||||
fontSize: 14)),
|
||||
],
|
||||
),
|
||||
],
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (context.read<SettingBloc>().isExpanded &&
|
||||
!isAutomation)
|
||||
SizedBox(
|
||||
width: 400,
|
||||
height: 150,
|
||||
child: state is LoadingState
|
||||
? const Center(
|
||||
child: CircularProgressIndicator())
|
||||
: GridView.builder(
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 6,
|
||||
crossAxisSpacing: 12,
|
||||
mainAxisSpacing: 12,
|
||||
),
|
||||
shrinkWrap: true,
|
||||
itemCount: list.length,
|
||||
itemBuilder: (context, index) {
|
||||
final iconModel = list[index];
|
||||
return SizedBox(
|
||||
width: 35,
|
||||
height: 35,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
BlocProvider.of<SettingBloc>(
|
||||
context)
|
||||
.add(SelectIcon(
|
||||
iconId: iconModel.uuid,
|
||||
));
|
||||
selectedIcon = iconModel.uuid;
|
||||
},
|
||||
child: SizedBox(
|
||||
child: ClipOval(
|
||||
child: Container(
|
||||
padding:
|
||||
const EdgeInsets.all(1),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: selectedIcon ==
|
||||
iconModel.uuid
|
||||
? ColorsManager
|
||||
.primaryColorWithOpacity
|
||||
: Colors
|
||||
.transparent,
|
||||
width: 2,
|
||||
),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Image.memory(
|
||||
iconModel.iconBytes,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)),
|
||||
if (context.read<SettingBloc>().isExpanded &&
|
||||
isAutomation)
|
||||
const SizedBox(
|
||||
height: 350,
|
||||
width: 400,
|
||||
child: EffectivePeriodView())
|
||||
],
|
||||
),
|
||||
Container(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Container(
|
||||
alignment: AlignmentDirectional.center,
|
||||
child: Text(
|
||||
'Cancel',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(
|
||||
color: ColorsManager.textGray,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 1,
|
||||
height: 50,
|
||||
color: ColorsManager.greyColor),
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop(selectedIcon);
|
||||
},
|
||||
child: Container(
|
||||
alignment: AlignmentDirectional.center,
|
||||
child: Text(
|
||||
'Confirm',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(
|
||||
color: ColorsManager
|
||||
.primaryColorWithOpacity,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
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/helper/duration_format_helper.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/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';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class ThreeGangSwitchHelper {
|
||||
static Future<Map<String, dynamic>?> showSwitchFunctionsDialog(
|
||||
BuildContext context,
|
||||
List<DeviceFunction> functions,
|
||||
AllDevicesModel? device,
|
||||
List<DeviceFunctionData>? deviceSelectedFunctions,
|
||||
String uniqueCustomId,
|
||||
) async {
|
||||
List<BaseSwitchFunction> switchFunctions =
|
||||
functions.whereType<BaseSwitchFunction>().toList();
|
||||
|
||||
return showDialog<Map<String, dynamic>?>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (_) => FunctionBloc()
|
||||
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
|
||||
child: AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
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(
|
||||
leading: SvgPicture.asset(
|
||||
function.icon,
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
title: Text(
|
||||
function.operationName,
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
trailing: const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 16,
|
||||
color: ColorsManager.textGray,
|
||||
),
|
||||
onTap: () {
|
||||
context
|
||||
.read<FunctionBloc>()
|
||||
.add(SelectFunction(
|
||||
functionCode: function.code,
|
||||
operationName:
|
||||
function.operationName,
|
||||
));
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
// 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,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
String? currentCondition,
|
||||
String selectCode,
|
||||
AllDevicesModel? device,
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
// Function(String) onConditionChanged,
|
||||
) {
|
||||
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(
|
||||
DurationFormatMixin.formatDuration(initialValue?.toInt() ?? 0),
|
||||
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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
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/helper/duration_format_helper.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/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';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class TwoGangSwitchHelper {
|
||||
static Future<Map<String, dynamic>?> showSwitchFunctionsDialog(
|
||||
BuildContext context,
|
||||
List<DeviceFunction> functions,
|
||||
AllDevicesModel? device,
|
||||
List<DeviceFunctionData>? deviceSelectedFunctions,
|
||||
String uniqueCustomId,
|
||||
) async {
|
||||
List<BaseSwitchFunction> switchFunctions =
|
||||
functions.whereType<BaseSwitchFunction>().toList();
|
||||
|
||||
return showDialog<Map<String, dynamic>?>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (_) => FunctionBloc()
|
||||
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
|
||||
child: AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
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(
|
||||
leading: SvgPicture.asset(
|
||||
function.icon,
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
title: Text(
|
||||
function.operationName,
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
trailing: const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 16,
|
||||
color: ColorsManager.textGray,
|
||||
),
|
||||
onTap: () {
|
||||
context
|
||||
.read<FunctionBloc>()
|
||||
.add(SelectFunction(
|
||||
functionCode: function.code,
|
||||
operationName:
|
||||
function.operationName,
|
||||
));
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
// 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,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
String? currentCondition,
|
||||
String selectCode,
|
||||
AllDevicesModel? device,
|
||||
String operationName,
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
// Function(String) onConditionChanged,
|
||||
) {
|
||||
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(
|
||||
DurationFormatMixin.formatDuration(initialValue?.toInt() ?? 0),
|
||||
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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user