mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 07:07:19 +00:00
Refactor wall presence sensor components and add new widgets for improved functionality and clarity to ensure reusability in the future for other devices.
This commit is contained in:
33
lib/pages/routines/widgets/condition_toggle.dart
Normal file
33
lib/pages/routines/widgets/condition_toggle.dart
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class ConditionToggle extends StatelessWidget {
|
||||||
|
final String? currentCondition;
|
||||||
|
final void Function(String condition) onChanged;
|
||||||
|
|
||||||
|
const ConditionToggle({
|
||||||
|
required this.onChanged,
|
||||||
|
this.currentCondition,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
static const _conditions = ["<", "==", ">"];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ToggleButtons(
|
||||||
|
onPressed: (index) => onChanged(_conditions[index]),
|
||||||
|
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(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
26
lib/pages/routines/widgets/function_slider.dart
Normal file
26
lib/pages/routines/widgets/function_slider.dart
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class FunctionSlider extends StatelessWidget {
|
||||||
|
final dynamic initialValue;
|
||||||
|
|
||||||
|
final (double min, double max) range;
|
||||||
|
final void Function(double value) onChanged;
|
||||||
|
|
||||||
|
const FunctionSlider({
|
||||||
|
required this.onChanged,
|
||||||
|
required this.initialValue,
|
||||||
|
required this.range,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Slider(
|
||||||
|
value: initialValue is int ? initialValue.toDouble() : range.$1,
|
||||||
|
min: range.$1,
|
||||||
|
max: range.$2,
|
||||||
|
divisions: (range.$2 - range.$1).toInt(),
|
||||||
|
onChanged: onChanged,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,16 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.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/device_managment/all_devices/models/devices_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/wps/wps_operational_value.dart';
|
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/time_wheel.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wps_value_selector_widget.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
|
||||||
|
|
||||||
class WallPresenceSensor extends StatefulWidget {
|
class WallPresenceSensor extends StatefulWidget {
|
||||||
final List<DeviceFunction> functions;
|
final List<DeviceFunction> functions;
|
||||||
@ -63,8 +62,7 @@ class _WallPresenceSensorState extends State<WallPresenceSensor> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_wpsFunctions =
|
_wpsFunctions = widget.functions.whereType<WpsFunctions>().where((function) {
|
||||||
widget.functions.whereType<WpsFunctions>().where((function) {
|
|
||||||
if (widget.dialogType == 'THEN') {
|
if (widget.dialogType == 'THEN') {
|
||||||
return function.type == 'THEN' || function.type == 'BOTH';
|
return function.type == 'THEN' || function.type == 'BOTH';
|
||||||
}
|
}
|
||||||
@ -176,10 +174,10 @@ class _WallPresenceSensorState extends State<WallPresenceSensor> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: _ValueSelector(
|
child: WpsValueSelectorWidget(
|
||||||
selectedFunction: selectedFunction,
|
selectedFunction: selectedFunction,
|
||||||
functionData: functionData,
|
functionData: functionData,
|
||||||
acFunctions: _wpsFunctions,
|
wpsFunctions: _wpsFunctions,
|
||||||
device: widget.device,
|
device: widget.device,
|
||||||
dialogType: widget.dialogType!,
|
dialogType: widget.dialogType!,
|
||||||
removeComparators: widget.removeComparetors,
|
removeComparators: widget.removeComparetors,
|
||||||
@ -208,342 +206,3 @@ class _WallPresenceSensorState extends State<WallPresenceSensor> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ValueSelector extends StatelessWidget {
|
|
||||||
final String selectedFunction;
|
|
||||||
final DeviceFunctionData functionData;
|
|
||||||
final List<WpsFunctions> acFunctions;
|
|
||||||
final AllDevicesModel? device;
|
|
||||||
final String dialogType;
|
|
||||||
final bool removeComparators;
|
|
||||||
|
|
||||||
const _ValueSelector({
|
|
||||||
required this.selectedFunction,
|
|
||||||
required this.functionData,
|
|
||||||
required this.acFunctions,
|
|
||||||
required this.device,
|
|
||||||
required this.dialogType,
|
|
||||||
required this.removeComparators,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final selectedFn =
|
|
||||||
acFunctions.firstWhere((f) => f.code == selectedFunction);
|
|
||||||
final values = selectedFn.getOperationalValues();
|
|
||||||
|
|
||||||
if (_isSliderFunction(selectedFunction)) {
|
|
||||||
return _SliderValueSelector(
|
|
||||||
selectedFunction: selectedFunction,
|
|
||||||
functionData: functionData,
|
|
||||||
device: device,
|
|
||||||
dialogType: dialogType,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _OperationalValuesList(
|
|
||||||
values: values,
|
|
||||||
selectedValue: functionData.value,
|
|
||||||
device: device,
|
|
||||||
operationName: selectedFn.operationName,
|
|
||||||
selectCode: selectedFunction,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _isSliderFunction(String function) =>
|
|
||||||
['dis_current', 'presence_time', 'illuminance_value'].contains(function);
|
|
||||||
}
|
|
||||||
|
|
||||||
class _SliderValueSelector extends StatelessWidget {
|
|
||||||
final String selectedFunction;
|
|
||||||
final DeviceFunctionData functionData;
|
|
||||||
final AllDevicesModel? device;
|
|
||||||
final String dialogType;
|
|
||||||
|
|
||||||
const _SliderValueSelector({
|
|
||||||
required this.selectedFunction,
|
|
||||||
required this.functionData,
|
|
||||||
required this.device,
|
|
||||||
required this.dialogType,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final initialValue = functionData.value ?? 250;
|
|
||||||
return Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
_ConditionToggle(
|
|
||||||
currentCondition: functionData.condition,
|
|
||||||
selectCode: selectedFunction,
|
|
||||||
device: device,
|
|
||||||
operationName: functionData.operationName,
|
|
||||||
selectedValue: functionData.value,
|
|
||||||
),
|
|
||||||
_ValueDisplay(
|
|
||||||
value: initialValue,
|
|
||||||
functionCode: selectedFunction,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
_FunctionSlider(
|
|
||||||
initialValue: initialValue,
|
|
||||||
functionCode: selectedFunction,
|
|
||||||
functionData: functionData,
|
|
||||||
device: device,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ConditionToggle extends StatelessWidget {
|
|
||||||
final String? currentCondition;
|
|
||||||
final String selectCode;
|
|
||||||
final AllDevicesModel? device;
|
|
||||||
final String operationName;
|
|
||||||
final dynamic selectedValue;
|
|
||||||
|
|
||||||
const _ConditionToggle({
|
|
||||||
this.currentCondition,
|
|
||||||
required this.selectCode,
|
|
||||||
this.device,
|
|
||||||
required this.operationName,
|
|
||||||
this.selectedValue,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
const conditions = ["<", "==", ">"];
|
|
||||||
return ToggleButtons(
|
|
||||||
onPressed: (index) => _updateCondition(context, conditions[index]),
|
|
||||||
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(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateCondition(BuildContext context, String condition) {
|
|
||||||
context.read<FunctionBloc>().add(
|
|
||||||
AddFunction(
|
|
||||||
functionData: DeviceFunctionData(
|
|
||||||
entityId: device?.uuid ?? '',
|
|
||||||
functionCode: selectCode,
|
|
||||||
operationName: operationName,
|
|
||||||
condition: condition,
|
|
||||||
value: selectedValue,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ValueDisplay extends StatelessWidget {
|
|
||||||
final dynamic value;
|
|
||||||
final String functionCode;
|
|
||||||
|
|
||||||
const _ValueDisplay({
|
|
||||||
required this.value,
|
|
||||||
required this.functionCode,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
_getDisplayText(),
|
|
||||||
style: context.textTheme.headlineMedium!.copyWith(
|
|
||||||
color: ColorsManager.primaryColorWithOpacity,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _getDisplayText() {
|
|
||||||
final intValue = (value as num?)?.toInt() ?? 0;
|
|
||||||
switch (functionCode) {
|
|
||||||
case 'presence_time':
|
|
||||||
return '$intValue Min';
|
|
||||||
case 'dis_current':
|
|
||||||
return '$intValue CM';
|
|
||||||
case 'illuminance_value':
|
|
||||||
return '$intValue Lux';
|
|
||||||
default:
|
|
||||||
return '$intValue';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _FunctionSlider extends StatelessWidget {
|
|
||||||
final dynamic initialValue;
|
|
||||||
final String functionCode;
|
|
||||||
final DeviceFunctionData functionData;
|
|
||||||
final AllDevicesModel? device;
|
|
||||||
|
|
||||||
const _FunctionSlider({
|
|
||||||
required this.initialValue,
|
|
||||||
required this.functionCode,
|
|
||||||
required this.functionData,
|
|
||||||
required this.device,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final (min, max) = _getSliderRange();
|
|
||||||
return Slider(
|
|
||||||
value: initialValue is int ? initialValue.toDouble() : min,
|
|
||||||
min: min,
|
|
||||||
max: max,
|
|
||||||
divisions: (max - min).toInt(),
|
|
||||||
onChanged: (value) => _updateValue(context, value.toInt()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
(double, double) _getSliderRange() {
|
|
||||||
switch (functionCode) {
|
|
||||||
case 'presence_time':
|
|
||||||
return (0, 65535);
|
|
||||||
case 'dis_current':
|
|
||||||
return (1, 600);
|
|
||||||
case 'illuminance_value':
|
|
||||||
return (0, 10000);
|
|
||||||
default:
|
|
||||||
return (200, 300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateValue(BuildContext context, int value) {
|
|
||||||
context.read<FunctionBloc>().add(
|
|
||||||
AddFunction(
|
|
||||||
functionData: DeviceFunctionData(
|
|
||||||
entityId: device?.uuid ?? '',
|
|
||||||
functionCode: functionCode,
|
|
||||||
operationName: functionData.operationName,
|
|
||||||
value: value,
|
|
||||||
condition: functionData.condition,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _OperationalValuesList extends StatelessWidget {
|
|
||||||
final List<WpsOperationalValue> values;
|
|
||||||
final dynamic selectedValue;
|
|
||||||
final AllDevicesModel? device;
|
|
||||||
final String operationName;
|
|
||||||
final String selectCode;
|
|
||||||
|
|
||||||
const _OperationalValuesList({
|
|
||||||
required this.values,
|
|
||||||
required this.selectedValue,
|
|
||||||
required this.device,
|
|
||||||
required this.operationName,
|
|
||||||
required this.selectCode,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return operationName == 'Nobody Time'
|
|
||||||
? _buildTimeWheel(context)
|
|
||||||
: ListView.builder(
|
|
||||||
padding: const EdgeInsets.all(20),
|
|
||||||
itemCount: values.length,
|
|
||||||
itemBuilder: (context, index) =>
|
|
||||||
_buildValueItem(context, values[index]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildTimeWheel(BuildContext context) {
|
|
||||||
final currentTotalSeconds = selectedValue as int? ?? 0;
|
|
||||||
return TimeWheelPicker(
|
|
||||||
initialHours: currentTotalSeconds ~/ 3600,
|
|
||||||
initialMinutes: (currentTotalSeconds % 3600) ~/ 60,
|
|
||||||
initialSeconds: currentTotalSeconds % 60,
|
|
||||||
onTimeChanged: (h, m, s) => _updateTotalSeconds(context, h, m, s),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildValueItem(BuildContext context, WpsOperationalValue value) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
_buildValueIcon(context, value),
|
|
||||||
Expanded(child: _buildValueDescription(value)),
|
|
||||||
_buildValueRadio(context, value),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildValueIcon(context, WpsOperationalValue value) {
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
if (_shouldShowTextDescription)
|
|
||||||
Text(value.description.replaceAll("cm", '')),
|
|
||||||
SvgPicture.asset(value.icon, width: 25, height: 25),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get _shouldShowTextDescription =>
|
|
||||||
operationName == 'Far Detection' ||
|
|
||||||
operationName == 'Motionless Detection Sensitivity';
|
|
||||||
|
|
||||||
Widget _buildValueDescription(WpsOperationalValue value) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
|
||||||
child: Text(value.description),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildValueRadio(context, WpsOperationalValue value) {
|
|
||||||
return Radio<dynamic>(
|
|
||||||
value: value.value,
|
|
||||||
groupValue: selectedValue,
|
|
||||||
onChanged: (_) => _selectValue(context, value.value),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _selectValue(BuildContext context, dynamic value) {
|
|
||||||
context.read<FunctionBloc>().add(
|
|
||||||
AddFunction(
|
|
||||||
functionData: DeviceFunctionData(
|
|
||||||
entityId: device?.uuid ?? '',
|
|
||||||
functionCode: selectCode,
|
|
||||||
operationName: operationName,
|
|
||||||
value: value,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateTotalSeconds(BuildContext context, int h, int m, int s) {
|
|
||||||
context.read<FunctionBloc>().add(
|
|
||||||
AddFunction(
|
|
||||||
functionData: DeviceFunctionData(
|
|
||||||
entityId: device?.uuid ?? '',
|
|
||||||
functionCode: selectCode,
|
|
||||||
operationName: operationName,
|
|
||||||
value: h * 3600 + m * 60 + s,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,114 @@
|
|||||||
|
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/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/models/wps/wps_operational_value.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/time_wheel.dart';
|
||||||
|
|
||||||
|
class WpsOperationalValuesList extends StatelessWidget {
|
||||||
|
final List<WpsOperationalValue> values;
|
||||||
|
final dynamic selectedValue;
|
||||||
|
final AllDevicesModel? device;
|
||||||
|
final String operationName;
|
||||||
|
final String selectCode;
|
||||||
|
|
||||||
|
const WpsOperationalValuesList({
|
||||||
|
required this.values,
|
||||||
|
required this.selectedValue,
|
||||||
|
required this.device,
|
||||||
|
required this.operationName,
|
||||||
|
required this.selectCode,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return operationName == 'Nobody Time'
|
||||||
|
? _buildTimeWheel(context)
|
||||||
|
: ListView.builder(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
itemCount: values.length,
|
||||||
|
itemBuilder: (context, index) => _buildValueItem(context, values[index]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTimeWheel(BuildContext context) {
|
||||||
|
final currentTotalSeconds = selectedValue as int? ?? 0;
|
||||||
|
return TimeWheelPicker(
|
||||||
|
initialHours: currentTotalSeconds ~/ 3600,
|
||||||
|
initialMinutes: (currentTotalSeconds % 3600) ~/ 60,
|
||||||
|
initialSeconds: currentTotalSeconds % 60,
|
||||||
|
onTimeChanged: (h, m, s) => _updateTotalSeconds(context, h, m, s),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildValueItem(BuildContext context, WpsOperationalValue value) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
_buildValueIcon(context, value),
|
||||||
|
Expanded(child: _buildValueDescription(value)),
|
||||||
|
_buildValueRadio(context, value),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildValueIcon(context, WpsOperationalValue value) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
if (_shouldShowTextDescription) Text(value.description.replaceAll("cm", '')),
|
||||||
|
SvgPicture.asset(value.icon, width: 25, height: 25),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get _shouldShowTextDescription =>
|
||||||
|
operationName == 'Far Detection' ||
|
||||||
|
operationName == 'Motionless Detection Sensitivity';
|
||||||
|
|
||||||
|
Widget _buildValueDescription(WpsOperationalValue value) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
|
child: Text(value.description),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildValueRadio(context, WpsOperationalValue value) {
|
||||||
|
return Radio<dynamic>(
|
||||||
|
value: value.value,
|
||||||
|
groupValue: selectedValue,
|
||||||
|
onChanged: (_) => _selectValue(context, value.value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _selectValue(BuildContext context, dynamic value) {
|
||||||
|
context.read<FunctionBloc>().add(
|
||||||
|
AddFunction(
|
||||||
|
functionData: DeviceFunctionData(
|
||||||
|
entityId: device?.uuid ?? '',
|
||||||
|
functionCode: selectCode,
|
||||||
|
operationName: operationName,
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateTotalSeconds(BuildContext context, int h, int m, int s) {
|
||||||
|
context.read<FunctionBloc>().add(
|
||||||
|
AddFunction(
|
||||||
|
functionData: DeviceFunctionData(
|
||||||
|
entityId: device?.uuid ?? '',
|
||||||
|
functionCode: selectCode,
|
||||||
|
operationName: operationName,
|
||||||
|
value: h * 3600 + m * 60 + s,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wps_operational_values_list.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
||||||
|
|
||||||
|
class WpsValueSelectorWidget extends StatelessWidget {
|
||||||
|
final String selectedFunction;
|
||||||
|
final DeviceFunctionData functionData;
|
||||||
|
final List<WpsFunctions> wpsFunctions;
|
||||||
|
final AllDevicesModel? device;
|
||||||
|
final String dialogType;
|
||||||
|
final bool removeComparators;
|
||||||
|
|
||||||
|
const WpsValueSelectorWidget({
|
||||||
|
required this.selectedFunction,
|
||||||
|
required this.functionData,
|
||||||
|
required this.wpsFunctions,
|
||||||
|
required this.device,
|
||||||
|
required this.dialogType,
|
||||||
|
required this.removeComparators,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final selectedFn = wpsFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||||
|
final values = selectedFn.getOperationalValues();
|
||||||
|
|
||||||
|
if (_isSliderFunction(selectedFunction)) {
|
||||||
|
return SliderValueSelector(
|
||||||
|
selectedFunction: selectedFunction,
|
||||||
|
functionData: functionData,
|
||||||
|
device: device,
|
||||||
|
dialogType: dialogType,
|
||||||
|
sliderRange: sliderRange,
|
||||||
|
displayedValue: getDisplayText,
|
||||||
|
initialValue: functionData.value ?? 250,
|
||||||
|
onConditionChanged: (condition) => context.read<FunctionBloc>().add(
|
||||||
|
AddFunction(
|
||||||
|
functionData: DeviceFunctionData(
|
||||||
|
entityId: device?.uuid ?? '',
|
||||||
|
functionCode: selectedFunction,
|
||||||
|
operationName: functionData.operationName,
|
||||||
|
condition: condition,
|
||||||
|
value: functionData.value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onSliderChanged: (value) => context.read<FunctionBloc>().add(
|
||||||
|
AddFunction(
|
||||||
|
functionData: DeviceFunctionData(
|
||||||
|
entityId: device?.uuid ?? '',
|
||||||
|
functionCode: selectedFunction,
|
||||||
|
operationName: functionData.operationName,
|
||||||
|
value: value.toInt(),
|
||||||
|
condition: functionData.condition,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return WpsOperationalValuesList(
|
||||||
|
values: values,
|
||||||
|
selectedValue: functionData.value,
|
||||||
|
device: device,
|
||||||
|
operationName: selectedFn.operationName,
|
||||||
|
selectCode: selectedFunction,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isSliderFunction(String function) =>
|
||||||
|
['dis_current', 'presence_time', 'illuminance_value'].contains(function);
|
||||||
|
|
||||||
|
(double, double) get sliderRange => switch (functionData.functionCode) {
|
||||||
|
'presence_time' => (0, 65535),
|
||||||
|
'dis_current' => (0, 600),
|
||||||
|
'illuminance_value' => (0, 10000),
|
||||||
|
_ => (200, 300),
|
||||||
|
};
|
||||||
|
|
||||||
|
String get getDisplayText {
|
||||||
|
final intValue = int.tryParse('${functionData.value ?? ''}');
|
||||||
|
return switch (functionData.functionCode) {
|
||||||
|
'presence_time' => '${intValue ?? '0'} Min',
|
||||||
|
'dis_current' => '${intValue ?? '250'} CM',
|
||||||
|
'illuminance_value' => '${intValue ?? '0'} Lux',
|
||||||
|
_ => '$intValue',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
67
lib/pages/routines/widgets/slider_value_selector.dart
Normal file
67
lib/pages/routines/widgets/slider_value_selector.dart
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/condition_toggle.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/function_slider.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/value_display.dart';
|
||||||
|
|
||||||
|
class SliderValueSelector extends StatelessWidget {
|
||||||
|
final String selectedFunction;
|
||||||
|
final DeviceFunctionData functionData;
|
||||||
|
final AllDevicesModel? device;
|
||||||
|
final String dialogType;
|
||||||
|
final (double, double) sliderRange;
|
||||||
|
final String displayedValue;
|
||||||
|
final Object? initialValue;
|
||||||
|
final void Function(String condition) onConditionChanged;
|
||||||
|
final void Function(double value) onSliderChanged;
|
||||||
|
|
||||||
|
const SliderValueSelector({
|
||||||
|
required this.selectedFunction,
|
||||||
|
required this.functionData,
|
||||||
|
required this.device,
|
||||||
|
required this.dialogType,
|
||||||
|
required this.sliderRange,
|
||||||
|
required this.displayedValue,
|
||||||
|
required this.initialValue,
|
||||||
|
required this.onConditionChanged,
|
||||||
|
required this.onSliderChanged,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
ConditionToggle(
|
||||||
|
currentCondition: functionData.condition,
|
||||||
|
onChanged: onConditionChanged,
|
||||||
|
),
|
||||||
|
ValueDisplay(
|
||||||
|
value: initialValue,
|
||||||
|
label: displayedValue,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
FunctionSlider(
|
||||||
|
initialValue: initialValue,
|
||||||
|
range: sliderRange, onChanged: onSliderChanged,
|
||||||
|
// void _updateValue(BuildContext context, int value) {
|
||||||
|
// context.read<FunctionBloc>().add(
|
||||||
|
// AddFunction(
|
||||||
|
// functionData: DeviceFunctionData(
|
||||||
|
// entityId: device?.uuid ?? '',
|
||||||
|
// functionCode: functionCode,
|
||||||
|
// operationName: functionData.operationName,
|
||||||
|
// value: value,
|
||||||
|
// condition: functionData.condition,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
31
lib/pages/routines/widgets/value_display.dart
Normal file
31
lib/pages/routines/widgets/value_display.dart
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class ValueDisplay extends StatelessWidget {
|
||||||
|
final dynamic value;
|
||||||
|
final String label;
|
||||||
|
|
||||||
|
const ValueDisplay({
|
||||||
|
required this.value,
|
||||||
|
required this.label,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: context.textTheme.headlineMedium!.copyWith(
|
||||||
|
color: ColorsManager.primaryColorWithOpacity,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user