mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 15:17:31 +00:00
push schedule basic design and bloc manegment
This commit is contained in:
@ -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();
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user