two gang schedule

This commit is contained in:
mohammad
2024-09-15 16:48:16 +03:00
parent ad47e34fa2
commit ddaf36797d
10 changed files with 694 additions and 180 deletions

View File

@ -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);
}
}

View File

@ -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});

View File

@ -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);
}

View 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,
};
}
}

View File

@ -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()),
)
],
),
),
),
),
);
},
),
),
);

View 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
),
),
],
),
),
),
],
),
);
}
}

View 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),
),
),
),
],
),
],
),);
}
}

View File

@ -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";
}

View File

@ -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;
}
}