mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 07:07:19 +00:00
Refactor SliderValueSelector and ValueDisplay to include unit handling and improve UI consistency across sensor dialogs.
This commit is contained in:
@ -29,13 +29,12 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SliderValueSelector(
|
return SliderValueSelector(
|
||||||
selectedFunction: selectedFunction,
|
currentCondition: selectedFunctionData.condition,
|
||||||
functionData: selectedFunctionData,
|
|
||||||
device: device,
|
|
||||||
dialogType: dialogType,
|
dialogType: dialogType,
|
||||||
sliderRange: _sliderRange,
|
sliderRange: _sliderRange,
|
||||||
displayedValue: _displayText,
|
displayedValue: _displayText,
|
||||||
initialValue: selectedFunctionData.value ?? 0,
|
initialValue: selectedFunctionData.value ?? 0,
|
||||||
|
unit: _unit,
|
||||||
onConditionChanged: (condition) => context.read<FunctionBloc>().add(
|
onConditionChanged: (condition) => context.read<FunctionBloc>().add(
|
||||||
AddFunction(
|
AddFunction(
|
||||||
functionData: DeviceFunctionData(
|
functionData: DeviceFunctionData(
|
||||||
@ -58,11 +57,8 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
double get sliderStepper {
|
);
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(double, double) get _sliderRange => switch (selectedFunctionData.functionCode) {
|
(double, double) get _sliderRange => switch (selectedFunctionData.functionCode) {
|
||||||
@ -94,12 +90,26 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
|||||||
'presence_range' ||
|
'presence_range' ||
|
||||||
'perceptual_boundary' ||
|
'perceptual_boundary' ||
|
||||||
'moving_boundary' =>
|
'moving_boundary' =>
|
||||||
'${parsedValue?.toStringAsFixed(1) ?? '0'} M',
|
'${parsedValue?.toStringAsFixed(1) ?? '0'}',
|
||||||
'moving_rigger_time' => '${parsedValue?.toStringAsFixed(3) ?? '0'} s',
|
'moving_rigger_time' => '${parsedValue?.toStringAsFixed(3) ?? '0'}',
|
||||||
'moving_static_time' ||
|
'moving_static_time' ||
|
||||||
'none_body_time' =>
|
'none_body_time' =>
|
||||||
'${parsedValue?.toStringAsFixed(0) ?? '0'} s',
|
'${parsedValue?.toStringAsFixed(0) ?? '0'}',
|
||||||
_ => '${parsedValue ?? 0}',
|
_ => '${parsedValue ?? 0}',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String get _unit {
|
||||||
|
return switch (selectedFunctionData.functionCode) {
|
||||||
|
'moving_max_dis' ||
|
||||||
|
'static_max_dis' ||
|
||||||
|
'moving_range' ||
|
||||||
|
'presence_range' ||
|
||||||
|
'perceptual_boundary' ||
|
||||||
|
'moving_boundary' =>
|
||||||
|
'M',
|
||||||
|
'moving_rigger_time' || 'moving_static_time' || 'none_body_time' => 'sec',
|
||||||
|
_ => '',
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,9 +32,7 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
|||||||
|
|
||||||
if (_isSliderFunction(selectedFunction)) {
|
if (_isSliderFunction(selectedFunction)) {
|
||||||
return SliderValueSelector(
|
return SliderValueSelector(
|
||||||
selectedFunction: selectedFunction,
|
currentCondition: functionData.condition,
|
||||||
functionData: functionData,
|
|
||||||
device: device,
|
|
||||||
dialogType: dialogType,
|
dialogType: dialogType,
|
||||||
sliderRange: sliderRange,
|
sliderRange: sliderRange,
|
||||||
displayedValue: getDisplayText,
|
displayedValue: getDisplayText,
|
||||||
@ -61,6 +59,7 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
unit: _unit,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,10 +85,17 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
|||||||
String get getDisplayText {
|
String get getDisplayText {
|
||||||
final intValue = int.tryParse('${functionData.value ?? ''}');
|
final intValue = int.tryParse('${functionData.value ?? ''}');
|
||||||
return switch (functionData.functionCode) {
|
return switch (functionData.functionCode) {
|
||||||
'presence_time' => '${intValue ?? '0'} Min',
|
'presence_time' => '${intValue ?? '0'}',
|
||||||
'dis_current' => '${intValue ?? '250'} CM',
|
'dis_current' => '${intValue ?? '250'}',
|
||||||
'illuminance_value' => '${intValue ?? '0'} Lux',
|
'illuminance_value' => '${intValue ?? '0'}',
|
||||||
_ => '$intValue',
|
_ => '$intValue',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String get _unit => switch (functionData.functionCode) {
|
||||||
|
'presence_time' => 'Min',
|
||||||
|
'dis_current' => 'CM',
|
||||||
|
'illuminance_value' => 'Lux',
|
||||||
|
_ => '',
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,47 +1,48 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
import 'package:flutter/services.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/condition_toggle.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/function_slider.dart';
|
import 'package:syncrow_web/pages/routines/widgets/function_slider.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/value_display.dart';
|
import 'package:syncrow_web/pages/routines/widgets/value_display.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
class SliderValueSelector extends StatelessWidget {
|
class SliderValueSelector extends StatelessWidget {
|
||||||
final String selectedFunction;
|
final String? currentCondition;
|
||||||
final DeviceFunctionData functionData;
|
|
||||||
final AllDevicesModel? device;
|
|
||||||
final String dialogType;
|
final String dialogType;
|
||||||
final (double, double) sliderRange;
|
final (double, double) sliderRange;
|
||||||
final String displayedValue;
|
final String displayedValue;
|
||||||
final Object? initialValue;
|
final Object? initialValue;
|
||||||
final void Function(String condition) onConditionChanged;
|
final void Function(String condition) onConditionChanged;
|
||||||
final void Function(double value) onSliderChanged;
|
final void Function(double value) onSliderChanged;
|
||||||
|
final String unit;
|
||||||
|
|
||||||
const SliderValueSelector({
|
const SliderValueSelector({
|
||||||
required this.selectedFunction,
|
|
||||||
required this.functionData,
|
|
||||||
required this.device,
|
|
||||||
required this.dialogType,
|
required this.dialogType,
|
||||||
required this.sliderRange,
|
required this.sliderRange,
|
||||||
required this.displayedValue,
|
required this.displayedValue,
|
||||||
required this.initialValue,
|
required this.initialValue,
|
||||||
required this.onConditionChanged,
|
required this.onConditionChanged,
|
||||||
required this.onSliderChanged,
|
required this.onSliderChanged,
|
||||||
|
required this.currentCondition,
|
||||||
|
required this.unit,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (dialogType == 'IF') {
|
||||||
return Column(
|
return Column(
|
||||||
spacing: 16,
|
spacing: 16,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
ConditionToggle(
|
ConditionToggle(
|
||||||
currentCondition: functionData.condition,
|
currentCondition: currentCondition,
|
||||||
onChanged: onConditionChanged,
|
onChanged: onConditionChanged,
|
||||||
),
|
),
|
||||||
ValueDisplay(
|
ValueDisplay(
|
||||||
value: initialValue,
|
value: initialValue,
|
||||||
label: displayedValue,
|
label: displayedValue,
|
||||||
|
unit: unit,
|
||||||
),
|
),
|
||||||
FunctionSlider(
|
FunctionSlider(
|
||||||
initialValue: initialValue,
|
initialValue: initialValue,
|
||||||
@ -51,4 +52,91 @@ class SliderValueSelector extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Spacer(),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
TextFormField(
|
||||||
|
onChanged: (value) => onSliderChanged(double.tryParse(value) ?? 0),
|
||||||
|
expands: false,
|
||||||
|
onTapOutside: (_) => FocusScope.of(context).unfocus(),
|
||||||
|
initialValue: displayedValue,
|
||||||
|
style: context.textTheme.headlineMedium!.copyWith(
|
||||||
|
color: ColorsManager.primaryColorWithOpacity,
|
||||||
|
),
|
||||||
|
inputFormatters: [
|
||||||
|
RangeInputFormatter(min: sliderRange.$1, max: sliderRange.$2),
|
||||||
|
],
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide.none,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
filled: true,
|
||||||
|
fillColor: ColorsManager.textFieldGreyColor.withOpacity(0.5),
|
||||||
|
suffixText: unit,
|
||||||
|
hintStyle: context.textTheme.headlineMedium!.copyWith(
|
||||||
|
color: ColorsManager.primaryColorWithOpacity,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Min: ${sliderRange.$1}',
|
||||||
|
style: context.textTheme.bodySmall!.copyWith(
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Text(
|
||||||
|
'Max: ${sliderRange.$2}',
|
||||||
|
style: context.textTheme.bodySmall!.copyWith(
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RangeInputFormatter extends TextInputFormatter {
|
||||||
|
const RangeInputFormatter({required this.min, required this.max});
|
||||||
|
|
||||||
|
final double min;
|
||||||
|
final double max;
|
||||||
|
|
||||||
|
@override
|
||||||
|
TextEditingValue formatEditUpdate(
|
||||||
|
TextEditingValue oldValue,
|
||||||
|
TextEditingValue newValue,
|
||||||
|
) {
|
||||||
|
final text = newValue.text;
|
||||||
|
if (text.isEmpty) {
|
||||||
|
return newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final value = double.tryParse(text);
|
||||||
|
if (value == null || value < min || value > max) {
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,13 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
|||||||
class ValueDisplay extends StatelessWidget {
|
class ValueDisplay extends StatelessWidget {
|
||||||
final dynamic value;
|
final dynamic value;
|
||||||
final String label;
|
final String label;
|
||||||
|
final String unit;
|
||||||
|
|
||||||
const ValueDisplay({
|
const ValueDisplay({
|
||||||
required this.value,
|
required this.value,
|
||||||
required this.label,
|
required this.label,
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.unit,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -21,7 +23,7 @@ class ValueDisplay extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
label,
|
'$label$unit',
|
||||||
style: context.textTheme.headlineMedium!.copyWith(
|
style: context.textTheme.headlineMedium!.copyWith(
|
||||||
color: ColorsManager.primaryColorWithOpacity,
|
color: ColorsManager.primaryColorWithOpacity,
|
||||||
),
|
),
|
||||||
|
Reference in New Issue
Block a user