mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-08-26 09:39:40 +00:00
Merge pull request #168 from SyncrowIOT/flush-presence-sensor-routines
fix real time garage door and add flush sensor to routines
This commit is contained in:
@ -0,0 +1,70 @@
|
||||
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/flush/flush_operational_value.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/time_wheel.dart';
|
||||
|
||||
class FlushOperationalValuesList extends StatelessWidget {
|
||||
final List<FlushOperationalValue> values;
|
||||
final dynamic selectedValue;
|
||||
final AllDevicesModel? device;
|
||||
final String operationName;
|
||||
final String selectCode;
|
||||
final ValueChanged<FlushOperationalValue> onSelect;
|
||||
const FlushOperationalValuesList({
|
||||
required this.values,
|
||||
required this.selectedValue,
|
||||
required this.device,
|
||||
required this.operationName,
|
||||
required this.selectCode,
|
||||
required this.onSelect,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(20),
|
||||
itemCount: values.length,
|
||||
itemBuilder: (context, index) =>
|
||||
_buildValueItem(context, values[index]),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Widget _buildValueItem(BuildContext context, FlushOperationalValue value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(child: _buildValueDescription(value)),
|
||||
_buildValueRadio(context, value),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Widget _buildValueDescription(FlushOperationalValue value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Text(value.description),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueRadio(context, FlushOperationalValue value) {
|
||||
return Radio<dynamic>(
|
||||
value: value.value,
|
||||
groupValue: selectedValue,
|
||||
onChanged: (_) => onSelect(value));
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,208 @@
|
||||
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/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/flush/flush_functions.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/routine_dialogs/flush_presence_sensor/flush_value_selector_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class FlushPresenceSensor extends StatefulWidget {
|
||||
final List<DeviceFunction> functions;
|
||||
final AllDevicesModel? device;
|
||||
final List<DeviceFunctionData>? deviceSelectedFunctions;
|
||||
final String? uniqueCustomId;
|
||||
final String dialogType;
|
||||
final bool removeComparetors;
|
||||
|
||||
const FlushPresenceSensor({
|
||||
super.key,
|
||||
required this.functions,
|
||||
this.device,
|
||||
this.deviceSelectedFunctions,
|
||||
this.uniqueCustomId,
|
||||
required this.dialogType,
|
||||
this.removeComparetors = false,
|
||||
});
|
||||
|
||||
static Future<Map<String, dynamic>?> showFlushFunctionsDialog({
|
||||
required BuildContext context,
|
||||
required List<DeviceFunction> functions,
|
||||
AllDevicesModel? device,
|
||||
List<DeviceFunctionData>? deviceSelectedFunctions,
|
||||
String? uniqueCustomId,
|
||||
required String dialogType,
|
||||
bool removeComparetors = false,
|
||||
}) async {
|
||||
return showDialog<Map<String, dynamic>?>(
|
||||
context: context,
|
||||
builder: (context) => FlushPresenceSensor(
|
||||
functions: functions,
|
||||
device: device,
|
||||
deviceSelectedFunctions: deviceSelectedFunctions,
|
||||
uniqueCustomId: uniqueCustomId,
|
||||
removeComparetors: removeComparetors,
|
||||
dialogType: dialogType,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
State<FlushPresenceSensor> createState() => _WallPresenceSensorState();
|
||||
}
|
||||
|
||||
class _WallPresenceSensorState extends State<FlushPresenceSensor> {
|
||||
late final List<FlushFunctions> _flushFunctions;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_flushFunctions =
|
||||
widget.functions.whereType<FlushFunctions>().where((function) {
|
||||
if (widget.dialogType == 'THEN') {
|
||||
return function.type == 'THEN' || function.type == 'BOTH';
|
||||
}
|
||||
return function.type == 'IF' || function.type == 'BOTH';
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (_) => FunctionBloc()
|
||||
..add(InitializeFunctions(widget.deviceSelectedFunctions ?? [])),
|
||||
child: _buildDialogContent(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDialogContent() {
|
||||
return AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
|
||||
builder: (context, state) {
|
||||
final selectedFunction = state.selectedFunction;
|
||||
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('Presence Sensor Condition'),
|
||||
Expanded(child: _buildMainContent(context, state)),
|
||||
_buildDialogFooter(context, state),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMainContent(BuildContext context, FunctionBlocState state) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildFunctionList(context),
|
||||
if (state.selectedFunction != null) _buildValueSelector(context, state),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFunctionList(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 360,
|
||||
child: ListView.separated(
|
||||
shrinkWrap: false,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
itemCount: _flushFunctions.length,
|
||||
separatorBuilder: (context, index) => const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 40.0),
|
||||
child: Divider(color: ColorsManager.dividerColor),
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final function = _flushFunctions[index];
|
||||
return ListTile(
|
||||
leading: SvgPicture.asset(
|
||||
function.icon,
|
||||
width: 24,
|
||||
height: 24,
|
||||
placeholderBuilder: (context) => const SizedBox(
|
||||
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,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueSelector(BuildContext context, FunctionBlocState state) {
|
||||
final selectedFunction = state.selectedFunction ?? '';
|
||||
final functionData = state.addedFunctions.firstWhere(
|
||||
(f) => f.functionCode == selectedFunction,
|
||||
orElse: () => DeviceFunctionData(
|
||||
entityId: '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: state.selectedOperationName ?? '',
|
||||
value: null,
|
||||
),
|
||||
);
|
||||
|
||||
return Expanded(
|
||||
child: FlushValueSelectorWidget(
|
||||
selectedFunction: selectedFunction,
|
||||
functionData: functionData,
|
||||
flushFunctions: _flushFunctions,
|
||||
device: widget.device,
|
||||
dialogType: widget.dialogType,
|
||||
removeComparators: widget.removeComparetors,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDialogFooter(BuildContext context, FunctionBlocState state) {
|
||||
return DialogFooter(
|
||||
onCancel: () => Navigator.pop(context),
|
||||
onConfirm: state.addedFunctions.isNotEmpty
|
||||
? () {
|
||||
context.read<RoutineBloc>().add(
|
||||
AddFunctionToRoutine(
|
||||
state.addedFunctions,
|
||||
widget.uniqueCustomId!,
|
||||
),
|
||||
);
|
||||
Navigator.pop(
|
||||
context,
|
||||
{'deviceId': widget.functions.first.deviceId},
|
||||
);
|
||||
}
|
||||
: null,
|
||||
isConfirmEnabled: state.selectedFunction != null,
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,178 @@
|
||||
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/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_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/flush/flush_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_operational_values_list.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
||||
|
||||
class FlushValueSelectorWidget extends StatelessWidget {
|
||||
final String selectedFunction;
|
||||
final DeviceFunctionData functionData;
|
||||
final List<FlushFunctions> flushFunctions;
|
||||
final AllDevicesModel? device;
|
||||
final String dialogType;
|
||||
final bool removeComparators;
|
||||
|
||||
const FlushValueSelectorWidget({
|
||||
required this.selectedFunction,
|
||||
required this.functionData,
|
||||
required this.flushFunctions,
|
||||
required this.device,
|
||||
required this.dialogType,
|
||||
required this.removeComparators,
|
||||
super.key,
|
||||
});
|
||||
|
||||
(double, double) get sliderRange => switch (functionData.functionCode) {
|
||||
FlushMountedPresenceSensorModel.codeOccurDistReduce => (0, 3),
|
||||
FlushMountedPresenceSensorModel.codeNoneDelay => (200, 3000),
|
||||
FlushMountedPresenceSensorModel.codeSensiReduce => (0, 3),
|
||||
FlushMountedPresenceSensorModel.codePresenceDelay => (0, 5),
|
||||
FlushMountedPresenceSensorModel.codeIlluminance => (0.0, 2000.0),
|
||||
FlushMountedPresenceSensorModel.codeFarDetection => (0.0, 9.5),
|
||||
FlushMountedPresenceSensorModel.codeNearDetection => (0.0, 9.5),
|
||||
_ => (0.0, 100.0),
|
||||
};
|
||||
|
||||
double get stepSize => switch (functionData.functionCode) {
|
||||
FlushMountedPresenceSensorModel.codeNoneDelay => 10.0,
|
||||
FlushMountedPresenceSensorModel.codeFarDetection => 0.5,
|
||||
FlushMountedPresenceSensorModel.codeNearDetection => 0.5,
|
||||
FlushMountedPresenceSensorModel.codePresenceDelay => 1.0,
|
||||
FlushMountedPresenceSensorModel.codeOccurDistReduce => 1.0,
|
||||
FlushMountedPresenceSensorModel.codeSensiReduce => 1.0,
|
||||
_ => 1.0,
|
||||
};
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selectedFn = flushFunctions.firstWhere(
|
||||
(f) => f.code == selectedFunction,
|
||||
orElse: () => throw Exception('Function $selectedFunction not found'),
|
||||
);
|
||||
|
||||
if (_isSliderFunction(selectedFunction)) {
|
||||
final isNearDetection =
|
||||
selectedFunction == FlushMountedPresenceSensorModel.codeNearDetection;
|
||||
final isFarDetection =
|
||||
selectedFunction == FlushMountedPresenceSensorModel.codeFarDetection;
|
||||
|
||||
final isDistanceDetection = isNearDetection || isFarDetection;
|
||||
double initialValue = (functionData.value as num?)?.toDouble() ?? 0.0;
|
||||
|
||||
if (isDistanceDetection) {
|
||||
initialValue = initialValue / 100;
|
||||
}
|
||||
return SliderValueSelector(
|
||||
currentCondition: functionData.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: sliderRange,
|
||||
displayedValue: getDisplayText,
|
||||
initialValue: initialValue,
|
||||
onConditionChanged: (condition) => context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: functionData.operationName,
|
||||
condition: condition,
|
||||
value: functionData.value ?? 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
onSliderChanged: (value) {
|
||||
final roundedValue = _roundToStep(value, stepSize);
|
||||
final finalValue =
|
||||
isDistanceDetection ? (roundedValue * 100).toInt() : roundedValue;
|
||||
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: functionData.operationName,
|
||||
value: finalValue,
|
||||
condition: functionData.condition,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
unit: _unit,
|
||||
dividendOfRange: stepSize,
|
||||
);
|
||||
}
|
||||
|
||||
return FlushOperationalValuesList(
|
||||
values: selectedFn.getOperationalValues(),
|
||||
selectedValue: functionData.value,
|
||||
device: device,
|
||||
operationName: selectedFn.operationName,
|
||||
selectCode: selectedFunction,
|
||||
onSelect: (selectedValue) async {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: functionData.operationName,
|
||||
value: selectedValue.value,
|
||||
condition: functionData.condition,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
double _roundToStep(double value, double step) {
|
||||
return (value / step).roundToDouble() * step;
|
||||
}
|
||||
|
||||
bool _isSliderFunction(String function) => [
|
||||
FlushMountedPresenceSensorModel.codeOccurDistReduce,
|
||||
FlushMountedPresenceSensorModel.codeSensiReduce,
|
||||
FlushMountedPresenceSensorModel.codeNoneDelay,
|
||||
FlushMountedPresenceSensorModel.codeIlluminance,
|
||||
FlushMountedPresenceSensorModel.codePresenceDelay,
|
||||
FlushMountedPresenceSensorModel.codeFarDetection,
|
||||
FlushMountedPresenceSensorModel.codeNearDetection,
|
||||
].contains(function);
|
||||
|
||||
String get _unit => switch (functionData.functionCode) {
|
||||
FlushMountedPresenceSensorModel.codeOccurDistReduce => 'Min',
|
||||
FlushMountedPresenceSensorModel.codeSensiReduce => 'Sec',
|
||||
FlushMountedPresenceSensorModel.codeNoneDelay => 'Sec',
|
||||
FlushMountedPresenceSensorModel.codePresenceDelay => 'Sec',
|
||||
FlushMountedPresenceSensorModel.codeIlluminance => 'Lux',
|
||||
FlushMountedPresenceSensorModel.codeFarDetection => 'm',
|
||||
FlushMountedPresenceSensorModel.codeNearDetection => 'm',
|
||||
_ => '',
|
||||
};
|
||||
|
||||
String get getDisplayText {
|
||||
final num? value = functionData.value;
|
||||
double displayValue = value?.toDouble() ?? 0.0;
|
||||
|
||||
if (functionData.functionCode ==
|
||||
FlushMountedPresenceSensorModel.codeNearDetection ||
|
||||
functionData.functionCode ==
|
||||
FlushMountedPresenceSensorModel.codeFarDetection) {
|
||||
displayValue = displayValue / 100;
|
||||
}
|
||||
|
||||
switch (functionData.functionCode) {
|
||||
case FlushMountedPresenceSensorModel.codeFarDetection:
|
||||
case FlushMountedPresenceSensorModel.codeNearDetection:
|
||||
return displayValue.toStringAsFixed(1);
|
||||
case FlushMountedPresenceSensorModel.codeOccurDistReduce:
|
||||
case FlushMountedPresenceSensorModel.codeSensiReduce:
|
||||
case FlushMountedPresenceSensorModel.codePresenceDelay:
|
||||
return displayValue.toStringAsFixed(0);
|
||||
default:
|
||||
return displayValue.toStringAsFixed(0);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class TimeWheelPicker extends StatefulWidget {
|
||||
final int initialHours;
|
||||
final int initialMinutes;
|
||||
final int initialSeconds;
|
||||
final Function(int, int, int) onTimeChanged;
|
||||
|
||||
const TimeWheelPicker({
|
||||
super.key,
|
||||
required this.initialHours,
|
||||
required this.initialMinutes,
|
||||
required this.initialSeconds,
|
||||
required this.onTimeChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
State<TimeWheelPicker> createState() => _TimeWheelPickerState();
|
||||
}
|
||||
|
||||
class _TimeWheelPickerState extends State<TimeWheelPicker> {
|
||||
late FixedExtentScrollController _hoursController;
|
||||
late FixedExtentScrollController _minutesController;
|
||||
late FixedExtentScrollController _secondsController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_hoursController =
|
||||
FixedExtentScrollController(initialItem: widget.initialHours);
|
||||
_minutesController =
|
||||
FixedExtentScrollController(initialItem: widget.initialMinutes);
|
||||
_secondsController =
|
||||
FixedExtentScrollController(initialItem: widget.initialSeconds);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(TimeWheelPicker oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.initialHours != widget.initialHours) {
|
||||
_hoursController.jumpToItem(widget.initialHours);
|
||||
}
|
||||
if (oldWidget.initialMinutes != widget.initialMinutes) {
|
||||
_minutesController.jumpToItem(widget.initialMinutes);
|
||||
}
|
||||
if (oldWidget.initialSeconds != widget.initialSeconds) {
|
||||
_secondsController.jumpToItem(widget.initialSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_hoursController.dispose();
|
||||
_minutesController.dispose();
|
||||
_secondsController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_buildPickerColumn(
|
||||
label: 'h',
|
||||
controller: _hoursController,
|
||||
itemCount: 3,
|
||||
onChanged: (value) {
|
||||
_handleTimeChange(
|
||||
value,
|
||||
_minutesController.selectedItem,
|
||||
_secondsController.selectedItem,
|
||||
);
|
||||
}),
|
||||
const SizedBox(width: 5),
|
||||
_buildPickerColumn(
|
||||
label: 'm',
|
||||
controller: _minutesController,
|
||||
itemCount: 60,
|
||||
onChanged: (value) {
|
||||
_handleTimeChange(
|
||||
_hoursController.selectedItem,
|
||||
value,
|
||||
_secondsController.selectedItem,
|
||||
);
|
||||
}),
|
||||
const SizedBox(width: 5),
|
||||
_buildPickerColumn(
|
||||
label: 's',
|
||||
controller: _secondsController,
|
||||
itemCount: 60,
|
||||
onChanged: (value) => _handleTimeChange(
|
||||
_hoursController.selectedItem,
|
||||
_minutesController.selectedItem,
|
||||
value,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _handleTimeChange(int hours, int minutes, int seconds) {
|
||||
int total = hours * 3600 + minutes * 60 + seconds;
|
||||
if (total > 10000) {
|
||||
hours = 2;
|
||||
minutes = 46;
|
||||
seconds = 40;
|
||||
total = 10000;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_hoursController.jumpToItem(hours);
|
||||
_minutesController.jumpToItem(minutes);
|
||||
_secondsController.jumpToItem(seconds);
|
||||
});
|
||||
}
|
||||
|
||||
widget.onTimeChanged(hours, minutes, seconds);
|
||||
}
|
||||
|
||||
Widget _buildPickerColumn({
|
||||
required String label,
|
||||
required FixedExtentScrollController controller,
|
||||
required int itemCount,
|
||||
required Function(int) onChanged,
|
||||
}) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
height: 40,
|
||||
width: 40,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.boxColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: ListWheelScrollView.useDelegate(
|
||||
controller: controller,
|
||||
itemExtent: 40.0,
|
||||
physics: const FixedExtentScrollPhysics(),
|
||||
onSelectedItemChanged: onChanged,
|
||||
childDelegate: ListWheelChildBuilderDelegate(
|
||||
builder: (context, index) => Center(
|
||||
child: Text(
|
||||
index.toString().padLeft(2),
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
color: ColorsManager.blue1,
|
||||
),
|
||||
),
|
||||
),
|
||||
childCount: itemCount,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user