mirror of
https://github.com/SyncrowIOT/syncrow-app.git
synced 2025-08-26 04:29:40 +00:00
two gang schedule
This commit is contained in:
@ -7,6 +7,7 @@ import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_state.d
|
||||
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/groupTwoGangModel.dart';
|
||||
import 'package:syncrow_app/features/devices/model/schedule_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/status_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/two_gang_model.dart';
|
||||
import 'package:syncrow_app/services/api/devices_api.dart';
|
||||
@ -14,11 +15,11 @@ import 'package:syncrow_app/services/api/devices_api.dart';
|
||||
class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
|
||||
final String twoGangId;
|
||||
TwoGangModel deviceStatus = TwoGangModel(
|
||||
firstSwitch: false,
|
||||
secondSwitch: false,
|
||||
firstCountDown: 0,
|
||||
secondCountDown: 0,
|
||||
);
|
||||
firstSwitch: false,
|
||||
secondSwitch: false,
|
||||
firstCountDown: 0,
|
||||
secondCountDown: 0,
|
||||
);
|
||||
Timer? _timer;
|
||||
// Timer? _firstSwitchTimer;
|
||||
// Timer? _secondSwitchTimer;
|
||||
@ -28,6 +29,20 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
|
||||
List<DeviceModel> devicesList = [];
|
||||
List<GroupTwoGangModel> groupTwoGangList = [];
|
||||
bool allSwitchesOn = true;
|
||||
List<String> selectedDays = [];
|
||||
|
||||
bool createSchedule = false;
|
||||
List<ScheduleModel> listSchedule = [
|
||||
ScheduleModel(
|
||||
id: '1',
|
||||
category: 'category',
|
||||
enable: true,
|
||||
function: FunctionModel(code: 'code', value: true),
|
||||
time: 'time',
|
||||
timerId: 'timerId',
|
||||
timezoneId: 'timezoneId',
|
||||
days: ['S'])
|
||||
];
|
||||
|
||||
TwoGangBloc({required this.twoGangId}) : super(InitialState()) {
|
||||
on<InitialEvent>(_fetchTwoGangStatus);
|
||||
@ -43,9 +58,20 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
|
||||
on<OnClose>(_onClose);
|
||||
on<GroupAllOnEvent>(_groupAllOn);
|
||||
on<GroupAllOffEvent>(_groupAllOff);
|
||||
on<ToggleDaySelectionEvent>(toggleDaySelection);
|
||||
}
|
||||
|
||||
void _fetchTwoGangStatus(InitialEvent event, Emitter<TwoGangState> emit) async {
|
||||
DateTime? selectedTime;
|
||||
|
||||
void toggleCreateSchedule() {
|
||||
emit(LoadingInitialState());
|
||||
createSchedule = !createSchedule;
|
||||
emit(UpdateCreateScheduleState(createSchedule));
|
||||
emit(ChangeSlidingSegmentState(value: 1));
|
||||
}
|
||||
|
||||
void _fetchTwoGangStatus(
|
||||
InitialEvent event, Emitter<TwoGangState> emit) async {
|
||||
emit(LoadingInitialState());
|
||||
try {
|
||||
twoGangGroup = event.groupScreen;
|
||||
@ -57,7 +83,8 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
|
||||
HomeCubit.getInstance().selectedSpace?.id ?? '', '2G');
|
||||
|
||||
for (int i = 0; i < devicesList.length; i++) {
|
||||
var response = await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? '');
|
||||
var response =
|
||||
await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? '');
|
||||
List<StatusModel> statusModelList = [];
|
||||
for (var status in response['status']) {
|
||||
statusModelList.add(StatusModel.fromJson(status));
|
||||
@ -65,23 +92,23 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
|
||||
deviceStatus = TwoGangModel.fromJson(statusModelList);
|
||||
|
||||
groupTwoGangList.add(GroupTwoGangModel(
|
||||
deviceId: devicesList[i].uuid ?? '',
|
||||
deviceName: devicesList[i].name ?? '',
|
||||
firstSwitch: deviceStatus.firstSwitch,
|
||||
secondSwitch: deviceStatus.secondSwitch,
|
||||
)
|
||||
);
|
||||
deviceId: devicesList[i].uuid ?? '',
|
||||
deviceName: devicesList[i].name ?? '',
|
||||
firstSwitch: deviceStatus.firstSwitch,
|
||||
secondSwitch: deviceStatus.secondSwitch,
|
||||
));
|
||||
}
|
||||
|
||||
if (groupTwoGangList.isNotEmpty) {
|
||||
groupTwoGangList.firstWhere((element) {
|
||||
if (!element.firstSwitch || !element.secondSwitch ) {
|
||||
if (!element.firstSwitch || !element.secondSwitch) {
|
||||
allSwitchesOn = false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
emit(UpdateGroupState(twoGangList: groupTwoGangList, allSwitches: allSwitchesOn));
|
||||
emit(UpdateGroupState(
|
||||
twoGangList: groupTwoGangList, allSwitches: allSwitchesOn));
|
||||
} else {
|
||||
var response = await DevicesAPI.getDeviceStatus(twoGangId);
|
||||
List<StatusModel> statusModelList = [];
|
||||
@ -100,18 +127,21 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
|
||||
|
||||
_listenToChanges() {
|
||||
try {
|
||||
DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$twoGangId');
|
||||
DatabaseReference ref =
|
||||
FirebaseDatabase.instance.ref('device-status/$twoGangId');
|
||||
Stream<DatabaseEvent> stream = ref.onValue;
|
||||
|
||||
stream.listen((DatabaseEvent event) async {
|
||||
if (_timer != null) {
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
}
|
||||
Map<dynamic, dynamic> usersMap = event.snapshot.value as Map<dynamic, dynamic>;
|
||||
Map<dynamic, dynamic> usersMap =
|
||||
event.snapshot.value as Map<dynamic, dynamic>;
|
||||
List<StatusModel> statusList = [];
|
||||
|
||||
usersMap['status'].forEach((element) {
|
||||
statusList.add(StatusModel(code: element['code'], value: element['value']));
|
||||
statusList
|
||||
.add(StatusModel(code: element['code'], value: element['value']));
|
||||
});
|
||||
|
||||
deviceStatus = TwoGangModel.fromJson(statusList);
|
||||
@ -126,7 +156,8 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
|
||||
emit(UpdateState(twoGangModel: deviceStatus));
|
||||
}
|
||||
|
||||
void _changeFirstSwitch(ChangeFirstSwitchStatusEvent event, Emitter<TwoGangState> emit) async {
|
||||
void _changeFirstSwitch(
|
||||
ChangeFirstSwitchStatusEvent event, Emitter<TwoGangState> emit) async {
|
||||
emit(LoadingNewSate(twoGangModel: deviceStatus));
|
||||
try {
|
||||
deviceStatus.firstSwitch = !event.value;
|
||||
@ -189,13 +220,16 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
|
||||
final response = await Future.wait([
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: twoGangId, code: 'switch_1', value: deviceStatus.firstSwitch),
|
||||
deviceId: twoGangId,
|
||||
code: 'switch_1',
|
||||
value: deviceStatus.firstSwitch),
|
||||
twoGangId),
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: twoGangId, code: 'switch_2', value: deviceStatus.secondSwitch),
|
||||
deviceId: twoGangId,
|
||||
code: 'switch_2',
|
||||
value: deviceStatus.secondSwitch),
|
||||
twoGangId),
|
||||
|
||||
]);
|
||||
|
||||
if (response.every((element) => !element['success'])) {
|
||||
@ -216,16 +250,17 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
|
||||
emit(UpdateState(twoGangModel: deviceStatus));
|
||||
final response = await Future.wait([
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: twoGangId, code: 'switch_1',
|
||||
value: deviceStatus.firstSwitch),
|
||||
DeviceControlModel(
|
||||
deviceId: twoGangId,
|
||||
code: 'switch_1',
|
||||
value: deviceStatus.firstSwitch),
|
||||
twoGangId),
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: twoGangId,
|
||||
code: 'switch_2',
|
||||
value: deviceStatus.secondSwitch),
|
||||
twoGangId),
|
||||
DeviceControlModel(
|
||||
deviceId: twoGangId,
|
||||
code: 'switch_2',
|
||||
value: deviceStatus.secondSwitch),
|
||||
twoGangId),
|
||||
]);
|
||||
if (response.every((element) => !element['success'])) {
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
@ -243,7 +278,6 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
|
||||
for (int i = 0; i < groupTwoGangList.length; i++) {
|
||||
groupTwoGangList[i].firstSwitch = true;
|
||||
groupTwoGangList[i].secondSwitch = true;
|
||||
|
||||
}
|
||||
emit(UpdateGroupState(twoGangList: groupTwoGangList, allSwitches: true));
|
||||
|
||||
@ -251,11 +285,15 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
|
||||
final response = await Future.wait([
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: groupTwoGangList[i].deviceId, code: 'switch_1', value: true),
|
||||
deviceId: groupTwoGangList[i].deviceId,
|
||||
code: 'switch_1',
|
||||
value: true),
|
||||
groupTwoGangList[i].deviceId),
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: groupTwoGangList[i].deviceId, code: 'switch_2', value: true),
|
||||
deviceId: groupTwoGangList[i].deviceId,
|
||||
code: 'switch_2',
|
||||
value: true),
|
||||
groupTwoGangList[i].deviceId),
|
||||
]);
|
||||
|
||||
@ -284,13 +322,16 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
|
||||
final response = await Future.wait([
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: groupTwoGangList[i].deviceId, code: 'switch_1', value: false),
|
||||
deviceId: groupTwoGangList[i].deviceId,
|
||||
code: 'switch_1',
|
||||
value: false),
|
||||
groupTwoGangList[i].deviceId),
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: groupTwoGangList[i].deviceId, code: 'switch_2', value: false),
|
||||
deviceId: groupTwoGangList[i].deviceId,
|
||||
code: 'switch_2',
|
||||
value: false),
|
||||
groupTwoGangList[i].deviceId),
|
||||
|
||||
]);
|
||||
|
||||
if (response.every((element) => !element['success'])) {
|
||||
@ -305,17 +346,20 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _changeSliding(ChangeSlidingSegment event, Emitter<TwoGangState> emit) async {
|
||||
void _changeSliding(
|
||||
ChangeSlidingSegment event, Emitter<TwoGangState> emit) async {
|
||||
emit(ChangeSlidingSegmentState(value: event.value));
|
||||
}
|
||||
|
||||
void _setCounterValue(SetCounterValue event, Emitter<TwoGangState> emit) async {
|
||||
void _setCounterValue(
|
||||
SetCounterValue event, Emitter<TwoGangState> emit) async {
|
||||
emit(LoadingNewSate(twoGangModel: deviceStatus));
|
||||
int seconds = 0;
|
||||
try {
|
||||
seconds = event.duration.inSeconds;
|
||||
final response = await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(deviceId: twoGangId, code: event.deviceCode, value: seconds),
|
||||
DeviceControlModel(
|
||||
deviceId: twoGangId, code: event.deviceCode, value: seconds),
|
||||
twoGangId);
|
||||
|
||||
if (response['success'] ?? false) {
|
||||
@ -340,7 +384,8 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _getCounterValue(GetCounterEvent event, Emitter<TwoGangState> emit) async {
|
||||
void _getCounterValue(
|
||||
GetCounterEvent event, Emitter<TwoGangState> emit) async {
|
||||
emit(LoadingInitialState());
|
||||
try {
|
||||
var response = await DevicesAPI.getDeviceStatus(twoGangId);
|
||||
@ -387,4 +432,38 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
|
||||
emit(TimerRunComplete());
|
||||
}
|
||||
}
|
||||
|
||||
List<Map<String, String>> days = [
|
||||
{"day": "Sun", "key": "Sun"},
|
||||
{"day": "Mon", "key": "Mon"},
|
||||
{"day": "Tue", "key": "Tue"},
|
||||
{"day": "Wed", "key": "Wed"},
|
||||
{"day": "Thu", "key": "Thu"},
|
||||
{"day": "Fri", "key": "Fri"},
|
||||
{"day": "Sat", "key": "Sat"},
|
||||
];
|
||||
|
||||
Future<void> toggleDaySelection(
|
||||
ToggleDaySelectionEvent event,
|
||||
Emitter<TwoGangState> emit,
|
||||
) async {
|
||||
emit(LoadingInitialState());
|
||||
if (selectedDays.contains(event.key)) {
|
||||
selectedDays.remove(event.key);
|
||||
} else {
|
||||
selectedDays.add(event.key);
|
||||
}
|
||||
emit(ChangeTimeState());
|
||||
add(ChangeSlidingSegment(value: 1));
|
||||
}
|
||||
|
||||
saveSchedule() async {
|
||||
final response = await DevicesAPI.postSchedule(
|
||||
category: 'switch_1',
|
||||
deviceId: twoGangId,
|
||||
time: 'selectedTime',
|
||||
code: 'switch_1',
|
||||
value: true,
|
||||
days: selectedDays);
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,14 @@ abstract class TwoGangEvent extends Equatable {
|
||||
class LoadingEvent extends TwoGangEvent {}
|
||||
|
||||
class TwoGangUpdated extends TwoGangEvent {}
|
||||
class ToggleDaySelectionEvent extends TwoGangEvent {
|
||||
|
||||
final String key;
|
||||
|
||||
const ToggleDaySelectionEvent({required this.key});
|
||||
@override
|
||||
List<Object> get props => [key];
|
||||
}
|
||||
|
||||
class InitialEvent extends TwoGangEvent {
|
||||
final bool groupScreen;
|
||||
@ -43,6 +51,13 @@ class GroupAllOnEvent extends TwoGangEvent {}
|
||||
|
||||
class GroupAllOffEvent extends TwoGangEvent {}
|
||||
|
||||
|
||||
// two_gang_event.dart
|
||||
class ToggleCreateScheduleEvent extends TwoGangEvent {}
|
||||
|
||||
|
||||
|
||||
|
||||
class ChangeSlidingSegment extends TwoGangEvent {
|
||||
final int value;
|
||||
const ChangeSlidingSegment({required this.value});
|
||||
|
@ -11,6 +11,7 @@ class TwoGangState extends Equatable {
|
||||
}
|
||||
|
||||
class InitialState extends TwoGangState {}
|
||||
class ChangeTimeState extends TwoGangState {}
|
||||
|
||||
class LoadingInitialState extends TwoGangState {}
|
||||
|
||||
@ -76,3 +77,11 @@ class TimerRunInProgress extends TwoGangState {
|
||||
}
|
||||
|
||||
class TimerRunComplete extends TwoGangState {}
|
||||
|
||||
|
||||
|
||||
// two_gang_state.dart
|
||||
class UpdateCreateScheduleState extends TwoGangState {
|
||||
final bool createSchedule;
|
||||
UpdateCreateScheduleState(this.createSchedule);
|
||||
}
|
||||
|
74
lib/features/devices/model/schedule_model.dart
Normal file
74
lib/features/devices/model/schedule_model.dart
Normal file
@ -0,0 +1,74 @@
|
||||
class FunctionModel {
|
||||
final String code;
|
||||
final bool value;
|
||||
|
||||
FunctionModel({
|
||||
required this.code,
|
||||
required this.value,
|
||||
});
|
||||
|
||||
// Convert a JSON object to a FunctionModel instance
|
||||
factory FunctionModel.fromJson(Map<String, dynamic> json) {
|
||||
return FunctionModel(
|
||||
code: json['code'],
|
||||
value: json['value'],
|
||||
);
|
||||
}
|
||||
|
||||
// Convert a FunctionModel instance to a JSON object
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'code': code,
|
||||
'value': value,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ScheduleModel {
|
||||
final String id;
|
||||
final String category;
|
||||
final bool enable;
|
||||
final FunctionModel function;
|
||||
final String time;
|
||||
final String timerId;
|
||||
final String timezoneId;
|
||||
final List<String> days;
|
||||
|
||||
ScheduleModel({
|
||||
required this.id,
|
||||
required this.category,
|
||||
required this.enable,
|
||||
required this.function,
|
||||
required this.time,
|
||||
required this.timerId,
|
||||
required this.timezoneId,
|
||||
required this.days,
|
||||
});
|
||||
|
||||
// Convert a JSON object to a Schedule instance
|
||||
factory ScheduleModel.fromJson(Map<String, dynamic> json) {
|
||||
return ScheduleModel(
|
||||
id: json['id'],
|
||||
category: json['category'],
|
||||
enable: json['enable'],
|
||||
function: FunctionModel.fromJson(json['function']),
|
||||
time: json['time'],
|
||||
timerId: json['timerId'],
|
||||
timezoneId: json['timezoneId'],
|
||||
days: List<String>.from(json['days']),
|
||||
);
|
||||
}
|
||||
|
||||
// Convert a Schedule instance to a JSON object
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'category': category,
|
||||
'enable': enable,
|
||||
'function': function.toJson(),
|
||||
'time': time,
|
||||
'timerId': timerId,
|
||||
'timezoneId': timezoneId,
|
||||
'days': days,
|
||||
};
|
||||
}
|
||||
}
|
@ -6,8 +6,11 @@ import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_state.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/create_schedule.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/empty_schedule.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/context_extension.dart';
|
||||
@ -15,11 +18,11 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_event.dart';
|
||||
|
||||
|
||||
class TwoTimerScreen extends StatelessWidget {
|
||||
final DeviceModel device;
|
||||
final String deviceCode;
|
||||
const TwoTimerScreen({required this.device, required this.deviceCode, super.key});
|
||||
const TwoTimerScreen(
|
||||
{required this.device, required this.deviceCode, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -28,151 +31,248 @@ class TwoTimerScreen extends StatelessWidget {
|
||||
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
),
|
||||
child: Scaffold(
|
||||
backgroundColor: ColorsManager.backgroundColor,
|
||||
extendBodyBehindAppBar: true,
|
||||
extendBody: true,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
centerTitle: true,
|
||||
title: const BodyLarge(
|
||||
text: 'Schedule',
|
||||
fontColor: ColorsManager.primaryColor,
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: BlocProvider(
|
||||
create: (context) => TwoGangBloc(twoGangId: device.uuid ?? '')
|
||||
..add(GetCounterEvent(deviceCode: deviceCode)),
|
||||
child: BlocBuilder<TwoGangBloc, TwoGangState>(
|
||||
builder: (context, state) {
|
||||
Duration duration = Duration.zero;
|
||||
int countNum = 0;
|
||||
int tabNum = 0;
|
||||
if (state is UpdateTimerState) {
|
||||
countNum = state.seconds;
|
||||
} else if (state is TimerRunInProgress) {
|
||||
countNum = state.remainingTime;
|
||||
} else if (state is TimerRunComplete) {
|
||||
countNum = 0;
|
||||
} else if (state is LoadingNewSate) {
|
||||
countNum = 0;
|
||||
}
|
||||
child: BlocProvider(
|
||||
create: (context) => TwoGangBloc(twoGangId: device.uuid ?? '')
|
||||
..add(GetCounterEvent(deviceCode: deviceCode)),
|
||||
child: BlocBuilder<TwoGangBloc, TwoGangState>(
|
||||
builder: (context, state) {
|
||||
final twoGangBloc = BlocProvider.of<TwoGangBloc>(context);
|
||||
|
||||
if (state is ChangeSlidingSegmentState) {
|
||||
tabNum = state.value;
|
||||
}
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (didPop) {
|
||||
if (!didPop) {
|
||||
BlocProvider.of<TwoGangBloc>(context).add(OnClose());
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
height: MediaQuery.sizeOf(context).height,
|
||||
padding: const EdgeInsets.all(24),
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(
|
||||
Assets.assetsImagesBackground,
|
||||
),
|
||||
fit: BoxFit.cover,
|
||||
opacity: 0.4,
|
||||
Duration duration = Duration.zero;
|
||||
int countNum = 0;
|
||||
int tabNum = 0;
|
||||
if (state is UpdateTimerState) {
|
||||
countNum = state.seconds;
|
||||
} else if (state is TimerRunInProgress) {
|
||||
countNum = state.remainingTime;
|
||||
} else if (state is TimerRunComplete) {
|
||||
countNum = 0;
|
||||
} else if (state is LoadingNewSate) {
|
||||
countNum = 0;
|
||||
} else if (state is ChangeSlidingSegmentState) {
|
||||
tabNum = state.value;
|
||||
}
|
||||
return Scaffold(
|
||||
backgroundColor: ColorsManager.backgroundColor,
|
||||
extendBodyBehindAppBar: true,
|
||||
extendBody: true,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
centerTitle: true,
|
||||
title: const BodyLarge(
|
||||
text: 'Schedule',
|
||||
fontColor: ColorsManager.primaryColor,
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
actions: [
|
||||
if (tabNum == 1)
|
||||
twoGangBloc.createSchedule == true
|
||||
? TextButton(onPressed: () {}, child: const Text('Save'))
|
||||
: IconButton(
|
||||
onPressed: () {
|
||||
twoGangBloc.toggleCreateSchedule();
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
),
|
||||
),
|
||||
child: state is LoadingInitialState || state is LoadingNewSate
|
||||
? const Center(
|
||||
child: DefaultContainer(
|
||||
width: 50, height: 50, child: CircularProgressIndicator()),
|
||||
)
|
||||
: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
decoration: const ShapeDecoration(
|
||||
color: ColorsManager.slidingBlueColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (didPop) {
|
||||
if (!didPop) {
|
||||
BlocProvider.of<TwoGangBloc>(context).add(OnClose());
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
height: MediaQuery.sizeOf(context).height,
|
||||
padding: const EdgeInsets.all(24),
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(Assets.assetsImagesBackground),
|
||||
fit: BoxFit.cover,
|
||||
opacity: 0.4,
|
||||
),
|
||||
),
|
||||
child: state is LoadingInitialState ||
|
||||
state is LoadingNewSate
|
||||
? const Center(
|
||||
child: DefaultContainer(
|
||||
width: 50,
|
||||
height: 50,
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
)
|
||||
: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
decoration: const ShapeDecoration(
|
||||
color: ColorsManager.slidingBlueColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
),
|
||||
child: CupertinoSlidingSegmentedControl<int>(
|
||||
thumbColor: ColorsManager.slidingBlueColor,
|
||||
onValueChanged: (value) {
|
||||
BlocProvider.of<TwoGangBloc>(context).add(
|
||||
ChangeSlidingSegment(
|
||||
value: value ?? 0));
|
||||
},
|
||||
groupValue: state is ChangeSlidingSegmentState
|
||||
? state.value
|
||||
: 0,
|
||||
backgroundColor: Colors.white,
|
||||
children: <int, Widget>{
|
||||
0: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10),
|
||||
child: BodySmall(
|
||||
text: 'Countdown',
|
||||
style: context.bodySmall.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: CupertinoSlidingSegmentedControl<int>(
|
||||
thumbColor: ColorsManager.slidingBlueColor,
|
||||
onValueChanged: (value) {
|
||||
BlocProvider.of<TwoGangBloc>(context)
|
||||
.add(ChangeSlidingSegment(value: value ?? 0));
|
||||
},
|
||||
groupValue:
|
||||
state is ChangeSlidingSegmentState ? state.value : 0,
|
||||
backgroundColor: Colors.white,
|
||||
children: <int, Widget>{
|
||||
0: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: BodySmall(
|
||||
text: 'Countdown',
|
||||
style: context.bodySmall.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
1: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10),
|
||||
child: Text(
|
||||
'Schedule',
|
||||
style: context.bodySmall.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
1: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Text(
|
||||
'Schedule',
|
||||
style: context.bodySmall.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
if (tabNum == 0)
|
||||
countNum > 0
|
||||
? BodyLarge(
|
||||
text: _formatDuration(countNum),
|
||||
fontColor: ColorsManager.slidingBlueColor,
|
||||
fontSize: 40,
|
||||
)
|
||||
: CupertinoTimerPicker(
|
||||
mode: CupertinoTimerPickerMode.hm,
|
||||
onTimerDurationChanged: (Duration newDuration) {
|
||||
duration = newDuration;
|
||||
},
|
||||
),
|
||||
if (tabNum == 0)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (state is LoadingNewSate) {
|
||||
return;
|
||||
}
|
||||
if (countNum > 0) {
|
||||
BlocProvider.of<TwoGangBloc>(context).add(
|
||||
SetCounterValue(
|
||||
deviceCode: deviceCode,
|
||||
duration: Duration.zero));
|
||||
} else if (duration != Duration.zero) {
|
||||
BlocProvider.of<TwoGangBloc>(context).add(
|
||||
SetCounterValue(
|
||||
deviceCode: deviceCode, duration: duration));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
if (tabNum == 0)
|
||||
countNum > 0
|
||||
? BodyLarge(
|
||||
text: _formatDuration(countNum),
|
||||
fontColor:
|
||||
ColorsManager.slidingBlueColor,
|
||||
fontSize: 40,
|
||||
)
|
||||
: CupertinoTimerPicker(
|
||||
mode: CupertinoTimerPickerMode.hm,
|
||||
onTimerDurationChanged:
|
||||
(Duration newDuration) {
|
||||
duration = newDuration;
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
countNum > 0 ? Assets.pauseIcon : Assets.playIcon)
|
||||
)
|
||||
],
|
||||
)));
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
if (tabNum == 0)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (state is LoadingNewSate) {
|
||||
return;
|
||||
}
|
||||
if (countNum > 0) {
|
||||
BlocProvider.of<TwoGangBloc>(context)
|
||||
.add(SetCounterValue(
|
||||
deviceCode: deviceCode,
|
||||
duration: Duration.zero));
|
||||
} else if (duration != Duration.zero) {
|
||||
BlocProvider.of<TwoGangBloc>(context)
|
||||
.add(SetCounterValue(
|
||||
deviceCode: deviceCode,
|
||||
duration: duration));
|
||||
}
|
||||
},
|
||||
child: SvgPicture.asset(countNum > 0
|
||||
? Assets.pauseIcon
|
||||
: Assets.playIcon)),
|
||||
if (tabNum == 1)
|
||||
SizedBox(
|
||||
height:
|
||||
MediaQuery.of(context).size.height / 2,
|
||||
child: twoGangBloc.createSchedule == true
|
||||
? CreateSchedule(
|
||||
onToggleChanged: (bool isOn) {
|
||||
print(
|
||||
'Toggle is now: ${isOn ? "ON" : "OFF"}');
|
||||
},
|
||||
onDateTimeChanged:
|
||||
(DateTime dateTime) {
|
||||
print('Selected Time: $dateTime');
|
||||
},
|
||||
days: twoGangBloc.days,
|
||||
selectDays:
|
||||
(List<String> selectedDays) {
|
||||
twoGangBloc.selectedDays =
|
||||
selectedDays;
|
||||
print(
|
||||
'Selected Days: ${twoGangBloc.selectedDays}');
|
||||
},
|
||||
)
|
||||
: Center(
|
||||
child: twoGangBloc.listSchedule.isNotEmpty?
|
||||
Container(
|
||||
height: MediaQuery.of(context).size.height / 2,
|
||||
child: ListView.builder(
|
||||
itemCount: twoGangBloc.listSchedule.length,
|
||||
itemBuilder: (context, index) {
|
||||
return
|
||||
|
||||
Dismissible(
|
||||
key: Key( twoGangBloc.listSchedule[index].id),
|
||||
child: InkWell(
|
||||
onTap: () {},
|
||||
child:
|
||||
DefaultContainer(
|
||||
padding: EdgeInsets.all(20),
|
||||
height: MediaQuery.of(context).size.height / 8,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(twoGangBloc.listSchedule[index].time),
|
||||
Text(twoGangBloc.listSchedule[index].days.toString()),
|
||||
Text(twoGangBloc.listSchedule[index].enable.toString())
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(child:ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
leading: const BodyMedium(
|
||||
text: '',
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
trailing: Transform.scale(
|
||||
scale: .8,
|
||||
child: CupertinoSwitch(
|
||||
value: true,
|
||||
onChanged: (value) {
|
||||
// smartDoorBloc.add(ToggleRepeatEvent());
|
||||
},
|
||||
applyTheme: true,
|
||||
)),
|
||||
),)
|
||||
],
|
||||
))));
|
||||
},),
|
||||
):
|
||||
EmptySchedule()),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
140
lib/features/shared_widgets/create_schedule.dart
Normal file
140
lib/features/shared_widgets/create_schedule.dart
Normal file
@ -0,0 +1,140 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
|
||||
class CreateSchedule extends StatefulWidget {
|
||||
final List<Map<String, String>> days;
|
||||
final void Function(List<String> selectedDays)? selectDays; // Callback for selected days
|
||||
final Function(DateTime) onDateTimeChanged;
|
||||
final void Function(bool isOn)? onToggleChanged; // Callback for toggle state
|
||||
|
||||
const CreateSchedule({
|
||||
Key? key,
|
||||
required this.days,
|
||||
required this.selectDays,
|
||||
required this.onDateTimeChanged,
|
||||
this.onToggleChanged, // Optional callback
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_CreateScheduleState createState() => _CreateScheduleState();
|
||||
}
|
||||
|
||||
class _CreateScheduleState extends State<CreateSchedule> {
|
||||
List<String> selectedDays = []; // List of selected days
|
||||
bool isOn = false; // Boolean state for the ON/OFF toggle
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
const Divider(
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
SizedBox(
|
||||
height: 110,
|
||||
child: CupertinoDatePicker(
|
||||
mode: CupertinoDatePickerMode.time,
|
||||
initialDateTime: DateTime.now(),
|
||||
onDateTimeChanged: widget.onDateTimeChanged,
|
||||
),
|
||||
),
|
||||
const Divider(
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.08,
|
||||
child: ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: widget.days.map((day) {
|
||||
bool isSelected = selectedDays.contains(day['day']); // Check if day is selected
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
if (isSelected) {
|
||||
selectedDays.remove(day['day']); // Deselect day
|
||||
} else {
|
||||
selectedDays.add(day['day']!); // Select day
|
||||
}
|
||||
if (widget.selectDays != null) {
|
||||
widget.selectDays!(selectedDays);
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
width: 50,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8, horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: isSelected
|
||||
? Colors.black
|
||||
: ColorsManager.grayColor,
|
||||
),
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
day['day']!,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: isSelected
|
||||
? Colors.black
|
||||
: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
isOn = !isOn; // Toggle the state
|
||||
if (widget.onToggleChanged != null) {
|
||||
widget.onToggleChanged!(isOn); // Pass the new toggle state
|
||||
}
|
||||
});
|
||||
},
|
||||
child: DefaultContainer(
|
||||
height: 60,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
isOn ? 'ON' : 'OFF', // Change text based on state
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isOn ? Colors.green : Colors.red, // Change color based on state
|
||||
),
|
||||
),
|
||||
CircleAvatar(
|
||||
backgroundColor: isOn
|
||||
? ColorsManager.secondaryColor.withOpacity(0.6)
|
||||
: Colors.red.withOpacity(0.6), // Change background color based on state
|
||||
child: Icon(
|
||||
Icons.power_settings_new,
|
||||
color: isOn ? Colors.green : Colors.red, // Change icon color
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
51
lib/features/shared_widgets/empty_schedule.dart
Normal file
51
lib/features/shared_widgets/empty_schedule.dart
Normal file
@ -0,0 +1,51 @@
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/context_extension.dart';
|
||||
|
||||
class EmptySchedule extends StatelessWidget {
|
||||
const EmptySchedule({super.key});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(child: Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
children: [
|
||||
Center(
|
||||
child: SvgPicture.asset(
|
||||
height: 100,
|
||||
width: 100,
|
||||
Assets.emptySchedule),
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
'Please add',
|
||||
style: context.displaySmall
|
||||
.copyWith(
|
||||
color: const Color(
|
||||
0XFFD5D5D5),
|
||||
),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Text(
|
||||
'a new schedule',
|
||||
style: context.displaySmall
|
||||
.copyWith(
|
||||
color: const Color(
|
||||
0XFFD5D5D5),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),);
|
||||
}
|
||||
}
|
@ -1035,4 +1035,6 @@ class Assets {
|
||||
static const String oneGang = "assets/icons/1gang.svg";
|
||||
|
||||
static const String twoGang = "assets/icons/2gang.svg";
|
||||
|
||||
static const String emptySchedule = "assets/icons/emptySchedule.svg";
|
||||
}
|
||||
|
@ -270,4 +270,33 @@ class DevicesAPI {
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> postSchedule({
|
||||
required String category,
|
||||
required String deviceId,
|
||||
required String time,
|
||||
required String code,
|
||||
required bool value,
|
||||
required List<String> days,
|
||||
|
||||
}) async {
|
||||
final response = await _httpService.post(
|
||||
path: ApiEndpoints.deleteTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
|
||||
showServerMessage: false,
|
||||
body:
|
||||
{
|
||||
"category": category,
|
||||
"time": time,
|
||||
"function":{
|
||||
"code": code,
|
||||
"value":value,
|
||||
},
|
||||
"days": days
|
||||
},
|
||||
expectedResponseModel: (json) {
|
||||
return json;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user