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 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.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/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/pages/device_managment/water_heater/models/water_heater_status_model.dart';
import 'package:syncrow_web/services/devices_mang_api.dart'; import 'package:syncrow_web/services/devices_mang_api.dart';
@ -17,6 +18,9 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
on<UpdateScheduleEvent>(_updateScheduleEvent); on<UpdateScheduleEvent>(_updateScheduleEvent);
on<StopScheduleEvent>(_stopScheduleEvent); on<StopScheduleEvent>(_stopScheduleEvent);
on<DecrementCountdownEvent>(_onDecrementCountdown); on<DecrementCountdownEvent>(_onDecrementCountdown);
on<AddScheduleEvent>(_onAddSchedule);
on<DeleteScheduleEvent>(_onDeleteSchedule);
on<UpdateScheduleEntryEvent>(_onUpdateSchedule);
} }
late WaterHeaterStatusModel deviceStatus; 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 @override
Future<void> close() { Future<void> close() {
_countdownTimer?.cancel(); _countdownTimer?.cancel();

View File

@ -1,4 +1,3 @@
part of 'water_heater_bloc.dart'; part of 'water_heater_bloc.dart';
sealed class WaterHeaterEvent extends Equatable { sealed class WaterHeaterEvent extends Equatable {
@ -63,3 +62,44 @@ final class WaterHeaterFetchBatchStatusEvent extends WaterHeaterEvent {
} }
final class DecrementCountdownEvent 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 int? minutes;
final bool? isActive; final bool? isActive;
final Duration? countdownRemaining; final Duration? countdownRemaining;
final List<ScheduleEntry> schedules;
const WaterHeaterDeviceStatusLoaded( const WaterHeaterDeviceStatusLoaded(
this.status, { this.status, {
@ -28,13 +29,20 @@ final class WaterHeaterDeviceStatusLoaded extends WaterHeaterState {
this.minutes, this.minutes,
this.isActive, this.isActive,
this.countdownRemaining, this.countdownRemaining,
this.schedules = const [],
}); });
@override @override
List<Object?> get props => List<Object?> get props => [
[status, scheduleMode, hours, minutes, isActive, countdownRemaining]; status,
scheduleMode,
hours,
minutes,
isActive,
countdownRemaining,
schedules,
];
/// Creates a new instance with updated fields.
WaterHeaterDeviceStatusLoaded copyWith({ WaterHeaterDeviceStatusLoaded copyWith({
WaterHeaterStatusModel? status, WaterHeaterStatusModel? status,
ScheduleModes? scheduleMode, ScheduleModes? scheduleMode,
@ -42,6 +50,7 @@ final class WaterHeaterDeviceStatusLoaded extends WaterHeaterState {
int? minutes, int? minutes,
bool? isActive, bool? isActive,
Duration? countdownRemaining, Duration? countdownRemaining,
List<ScheduleEntry>? schedules,
}) { }) {
return WaterHeaterDeviceStatusLoaded( return WaterHeaterDeviceStatusLoaded(
status ?? this.status, status ?? this.status,
@ -50,10 +59,27 @@ final class WaterHeaterDeviceStatusLoaded extends WaterHeaterState {
minutes: minutes ?? this.minutes, minutes: minutes ?? this.minutes,
isActive: isActive ?? this.isActive, isActive: isActive ?? this.isActive,
countdownRemaining: countdownRemaining ?? this.countdownRemaining, 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 class WaterHeaterFailedState extends WaterHeaterState {
final String error; final String error;

View File

@ -1,4 +1,3 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart';
@ -17,56 +16,12 @@ class BuildScheduleView extends StatefulWidget {
} }
class _BuildScheduleViewState extends State<BuildScheduleView> { 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Dialog( final bloc = BlocProvider.of<WaterHeaterBloc>(context);
return BlocProvider.value(
value: bloc,
child: Dialog(
backgroundColor: Colors.white, backgroundColor: Colors.white,
insetPadding: const EdgeInsets.all(20), insetPadding: const EdgeInsets.all(20),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
@ -76,15 +31,40 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
width: 700, width: 700,
child: SingleChildScrollView( child: SingleChildScrollView(
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 40.0, vertical: 20), padding:
const EdgeInsets.symmetric(horizontal: 40.0, vertical: 20),
child: BlocBuilder<WaterHeaterBloc, WaterHeaterState>( child: BlocBuilder<WaterHeaterBloc, WaterHeaterState>(
builder: (context, state) { builder: (context, state) {
if (state is WaterHeaterDeviceStatusLoaded) { if (state is WaterHeaterDeviceStatusLoaded) {
//_updateControllers(state.hours ?? 0, state.minutes ?? 0);
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ 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, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const SizedBox(), 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( Text(
'Type:', 'Type:',
style: context.textTheme.bodySmall!.copyWith( style: context.textTheme.bodySmall!.copyWith(
@ -129,138 +115,369 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
), ),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
SizedBox( Row(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
Flexible( _buildRadioTile(
child: ListTile( context, 'Countdown', ScheduleModes.countdown, state),
contentPadding: EdgeInsets.zero, _buildRadioTile(context, 'Schedule', ScheduleModes.schedule, state),
title: Text( _buildRadioTile(
'Countdown', context, 'Circulate', ScheduleModes.circulate, state),
style: context.textTheme.bodySmall!.copyWith( _buildRadioTile(context, 'Inching', ScheduleModes.inching, state),
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,
));
}
},
),
),
),
], ],
), ),
],
);
}
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), const SizedBox(height: 20),
if (state.scheduleMode == ScheduleModes.countdown || _buildScheduleTable(state),
state.scheduleMode == ScheduleModes.inching) ...[ ],
Text( );
'Countdown:', }
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( style: context.textTheme.bodySmall!.copyWith(
fontSize: 13, fontSize: 13,
color: ColorsManager.grayColor, 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( 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( child: SizedBox(
width: 400, width: 400,
height: 50, height: 50,
child: Center(
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
@ -280,26 +497,24 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
), ),
const SizedBox(width: 20), const SizedBox(width: 20),
Expanded( Expanded(
child: (state.countdownRemaining != null && child:
state.isActive == true) (state.countdownRemaining != null && state.isActive == true)
? DefaultButton( ? DefaultButton(
height: 40, height: 40,
onPressed: () { onPressed: () {
late String code; late String code;
if (state.scheduleMode == if (state.scheduleMode == ScheduleModes.countdown) {
ScheduleModes.countdown) {
code = 'countdown_1'; code = 'countdown_1';
} else if (state.scheduleMode == } else if (state.scheduleMode ==
ScheduleModes.inching) { ScheduleModes.inching) {
code = 'switch_inching'; code = 'switch_inching';
} }
context.read<WaterHeaterBloc>().add( context
StopScheduleEvent( .read<WaterHeaterBloc>()
widget.status.uuid)); .add(StopScheduleEvent(widget.status.uuid));
context.read<WaterHeaterBloc>().add( context.read<WaterHeaterBloc>().add(
ToggleWaterHeaterEvent( ToggleWaterHeaterEvent(
deviceId: deviceId: widget.status.uuid,
widget.status.uuid,
code: code, code: code,
value: 0, value: 0,
), ),
@ -312,8 +527,7 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
height: 40, height: 40,
onPressed: () { onPressed: () {
late String code; late String code;
if (state.scheduleMode == if (state.scheduleMode == ScheduleModes.countdown) {
ScheduleModes.countdown) {
code = 'countdown_1'; code = 'countdown_1';
} else if (state.scheduleMode == } else if (state.scheduleMode ==
ScheduleModes.inching) { ScheduleModes.inching) {
@ -321,40 +535,38 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
} }
context.read<WaterHeaterBloc>().add( context.read<WaterHeaterBloc>().add(
ToggleWaterHeaterEvent( ToggleWaterHeaterEvent(
deviceId: deviceId: widget.status.uuid,
widget.status.uuid,
code: code, code: code,
value: Duration( value: Duration(
hours: hours: state.hours ?? 0,
state.hours ?? minutes: state.minutes ?? 0)
0,
minutes:
state.minutes ??
0)
.inSeconds, .inSeconds,
), ),
); );
}, },
backgroundColor: backgroundColor: ColorsManager.primaryColor,
ColorsManager.primaryColor,
child: const Text('Save'), 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( Row _hourMinutesWheel(
@ -362,61 +574,28 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
// Hours Picker _buildPickerColumn(context, 'h', state.hours ?? 0, 24, (value) {
Row( context.read<WaterHeaterBloc>().add(UpdateScheduleEvent(
mainAxisSize: MainAxisSize.min, scheduleMode: state.scheduleMode ?? ScheduleModes.countdown,
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,
hours: value, hours: value,
minutes: state.minutes ?? 0, 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), const SizedBox(width: 10),
// Minutes Picker _buildPickerColumn(context, 'm', state.minutes ?? 0, 60, (value) {
Row( 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, mainAxisSize: MainAxisSize.min,
children: [ children: [
Container( Container(
@ -428,22 +607,13 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: ListWheelScrollView.useDelegate( child: ListWheelScrollView.useDelegate(
key: ValueKey('minutes_${state.minutes}'), key: ValueKey('$label-$initialValue'),
controller: FixedExtentScrollController( controller: FixedExtentScrollController(
initialItem: state.minutes ?? 0, initialItem: initialValue,
), ),
itemExtent: 40.0, itemExtent: 40.0,
physics: const FixedExtentScrollPhysics(), physics: const FixedExtentScrollPhysics(),
onSelectedItemChanged: (int value) { onSelectedItemChanged: onSelected,
context.read<WaterHeaterBloc>().add(
UpdateScheduleEvent(
scheduleMode:
state.scheduleMode ?? ScheduleModes.countdown,
hours: state.hours ?? 0,
minutes: value,
),
);
},
childDelegate: ListWheelChildBuilderDelegate( childDelegate: ListWheelChildBuilderDelegate(
builder: (context, index) { builder: (context, index) {
return Center( return Center(
@ -453,21 +623,19 @@ class _BuildScheduleViewState extends State<BuildScheduleView> {
), ),
); );
}, },
childCount: 60, childCount: itemCount,
), ),
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
const Text( Text(
'm', label,
style: TextStyle( style: const TextStyle(
color: ColorsManager.grayColor, color: ColorsManager.grayColor,
fontSize: 18, fontSize: 18,
), ),
), ),
], ],
),
],
); );
} }
} }