push schedule basic design and bloc manegment

This commit is contained in:
ashrafzarkanisala
2024-09-20 02:27:07 +03:00
parent 26816b99cd
commit 921ccf0132
4 changed files with 725 additions and 439 deletions

View File

@ -3,6 +3,7 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart';
import 'package:syncrow_web/services/devices_mang_api.dart';
@ -17,6 +18,9 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
on<UpdateScheduleEvent>(_updateScheduleEvent);
on<StopScheduleEvent>(_stopScheduleEvent);
on<DecrementCountdownEvent>(_onDecrementCountdown);
on<AddScheduleEvent>(_onAddSchedule);
on<DeleteScheduleEvent>(_onDeleteSchedule);
on<UpdateScheduleEntryEvent>(_onUpdateSchedule);
}
late WaterHeaterStatusModel deviceStatus;
@ -294,6 +298,54 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
}
}
FutureOr<void> _onAddSchedule(
AddScheduleEvent event,
Emitter<WaterHeaterState> emit,
) {
if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded;
final newSchedule = ScheduleEntry(
selectedDays: event.selectedDays,
time: event.time,
functionOn: event.functionOn,
);
final updatedSchedules = List<ScheduleEntry>.from(currentState.schedules)
..add(newSchedule);
emit(currentState.copyWith(schedules: updatedSchedules));
}
}
FutureOr<void> _onDeleteSchedule(
DeleteScheduleEvent event,
Emitter<WaterHeaterState> emit,
) {
if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded;
final updatedSchedules = List<ScheduleEntry>.from(currentState.schedules)
..removeAt(event.index);
emit(currentState.copyWith(schedules: updatedSchedules));
}
}
FutureOr<void> _onUpdateSchedule(
UpdateScheduleEntryEvent event,
Emitter<WaterHeaterState> emit,
) {
if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded;
final updatedSchedules = List<ScheduleEntry>.from(currentState.schedules);
updatedSchedules[event.index] = ScheduleEntry(
selectedDays: event.selectedDays,
time: event.time,
functionOn: event.functionOn,
);
emit(currentState.copyWith(schedules: updatedSchedules));
}
}
@override
Future<void> close() {
_countdownTimer?.cancel();

View File

@ -1,4 +1,3 @@
part of 'water_heater_bloc.dart';
sealed class WaterHeaterEvent extends Equatable {
@ -63,3 +62,44 @@ final class WaterHeaterFetchBatchStatusEvent extends WaterHeaterEvent {
}
final class DecrementCountdownEvent extends WaterHeaterEvent {}
final class AddScheduleEvent extends WaterHeaterEvent {
final List<bool> selectedDays;
final TimeOfDay time;
final bool functionOn;
const AddScheduleEvent({
required this.selectedDays,
required this.time,
required this.functionOn,
});
@override
List<Object?> get props => [selectedDays, time, functionOn];
}
final class DeleteScheduleEvent extends WaterHeaterEvent {
final int index;
const DeleteScheduleEvent(this.index);
@override
List<Object?> get props => [index];
}
final class UpdateScheduleEntryEvent extends WaterHeaterEvent {
final int index;
final List<bool> selectedDays;
final TimeOfDay time;
final bool functionOn;
const UpdateScheduleEntryEvent({
required this.index,
required this.selectedDays,
required this.time,
required this.functionOn,
});
@override
List<Object?> get props => [index, selectedDays, time, functionOn];
}

View File

@ -20,6 +20,7 @@ final class WaterHeaterDeviceStatusLoaded extends WaterHeaterState {
final int? minutes;
final bool? isActive;
final Duration? countdownRemaining;
final List<ScheduleEntry> schedules;
const WaterHeaterDeviceStatusLoaded(
this.status, {
@ -28,13 +29,20 @@ final class WaterHeaterDeviceStatusLoaded extends WaterHeaterState {
this.minutes,
this.isActive,
this.countdownRemaining,
this.schedules = const [],
});
@override
List<Object?> get props =>
[status, scheduleMode, hours, minutes, isActive, countdownRemaining];
List<Object?> get props => [
status,
scheduleMode,
hours,
minutes,
isActive,
countdownRemaining,
schedules,
];
/// Creates a new instance with updated fields.
WaterHeaterDeviceStatusLoaded copyWith({
WaterHeaterStatusModel? status,
ScheduleModes? scheduleMode,
@ -42,6 +50,7 @@ final class WaterHeaterDeviceStatusLoaded extends WaterHeaterState {
int? minutes,
bool? isActive,
Duration? countdownRemaining,
List<ScheduleEntry>? schedules,
}) {
return WaterHeaterDeviceStatusLoaded(
status ?? this.status,
@ -50,10 +59,27 @@ final class WaterHeaterDeviceStatusLoaded extends WaterHeaterState {
minutes: minutes ?? this.minutes,
isActive: isActive ?? this.isActive,
countdownRemaining: countdownRemaining ?? this.countdownRemaining,
schedules: schedules ?? this.schedules,
);
}
}
class ScheduleEntry {
final List<bool> selectedDays;
final TimeOfDay time;
final bool functionOn;
ScheduleEntry({
required this.selectedDays,
required this.time,
required this.functionOn,
});
@override
String toString() =>
'ScheduleEntry(selectedDays: $selectedDays, time: $time, functionOn: $functionOn)';
}
final class WaterHeaterFailedState extends WaterHeaterState {
final String error;

View File

@ -1,4 +1,3 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
@ -17,56 +16,12 @@ class BuildScheduleView extends StatefulWidget {
}
class _BuildScheduleViewState extends State<BuildScheduleView> {
// late FixedExtentScrollController hoursController;
// late FixedExtentScrollController minutesController;
// @override
// void initState() {
// super.initState();
// _initializeControllers();
// }
// @override
// void didUpdateWidget(covariant BuildScheduleView oldWidget) {
// super.didUpdateWidget(oldWidget);
// final state = context.read<WaterHeaterBloc>().state;
// if (state is WaterHeaterDeviceStatusLoaded) {
// _initializeControllers();
// }
// }
// void _initializeControllers() {
// final state = context.read<WaterHeaterBloc>().state;
// if (state is WaterHeaterDeviceStatusLoaded) {
// hoursController =
// FixedExtentScrollController(initialItem: state.hours ?? 0);
// minutesController =
// FixedExtentScrollController(initialItem: state.minutes ?? 0);
// } else {
// hoursController = FixedExtentScrollController(initialItem: 0);
// minutesController = FixedExtentScrollController(initialItem: 0);
// }
// }
// void _updateControllers(int hours, int minutes) {
// if (hoursController.hasClients) {
// hoursController.jumpToItem(hours);
// }
// if (minutesController.hasClients) {
// minutesController.jumpToItem(minutes);
// }
// }
// @override
// void dispose() {
// hoursController.dispose();
// minutesController.dispose();
// super.dispose();
// }
@override
Widget build(BuildContext context) {
return Dialog(
final bloc = BlocProvider.of<WaterHeaterBloc>(context);
return BlocProvider.value(
value: bloc,
child: Dialog(
backgroundColor: Colors.white,
insetPadding: const EdgeInsets.all(20),
shape: RoundedRectangleBorder(
@ -76,15 +31,40 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
width: 700,
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 40.0, vertical: 20),
padding:
const EdgeInsets.symmetric(horizontal: 40.0, vertical: 20),
child: BlocBuilder<WaterHeaterBloc, WaterHeaterState>(
builder: (context, state) {
if (state is WaterHeaterDeviceStatusLoaded) {
//_updateControllers(state.hours ?? 0, state.minutes ?? 0);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
_scheduleHeader(context),
const SizedBox(height: 20),
_buildScheduleModeSelector(context, state),
const SizedBox(height: 20),
if (state.scheduleMode == ScheduleModes.schedule)
_buildScheduleManagementUI(state),
if (state.scheduleMode == ScheduleModes.countdown ||
state.scheduleMode == ScheduleModes.inching)
..._buildCountDownAngInchingView(context, state),
const SizedBox(height: 20),
_buildSaveStopCancelButtons(context, state),
],
);
}
return const SizedBox();
},
),
),
),
),
),
);
}
Row _scheduleHeader(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const SizedBox(),
@ -119,8 +99,14 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
),
),
],
),
const SizedBox(height: 20),
);
}
Widget _buildScheduleModeSelector(
BuildContext context, WaterHeaterDeviceStatusLoaded state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Type:',
style: context.textTheme.bodySmall!.copyWith(
@ -129,138 +115,369 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
),
),
const SizedBox(height: 4),
SizedBox(
child: Row(
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Flexible(
child: ListTile(
contentPadding: EdgeInsets.zero,
title: Text(
'Countdown',
style: context.textTheme.bodySmall!.copyWith(
fontSize: 13,
color: ColorsManager.blackColor,
),
),
leading: Radio<ScheduleModes>(
value: ScheduleModes.countdown,
groupValue: state.scheduleMode,
onChanged: (ScheduleModes? value) {
if (value != null) {
context
.read<WaterHeaterBloc>()
.add(UpdateScheduleEvent(
scheduleMode: value,
hours: state.hours ?? 0,
minutes: state.minutes ?? 0,
));
}
},
),
),
),
Flexible(
child: ListTile(
contentPadding: EdgeInsets.zero,
title: Text(
'Schedule',
style: context.textTheme.bodySmall!.copyWith(
fontSize: 13,
color: ColorsManager.blackColor,
),
),
leading: Radio<ScheduleModes>(
value: ScheduleModes.schedule,
groupValue: state.scheduleMode,
onChanged: (ScheduleModes? value) {
if (value != null) {
context
.read<WaterHeaterBloc>()
.add(UpdateScheduleEvent(
scheduleMode: value,
hours: state.hours ?? 0,
minutes: state.minutes ?? 0,
));
}
},
),
),
),
Flexible(
child: ListTile(
title: Text(
'Circulate',
style: context.textTheme.bodySmall!.copyWith(
fontSize: 13,
color: ColorsManager.blackColor,
),
),
leading: Radio<ScheduleModes>(
value: ScheduleModes.circulate,
groupValue: state.scheduleMode,
onChanged: (ScheduleModes? value) {
if (value != null) {
context
.read<WaterHeaterBloc>()
.add(UpdateScheduleEvent(
scheduleMode: value,
hours: state.hours ?? 0,
minutes: state.minutes ?? 0,
));
}
},
),
),
),
Flexible(
child: ListTile(
title: Text(
'Inching',
style: context.textTheme.bodySmall!.copyWith(
fontSize: 13,
color: ColorsManager.blackColor,
),
),
leading: Radio<ScheduleModes>(
value: ScheduleModes.inching,
groupValue: state.scheduleMode,
onChanged: (ScheduleModes? value) {
if (value != null) {
context
.read<WaterHeaterBloc>()
.add(UpdateScheduleEvent(
scheduleMode: value,
hours: state.hours ?? 0,
minutes: state.minutes ?? 0,
));
}
},
),
),
),
_buildRadioTile(
context, 'Countdown', ScheduleModes.countdown, state),
_buildRadioTile(context, 'Schedule', ScheduleModes.schedule, state),
_buildRadioTile(
context, 'Circulate', ScheduleModes.circulate, state),
_buildRadioTile(context, 'Inching', ScheduleModes.inching, state),
],
),
],
);
}
Widget _buildRadioTile(BuildContext context, String label, ScheduleModes mode,
WaterHeaterDeviceStatusLoaded state) {
return Flexible(
child: ListTile(
contentPadding: EdgeInsets.zero,
title: Text(
label,
style: context.textTheme.bodySmall!.copyWith(
fontSize: 13,
color: ColorsManager.blackColor,
),
),
leading: Radio<ScheduleModes>(
value: mode,
groupValue: state.scheduleMode,
onChanged: (ScheduleModes? value) {
if (value != null) {
context.read<WaterHeaterBloc>().add(UpdateScheduleEvent(
scheduleMode: value,
hours: state.hours ?? 0,
minutes: state.minutes ?? 0,
));
}
},
),
),
);
}
Widget _buildScheduleManagementUI(WaterHeaterDeviceStatusLoaded state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ElevatedButton(
onPressed: () => _showAddScheduleDialog(context),
child: Text('+ Add new schedule'),
),
const SizedBox(height: 20),
if (state.scheduleMode == ScheduleModes.countdown ||
state.scheduleMode == ScheduleModes.inching) ...[
Text(
'Countdown:',
_buildScheduleTable(state),
],
);
}
Widget _buildScheduleTable(WaterHeaterDeviceStatusLoaded state) {
return Table(
border: TableBorder.all(color: Colors.grey),
children: [
TableRow(
children: [
_buildTableHeader('Active'),
_buildTableHeader('Days'),
_buildTableHeader('Time'),
_buildTableHeader('Function'),
_buildTableHeader('Action'),
],
),
if (state.schedules.isEmpty)
TableRow(
children: [
Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'No schedules added yet',
style: context.textTheme.bodySmall!.copyWith(
fontSize: 13,
color: ColorsManager.grayColor,
),
),
const SizedBox(height: 4),
_hourMinutesWheel(state, context)
),
),
const SizedBox(),
const SizedBox(),
const SizedBox(),
const SizedBox(),
],
const SizedBox(height: 20),
),
for (int i = 0; i < state.schedules.length; i++)
_buildScheduleRow(state.schedules[i], i, context),
],
);
}
TableRow _buildScheduleRow(
ScheduleEntry schedule, int index, BuildContext context) {
return TableRow(
children: [
Center(
child: schedule.functionOn
? Icon(Icons.radio_button_checked)
: Icon(Icons.radio_button_unchecked)),
Center(child: Text(_getSelectedDays(schedule.selectedDays))),
Center(child: Text(schedule.time.format(context))),
Center(child: Text(schedule.functionOn ? 'On' : 'Off')),
Center(
child: Wrap(
runAlignment: WrapAlignment.center,
children: [
TextButton(
onPressed: () {
_showEditScheduleDialog(context, schedule, index);
},
child: Text(
'Edit',
style: context.textTheme.bodySmall!
.copyWith(color: ColorsManager.blueColor),
),
),
TextButton(
onPressed: () {
context
.read<WaterHeaterBloc>()
.add(DeleteScheduleEvent(index));
},
child: Text(
'Delete',
style: context.textTheme.bodySmall!
.copyWith(color: ColorsManager.blueColor),
),
),
],
),
),
],
);
}
String _getSelectedDays(List<bool> selectedDays) {
final days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
List<String> selectedDaysStr = [];
for (int i = 0; i < selectedDays.length; i++) {
if (selectedDays[i]) {
selectedDaysStr.add(days[i]);
}
}
return selectedDaysStr.join(', ');
}
void _showEditScheduleDialog(
BuildContext context, ScheduleEntry schedule, int index) {
List<bool> selectedDays = List.from(schedule.selectedDays);
TimeOfDay selectedTime = schedule.time;
bool isOn = schedule.functionOn;
showDialog(
context: context,
builder: (ctx) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: const Text('Edit Schedule'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
onPressed: () async {
TimeOfDay? time = await showTimePicker(
context: context,
initialTime: selectedTime,
);
if (time != null) {
setState(() {
selectedTime = time;
});
}
},
child: Text(selectedTime.format(context)),
),
// Same checkboxes and function on/off logic as before
],
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Cancel'),
),
TextButton(
onPressed: () {
context.read<WaterHeaterBloc>().add(
UpdateScheduleEntryEvent(
index: index,
selectedDays: selectedDays,
time: selectedTime,
functionOn: isOn,
),
);
Navigator.pop(context);
},
child: const Text('Save'),
),
],
);
},
);
},
);
}
Widget _buildTableHeader(String label) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
label,
style: const TextStyle(fontWeight: FontWeight.bold),
),
);
}
void _showAddScheduleDialog(BuildContext context) {
showDialog(
context: context,
builder: (ctx) {
List<bool> selectedDays = [
false,
false,
false,
false,
false,
false,
false
]; // Mon - Sun
TimeOfDay? selectedTime;
bool isOn = false;
return BlocProvider.value(
value: BlocProvider.of<WaterHeaterBloc>(context),
child: StatefulBuilder(
builder: (ctx, setState) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
title: const Text('Scheduling'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
onPressed: () async {
TimeOfDay? time = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);
if (time != null) {
setState(() {
selectedTime = time;
});
}
},
child: Text(
selectedTime == null
? 'Time'
: '${selectedTime!.format(context)}',
),
),
const SizedBox(height: 10),
Row(
children: [
_buildDayCheckbox(setState, 'Mon', 0, selectedDays),
_buildDayCheckbox(setState, 'Tue', 1, selectedDays),
_buildDayCheckbox(setState, 'Wed', 2, selectedDays),
_buildDayCheckbox(setState, 'Thu', 3, selectedDays),
_buildDayCheckbox(setState, 'Fri', 4, selectedDays),
_buildDayCheckbox(setState, 'Sat', 5, selectedDays),
_buildDayCheckbox(setState, 'Sun', 6, selectedDays),
],
),
const SizedBox(height: 10),
Row(
children: [
const Text('Function:'),
Radio<bool>(
value: true,
groupValue: isOn,
onChanged: (bool? value) {
setState(() {
isOn = value!;
});
},
),
const Text('On'),
Radio<bool>(
value: false,
groupValue: isOn,
onChanged: (bool? value) {
setState(() {
isOn = value!;
});
},
),
const Text('Off'),
],
),
],
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Cancel'),
),
TextButton(
onPressed: () {
// Dispatch Bloc Event to Add Schedule
if (selectedTime != null) {
context.read<WaterHeaterBloc>().add(AddScheduleEvent(
time: selectedTime!,
selectedDays: selectedDays,
functionOn: isOn,
));
Navigator.pop(context); // Close the dialog
}
},
child: const Text('Save'),
),
],
);
},
),
);
},
);
}
Widget _buildDayCheckbox(void Function(void Function()) setState, String day,
int index, List<bool> selectedDays) {
return Row(
children: [
Checkbox(
value: selectedDays[index],
onChanged: (bool? value) {
setState(() {
selectedDays[index] = value!;
});
},
),
Text(day),
],
);
}
Center _buildSaveStopCancelButtons(
BuildContext context, WaterHeaterDeviceStatusLoaded state) {
return Center(
child: SizedBox(
width: 400,
height: 50,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
@ -280,26 +497,24 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
),
const SizedBox(width: 20),
Expanded(
child: (state.countdownRemaining != null &&
state.isActive == true)
child:
(state.countdownRemaining != null && state.isActive == true)
? DefaultButton(
height: 40,
onPressed: () {
late String code;
if (state.scheduleMode ==
ScheduleModes.countdown) {
if (state.scheduleMode == ScheduleModes.countdown) {
code = 'countdown_1';
} else if (state.scheduleMode ==
ScheduleModes.inching) {
code = 'switch_inching';
}
context.read<WaterHeaterBloc>().add(
StopScheduleEvent(
widget.status.uuid));
context
.read<WaterHeaterBloc>()
.add(StopScheduleEvent(widget.status.uuid));
context.read<WaterHeaterBloc>().add(
ToggleWaterHeaterEvent(
deviceId:
widget.status.uuid,
deviceId: widget.status.uuid,
code: code,
value: 0,
),
@ -312,8 +527,7 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
height: 40,
onPressed: () {
late String code;
if (state.scheduleMode ==
ScheduleModes.countdown) {
if (state.scheduleMode == ScheduleModes.countdown) {
code = 'countdown_1';
} else if (state.scheduleMode ==
ScheduleModes.inching) {
@ -321,40 +535,38 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
}
context.read<WaterHeaterBloc>().add(
ToggleWaterHeaterEvent(
deviceId:
widget.status.uuid,
deviceId: widget.status.uuid,
code: code,
value: Duration(
hours:
state.hours ??
0,
minutes:
state.minutes ??
0)
hours: state.hours ?? 0,
minutes: state.minutes ?? 0)
.inSeconds,
),
);
},
backgroundColor:
ColorsManager.primaryColor,
backgroundColor: ColorsManager.primaryColor,
child: const Text('Save'),
),
),
],
),
),
),
),
],
);
}
return const SizedBox();
},
List<Widget> _buildCountDownAngInchingView(
BuildContext context, WaterHeaterDeviceStatusLoaded state) {
return [
Text(
'Countdown:',
style: context.textTheme.bodySmall!.copyWith(
fontSize: 13,
color: ColorsManager.grayColor,
),
),
),
),
);
const SizedBox(height: 4),
_hourMinutesWheel(state, context)
];
}
Row _hourMinutesWheel(
@ -362,61 +574,28 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
// Hours Picker
Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
height: 50,
width: 80,
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: ColorsManager.boxColor,
borderRadius: BorderRadius.circular(8),
),
child: ListWheelScrollView.useDelegate(
key: ValueKey('hours_${state.hours}'),
controller: FixedExtentScrollController(
initialItem: state.hours ?? 0,
),
itemExtent: 40.0,
physics: const FixedExtentScrollPhysics(),
onSelectedItemChanged: (int value) {
context.read<WaterHeaterBloc>().add(
UpdateScheduleEvent(
scheduleMode:
state.scheduleMode ?? ScheduleModes.countdown,
_buildPickerColumn(context, 'h', state.hours ?? 0, 24, (value) {
context.read<WaterHeaterBloc>().add(UpdateScheduleEvent(
scheduleMode: state.scheduleMode ?? ScheduleModes.countdown,
hours: value,
minutes: state.minutes ?? 0,
),
);
},
childDelegate: ListWheelChildBuilderDelegate(
builder: (context, index) {
return Center(
child: Text(
index.toString().padLeft(2, '0'),
style: const TextStyle(fontSize: 24),
),
);
},
childCount: 24,
),
),
),
const SizedBox(height: 8),
const Text(
'h',
style: TextStyle(
color: ColorsManager.grayColor,
fontSize: 18,
),
),
],
),
));
}),
const SizedBox(width: 10),
// Minutes Picker
Row(
_buildPickerColumn(context, 'm', state.minutes ?? 0, 60, (value) {
context.read<WaterHeaterBloc>().add(UpdateScheduleEvent(
scheduleMode: state.scheduleMode ?? ScheduleModes.countdown,
hours: state.hours ?? 0,
minutes: value,
));
}),
],
);
}
Widget _buildPickerColumn(BuildContext context, String label,
int initialValue, int itemCount, ValueChanged<int> onSelected) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
@ -428,22 +607,13 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
borderRadius: BorderRadius.circular(8),
),
child: ListWheelScrollView.useDelegate(
key: ValueKey('minutes_${state.minutes}'),
key: ValueKey('$label-$initialValue'),
controller: FixedExtentScrollController(
initialItem: state.minutes ?? 0,
initialItem: initialValue,
),
itemExtent: 40.0,
physics: const FixedExtentScrollPhysics(),
onSelectedItemChanged: (int value) {
context.read<WaterHeaterBloc>().add(
UpdateScheduleEvent(
scheduleMode:
state.scheduleMode ?? ScheduleModes.countdown,
hours: state.hours ?? 0,
minutes: value,
),
);
},
onSelectedItemChanged: onSelected,
childDelegate: ListWheelChildBuilderDelegate(
builder: (context, index) {
return Center(
@ -453,21 +623,19 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
),
);
},
childCount: 60,
childCount: itemCount,
),
),
),
const SizedBox(height: 8),
const Text(
'm',
style: TextStyle(
Text(
label,
style: const TextStyle(
color: ColorsManager.grayColor,
fontSize: 18,
),
),
],
),
],
);
}
}