sos_device

This commit is contained in:
mohammad
2024-11-24 15:05:33 +03:00
parent 6733c4f4ab
commit b365f7e347
31 changed files with 3691 additions and 33 deletions

View File

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.3258 4.08383C15.0587 3.35098 15.0585 2.16495 14.3258 1.43217L13.4419 0.548297C12.7108 -0.182766 11.5213 -0.182766 10.7902 0.548297L2.54069 8.79789C2.06572 9.27286 1.75181 9.90067 1.65681 10.5656L1.21484 13.6592L4.30847 13.2173C4.97341 13.1223 5.60122 12.8084 6.07619 12.3334L14.3258 4.08383ZM4.16112 8.9452L9.46444 3.64189L11.2322 5.40964L5.92891 10.713L4.16112 8.9452ZM2.688 12.1861L2.89425 10.7425C2.94141 10.4125 3.07863 10.0979 3.28581 9.83764L5.0365 11.5883C4.77628 11.7955 4.46172 11.9327 4.13172 11.9799L2.688 12.1861ZM13.4419 3.19992L12.1161 4.52577L10.3483 2.75802L11.6742 1.4322C11.9178 1.18852 12.3143 1.18852 12.558 1.4322L13.4419 2.31608C13.6862 2.56039 13.6861 2.95577 13.4419 3.19992Z" fill="#D5D5D5"/>
<path d="M0 14.749H16V15.999H0V14.749Z" fill="#D5D5D5"/>
</svg>

After

Width:  |  Height:  |  Size: 891 B

View File

@ -8,7 +8,7 @@ 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/device_report_model.dart';
import 'package:syncrow_app/features/devices/model/group_devices_model.dart';
import 'package:syncrow_app/features/devices/model/scene_switch_model.dart';
import 'package:syncrow_app/features/devices/model/device_info_model.dart';
import 'package:syncrow_app/features/devices/model/sex_scene_question_model.dart';
import 'package:syncrow_app/features/devices/model/six_scene_model.dart';
import 'package:syncrow_app/features/devices/model/status_model.dart';
@ -68,7 +68,7 @@ class SixSceneBloc extends Bloc<SixSceneEvent, SixSceneState> {
scene_id_group_id: '',
switch_backlight: false);
SceneSwitch deviceInfo = SceneSwitch(
DeviceInfoModel deviceInfo = DeviceInfoModel(
activeTime: 0,
category: "",
categoryName: "",
@ -175,7 +175,7 @@ class SixSceneBloc extends Bloc<SixSceneEvent, SixSceneState> {
try {
emit(SixSceneLoadingState());
var response = await DevicesAPI.getDeviceInfo(sixSceneId);
deviceInfo = SceneSwitch.fromJson(response);
deviceInfo = DeviceInfoModel.fromJson(response);
deviceName = deviceInfo.name;
emit(LoadingDeviceInfo(deviceInfo: deviceInfo));
} catch (e) {

View File

@ -1,7 +1,7 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/group_devices_model.dart';
import 'package:syncrow_app/features/devices/model/scene_switch_model.dart';
import 'package:syncrow_app/features/devices/model/device_info_model.dart';
import 'package:syncrow_app/features/devices/model/sex_scene_question_model.dart';
import 'package:syncrow_app/features/devices/model/six_scene_model.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
@ -121,7 +121,7 @@ class SceneSelectionUpdatedState extends SixSceneState {
}
class LoadingDeviceInfo extends SixSceneState {
final SceneSwitch deviceInfo;
final DeviceInfoModel deviceInfo;
const LoadingDeviceInfo({required this.deviceInfo});
@override

View File

@ -8,10 +8,10 @@ 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/device_report_model.dart';
import 'package:syncrow_app/features/devices/model/four_scene_model.dart';
import 'package:syncrow_app/features/devices/model/four_scene_question_model.dart';
import 'package:syncrow_app/features/devices/model/question_model.dart';
import 'package:syncrow_app/features/devices/model/four_scene_switch_model.dart';
import 'package:syncrow_app/features/devices/model/group_devices_model.dart';
import 'package:syncrow_app/features/devices/model/scene_switch_model.dart';
import 'package:syncrow_app/features/devices/model/device_info_model.dart';
import 'package:syncrow_app/features/devices/model/status_model.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
import 'package:syncrow_app/features/scene/model/scenes_model.dart';
@ -68,7 +68,7 @@ class FourSceneBloc extends Bloc<FourSceneEvent, FourSceneState> {
scene_id_group_id: '',
switch_backlight: false);
SceneSwitch deviceInfo = SceneSwitch(
DeviceInfoModel deviceInfo = DeviceInfoModel(
activeTime: 0,
category: "",
categoryName: "",
@ -285,7 +285,7 @@ class FourSceneBloc extends Bloc<FourSceneEvent, FourSceneState> {
try {
emit(FourSceneLoadingState());
var response = await DevicesAPI.getDeviceInfo(fourSceneId);
deviceInfo = SceneSwitch.fromJson(response);
deviceInfo = DeviceInfoModel.fromJson(response);
deviceName = deviceInfo.name;
emit(LoadingDeviceInfo(deviceInfo: deviceInfo));
} catch (e) {
@ -295,7 +295,7 @@ class FourSceneBloc extends Bloc<FourSceneEvent, FourSceneState> {
void _onSearchFaq(SearchFaqEvent event, Emitter<FourSceneState> emit) {
emit(FourSceneLoadingState());
List<FourSceneQuestionModel> _faqQuestions = faqQuestions.where((question) {
List<QuestionModel> _faqQuestions = faqQuestions.where((question) {
return question.question
.toLowerCase()
.contains(event.query.toLowerCase());
@ -339,31 +339,31 @@ class FourSceneBloc extends Bloc<FourSceneEvent, FourSceneState> {
DeviceReport recordGroups =
DeviceReport(startTime: '0', endTime: '0', data: []);
final List<FourSceneQuestionModel> faqQuestions = [
FourSceneQuestionModel(
final List<QuestionModel> faqQuestions = [
QuestionModel(
id: 1,
question: 'How does an SOS emergency button work?',
answer:
'The SOS emergency button sends an alert to your contacts when pressed.',
),
FourSceneQuestionModel(
QuestionModel(
id: 2,
question: 'How long will an SOS alarm persist?',
answer:
'The SOS alarm will persist until it is manually turned off or after a set time.',
),
FourSceneQuestionModel(
QuestionModel(
id: 3,
question: 'What should I do if the SOS button is unresponsive?',
answer: 'Try restarting the device. If it persists, contact support.',
),
FourSceneQuestionModel(
QuestionModel(
id: 4,
question: 'Can I use the SOS feature without a network connection?',
answer:
'No, a network connection is required to send the alert to your contacts.',
),
FourSceneQuestionModel(
QuestionModel(
id: 5,
question: 'How often should I check the SOS battery?',
answer:

View File

@ -1,9 +1,9 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/four_scene_model.dart';
import 'package:syncrow_app/features/devices/model/four_scene_question_model.dart';
import 'package:syncrow_app/features/devices/model/question_model.dart';
import 'package:syncrow_app/features/devices/model/group_devices_model.dart';
import 'package:syncrow_app/features/devices/model/scene_switch_model.dart';
import 'package:syncrow_app/features/devices/model/device_info_model.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
import 'package:syncrow_app/features/scene/model/scenes_model.dart';
@ -58,13 +58,13 @@ class NameEditingState extends FourSceneState {
}
class FaqLoadedState extends FourSceneState {
final List<FourSceneQuestionModel> filteredFaqQuestions;
final List<QuestionModel> filteredFaqQuestions;
FaqLoadedState({this.filteredFaqQuestions = const []});
}
class FaqSearchState extends FourSceneState {
final List<FourSceneQuestionModel> filteredFaqQuestions;
final List<QuestionModel> filteredFaqQuestions;
const FaqSearchState({this.filteredFaqQuestions = const []});
}
@ -116,7 +116,7 @@ class OptionSelectedState extends FourSceneState {
}
class LoadingDeviceInfo extends FourSceneState {
final SceneSwitch deviceInfo;
final DeviceInfoModel deviceInfo;
const LoadingDeviceInfo({required this.deviceInfo});
@override

View File

@ -0,0 +1,452 @@
import 'dart:async';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/app_layout/model/community_model.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/features/devices/model/device_info_model.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/device_report_model.dart';
import 'package:syncrow_app/features/devices/model/question_model.dart';
import 'package:syncrow_app/features/devices/model/sos_model.dart';
import 'package:syncrow_app/features/devices/model/status_model.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
import 'package:syncrow_app/navigation/routing_constants.dart';
import 'package:syncrow_app/services/api/devices_api.dart';
import 'package:syncrow_app/services/api/home_management_api.dart';
import 'package:syncrow_app/services/api/spaces_api.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
class SosBloc extends Bloc<SosEvent, SosState> {
final String sosId;
SosBloc({
required this.sosId,
}) : super(const SosState()) {
on<SosInitial>(_fetchStatus);
on<ReportLogsInitial>(fetchLogsForLastMonth);
on<ToggleEnableAlarmEvent>(_toggleLowBattery);
on<ToggleClosingReminderEvent>(_toggleClosingReminder);
on<ChangeNameEvent>(_changeName);
on<SearchFaqEvent>(_onSearchFaq);
on<SosInitialQuestion>(_onSosInitial);
on<FetchRoomsEvent>(_fetchRoomsAndDevices);
on<SelectOptionEvent>(_onOptionSelected);
on<SaveSelectionEvent>(_onSaveSelection);
on<AssignRoomEvent>(_assignDevice);
on<SosInitialDeviseInfo>(fetchDeviceInfo);
// on<UnassignRoomEvent>(_unassignDevice);
// on<ToggleWaterLeakAlarmEvent>(_toggleWaterLeakAlarm);
}
final TextEditingController nameController =
TextEditingController(text: '${'firstName'}');
bool isSaving = false;
bool editName = false;
final FocusNode focusNode = FocusNode();
Timer? _timer;
bool enableAlarm = false;
bool closingReminder = false;
bool waterAlarm = false;
SosModel deviceStatus =
SosModel(sosContactState: 'normal', batteryPercentage: 0);
void _fetchStatus(SosInitial event, Emitter<SosState> emit) async {
emit(SosLoadingState());
try {
var response = await DevicesAPI.getDeviceStatus(sosId);
List<StatusModel> statusModelList = [];
for (var status in response['status']) {
statusModelList.add(StatusModel.fromJson(status));
}
deviceStatus = SosModel.fromJson(
statusModelList,
);
emit(UpdateState(sensor: deviceStatus));
Future.delayed(const Duration(milliseconds: 500));
// _listenToChanges();
} catch (e) {
emit(SosFailedState(errorMessage: e.toString()));
return;
}
}
DeviceInfoModel deviceInfo = DeviceInfoModel(
activeTime: 0,
category: "",
categoryName: "",
createTime: 0,
gatewayId: "",
icon: "",
ip: "",
lat: "",
localKey: "",
lon: "",
model: "",
name: "",
nodeId: "",
online: false,
ownerId: "",
productName: "",
sub: false,
timeZone: "",
updateTime: 0,
uuid: "",
productUuid: "",
productType: "",
permissionType: "",
macAddress: "",
subspace: Subspace(
uuid: "",
createdAt: "",
updatedAt: "",
subspaceName: "",
),
);
void _onSearchFaq(SearchFaqEvent event, Emitter<SosState> emit) {
emit(SosLoadingState());
List<QuestionModel> _faqQuestions = faqQuestions.where((question) {
return question.question
.toLowerCase()
.contains(event.query.toLowerCase());
}).toList();
emit(FaqSearchState(filteredFaqQuestions: _faqQuestions));
}
void _changeName(ChangeNameEvent event, Emitter<SosState> emit) {
emit(SosLoadingState());
editName = event.value!;
if (editName) {
Future.delayed(const Duration(milliseconds: 500), () {
focusNode.requestFocus();
});
} else {
focusNode.unfocus();
}
emit(NameEditingState(editName: editName));
}
void _toggleLowBattery(
ToggleEnableAlarmEvent event, Emitter<SosState> emit) async {
emit(LoadingNewSate(sosSensor: deviceStatus));
try {
enableAlarm = event.isLowBatteryEnabled;
emit(UpdateState(sensor: deviceStatus));
await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: sosId,
code: 'low_battery',
value: enableAlarm,
),
sosId,
);
} catch (e) {
emit(SosFailedState(errorMessage: e.toString()));
}
}
void _toggleClosingReminder(
ToggleClosingReminderEvent event, Emitter<SosState> emit) async {
emit(LoadingNewSate(sosSensor: deviceStatus));
try {
closingReminder = event.isClosingReminderEnabled;
emit(UpdateState(sensor: deviceStatus));
// API call to update the state, if necessary
// await DevicesAPI.controlDevice(
// DeviceControlModel(
// deviceId: sosId,
// code: 'closing_reminder',
// value: closingReminder,
// ),
// sosId,
// );
} catch (e) {
emit(SosFailedState(errorMessage: e.toString()));
}
}
DeviceReport recordGroups =
DeviceReport(startTime: '0', endTime: '0', data: []);
Future<void> fetchLogsForLastMonth(
ReportLogsInitial event, Emitter<SosState> emit) async {
DateTime now = DateTime.now();
DateTime lastMonth = DateTime(now.year, now.month - 1, now.day);
int startTime = lastMonth.millisecondsSinceEpoch;
int endTime = now.millisecondsSinceEpoch;
try {
emit(SosLoadingState());
var response = await DevicesAPI.getReportLogs(
startTime: startTime.toString(),
endTime: endTime.toString(),
deviceUuid: sosId,
code: 'sos',
);
recordGroups = response;
emit(UpdateState(sensor: deviceStatus));
} on DioException catch (e) {
final errorData = e.response!.data;
String errorMessage = errorData['message'];
emit(SosFailedState(errorMessage: e.toString()));
}
}
static String deviceName = '';
fetchDeviceInfo(SosInitialDeviseInfo event, Emitter<SosState> emit) async {
try {
emit(SosLoadingState());
var response = await DevicesAPI.getDeviceInfo(sosId);
deviceInfo = DeviceInfoModel.fromJson(response);
deviceName = deviceInfo.name;
emit(LoadingSosDeviceInfo(deviceInfo: deviceInfo));
} catch (e) {
emit(SosFailedState(errorMessage: e.toString()));
}
}
// Demo list of FAQ questions using the QuestionModel class
final List<QuestionModel> faqQuestions = [
QuestionModel(
id: 1,
question: 'How does an SOS emergency button work?',
answer:
'The SOS emergency button sends an alert to your contacts when pressed.',
),
QuestionModel(
id: 2,
question: 'How long will an SOS alarm persist?',
answer:
'The SOS alarm will persist until it is manually turned off or after a set time.',
),
QuestionModel(
id: 3,
question: 'What should I do if the SOS button is unresponsive?',
answer: 'Try restarting the device. If it persists, contact support.',
),
QuestionModel(
id: 4,
question: 'Can I use the SOS feature without a network connection?',
answer:
'No, a network connection is required to send the alert to your contacts.',
),
QuestionModel(
id: 5,
question: 'How often should I check the SOS battery?',
answer:
'Check the SOS battery at least once a month to ensure it is operational.',
),
];
Future<void> _onSosInitial(
SosInitialQuestion event, Emitter<SosState> emit) async {
emit(SosLoadingState());
// SosModel sosModel = await fetchSosData(sosId); // Define this function as needed
emit(FaqLoadedState(filteredFaqQuestions: faqQuestions));
}
List<DeviceModel> allDevices = [];
List<SubSpaceModel> roomsList = [];
void _fetchRoomsAndDevices(
FetchRoomsEvent event, Emitter<SosState> emit) async {
try {
emit(SosLoadingState());
roomsList = await SpacesAPI.getSubSpaceBySpaceId(
event.unit.community.uuid, event.unit.id);
emit(FetchRoomsState(devicesList: allDevices, roomsList: roomsList));
} catch (e) {
emit(const SosFailedState(errorMessage: 'Something went wrong'));
return;
}
}
String roomId = '';
SpaceModel unit =
SpaceModel(id: '', name: '', community: Community(uuid: '', name: ''));
String _selectedOption = '';
bool _hasSelectionChanged = false;
void _onOptionSelected(SelectOptionEvent event, Emitter<SosState> emit) {
emit(SosLoadingState());
_selectedOption = event.selectedOption;
_hasSelectionChanged = true;
emit(OptionSelectedState(
selectedOption: _selectedOption,
hasSelectionChanged: _hasSelectionChanged));
}
void _onSaveSelection(SaveSelectionEvent event, Emitter<SosState> emit) {
if (_hasSelectionChanged) {
_hasSelectionChanged = false;
add(AssignRoomEvent(roomId: roomId, unit: unit, context: event.context));
emit(SaveSelectionSuccessState());
// Navigator.pushNamedAndRemoveUntil(
// event.context, Routes.homeRoute, (route) => false);
var cubit = HomeCubit.getInstance();
cubit.updatePageIndex(1);
Navigator.pushReplacementNamed(event.context, Routes.homeRoute);
}
}
void _assignDevice(AssignRoomEvent event, Emitter<SosState> emit) async {
try {
// Map<String, bool> roomDevicesId = {};
emit(SosLoadingState());
await HomeManagementAPI.assignDeviceToRoom(
event.unit.community.uuid, event.unit.id, event.roomId, sosId);
final devicesList = await DevicesAPI.getDevicesByRoomId(
communityUuid: event.unit.community.uuid,
spaceUuid: event.unit.id,
roomId: event.roomId);
List<String> allDevicesIds = [];
allDevices.forEach((element) {
allDevicesIds.add(element.uuid!);
});
emit(SaveSelectionSuccessState());
// devicesList.forEach((e) {
// if (allDevicesIds.contains(e.uuid!)) {
// roomDevicesId[e.uuid!] = true;
// } else {
// roomDevicesId[e.uuid!] = false;
// }
// });
// emit(FetchDeviceByRoomIdState(
// roomDevices: devicesList,
// allDevices: allDevices,
// roomDevicesId: roomDevicesId,
// roomId: event.roomId));
} catch (e) {
emit(const SosFailedState(errorMessage: 'Something went wrong'));
return;
}
}
void _unassignDevice(UnassignRoomEvent event, Emitter<SosState> emit) async {
try {
Map<String, bool> roomDevicesId = {};
emit(SosLoadingState());
await HomeManagementAPI.unAssignDeviceToRoom(
event.unit.community.uuid, event.unit.id, event.roomId, sosId);
final devicesList = await DevicesAPI.getDevicesByRoomId(
communityUuid: event.unit.community.uuid,
spaceUuid: event.unit.id,
roomId: event.roomId);
List<String> allDevicesIds = [];
allDevices.forEach((element) {
allDevicesIds.add(element.uuid!);
});
devicesList.forEach((e) {
if (allDevicesIds.contains(e.uuid!)) {
roomDevicesId[e.uuid!] = true;
} else {
roomDevicesId[e.uuid!] = false;
}
});
// emit(FetchDeviceByRoomIdState(
// roomDevices: devicesList,
// allDevices: allDevices,
// roomDevicesId: roomDevicesId,
// roomId: event.roomId));
} catch (e) {
emit(const SosFailedState(errorMessage: 'Something went wrong'));
return;
}
}
Map<String, List<DeviceModel>> devicesByRoom = {};
// Your existing function
void _fetchDevicesByRoomId(
FetchDevicesByRoomIdEvent event, Emitter<SosState> emit) async {
try {
Map<String, bool> roomDevicesId = {};
emit(SosLoadingState());
// Fetch devices for the specified room
final devicesList = await DevicesAPI.getDevicesByRoomId(
communityUuid: event.unit.community.uuid,
spaceUuid: event.unit.id,
roomId: event.roomId);
// Fetch all devices accessible by the user
allDevices = await HomeManagementAPI.fetchDevicesByUserId();
// Map all accessible device IDs
List<String> allDevicesIds = [];
allDevices.forEach((element) {
allDevicesIds.add(element.uuid!);
});
// Mark devices as accessible/inaccessible for the room and add to devicesByRoom map
devicesList.forEach((e) {
roomDevicesId[e.uuid!] = allDevicesIds.contains(e.uuid!);
});
// Update the devicesByRoom map with the devices in the current room
devicesByRoom[event.roomId] = devicesList;
// emit(FetchDeviceByRoomIdState(
// roomDevices: devicesList,
// allDevices: allDevices,
// roomDevicesId: roomDevicesId,
// roomId: event.roomId));
} catch (e) {
emit(const SosFailedState(errorMessage: 'Something went wrong'));
return;
}
}
Future<void> saveName(SaveNameEvent event, Emitter<SosState> emit) async {
if (_validateInputs()) return;
try {
add(const ChangeNameEvent(value: false));
isSaving = true;
emit(SosLoadingState());
var response = await DevicesAPI.putDeviceName(
deviceId: sosId, deviceName: nameController.text);
add(SosInitialDeviseInfo());
CustomSnackBar.displaySnackBar('Save Successfully');
emit(SaveState());
} catch (e) {
emit(SosFailedState(errorMessage: e.toString()));
} finally {
isSaving = false;
}
}
bool _validateInputs() {
final nameError = fullNameValidator(nameController.text);
if (nameError != null) {
CustomSnackBar.displaySnackBar(nameError);
return true;
}
return false;
}
String? fullNameValidator(String? value) {
if (value == null) return 'name is required';
final withoutExtraSpaces = value.replaceAll(RegExp(r"\s+"), ' ').trim();
if (withoutExtraSpaces.length < 2 || withoutExtraSpaces.length > 30) {
return 'name must be between 2 and 30 characters long';
}
if (RegExp(r"/[^ a-zA-Z0-9-\']/").hasMatch(withoutExtraSpaces)) {
return 'Only alphanumeric characters, space, dash and single quote are allowed';
}
return null;
}
}

View File

@ -0,0 +1,196 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/devices/model/device_info_model.dart';
abstract class SosEvent extends Equatable {
const SosEvent();
@override
List<Object> get props => [];
}
class SosLoading extends SosEvent {}
class SosSwitch extends SosEvent {
final String switchD;
final String deviceId;
final String productId;
const SosSwitch(
{required this.switchD, this.deviceId = '', this.productId = ''});
@override
List<Object> get props => [switchD, deviceId, productId];
}
class SosUpdated extends SosEvent {}
class SosInitialDeviseInfo extends SosEvent {}
class SosInitial extends SosEvent {
const SosInitial();
}
class ReportLogsInitial extends SosEvent {
const ReportLogsInitial();
}
class SosChangeStatus extends SosEvent {}
class GetCounterEvent extends SosEvent {
final String deviceCode;
const GetCounterEvent({required this.deviceCode});
@override
List<Object> get props => [deviceCode];
}
class ToggleEnableAlarmEvent extends SosEvent {
final bool isLowBatteryEnabled;
const ToggleEnableAlarmEvent(this.isLowBatteryEnabled);
@override
List<Object> get props => [isLowBatteryEnabled];
}
class ToggleClosingReminderEvent extends SosEvent {
final bool isClosingReminderEnabled;
const ToggleClosingReminderEvent(this.isClosingReminderEnabled);
@override
List<Object> get props => [isClosingReminderEnabled];
}
class ToggleSosAlarmEvent extends SosEvent {
final bool isSosAlarmEnabled;
const ToggleSosAlarmEvent(this.isSosAlarmEnabled);
@override
List<Object> get props => [isSosAlarmEnabled];
}
class SetCounterValue extends SosEvent {
final Duration duration;
final String deviceCode;
const SetCounterValue({required this.duration, required this.deviceCode});
@override
List<Object> get props => [duration, deviceCode];
}
class StartTimer extends SosEvent {
final int duration;
const StartTimer(this.duration);
@override
List<Object> get props => [duration];
}
class TickTimer extends SosEvent {
final int remainingTime;
const TickTimer(this.remainingTime);
@override
List<Object> get props => [remainingTime];
}
class StopTimer extends SosEvent {}
class OnClose extends SosEvent {}
class SaveNameEvent extends SosEvent {}
class ChangeNameEvent extends SosEvent {
final bool? value;
const ChangeNameEvent({this.value});
}
class SearchFaqEvent extends SosEvent {
final String query;
const SearchFaqEvent(this.query);
}
class SosInitialQuestion extends SosEvent {
const SosInitialQuestion();
}
class FetchRoomsEvent extends SosEvent {
final SpaceModel unit;
const FetchRoomsEvent({required this.unit});
@override
List<Object> get props => [unit];
}
class SelectOptionEvent extends SosEvent {
dynamic selectedOption;
SelectOptionEvent({
this.selectedOption,
});
}
class SaveSelectionEvent extends SosEvent {
BuildContext context;
SaveSelectionEvent({
required this.context,
});
}
class AssignRoomEvent extends SosEvent {
final String roomId;
final SpaceModel unit;
final BuildContext context;
const AssignRoomEvent({
required this.roomId,
required this.unit,
required this.context,
});
@override
List<Object> get props => [
roomId,
unit,
context,
];
}
class UnassignRoomEvent extends SosEvent {
final String roomId;
final String deviceId;
final SpaceModel unit;
const UnassignRoomEvent(
{required this.roomId, required this.deviceId, required this.unit});
@override
List<Object> get props => [roomId, unit];
}
class FetchDevicesByRoomIdEvent extends SosEvent {
final SpaceModel unit; // Represents the unit (e.g., room or space) context
final String roomId; // Unique identifier for the room
// Constructor
FetchDevicesByRoomIdEvent({
required this.unit,
required this.roomId,
});
// Adding properties for Equatable to compare
@override
List<Object> get props => [unit, roomId];
}
class LoadingDeviceInfo extends SosEvent {
DeviceInfoModel deviceInfo;
LoadingDeviceInfo({required this.deviceInfo});
@override
List<Object> get props => [deviceInfo];
}

View File

@ -0,0 +1,94 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/device_info_model.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/question_model.dart';
import 'package:syncrow_app/features/devices/model/sos_model.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
class SosState extends Equatable {
const SosState();
@override
List<Object> get props => [];
}
class SosInitialState extends SosState {}
class SosLoadingState extends SosState {}
class SaveState extends SosState {}
class LoadingSosDeviceInfo extends SosState {
final DeviceInfoModel? deviceInfo;
const LoadingSosDeviceInfo({this.deviceInfo});
}
class SosFailedState extends SosState {
final String errorMessage;
const SosFailedState({required this.errorMessage});
@override
List<Object> get props => [errorMessage];
}
class UpdateState extends SosState {
final SosModel sensor;
const UpdateState({required this.sensor});
@override
List<Object> get props => [sensor];
}
class LoadingNewSate extends SosState {
final SosModel sosSensor;
const LoadingNewSate({required this.sosSensor});
@override
List<Object> get props => [sosSensor];
}
class NameEditingState extends SosState {
final bool editName;
NameEditingState({required this.editName});
}
class FaqLoadedState extends SosState {
final List<QuestionModel> filteredFaqQuestions;
FaqLoadedState({this.filteredFaqQuestions = const []});
}
class FaqSearchState extends SosState {
final List<QuestionModel> filteredFaqQuestions;
const FaqSearchState({this.filteredFaqQuestions = const []});
}
class FetchRoomsState extends SosState {
final List<SubSpaceModel> roomsList;
final List<DeviceModel> devicesList;
const FetchRoomsState({required this.devicesList, required this.roomsList});
@override
List<Object> get props => [devicesList];
}
class OptionSelectedState extends SosState {
final String selectedOption;
final bool hasSelectionChanged;
OptionSelectedState({
required this.selectedOption,
required this.hasSelectionChanged,
});
@override
List<Object> get props => [selectedOption, hasSelectionChanged];
}
class SaveSelectionSuccessState extends SosState {
@override
List<Object> get props => [];
}

View File

@ -1,6 +1,6 @@
import 'dart:convert';
class SceneSwitch {
class DeviceInfoModel {
final int activeTime;
final String category;
final String categoryName;
@ -27,7 +27,7 @@ class SceneSwitch {
final String macAddress;
final Subspace subspace;
SceneSwitch({
DeviceInfoModel({
required this.activeTime,
required this.category,
required this.categoryName,
@ -55,8 +55,8 @@ class SceneSwitch {
required this.subspace,
});
factory SceneSwitch.fromJson(Map<String, dynamic> json) {
return SceneSwitch(
factory DeviceInfoModel.fromJson(Map<String, dynamic> json) {
return DeviceInfoModel(
activeTime: json['activeTime'],
category: json['category'],
categoryName: json['categoryName'],

View File

@ -1,9 +1,9 @@
class FourSceneQuestionModel {
class QuestionModel {
final int id;
final String question;
final String answer;
FourSceneQuestionModel({
QuestionModel({
required this.id,
required this.question,
required this.answer,

View File

@ -0,0 +1,28 @@
import 'package:syncrow_app/features/devices/model/status_model.dart';
class SosModel {
String sosContactState;
int batteryPercentage;
SosModel({
required this.sosContactState,
required this.batteryPercentage,
});
factory SosModel.fromJson(List<StatusModel> jsonList) {
late String _sosContactState;
late int _batteryPercentage;
for (int i = 0; i < jsonList.length; i++) {
if (jsonList[i].code == 'sos') {
_sosContactState = jsonList[i].value ?? '';
} else if (jsonList[i].code == 'battery_percentage') {
_batteryPercentage = jsonList[i].value ?? 0;
}
}
return SosModel(
sosContactState: _sosContactState,
batteryPercentage: _batteryPercentage,
);
}
}

View File

@ -5,7 +5,7 @@ import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_blo
import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_event.dart';
import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_state.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/four_scene_question_model.dart';
import 'package:syncrow_app/features/devices/model/question_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/four_scene_switch/four_scene_setting/question_page_four_scene.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
@ -30,7 +30,7 @@ class FaqFourScenePage extends StatelessWidget {
builder: (context, state) {
final sensor = BlocProvider.of<FourSceneBloc>(context);
List<FourSceneQuestionModel> displayedQuestions = [];
List<QuestionModel> displayedQuestions = [];
if (state is FaqSearchState) {
displayedQuestions = state.filteredFaqQuestions;
} else if (state is FaqLoadedState) {

View File

@ -5,7 +5,7 @@ import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_blo
import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_event.dart';
import 'package:syncrow_app/features/devices/bloc/four_scene_bloc/four_scene_state.dart';
import 'package:syncrow_app/features/devices/model/four_scene_model.dart';
import 'package:syncrow_app/features/devices/model/four_scene_question_model.dart';
import 'package:syncrow_app/features/devices/model/question_model.dart';
import 'package:syncrow_app/features/shared_widgets/default_button.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
@ -15,7 +15,7 @@ import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class QuestionPageFourScene extends StatelessWidget {
final FourSceneQuestionModel? questionModel;
final QuestionModel? questionModel;
const QuestionPageFourScene({super.key, this.questionModel});

View File

@ -18,6 +18,7 @@ import 'package:syncrow_app/features/devices/view/widgets/lights/light_interface
import 'package:syncrow_app/features/devices/view/widgets/one_gang/one_gang_Interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/one_touch/one_touch_screen.dart';
import 'package:syncrow_app/features/devices/view/widgets/power_clamp/power_clamp_page.dart';
import 'package:syncrow_app/features/devices/view/widgets/sos/sos_screen.dart';
import 'package:syncrow_app/features/devices/view/widgets/three_touch/three_touch_interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_gang_Interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/two_touch/two_touch_Interface.dart';
@ -228,6 +229,13 @@ void showDeviceInterface(DeviceModel device, BuildContext context) {
pageBuilder: (context, animation1, animation2) =>
FourSceneScreen(device: device)));
case DeviceType.SOS:
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation1, animation2) =>
SosScreen(device: device)));
break;
default:
}

View File

@ -0,0 +1,112 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.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/utils/resource_manager/color_manager.dart';
class AlarmManagementPage extends StatelessWidget {
const AlarmManagementPage({super.key});
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: 'Alarm Settings',
child: BlocProvider(
create: (context) => SosBloc(sosId: ''),
child: BlocBuilder<SosBloc, SosState>(
builder: (context, state) {
final _bloc = BlocProvider.of<SosBloc>(context);
return state is LoadingNewSate
? const Center(
child: DefaultContainer(
width: 50,
height: 50,
child: CircularProgressIndicator()),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 10,
),
const BodyMedium(
text: 'The Alarm Management',
fontWeight: FontWeight.w700,
fontSize: 12,
fontColor: ColorsManager.grayColor,
),
const SizedBox(
height: 5,
),
DefaultContainer(
child: Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 50,
child: ListTile(
contentPadding: EdgeInsets.zero,
leading: const BodyMedium(
text: 'SOS Alarm',
fontWeight: FontWeight.w400,
fontSize: 15,
),
trailing: Transform.scale(
scale: .8,
child: CupertinoSwitch(
value: _bloc.enableAlarm,
onChanged: (value) {
context.read<SosBloc>().add(
ToggleEnableAlarmEvent(value));
},
applyTheme: true,
)),
),
),
const Divider(
color: ColorsManager.graysColor,
),
SizedBox(
height: 50,
child: ListTile(
contentPadding: EdgeInsets.zero,
leading: const BodyLarge(
text: 'Low Battery Reminder',
fontWeight: FontWeight.w400,
fontSize: 15,
),
trailing: Transform.scale(
scale: .8,
child: CupertinoSwitch(
value: _bloc.closingReminder,
onChanged: (value) {
context.read<SosBloc>().add(
ToggleClosingReminderEvent(
value));
},
applyTheme: true,
)),
),
),
],
),
),
),
],
);
},
),
));
}
}

View File

@ -0,0 +1,231 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:intl/intl.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart';
import 'package:syncrow_app/features/devices/model/device_report_model.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.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';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class SosRecordsScreen extends StatefulWidget {
final String sosId;
const SosRecordsScreen({super.key, required this.sosId});
@override
State<SosRecordsScreen> createState() => _SosRecordsScreenState();
}
class _SosRecordsScreenState extends State<SosRecordsScreen> {
int _selectedIndex = 0;
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: 'Records',
child: BlocProvider(
create: (context) =>
SosBloc(sosId: widget.sosId)..add(const ReportLogsInitial()),
child: BlocBuilder<SosBloc, SosState>(
builder: (context, state) {
final waterSensorBloc = BlocProvider.of<SosBloc>(context);
final Map<String, List<DeviceEvent>> groupedRecords = {};
if (state is LoadingNewSate) {
return const Center(
child: DefaultContainer(
width: 50, height: 50, child: CircularProgressIndicator()),
);
} else if (state is UpdateState) {
for (var record in waterSensorBloc.recordGroups.data!) {
final DateTime eventDateTime =
DateTime.fromMillisecondsSinceEpoch(record.eventTime!);
final String formattedDate =
DateFormat('EEEE, dd/MM/yyyy').format(eventDateTime);
if (groupedRecords.containsKey(formattedDate)) {
groupedRecords[formattedDate]!.add(record);
} else {
groupedRecords[formattedDate] = [record];
}
}
}
return DefaultTabController(
length: 2,
child: Column(
children: [
Container(
width: MediaQuery.of(context).size.width,
decoration: const ShapeDecoration(
color: ColorsManager.onPrimaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(30)),
),
),
child: TabBar(
onTap: (value) {
setState(() {
_selectedIndex = value;
});
},
indicatorColor: Colors.white,
dividerHeight: 0,
indicatorSize: TabBarIndicatorSize.tab,
indicator: const ShapeDecoration(
color: ColorsManager.slidingBlueColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20)),
),
),
tabs: [
Tab(
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10),
child: BodySmall(
text: 'Record',
style: context.bodySmall.copyWith(
color: _selectedIndex == 0
? Colors.white
: ColorsManager.blackColor,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
),
),
Tab(
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Text(
'Automation Log',
style: context.bodySmall.copyWith(
color: _selectedIndex == 1
? Colors.white
: ColorsManager.blackColor,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
),
),
],
),
),
Expanded(
child: TabBarView(
physics: const NeverScrollableScrollPhysics(),
children: [
groupedRecords.isEmpty
? Center(
child: SvgPicture.asset(
Assets.emptyLog,
fit: BoxFit.fill,
),
)
: ListView.builder(
itemCount: groupedRecords.length,
itemBuilder: (context, index) {
final String date =
groupedRecords.keys.elementAt(index);
final List<DeviceEvent> recordsForDate =
groupedRecords[date]!;
return Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
date,
style: const TextStyle(
color: ColorsManager.grayColor,
fontSize: 13,
fontWeight: FontWeight.w700,
),
),
),
DefaultContainer(
child: Column(
children: [
...recordsForDate
.asMap()
.entries
.map((entry) {
final int idx = entry.key;
final DeviceEvent record =
entry.value;
final DateTime eventDateTime =
DateTime
.fromMillisecondsSinceEpoch(
record.eventTime!);
final String formattedTime =
DateFormat('HH:mm:ss')
.format(eventDateTime);
return Column(
children: [
ListTile(
leading: SvgPicture.asset(
record.value == 'true'
? Assets
.leakDetectedIcon
: Assets
.leakNormalIcon,
height: 25,
width: 25,
),
title: const Text(
"SOS Alert Triggered",
style: const TextStyle(
fontWeight:
FontWeight.bold,
fontSize: 18,
),
),
subtitle:
Text(formattedTime),
),
if (idx !=
recordsForDate.length - 1)
const Divider(
color: ColorsManager
.graysColor,
),
],
);
}).toList(),
],
),
),
],
);
},
),
Container(
height: 10,
width: 20,
child: Center(
child: SvgPicture.asset(
Assets.emptyLog,
fit: BoxFit.fill,
),
),
),
],
),
),
],
),
);
},
),
),
);
}
}

View File

@ -0,0 +1,217 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/sos_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/sos/sos_alarm_management_page.dart';
import 'package:syncrow_app/features/devices/view/widgets/sos/sos_records_screen.dart';
import 'package:syncrow_app/features/devices/view/widgets/sos/sos_settings.dart';
import 'package:syncrow_app/features/shared_widgets/battery_bar.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
import 'package:syncrow_app/generated/assets.dart';
class SosScreen extends StatelessWidget {
final DeviceModel? device;
const SosScreen({super.key, this.device});
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: 'SOS',
actions: [
InkWell(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SosSettings(device: device!)),
);
},
child: SvgPicture.asset(Assets.assetsIconsSettings)),
const SizedBox(
width: 10,
)
],
child: BlocProvider(
create: (context) =>
SosBloc(sosId: device?.uuid ?? '')..add(const SosInitial()),
child: BlocBuilder<SosBloc, SosState>(
builder: (context, state) {
final sensor = BlocProvider.of<SosBloc>(context);
SosModel model =
SosModel(batteryPercentage: 0, sosContactState: 'normal');
if (state is LoadingNewSate) {
model = state.sosSensor;
} else if (state is UpdateState) {
model = state.sensor;
}
return state is SosLoadingState
? const Center(
child: DefaultContainer(
width: 50,
height: 50,
child: CircularProgressIndicator()),
)
: RefreshIndicator(
onRefresh: () async {
sensor.add(const SosInitial());
},
child: ListView(
children: [
SizedBox(
height: MediaQuery.sizeOf(context).height * 0.8,
child: Column(
children: [
BatteryBar(
batteryPercentage: model.batteryPercentage,
),
Expanded(
flex: 4,
child: InkWell(
overlayColor: WidgetStateProperty.all(
Colors.transparent),
onTap: () {},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(890),
boxShadow: [
BoxShadow(
color:
Colors.white.withOpacity(0.1),
blurRadius: 24,
offset: const Offset(-5, -5),
blurStyle: BlurStyle.outer,
),
BoxShadow(
color: Colors.black
.withOpacity(0.11),
blurRadius: 25,
offset: const Offset(5, 5),
blurStyle: BlurStyle.outer,
),
BoxShadow(
color: Colors.black
.withOpacity(0.13),
blurRadius: 30,
offset: const Offset(5, 5),
blurStyle: BlurStyle.inner,
),
],
),
child: SvgPicture.asset(
model.sosContactState == 'normal'
? Assets.redSos
: Assets.greenSos,
fit: BoxFit.fill,
),
),
],
),
),
),
Flexible(
child: Row(
children: [
Expanded(
child: DefaultContainer(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
SosRecordsScreen(
sosId: device!.uuid!)),
);
},
child: Column(
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.center,
children: [
ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 46, maxWidth: 50),
child: SvgPicture.asset(
Assets.doorRecordsIcon),
),
const SizedBox(
height: 15,
),
const Flexible(
child: FittedBox(
child: BodySmall(
text: 'Records',
textAlign: TextAlign.center,
),
),
),
],
),
),
),
const SizedBox(
width: 10,
),
Expanded(
child: DefaultContainer(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
const AlarmManagementPage()),
);
},
child: Column(
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.center,
children: [
ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 46, maxWidth: 50),
child: SvgPicture.asset(Assets
.doorNotificationSetting),
),
const SizedBox(
height: 15,
),
const Flexible(
child: FittedBox(
child: BodySmall(
text: 'Alarm Settings',
textAlign: TextAlign.center,
),
),
),
],
),
),
),
],
),
)
],
),
),
],
),
);
},
),
),
);
}
}

View File

@ -0,0 +1,227 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class DisconnectDeviceDialog extends StatelessWidget {
final Function()? cancelTab;
final Function()? confirmTab;
const DisconnectDeviceDialog({
super.key,
required this.cancelTab,
required this.confirmTab,
});
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const SizedBox(
height: 10,
),
const BodyLarge(
text: 'Disconnect Device',
fontWeight: FontWeight.w700,
fontColor: ColorsManager.red,
fontSize: 16,
),
const Padding(
padding: EdgeInsets.only(left: 15, right: 15),
child: Divider(
color: ColorsManager.textGray,
),
),
const Padding(
padding: EdgeInsets.only(left: 15, right: 20, top: 15, bottom: 20),
child: Column(
children: [
Center(
child: Text(
'This will disconnect your device from this Application')),
],
),
),
Row(
children: [
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
right: BorderSide(
color: ColorsManager.textGray,
width: 0.5,
),
top: BorderSide(
color: ColorsManager.textGray,
width: 1.0,
),
)),
child: SizedBox(
child: InkWell(
onTap: cancelTab,
child: const Padding(
padding: EdgeInsets.all(15),
child: Center(
child: Text(
'Cancel',
style: TextStyle(
color: ColorsManager.textGray,
fontSize: 14,
fontWeight: FontWeight.w400),
),
),
),
),
),
),
),
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
left: BorderSide(
color: ColorsManager.textGray,
width: 0.5,
),
top: BorderSide(
color: ColorsManager.textGray,
width: 1.0,
),
)),
child: InkWell(
onTap: confirmTab,
child: const Padding(
padding: EdgeInsets.all(15),
child: Center(
child: Text(
'Disconnect',
style: TextStyle(
color: ColorsManager.red,
fontSize: 14,
fontWeight: FontWeight.w400),
),
),
)),
))
],
)
],
),
);
}
}
class DisconnectWipeData extends StatelessWidget {
final Function()? cancelTab;
final Function()? confirmTab;
const DisconnectWipeData({
super.key,
required this.cancelTab,
required this.confirmTab,
});
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const SizedBox(
height: 10,
),
const BodyLarge(
text: 'Disconnect and Wipe Data',
fontWeight: FontWeight.w700,
fontColor: ColorsManager.red,
fontSize: 16,
),
const Padding(
padding: EdgeInsets.only(left: 15, right: 15),
child: Divider(
color: ColorsManager.textGray,
),
),
const Padding(
padding: EdgeInsets.only(left: 15, right: 20, top: 15, bottom: 20),
child: Column(
children: [
Center(
child: Text(
'This will disconnect your device from this Application and wipe all the data')),
],
),
),
Row(
children: [
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
right: BorderSide(
color: ColorsManager.textGray,
width: 0.5,
),
top: BorderSide(
color: ColorsManager.textGray,
width: 1.0,
),
)),
child: SizedBox(
child: InkWell(
onTap: cancelTab,
child: const Padding(
padding: EdgeInsets.all(15),
child: Center(
child: Text(
'Cancel',
style: TextStyle(
color: ColorsManager.textGray,
fontSize: 14,
fontWeight: FontWeight.w400),
),
),
),
),
),
),
),
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
left: BorderSide(
color: ColorsManager.textGray,
width: 0.5,
),
top: BorderSide(
color: ColorsManager.textGray,
width: 1.0,
),
)),
child: InkWell(
onTap: confirmTab,
child: const Padding(
padding: EdgeInsets.all(15),
child: Center(
child: Text(
'Disconnect',
style: TextStyle(
color: ColorsManager.red,
fontSize: 14,
fontWeight: FontWeight.w400),
),
),
)),
))
],
)
],
),
);
}
}

View File

@ -0,0 +1,151 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/question_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/sos/sos_setting/question_page.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class FaqSosPage extends StatelessWidget {
final DeviceModel? device;
const FaqSosPage({super.key, this.device});
@override
Widget build(BuildContext context) {
TextEditingController _searchController = TextEditingController();
return DefaultScaffold(
title: 'FAQ',
child: BlocProvider(
create: (context) =>
SosBloc(sosId: device?.uuid ?? '')..add(const SosInitialQuestion()),
child: BlocBuilder<SosBloc, SosState>(
builder: (context, state) {
final sensor = BlocProvider.of<SosBloc>(context);
List<QuestionModel> displayedQuestions = [];
if (state is FaqSearchState) {
displayedQuestions = state.filteredFaqQuestions;
} else if (state is FaqLoadedState) {
displayedQuestions = state.filteredFaqQuestions;
}
return state is SosLoadingState
? const Center(
child: DefaultContainer(
width: 50,
height: 50,
child: CircularProgressIndicator()),
)
: RefreshIndicator(
onRefresh: () async {
sensor.add(const SosInitial());
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
DefaultContainer(
padding: const EdgeInsets.all(5),
child: TextFormField(
controller: _searchController,
onChanged: (value) {
sensor.add(SearchFaqEvent(value));
},
decoration: InputDecoration(
hintText: 'Enter your questions',
hintStyle: const TextStyle(
color: ColorsManager.textGray,
fontSize: 16,
fontWeight: FontWeight.w400),
suffixIcon: Container(
padding: const EdgeInsets.all(5.0),
margin: const EdgeInsets.all(10.0),
child: SvgPicture.asset(
Assets.searchIcon,
fit: BoxFit.contain,
),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
),
),
),
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.04,
),
BodyMedium(
text: _searchController.text.isEmpty
? 'Device Related FAQs'
: '${displayedQuestions.length} Help Topics',
fontWeight: FontWeight.w700,
fontSize: 12,
fontColor: ColorsManager.grayColor,
),
Expanded(
child: DefaultContainer(
child: ListView.builder(
shrinkWrap: true,
itemCount: displayedQuestions.length,
itemBuilder: (context, index) {
final faq = displayedQuestions[index];
return InkWell(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => QuestionPage(
questionModel: faq,
)),
);
},
child: SizedBox(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
child: BodyMedium(
fontSize: 14,
fontWeight: FontWeight.w400,
text: faq.question,
),
),
const Icon(
Icons.keyboard_arrow_right,
color: ColorsManager.textGray,
),
],
),
),
const Divider(
color: ColorsManager.dividerColor,
),
],
),
),
);
},
)),
),
],
),
);
},
),
),
);
}
}

View File

@ -0,0 +1,219 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart';
import 'package:syncrow_app/features/devices/model/sos_model.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/navigation/routing_constants.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class LocationSosPage extends StatelessWidget {
final SpaceModel? space;
final String? deviceId;
const LocationSosPage({
super.key,
this.space,
this.deviceId,
});
@override
Widget build(BuildContext context) {
String roomIdSelected = '';
return Scaffold(
body: BlocProvider(
create: (context) => SosBloc(sosId: deviceId ?? '')
..add(const SosInitial())
..add(SosInitialDeviseInfo())
..add(FetchRoomsEvent(unit: space!)),
child: BlocBuilder<SosBloc, SosState>(
builder: (context, state) {
final _bloc = BlocProvider.of<SosBloc>(context);
SosModel model =
SosModel(batteryPercentage: 0, sosContactState: '');
if (state is SaveSelectionSuccessState) {
new Future.delayed(const Duration(microseconds: 500), () {
_bloc.add(SosInitial());
Navigator.of(context).pop(true);
});
}
return state is SosLoadingState
? const Center(
child: DefaultContainer(
width: 50,
height: 50,
child: CircularProgressIndicator(),
),
)
: DefaultScaffold(
actions: [
BlocBuilder<SosBloc, SosState>(
builder: (context, state) {
final bool canSave = state is OptionSelectedState &&
state.hasSelectionChanged;
return InkWell(
onTap: canSave
? () {
context.read<SosBloc>().add(AssignRoomEvent(
context: context,
roomId: roomIdSelected,
unit: space!));
}
: null,
child: BodyMedium(
text: 'Save',
fontWeight: FontWeight.w700,
fontSize: 16,
fontColor: canSave
? ColorsManager.slidingBlueColor
: ColorsManager.primaryTextColor,
),
);
},
),
const SizedBox(width: 20),
],
child: RefreshIndicator(
onRefresh: () async {
// sensor.add(const SosInitial());
},
child: ListView(
shrinkWrap: true,
padding: const EdgeInsets.symmetric(vertical: 20),
children: [
const BodyMedium(
text: 'Smart Device Location',
fontWeight: FontWeight.w700,
fontSize: 12,
fontColor: ColorsManager.grayColor,
),
const SizedBox(height: 5),
DefaultContainer(
padding: const EdgeInsets.all(20),
child: ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: _bloc.roomsList.length,
itemBuilder: (context, index) {
final fromRoom = _bloc.roomsList[index];
final isSelected = (state
is OptionSelectedState &&
state.selectedOption == fromRoom.id) ||
(state is! OptionSelectedState &&
fromRoom.id ==
_bloc.deviceInfo.subspace.uuid);
return Column(
children: [
_buildCheckboxOption(
label: fromRoom.name!,
isSelected: isSelected,
onTap: (label) {
context.read<SosBloc>().add(
SelectOptionEvent(
selectedOption: fromRoom.id!,
),
);
roomIdSelected = fromRoom.id!;
},
),
if (index < _bloc.roomsList.length - 1) ...[
const SizedBox(height: 10),
const Divider(
color: ColorsManager.dividerColor,
),
const SizedBox(height: 10),
],
],
);
},
),
),
],
),
),
);
},
),
),
);
}
}
class CircularCheckbox extends StatefulWidget {
final bool value;
final ValueChanged<bool?> onChanged;
CircularCheckbox({required this.value, required this.onChanged});
@override
_CircularCheckboxState createState() => _CircularCheckboxState();
}
class _CircularCheckboxState extends State<CircularCheckbox> {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
widget.onChanged(!widget.value);
},
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: widget.value
? ColorsManager.primaryColorWithOpacity.withOpacity(0.01)
: Colors.grey,
width: 2.0,
),
color: widget.value
? ColorsManager.primaryColorWithOpacity
: Colors.transparent,
),
width: 24.0,
height: 24.0,
child: widget.value
? const Icon(
Icons.check,
color: Colors.white,
size: 16.0,
)
: null,
),
);
}
}
Widget _buildCheckboxOption({
required String label,
required bool isSelected,
required Function(String) onTap,
}) {
return Padding(
padding: const EdgeInsets.only(bottom: 10, top: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BodyMedium(
text: label,
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w400),
),
CircularCheckbox(
value: isSelected,
onChanged: (bool? value) {
if (value == true) {
onTap(label);
}
},
),
],
),
);
}

View File

@ -0,0 +1,143 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart';
import 'package:syncrow_app/features/devices/model/question_model.dart';
import 'package:syncrow_app/features/devices/model/sos_model.dart';
import 'package:syncrow_app/features/shared_widgets/default_button.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.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/generated/assets.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class QuestionPage extends StatelessWidget {
final QuestionModel? questionModel;
const QuestionPage({super.key, this.questionModel});
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: 'FAQ',
child: BlocProvider(
create: (context) => SosBloc(sosId: '')..add(const SosInitial()),
child: BlocBuilder<SosBloc, SosState>(
builder: (context, state) {
final sensor = BlocProvider.of<SosBloc>(context);
SosModel model =
SosModel(batteryPercentage: 0, sosContactState: 'normal');
if (state is LoadingNewSate) {
model = state.sosSensor;
} else if (state is UpdateState) {
model = state.sensor;
}
return state is SosLoadingState
? const Center(
child: DefaultContainer(
width: 50,
height: 50,
child: CircularProgressIndicator()),
)
: RefreshIndicator(
onRefresh: () async {
sensor.add(const SosInitial());
},
child: Column(
children: [
DefaultContainer(
padding: EdgeInsets.all(15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
BodyLarge(
text: questionModel!.question,
fontSize: 22,
fontWeight: FontWeight.w400,
fontColor: ColorsManager.blackColor,
),
SizedBox(
height: 15,
),
BodyMedium(
text: questionModel!.answer,
fontSize: 14,
fontWeight: FontWeight.w400,
fontColor: ColorsManager.secondaryTextColor,
),
],
),
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.15,
),
Center(
child: SizedBox(
width: 180,
child: DefaultButton(
backgroundColor: ColorsManager.grayButtonColors,
borderRadius: 50,
onPressed: () {},
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Assets.thumbUp,
fit: BoxFit.fill,
),
SizedBox(
width: 10,
),
BodyMedium(
text: 'Helpful',
fontSize: 12,
fontWeight: FontWeight.w400,
fontColor: ColorsManager.blackColor,
),
],
)),
),
),
SizedBox(
height: 15,
),
Center(
child: SizedBox(
width: 180,
child: DefaultButton(
backgroundColor: ColorsManager.grayButtonColors,
borderRadius: 50,
onPressed: () {},
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Assets.thumbDown,
fit: BoxFit.fill,
),
SizedBox(
width: 10,
),
BodyMedium(
text: 'Not Helpful',
fontSize: 12,
fontWeight: FontWeight.w400,
fontColor: ColorsManager.blackColor,
),
],
)),
),
),
],
));
},
),
),
);
}
}

View File

@ -0,0 +1,102 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/sos_model.dart';
import 'package:syncrow_app/features/shared_widgets/default_button.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.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/utils/resource_manager/color_manager.dart';
class ShareSosPage extends StatelessWidget {
final DeviceModel? device;
const ShareSosPage({super.key, this.device});
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: 'Share Device',
child: BlocProvider(
create: (context) =>
SosBloc(sosId: device?.uuid ?? '')..add(const SosInitial()),
child: BlocBuilder<SosBloc, SosState>(
builder: (context, state) {
final sensor = BlocProvider.of<SosBloc>(context);
SosModel model =
SosModel(batteryPercentage: 0, sosContactState: 'normal');
if (state is LoadingNewSate) {
model = state.sosSensor;
} else if (state is UpdateState) {
model = state.sensor;
}
return state is SosLoadingState
? const Center(
child: DefaultContainer(
width: 50,
height: 50,
child: CircularProgressIndicator()),
)
: RefreshIndicator(
onRefresh: () async {
sensor.add(const SosInitial());
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const BodyLarge(
text: 'Sharing Method Not Supported',
fontSize: 15,
fontWeight: FontWeight.w400,
fontColor: ColorsManager.blackColor,
),
const BodyMedium(
text:
'Currently, you cannot use the specified method to share Bluetooth mesh devices Zigbee devices, infrared devices, Bluetooth Beacon Devices, and certain Bluetooth LE devices with other users.',
fontSize: 14,
fontWeight: FontWeight.w400,
fontColor: ColorsManager.secondaryTextColor,
),
const SizedBox(
height: 10,
),
const BodyLarge(
text: 'Recommended Sharing Method',
fontSize: 15,
fontWeight: FontWeight.w400,
fontColor: ColorsManager.blackColor,
),
const BodyMedium(
text:
'If the recipient is a home member or a reliable user, tap Me > Home Management > Add Member and add the recipient to your home. Then, devices in the home can be shared with the recipient in bulk.',
fontSize: 14,
fontWeight: FontWeight.w400,
fontColor: ColorsManager.secondaryTextColor,
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.15,
),
Center(
child: SizedBox(
width: 250,
child: DefaultButton(
backgroundColor: ColorsManager.blueColor1,
borderRadius: 50,
onPressed: () {
},
child: Text('Add Home Member')),
),
)
],
));
},
),
),
);
}
}

View File

@ -0,0 +1,145 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/sos_model.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.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/utils/resource_manager/color_manager.dart';
class SosInfoPage extends StatelessWidget {
final DeviceModel? device;
const SosInfoPage({super.key, this.device});
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: 'Device Information',
child: BlocProvider(
create: (context) => SosBloc(sosId: device?.uuid ?? '')
..add(const SosInitial())
..add(SosInitialDeviseInfo()),
child: BlocBuilder<SosBloc, SosState>(
builder: (context, state) {
final _bloc = BlocProvider.of<SosBloc>(context);
SosModel model =
SosModel(batteryPercentage: 0, sosContactState: '');
if (state is LoadingNewSate) {
model = state.sosSensor;
} else if (state is UpdateState) {
model = state.sensor;
}
return state is SosLoadingState
? const Center(
child: DefaultContainer(
width: 50,
height: 50,
child: CircularProgressIndicator()),
)
: RefreshIndicator(
onRefresh: () async {
_bloc.add(const SosInitial());
},
child: DefaultContainer(
child: Padding(
padding: const EdgeInsets.only(left: 5, right: 5),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const BodyLarge(
text: 'Virtual ID',
fontSize: 15,
fontWeight: FontWeight.w400,
fontColor: ColorsManager.blackColor,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BodySmall(
text: _bloc.deviceInfo.productUuid,
fontColor: ColorsManager.primaryTextColor,
),
InkWell(
onTap: () {
Clipboard.setData(
ClipboardData(
text: _bloc.deviceInfo.productUuid,
),
);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Copied to Clipboard"),
),
);
},
child: const Row(
children: [
Icon(
Icons.copy,
color: ColorsManager.blueColor,
),
BodyMedium(
text: 'Copy',
fontColor: ColorsManager.blueColor,
),
],
),
)
],
),
const Divider(
color: ColorsManager.dividerColor,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const BodyLarge(
text: 'MAC',
fontSize: 15,
fontWeight: FontWeight.w400,
fontColor: ColorsManager.blackColor,
),
BodySmall(
text: _bloc.deviceInfo.macAddress,
fontColor: ColorsManager.primaryTextColor,
),
],
),
const Divider(
color: ColorsManager.dividerColor,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const BodyLarge(
text: 'Time Zone',
fontSize: 15,
fontWeight: FontWeight.w400,
fontColor: ColorsManager.blackColor,
),
BodySmall(
text: _bloc.deviceInfo.timeZone,
fontColor: ColorsManager.primaryTextColor,
),
],
),
]),
)));
},
),
),
);
}
}

View File

@ -0,0 +1,191 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/sos_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/sos/sos_setting/location_setting.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class SosProfilePage extends StatelessWidget {
final DeviceModel? device;
const SosProfilePage({super.key, this.device});
@override
Widget build(BuildContext context) {
var spaces = HomeCubit.getInstance().spaces;
return DefaultScaffold(
title: 'Device Settings',
leading: IconButton(
onPressed: () {
Navigator.of(context).pop(true);
},
icon: const Icon(Icons.arrow_back_ios)),
child: BlocProvider(
create: (context) => SosBloc(sosId: device?.uuid ?? '')
..add(const SosInitial())
..add(SosInitialDeviseInfo()),
child: BlocBuilder<SosBloc, SosState>(
builder: (context, state) {
final _bloc = BlocProvider.of<SosBloc>(context);
SosModel model =
SosModel(batteryPercentage: 0, sosContactState: '');
if (state is LoadingNewSate) {
model = state.sosSensor;
} else if (state is UpdateState) {
model = state.sensor;
}
return state is SosLoadingState
? const Center(
child: DefaultContainer(
width: 50,
height: 50,
child: CircularProgressIndicator()),
)
: RefreshIndicator(
onRefresh: () async {
_bloc.add(const SosInitial());
},
child: ListView(
children: [
CircleAvatar(
radius: 60,
backgroundColor: Colors.white,
child: CircleAvatar(
radius: 55,
backgroundColor: ColorsManager.graysColor,
child: ClipOval(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
height: 10,
),
Center(
child: SvgPicture.asset(
Assets.fourSceneIcon,
fit: BoxFit.contain,
),
),
],
),
),
),
),
const SizedBox(
height: 10,
),
SizedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IntrinsicWidth(
child: ConstrainedBox(
constraints:
const BoxConstraints(maxWidth: 200),
child: TextFormField(
maxLength: 30,
style: const TextStyle(
color: Colors.black,
),
textAlign: TextAlign.center,
focusNode: _bloc.focusNode,
controller: _bloc.nameController,
enabled: _bloc.editName,
onEditingComplete: () {
_bloc.add(SaveNameEvent());
},
decoration: const InputDecoration(
hintText: "Your Name",
border: InputBorder.none,
fillColor: Colors.white10,
counterText: '',
),
),
),
),
const SizedBox(width: 5),
InkWell(
onTap: () {
_bloc.add(const ChangeNameEvent(value: true));
},
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Icon(
Icons.edit_outlined,
size: 20,
color: ColorsManager.textPrimaryColor,
),
),
),
],
),
),
const SizedBox(height: 20),
const BodyMedium(
text: 'Smart Device Information',
fontWeight: FontWeight.w700,
fontSize: 12,
fontColor: ColorsManager.grayColor,
),
DefaultContainer(
padding: const EdgeInsets.all(20),
child: InkWell(
onTap: () async {
bool val = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => LocationSosPage(
space: spaces!.first,
deviceId: device?.uuid ?? '',
)),
);
if (val == true) {
_bloc.add(SosInitialDeviseInfo());
}
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const SizedBox(
child: Text('Location'),
),
Row(
children: [
SizedBox(
child: BodyMedium(
text: _bloc
.deviceInfo.subspace.subspaceName,
fontColor: ColorsManager.textGray,
),
),
const Icon(
Icons.arrow_forward_ios,
size: 15,
color: ColorsManager.textGray,
),
],
)
],
),
),
)
],
),
);
},
),
),
);
}
}

View File

@ -0,0 +1,118 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class SosUpdateNote extends StatelessWidget {
final Function()? cancelTab;
final Function()? confirmTab;
const SosUpdateNote({
super.key,
required this.cancelTab,
required this.confirmTab,
});
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const SizedBox(
height: 10,
),
BodyLarge(
text: 'Update Note',
fontWeight: FontWeight.w700,
fontColor: ColorsManager.switchButton.withOpacity(0.6),
fontSize: 16,
),
const Padding(
padding: EdgeInsets.only(left: 15, right: 15),
child: Divider(
color: ColorsManager.textGray,
),
),
const Padding(
padding: EdgeInsets.only(left: 15, right: 20, top: 15, bottom: 20),
child: Column(
children: [
Center(
child: Text(
'This update may take a long time. Make sure that the device is fully charged. The device will be unavailable during the update.',
textAlign: TextAlign.center,
)),
],
),
),
Row(
children: [
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
right: BorderSide(
color: ColorsManager.textGray,
width: 0.5,
),
top: BorderSide(
color: ColorsManager.textGray,
width: 1.0,
),
)),
child: SizedBox(
child: InkWell(
onTap: cancelTab,
child: const Padding(
padding: const EdgeInsets.only(top: 15, bottom: 15),
child: Center(
child: Text(
'Cancel',
style: TextStyle(
color: ColorsManager.textGray,
fontSize: 14,
fontWeight: FontWeight.w400),
),
),
),
),
),
),
),
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
left: BorderSide(
color: ColorsManager.textGray,
width: 0.5,
),
top: BorderSide(
color: ColorsManager.textGray,
width: 1.0,
),
)),
child: InkWell(
onTap: confirmTab,
child: Padding(
padding: const EdgeInsets.only(top: 15, bottom: 15),
child: Center(
child: Text(
'Start Update',
style: TextStyle(
color:
ColorsManager.switchButton.withOpacity(0.6),
fontSize: 14,
fontWeight: FontWeight.w400),
),
),
)),
))
],
)
],
),
);
}
}

View File

@ -0,0 +1,344 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:percent_indicator/linear_percent_indicator.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart';
import 'package:syncrow_app/features/devices/view/widgets/sos/sos_setting/sos_update_note.dart';
import 'package:syncrow_app/features/shared_widgets/default_button.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class SosUpdatePage extends StatelessWidget {
const SosUpdatePage({super.key});
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: 'Device Update',
child: BlocProvider(
create: (context) => SosBloc(sosId: '')..add(const SosInitial()),
child: BlocBuilder<SosBloc, SosState>(
builder: (context, state) {
return state is SosLoadingState
? const Center(
child: DefaultContainer(
width: 50,
height: 50,
child: CircularProgressIndicator()),
)
: RefreshIndicator(
onRefresh: () async {},
child: Column(
children: [
// SizedBox(
// height: MediaQuery.of(context).size.height * 0.15,
// ),
DefaultContainer(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 50,
child: ListTile(
contentPadding: EdgeInsets.zero,
leading: SizedBox(
width: 200,
child: Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: const BoxDecoration(
color:
ColorsManager.primaryColor,
borderRadius: BorderRadius.all(
Radius.circular(50))),
child: SvgPicture.asset(
Assets.checkUpdateIcon,
fit: BoxFit.fill,
height: 25,
),
),
const SizedBox(
width: 10,
),
InkWell(
onTap: () {},
child: const BodyMedium(
text: 'Automatic Update',
fontWeight: FontWeight.normal,
),
),
],
),
),
trailing: Container(
width: 100,
child: Row(
mainAxisAlignment:
MainAxisAlignment.end,
children: [
const BodyMedium(
text: 'Off',
fontColor: ColorsManager.textGray,
),
Transform.scale(
scale: .8,
child: CupertinoSwitch(
value: true,
onChanged: (value) {},
applyTheme: true,
),
),
],
),
)),
),
],
),
),
const SizedBox(
height: 10,
),
UpdateSosContainerWithProgressBar(
sosDescription:
'Connectivity Issue Resolved Fixed a bug that caused the SOS button to disconnect from the app intermittently.',
sosVersion: 'SOS v2.0.5',
),
// const UpdatedContainer(
// sosVersion: 'SOS v1.0.13',
// sosDescription: 'SOS is up to date',
// ),
// const NewUpdateContainer(
// sosVersion: 'SOS v2.0.5',
// sosDescription:
// 'Connectivity Issue Resolved Fixed a bug that caused the SOS button to disconnect from the app intermittently.',
// ),
const SizedBox(
height: 15,
),
],
));
},
),
),
);
}
}
class UpdatedContainer extends StatelessWidget {
final String? sosVersion;
final String? sosDescription;
const UpdatedContainer({
this.sosVersion,
this.sosDescription,
super.key,
});
@override
Widget build(BuildContext context) {
return DefaultContainer(
height: MediaQuery.of(context).size.height * 0.35,
child: Padding(
padding: const EdgeInsets.all(25),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SvgPicture.asset(
Assets.emptyUpdateIcon,
fit: BoxFit.fill,
),
BodyMedium(
text: sosVersion!,
fontColor: ColorsManager.primaryTextColor,
),
BodyMedium(
text: sosDescription!,
fontColor: ColorsManager.blackColor,
),
],
),
],
),
),
);
}
}
class NewUpdateContainer extends StatelessWidget {
final String? sosVersion;
final String? sosDescription;
const NewUpdateContainer({
this.sosVersion,
this.sosDescription,
super.key,
});
@override
Widget build(BuildContext context) {
return DefaultContainer(
height: MediaQuery.of(context).size.height * 0.50,
child: Padding(
padding: const EdgeInsets.all(25),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SvgPicture.asset(
Assets.emptyUpdateIcon,
fit: BoxFit.fill,
),
const BodyMedium(
text: 'New Update Available Now!',
fontColor: ColorsManager.blueColor,
),
BodyMedium(
text: sosVersion!,
fontColor: ColorsManager.primaryTextColor,
),
Container(
width: MediaQuery.of(context).size.width * 0.7,
child: BodyMedium(
text: sosDescription!,
fontColor: ColorsManager.textPrimaryColor,
textAlign: TextAlign.center,
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.6,
child: DefaultButton(
borderRadius: 25,
backgroundColor: ColorsManager.blueColor1,
height: 150,
onPressed: () {
showDialog(
context: context,
builder: (context) {
return SosUpdateNote(
cancelTab: () {
Navigator.of(context).pop();
},
confirmTab: () {
Navigator.of(context).pop();
},
);
},
);
},
child: const BodyMedium(
text: 'Update Now',
fontColor: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w700,
textAlign: TextAlign.center,
),
),
)
],
),
],
),
),
);
}
}
class UpdateSosContainerWithProgressBar extends StatelessWidget {
final String? sosVersion;
final String? sosDescription;
const UpdateSosContainerWithProgressBar({
this.sosVersion,
this.sosDescription,
super.key,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
DefaultContainer(
height: MediaQuery.of(context).size.height * 0.50,
child: Padding(
padding: const EdgeInsets.all(25),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SvgPicture.asset(
Assets.emptyUpdateIcon,
fit: BoxFit.fill,
),
const BodyMedium(
text: 'New Update Available Now!',
fontColor: ColorsManager.blueColor,
),
BodyMedium(
text: sosVersion!,
fontColor: ColorsManager.primaryTextColor,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.7,
child: BodyMedium(
text: sosDescription!,
fontColor: ColorsManager.textPrimaryColor,
textAlign: TextAlign.center,
),
),
LinearPercentIndicator(
barRadius: Radius.circular(10),
width: 170.0,
animation: true,
animationDuration: 1000,
lineHeight: 5.0,
percent: 0.2,
linearStrokeCap: LinearStrokeCap.butt,
progressColor: ColorsManager.blueColor1,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.7,
child: const BodyMedium(
text: 'Downloading Update please be patient',
fontColor: ColorsManager.textPrimaryColor,
textAlign: TextAlign.center,
),
),
],
),
],
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.7,
child: const BodyMedium(
text:
'Please keep the power of the device connected during the upgrade process.',
fontColor: ColorsManager.textPrimaryColor,
textAlign: TextAlign.center,
),
),
],
);
}
}

View File

@ -0,0 +1,118 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class UpdateInfoDialog extends StatelessWidget {
final Function()? cancelTab;
final Function()? confirmTab;
const UpdateInfoDialog({
super.key,
required this.cancelTab,
required this.confirmTab,
});
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const SizedBox(
height: 10,
),
BodyLarge(
text: 'Update Available',
fontWeight: FontWeight.w700,
fontColor: ColorsManager.switchButton.withOpacity(0.6),
fontSize: 16,
),
const Padding(
padding: EdgeInsets.only(left: 15, right: 15),
child: Divider(
color: ColorsManager.textGray,
),
),
const Padding(
padding: EdgeInsets.only(left: 15, right: 20, top: 15, bottom: 20),
child: Column(
children: [
Center(
child: Text(
'An update is available for your device. Version 2.1.0 includes new features and important fixes to enhance performance and security.',
textAlign: TextAlign.center,
)),
],
),
),
Row(
children: [
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
right: BorderSide(
color: ColorsManager.textGray,
width: 0.5,
),
top: BorderSide(
color: ColorsManager.textGray,
width: 1.0,
),
)),
child: SizedBox(
child: InkWell(
onTap: cancelTab,
child: const Padding(
padding: const EdgeInsets.only(top: 15, bottom: 15),
child: Center(
child: Text(
'Remind me later',
style: TextStyle(
color: ColorsManager.textGray,
fontSize: 14,
fontWeight: FontWeight.w400),
),
),
),
),
),
),
),
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
left: BorderSide(
color: ColorsManager.textGray,
width: 0.5,
),
top: BorderSide(
color: ColorsManager.textGray,
width: 1.0,
),
)),
child: InkWell(
onTap: confirmTab,
child: Padding(
padding: const EdgeInsets.only(top: 15, bottom: 15),
child: Center(
child: Text(
'Update Now',
style: TextStyle(
color:
ColorsManager.switchButton.withOpacity(0.6),
fontSize: 14,
fontWeight: FontWeight.w400),
),
),
)),
))
],
)
],
),
);
}
}

View File

@ -0,0 +1,484 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_event.dart';
import 'package:syncrow_app/features/devices/bloc/sos_bloc/sos_state.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/sos_model.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/sos/sos_setting/delete_dialog.dart';
import 'package:syncrow_app/features/devices/view/widgets/sos/sos_setting/sos_profile_page.dart';
import 'package:syncrow_app/features/devices/view/widgets/sos/sos_setting/faq_sos_page.dart';
import 'package:syncrow_app/features/devices/view/widgets/sos/sos_setting/share_sos_page.dart';
import 'package:syncrow_app/features/devices/view/widgets/sos/sos_setting/sos_info_page.dart';
import 'package:syncrow_app/features/devices/view/widgets/sos/sos_setting/sos_update_page.dart';
import 'package:syncrow_app/features/devices/view/widgets/sos/sos_setting/update_dialog_sos.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.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/resource_manager/color_manager.dart';
class SosSettings extends StatelessWidget {
final DeviceModel? device;
const SosSettings({
super.key,
this.device,
});
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: 'Device Settings',
child: BlocProvider(
create: (context) => SosBloc(sosId: device?.uuid ?? '')
..add(const SosInitial())
..add(SosInitialDeviseInfo()),
child: BlocBuilder<SosBloc, SosState>(
builder: (context, state) {
final _bloc = BlocProvider.of<SosBloc>(context);
SosModel model =
SosModel(batteryPercentage: 0, sosContactState: 'normal');
if (state is LoadingNewSate) {
model = state.sosSensor;
} else if (state is UpdateState) {
model = state.sensor;
}
return state is SosLoadingState
? const Center(
child: DefaultContainer(
width: 50,
height: 50,
child: CircularProgressIndicator()),
)
: ListView(
children: [
Padding(
padding: const EdgeInsets.symmetric(
vertical: 10,
),
child: InkWell(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SosProfilePage(
device: device,
),
),
);
},
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 20),
DefaultContainer(
borderRadius: const BorderRadius.all(
Radius.circular(30)),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Padding(
padding:
const EdgeInsets.only(left: 90),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
const BodyMedium(
text: 'SOS',
fontWeight: FontWeight.bold,
),
const SizedBox(
height: 5,
),
BodySmall(
text: _bloc.deviceInfo
.subspace.subspaceName),
],
),
const Icon(
Icons.edit_outlined,
color: ColorsManager.grayColor,
),
],
),
),
),
),
],
),
Positioned(
top: 0,
left: 20,
child: CircleAvatar(
radius: 40,
backgroundColor: Colors.white,
child: CircleAvatar(
radius: 40,
backgroundColor: Colors.grey,
child: ClipOval(
child: SvgPicture.asset(
Assets.sosProfileIcon,
fit: BoxFit.fill,
),
),
),
),
),
],
),
),
),
const SizedBox(height: 20),
const BodyMedium(
text: 'Device Management',
fontWeight: FontWeight.w700,
fontSize: 12,
fontColor: ColorsManager.grayColor,
),
DefaultContainer(
child: SettingWidget(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
SosInfoPage(device: device!)),
);
},
text: 'Device Information',
icon: Assets.infoIcon,
),
),
const SizedBox(height: 20),
const BodyMedium(
text: 'Device Offline Notification',
fontWeight: FontWeight.w700,
fontSize: 12,
fontColor: ColorsManager.grayColor,
),
DefaultContainer(
child: Column(
children: [
SettingWidget(
onChanged: (p0) {},
isNotification: true,
onTap: () {},
text: 'Offline Notification',
icon: Assets.notificationIcon,
),
],
),
),
const SizedBox(height: 20),
const BodyMedium(
text: 'Others',
fontWeight: FontWeight.w700,
fontSize: 12,
fontColor: ColorsManager.grayColor,
),
const SizedBox(height: 5),
DefaultContainer(
child: Column(
children: [
SettingWidget(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
ShareSosPage(device: device!)),
);
},
text: 'Share Device',
icon: Assets.shareIcon,
),
const Divider(
color: ColorsManager.dividerColor,
),
SettingWidget(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
FaqSosPage(device: device!)),
);
},
text: 'Device FAQ',
icon: Assets.faqIcon,
),
const Divider(
color: ColorsManager.dividerColor,
),
SettingWidget(
onTapUpdate: () {
showDialog(
context: context,
builder: (context) {
return UpdateInfoDialog(
cancelTab: () {
Navigator.of(context).pop();
},
confirmTab: () {
// context
// .read<
// CreateSceneBloc>()
// .add(DeleteSceneEvent(
// sceneId: sceneId,
// unitUuid: HomeCubit
// .getInstance()
// .selectedSpace!
// .id!,
// ));
Navigator.of(context).pop();
},
);
},
);
},
isUpdate: true,
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
const SosUpdatePage()),
);
},
text: 'Device Update',
icon: Assets.updateIcon,
),
],
),
),
const SizedBox(height: 20),
InkWell(
onTap: () {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
height: 200,
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const BodyMedium(
text: 'Remove Device',
fontWeight: FontWeight.w700,
fontSize: 16,
fontColor: ColorsManager.red,
),
const SizedBox(height: 10),
const SizedBox(
width: 250,
child: Divider(
color: ColorsManager.dividerColor,
),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () {
showDialog(
context: context,
builder: (context) {
return DisconnectDeviceDialog(
cancelTab: () {
Navigator.of(context).pop();
},
confirmTab: () {
// context
// .read<
// CreateSceneBloc>()
// .add(DeleteSceneEvent(
// sceneId: sceneId,
// unitUuid: HomeCubit
// .getInstance()
// .selectedSpace!
// .id!,
// ));
Navigator.of(context).pop();
},
);
},
);
},
child: const BodyMedium(
text: 'Disconnect Device',
fontWeight: FontWeight.w400,
fontSize: 15,
fontColor:
ColorsManager.textPrimaryColor,
),
),
const Icon(
Icons.keyboard_arrow_right,
color: ColorsManager.textGray,
)
],
),
const SizedBox(height: 20),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () {
showDialog(
context: context,
builder: (context) {
return DisconnectWipeData(
cancelTab: () {
Navigator.of(context).pop();
},
confirmTab: () {
// context
// .read<
// CreateSceneBloc>()
// .add(DeleteSceneEvent(
// sceneId: sceneId,
// unitUuid: HomeCubit
// .getInstance()
// .selectedSpace!
// .id!,
// ));
Navigator.of(context).pop();
},
);
},
);
},
child: const BodyMedium(
text:
'Disconnect Device and Wipe Data',
fontWeight: FontWeight.w400,
fontSize: 15,
fontColor:
ColorsManager.textPrimaryColor,
),
),
const Icon(
Icons.keyboard_arrow_right,
color: ColorsManager.textGray,
)
],
),
],
),
);
},
);
},
child: const Center(
child: BodyMedium(
text: 'Remove Device',
fontWeight: FontWeight.w400,
fontSize: 15,
fontColor: ColorsManager.red,
),
),
),
],
);
},
),
),
);
}
}
class SettingWidget extends StatelessWidget {
final String? text;
final bool? isUpdate;
final bool? isNotification;
final String? icon;
final Function()? onTap;
final Function()? onTapUpdate;
final Function(bool)? onChanged;
const SettingWidget(
{super.key,
this.text,
this.icon,
this.onTap,
this.isUpdate,
this.onChanged,
this.isNotification = false,
this.onTapUpdate});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Row(
children: [
Expanded(
flex: 2,
child: Container(
padding: EdgeInsets.all(8),
decoration: const BoxDecoration(
color: ColorsManager.primaryColor,
borderRadius: BorderRadius.all(Radius.circular(20))),
child: SvgPicture.asset(
icon!,
fit: BoxFit.none,
height: 30,
),
),
),
const SizedBox(
width: 8,
),
Expanded(
flex: isUpdate == true ? 5 : 10,
child: BodyMedium(
text: text!,
fontSize: 15,
fontWeight: FontWeight.w400,
)),
],
),
),
isUpdate == true
? InkWell(
onTap: onTapUpdate,
child: const BodyMedium(
text: '1 Update Available',
fontSize: 13,
fontWeight: FontWeight.w400,
fontColor: ColorsManager.blueColor,
),
)
: SizedBox(),
isNotification == false
? const Icon(
Icons.arrow_forward_ios,
color: ColorsManager.graysColor,
size: 20,
)
: Transform.scale(
scale: .8,
child: CupertinoSwitch(
value: true,
onChanged: onChanged,
applyTheme: true,
),
),
],
),
);
}
}

View File

@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_app/features/devices/model/smart_door_model.dart';
import 'package:syncrow_app/generated/assets.dart';
class SosStatusBar extends StatelessWidget {
const SosStatusBar({
required this.smartDoorModel,
super.key,
});
final SmartDoorModel smartDoorModel;
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SvgPicture.asset(Assets.assetsIconsWifi),
Transform.rotate(
angle: 1.5708, // 90 degrees in radians (π/2 or 1.5708)
child: Icon(
_getBatteryIcon(smartDoorModel.residualElectricity),
color: _getBatteryColor(smartDoorModel.residualElectricity),
size: 30,
),
),
],
);
}
IconData _getBatteryIcon(int batteryLevel) {
// if (batteryState == BatteryState.charging) {
// return Icons.battery_charging_full;
// } else
if (batteryLevel >= 80) {
return Icons.battery_full;
} else if (batteryLevel >= 60) {
return Icons.battery_4_bar;
} else if (batteryLevel >= 40) {
return Icons.battery_3_bar;
} else if (batteryLevel >= 20) {
return Icons.battery_2_bar;
} else {
return Icons.battery_alert;
}
}
Color _getBatteryColor(int batteryLevel) {
if (batteryLevel >= 80) {
return Colors.green;
} else if (batteryLevel >= 40) {
return Colors.yellowAccent;
} else {
return Colors.red;
}
}
}

View File

@ -1118,6 +1118,9 @@ class Assets {
static const String minusIcon = "assets/icons/minus_icon.svg";
static const String addDevicesIcon = "assets/icons/add_devices_icon.svg";
static const String fourSceneIcon = "assets/icons/four_scene_icon.svg";
static const String fourSceneHomeIcon = "assets/icons/four_scene_home_icon.svg";
static const String fourSceneHomeIcon =
"assets/icons/four_scene_home_icon.svg";
static const String sixSceneHomeIcon = "assets/icons/six_scene_home_icon.svg";
static const String editDeviceNameIcon =
"assets/icons/edit_device_name_icon.svg";
}

View File

@ -58,6 +58,7 @@ enum DeviceType {
PC,
FourScene,
SixScene,
SOS,
Other,
}
@ -92,7 +93,9 @@ Map<String, DeviceType> devicesTypesMap = {
"PC": DeviceType.PC,
"4S": DeviceType.FourScene,
"6S": DeviceType.SixScene,
"SOS": DeviceType.SOS,
};
Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
DeviceType.AC: [
FunctionModel(
@ -592,7 +595,17 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
code: 'switch_backlight',
type: functionTypesMap['Enum'],
values: ValueModel.fromJson({})),
]
],
DeviceType.SOS: [
FunctionModel(
code: 'contact_state',
type: functionTypesMap['Raw'],
values: ValueModel.fromJson({})),
FunctionModel(
code: 'battery_percentage',
type: functionTypesMap['Integer'],
values: ValueModel.fromJson({})),
],
};
enum TempModes { hot, cold, wind }