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

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