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