add new flush sensor device to app

This commit is contained in:
mohammad
2025-05-06 16:20:05 +03:00
parent 8a21828dd4
commit 14a5d26ef9
20 changed files with 1112 additions and 7 deletions
@@ -0,0 +1,129 @@
import 'dart:async';
import 'dart:developer';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart';
import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart';
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/flush_sensor_model.dart';
import 'package:syncrow_app/features/devices/model/status_model.dart';
import 'package:syncrow_app/services/api/devices_api.dart';
class FlushSensorBloc extends Bloc<FlushSensorEvent, FlushSensorState> {
final String deviceId;
late DeviceModel deviceModel;
late FlushSensorModel deviceStatus;
FlushSensorBloc({required this.deviceId}) : super(InitialState()) {
on<InitialEvent>(_fetchFlushSensorStatus);
on<ChangeIndicatorEvent>(_changeIndicator);
on<ChangeValueEvent>(_changeValue);
on<WallSensorUpdatedEvent>(_flushSensorUpdated);
on<GetDeviceReportsEvent>(_getDeviceReports);
}
void _fetchFlushSensorStatus(
InitialEvent event, Emitter<FlushSensorState> emit) async {
emit(LoadingInitialState());
try {
var response = await DevicesAPI.getDeviceStatus(deviceId);
List<StatusModel> statusModelList = [];
for (var status in response['status']) {
statusModelList.add(StatusModel.fromJson(status));
}
deviceStatus = FlushSensorModel.fromJson(statusModelList);
emit(UpdateState(flushSensorModel: deviceStatus));
_listenToChanges();
} catch (e) {
emit(FailedState(error: e.toString()));
return;
}
}
StreamSubscription<DatabaseEvent>? _streamSubscription;
void _listenToChanges() {
try {
_streamSubscription?.cancel();
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
_streamSubscription = stream.listen((DatabaseEvent event) async {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<StatusModel> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(StatusModel(code: element['code'], value: element['value']));
});
deviceStatus = FlushSensorModel.fromJson(statusList);
if (!isClosed) {
add(WallSensorUpdatedEvent());
}
});
} catch (_) {
log(
'Error listening to changes',
name: 'FlushMountedPresenceSensorBloc._listenToChanges',
);
}
}
@override
Future<void> close() async {
_streamSubscription?.cancel();
_streamSubscription = null;
return super.close();
}
_flushSensorUpdated(
WallSensorUpdatedEvent event, Emitter<FlushSensorState> emit) {
emit(UpdateState(flushSensorModel: deviceStatus));
}
void _changeIndicator(
ChangeIndicatorEvent event, Emitter<FlushSensorState> emit) async {
emit(LoadingNewSate(flushSensorModel: deviceStatus));
try {
final response = await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: deviceId, code: 'indicator', value: !event.value),
deviceId);
} catch (e) {
emit(FailedState(error: e.toString()));
return;
}
emit(UpdateState(flushSensorModel: deviceStatus));
}
void _changeValue(
ChangeValueEvent event, Emitter<FlushSensorState> emit) async {
emit(LoadingNewSate(flushSensorModel: deviceStatus));
try {
final response = await DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: deviceId, code: event.code, value: event.value),
deviceId);
} catch (e) {
emit(FailedState(error: e.toString()));
return;
}
emit(UpdateState(flushSensorModel: deviceStatus));
}
void _getDeviceReports(
GetDeviceReportsEvent event, Emitter<FlushSensorState> emit) async {
emit(LoadingInitialState());
try {
await DevicesAPI.getDeviceReports(deviceId, event.code).then((value) {
emit(DeviceReportsState(deviceReport: value, code: event.code));
});
} catch (e) {
emit(FailedState(error: e.toString()));
return;
}
}
}
@@ -0,0 +1,43 @@
import 'package:equatable/equatable.dart';
abstract class FlushSensorEvent extends Equatable {
const FlushSensorEvent();
@override
List<Object> get props => [];
}
class LoadingEvent extends FlushSensorEvent {}
class InitialEvent extends FlushSensorEvent {}
class WallSensorUpdatedEvent extends FlushSensorEvent {}
class ChangeIndicatorEvent extends FlushSensorEvent {
final bool value;
const ChangeIndicatorEvent({required this.value});
@override
List<Object> get props => [value];
}
class ChangeValueEvent extends FlushSensorEvent {
final dynamic value;
final String code;
const ChangeValueEvent({required this.value, required this.code});
@override
List<Object> get props => [value, code];
}
class GetDeviceReportsEvent extends FlushSensorEvent {
final String deviceUuid;
final String code;
const GetDeviceReportsEvent({
required this.deviceUuid,
required this.code,
});
@override
List<Object> get props => [deviceUuid, code];
}
@@ -0,0 +1,45 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/device_report_model.dart';
import 'package:syncrow_app/features/devices/model/flush_sensor_model.dart';
class FlushSensorState extends Equatable {
const FlushSensorState();
@override
List<Object> get props => [];
}
class InitialState extends FlushSensorState {}
class LoadingInitialState extends FlushSensorState {}
class UpdateState extends FlushSensorState {
final FlushSensorModel flushSensorModel;
const UpdateState({required this.flushSensorModel});
@override
List<Object> get props => [flushSensorModel];
}
class LoadingNewSate extends FlushSensorState {
final FlushSensorModel flushSensorModel;
const LoadingNewSate({required this.flushSensorModel});
@override
List<Object> get props => [flushSensorModel];
}
class FailedState extends FlushSensorState {
final String error;
const FailedState({required this.error});
@override
List<Object> get props => [error];
}
class DeviceReportsState extends FlushSensorState {
final DeviceReport deviceReport;
final String code;
const DeviceReportsState({required this.deviceReport, required this.code});
}
@@ -92,6 +92,8 @@ class DeviceModel {
tempIcon = Assets.sixSceneHomeIcon;
} else if (type == DeviceType.SOS) {
tempIcon = Assets.sosHomeIcon;
} else if (type == DeviceType.FlushMountedSensor) {
tempIcon = Assets.sosHomeIcon;
} else {
tempIcon = Assets.assetsIconsLogo;
}
@@ -0,0 +1,100 @@
import 'package:syncrow_app/features/devices/model/status_model.dart';
class FlushSensorModel {
FlushSensorModel({
required this.presenceState,
required this.farDetection,
required this.illuminance,
required this.sensitivity,
required this.occurDistReduce,
required this.noneDelay,
required this.presenceDelay,
required this.nearDetection,
required this.sensiReduce,
required this.checkingResult,
});
static const String codePresenceState = 'presence_state';
static const String codeSensitivity = 'sensitivity';
static const String codeNearDetection = 'near_detection';
static const String codeFarDetection = 'far_detection';
static const String codeCheckingResult = 'checking_result';
static const String codePresenceDelay = 'presence_delay';
static const String codeNoneDelay = 'none_delay';
static const String codeOccurDistReduce = 'occur_dist_reduce';
static const String codeIlluminance = 'illum_value';
static const String codeSensiReduce = 'sensi_reduce';
String presenceState;
int sensitivity;
int nearDetection;
int farDetection;
String checkingResult;
int presenceDelay;
int noneDelay;
int occurDistReduce;
int illuminance;
int sensiReduce;
factory FlushSensorModel.fromJson(List<StatusModel> jsonList) {
String presenceState = 'none';
int sensitivity = 0;
int nearDetection = 0;
int farDetection = 0;
String checkingResult = 'none';
int presenceDelay = 0;
int noneDelay = 0;
int occurDistReduce = 0;
int illuminance = 0;
int sensiReduce = 0;
for (var status in jsonList) {
switch (status.code) {
case codePresenceState:
presenceState = status.value ?? 'presence';
break;
case codeSensitivity:
sensitivity = status.value ?? 0;
break;
case codeNearDetection:
nearDetection = status.value ?? 0;
break;
case codeFarDetection:
farDetection = status.value ?? 0;
break;
case codeCheckingResult:
checkingResult = status.value ?? 'check_success';
break;
case codePresenceDelay:
presenceDelay = status.value ?? 0;
break;
case codeNoneDelay:
noneDelay = status.value ?? 0;
break;
case codeOccurDistReduce:
occurDistReduce = status.value ?? 0;
break;
case codeIlluminance:
illuminance = status.value ?? 0;
break;
case codeSensiReduce:
sensiReduce = status.value ?? 0;
break;
}
}
return FlushSensorModel(
presenceState: presenceState,
sensitivity: sensitivity,
nearDetection: nearDetection,
farDetection: farDetection,
checkingResult: checkingResult,
presenceDelay: presenceDelay,
noneDelay: noneDelay,
occurDistReduce: occurDistReduce,
illuminance: illuminance,
sensiReduce: sensiReduce,
);
}
}
@@ -0,0 +1,120 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart';
import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_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/utils/resource_manager/color_manager.dart';
class FlushPresenceRecords extends StatelessWidget {
final String deviceId;
final String code;
final String title;
const FlushPresenceRecords(
{super.key, required this.deviceId, required this.code, required this.title});
@override
Widget build(BuildContext context) {
return DefaultScaffold(
title: title,
child: BlocProvider(
create: (context) => FlushSensorBloc(deviceId: deviceId)
..add(GetDeviceReportsEvent(deviceUuid: deviceId, code: code)),
child: BlocBuilder<FlushSensorBloc, FlushSensorState>(builder: (context, state) {
final Map<String, List<DeviceEvent>> groupedRecords = {};
if (state is LoadingInitialState) {
return const Center(
child: DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()),
);
} else if (state is DeviceReportsState) {
for (var record in state.deviceReport.data ?? []) {
final DateTime eventDateTime = DateTime.fromMillisecondsSinceEpoch(record.eventTime!);
final String formattedDate = DateFormat('EEEE, dd/MM/yyyy').format(eventDateTime);
// Group by formatted date
if (groupedRecords.containsKey(formattedDate)) {
groupedRecords[formattedDate]!.add(record);
} else {
groupedRecords[formattedDate] = [record];
}
}
}
return groupedRecords.isEmpty
? const Center(
child: Text('No records found'),
)
: 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.only(bottom: 5, top: 10),
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: [
SizedBox(
child: ListTile(
leading: Icon(
record.value == 'true'
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
color: record.value == 'true' ? Colors.blue : Colors.grey,
),
title: Text(
record.value == 'true' ? "Opened" : "Closed",
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
subtitle: Text('$formattedTime'),
),
),
if (idx != recordsForDate.length - 1)
const Divider(
color: ColorsManager.graysColor,
),
],
);
}).toList(),
],
),
),
],
);
},
);
}),
),
);
}
}
@@ -0,0 +1,113 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_event.dart';
import 'package:syncrow_app/features/devices/bloc/flush_sensor_bloc/flush_sensor_state.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/flush_sensor_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/device_appbar.dart';
import 'package:syncrow_app/features/devices/view/widgets/flush_sensor/flush_persence_records.dart';
import 'package:syncrow_app/features/devices/view/widgets/flush_sensor/flush_sensor_option.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/helpers/misc_string_helpers.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
part "presence_indicator.dart";
part "flush_sensor_options_list.dart";
part 'flush_sensor_parameter_control_dialog.dart';
class FlushMountedInterface extends StatelessWidget {
const FlushMountedInterface({super.key, required this.deviceModel});
final DeviceModel deviceModel;
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => FlushSensorBloc(deviceId: deviceModel.uuid ?? '')
..add(InitialEvent()),
child: BlocBuilder<FlushSensorBloc, FlushSensorState>(
builder: (context, state) {
final bloc = BlocProvider.of<FlushSensorBloc>(context);
FlushSensorModel flushSensorModel = FlushSensorModel(
presenceState: 'none',
farDetection: 0,
illuminance: 0,
checkingResult: '',
nearDetection: 0,
noneDelay: 0,
occurDistReduce: 0,
presenceDelay: 0,
sensiReduce: 0,
sensitivity: 0);
if (state is UpdateState) {
flushSensorModel = state.flushSensorModel;
} else if (state is LoadingNewSate) {
flushSensorModel = state.flushSensorModel;
}
return AnnotatedRegion(
value: SystemUiOverlayStyle(
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
statusBarIconBrightness: Brightness.light,
),
child: Scaffold(
backgroundColor: ColorsManager.backgroundColor,
extendBodyBehindAppBar: true,
extendBody: true,
appBar: DeviceAppbar(
deviceName: deviceModel.name!,
deviceUuid: deviceModel.uuid!,
),
body: Container(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
padding: const EdgeInsets.all(Constants.defaultPadding),
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(
Assets.assetsImagesBackground,
),
fit: BoxFit.cover,
opacity: 0.4,
),
),
child: state is LoadingInitialState
? const Center(
child: RefreshProgressIndicator(),
)
: SafeArea(
child: RefreshIndicator(
onRefresh: () async {
bloc.add(InitialEvent());
},
child: ListView(children: [
SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(children: [
PresenceIndicator(
state: flushSensorModel.presenceState,
),
Expanded(
flex: 2,
child: FlushSensorOptionsList(
bloc: bloc,
flushSensorModel: flushSensorModel,
deviceModel: deviceModel,
),
)
]),
),
]),
),
))));
}),
);
}
}
@@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class FlushSensorOption extends StatelessWidget {
final String icon;
final String label;
final VoidCallback onTap;
final String? sliderValue;
final bool withArrow;
const FlushSensorOption({
super.key,
required this.icon,
required this.label,
required this.onTap,
required this.withArrow,
this.sliderValue = '',
});
@override
Widget build(BuildContext context) {
return DefaultContainer(
margin: const EdgeInsets.only(bottom: 5),
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 20),
onTap: onTap,
child: Row(
children: [
SvgPicture.asset(icon),
const SizedBox(width: 25),
Expanded(
child:
Text(label, style: Theme.of(context).textTheme.bodyMedium)),
Text(sliderValue!,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: ColorsManager.grayTextColor,
fontWeight: FontWeight.bold)),
withArrow == true
? const Icon(Icons.arrow_forward_ios,
color: ColorsManager.grayTextColor, size: 15)
: SizedBox(),
],
),
);
}
}
@@ -0,0 +1,193 @@
part of "flush_sensor_interface.dart";
class FlushSensorOptionsList extends StatelessWidget {
final FlushSensorModel flushSensorModel;
final DeviceModel deviceModel;
final FlushSensorBloc bloc;
const FlushSensorOptionsList(
{super.key,
required this.flushSensorModel,
required this.bloc,
required this.deviceModel});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FlushSensorOption(
withArrow: false,
sliderValue: '${flushSensorModel.illuminance.toString()} Lux',
icon: Assets.assetsIconsPresenceSensorAssetsIlluminanceValue,
label: 'Illuminance Value',
onTap: () {},
),
FlushSensorOption(
withArrow: false,
icon: Assets.assetsIconsPresenceSensorAssetsIlluminanceRecord,
label: 'Presence Record',
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => FlushPresenceRecords(
deviceId: deviceModel.uuid!,
code: FlushSensorModel.codePresenceState,
title: 'Illuminance Record'),
));
}),
Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Text(
'Configuration',
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(fontWeight: FontWeight.bold),
),
),
FlushSensorOption(
withArrow: true,
sliderValue: flushSensorModel.sensitivity.toString(),
icon: Assets.assetsSensitivityFunction,
label: 'Sensitivity',
onTap: () => _showParameterDialog(
min: 0,
max: 9,
value: flushSensorModel.sensitivity.toDouble(),
context: context,
title: 'Sensitivity',
controlCode: FlushSensorModel.codeSensitivity,
step: 1.0,
),
),
FlushSensorOption(
withArrow: true,
sliderValue: "${flushSensorModel.nearDetection / 100} m",
icon: Assets.detectionDistanceIcon,
label: 'Min Detection Distance',
onTap: () => _showParameterDialog(
min: 0.0,
max: 9.5,
value: flushSensorModel.nearDetection.toDouble() / 100,
context: context,
title: 'Min Detection Distance',
controlCode: FlushSensorModel.codeNearDetection,
step: 0.1,
description: 'm',
),
),
FlushSensorOption(
withArrow: true,
sliderValue: '${flushSensorModel.farDetection / 100} m',
icon: Assets.detectionDistanceIcon,
label: 'Max Detection Distance',
onTap: () => _showParameterDialog(
description: 'm',
min: 0.0,
max: 9.5,
value: flushSensorModel.farDetection / 100,
context: context,
title: 'Max Detection Distance',
controlCode: FlushSensorModel.codeFarDetection,
step: 0.1),
),
FlushSensorOption(
sliderValue: flushSensorModel.sensiReduce.toString(),
withArrow: true,
icon: Assets.triggerLevelIcon,
label: 'Trigger Level',
onTap: () => _showParameterDialog(
min: 0,
max: 3,
value: flushSensorModel.sensiReduce.toDouble(),
context: context,
title: 'Trigger Level',
controlCode: FlushSensorModel.codeSensiReduce,
step: 1),
),
FlushSensorOption(
sliderValue: flushSensorModel.occurDistReduce.toString(),
withArrow: true,
icon: Assets.indentLevelIcon,
label: 'Indent Level',
onTap: () => _showParameterDialog(
min: 0,
max: 3,
value: flushSensorModel.occurDistReduce.toDouble(),
context: context,
title: 'Indent Level',
controlCode: FlushSensorModel.codeOccurDistReduce,
step: 1),
),
FlushSensorOption(
sliderValue: '${flushSensorModel.presenceDelay / 10}',
withArrow: true,
icon: Assets.targetConfirmTimeIcon,
label: 'Target Confirm Time',
onTap: () => _showParameterDialog(
min: 0.0,
max: 0.5,
value: flushSensorModel.presenceDelay.toDouble() / 10,
context: context,
title: 'Target Confirm Time',
controlCode: FlushSensorModel.codePresenceDelay,
step: 0.1),
),
FlushSensorOption(
sliderValue: '${flushSensorModel.noneDelay.toDouble() / 10}',
withArrow: true,
icon: Assets.disappeDelayIcon,
label: 'Disappe Delay',
onTap: () => _showParameterDialog(
min: 20,
max: 300,
value: flushSensorModel.noneDelay.toDouble() / 10,
context: context,
title: 'Disappe Delay',
controlCode: FlushSensorModel.codeNoneDelay,
step: 1),
),
],
);
}
void _showParameterDialog({
required double step,
required BuildContext context,
required String title,
required String controlCode,
required double min,
required double max,
required double value,
String? description = '',
}) async {
var result = await showDialog(
context: context,
builder: (context) {
return FlushParameterControlDialog(
title: title,
sensor: deviceModel,
value: value,
min: min,
max: max,
step: step,
description: description);
},
);
if (result != null) {
if (controlCode == FlushSensorModel.codeNearDetection ||
controlCode == FlushSensorModel.codeFarDetection) {
result = (result * 100).toInt();
}
if (controlCode == FlushSensorModel.codeNoneDelay) {
result = (result * 10).toInt();
}
if (controlCode == FlushSensorModel.codePresenceDelay) {
result = (result * 10).toInt();
}
bloc.add(
ChangeValueEvent(value: result, code: controlCode),
);
}
}
}
@@ -0,0 +1,181 @@
part of 'flush_sensor_interface.dart';
class FlushParameterControlDialog extends StatefulWidget {
final String title;
final String? description;
final DeviceModel sensor;
final double value;
final double min;
final double max;
final double step;
const FlushParameterControlDialog({
super.key,
required this.title,
required this.sensor,
required this.value,
required this.min,
required this.max,
required this.step,
this.description = '',
});
@override
FlushParameterControlDialogState createState() =>
FlushParameterControlDialogState();
}
class FlushParameterControlDialogState
extends State<FlushParameterControlDialog> {
late double _value;
@override
void initState() {
super.initState();
_value = widget.value;
}
@override
Widget build(BuildContext context) {
final divisions = ((widget.max - widget.min) / widget.step).toInt();
final decimalPlaces = widget.step % 1 == 0 ? 0 : 1;
return Dialog(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.only(top: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
BodyMedium(
text: widget.title,
style: context.bodyMedium.copyWith(
color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontsManager.extraBold,
),
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 15,
horizontal: 50,
),
child: Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 10,
),
child: TitleMedium(
text:
"${_value.toStringAsFixed(decimalPlaces)} ${widget.description}",
style: context.titleMedium.copyWith(
color: Colors.black,
fontWeight: FontsManager.bold,
),
),
),
SizedBox(
width: MediaQuery.sizeOf(context).width,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
onPressed: () {
setState(() {
_value = (_value - widget.step)
.clamp(widget.min, widget.max);
_value =
double.parse(_value.toStringAsFixed(decimalPlaces));
});
},
icon: const Icon(Icons.remove, color: Colors.grey),
),
Flexible(
child: Slider(
value: _value,
onChanged: (value) {
final newValue =
(value / widget.step).round() * widget.step;
setState(() {
_value = newValue.clamp(widget.min, widget.max);
});
},
min: widget.min,
max: widget.max,
divisions: divisions,
label: _value.toStringAsFixed(decimalPlaces),
),
),
IconButton(
onPressed: () {
setState(() {
_value = (_value + widget.step)
.clamp(widget.min, widget.max);
_value =
double.parse(_value.toStringAsFixed(decimalPlaces));
});
},
icon: const Icon(Icons.add, color: Colors.grey),
),
],
),
),
Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
InkWell(
onTap: () {
Navigator.pop(context);
},
child: Center(
child: BodyMedium(
text: 'Cancel',
style: context.bodyMedium
.copyWith(color: ColorsManager.greyColor),
),
),
),
Container(
height: 50,
width: 1,
color: ColorsManager.greyColor,
),
InkWell(
onTap: () {
if (widget.sensor.isOnline == null) {
CustomSnackBar.displaySnackBar('The device is offline');
return;
}
if (!widget.sensor.isOnline!) {
CustomSnackBar.displaySnackBar('The device is offline');
return;
}
Navigator.pop(context, _value);
},
child: Center(
child: BodyMedium(
text: 'Confirm',
style: context.bodyMedium.copyWith(
color: ColorsManager.primaryColorWithOpacity),
),
),
),
],
)
],
),
),
);
}
}
@@ -0,0 +1,31 @@
part of "flush_sensor_interface.dart";
class PresenceIndicator extends StatelessWidget {
const PresenceIndicator({super.key, required this.state});
final String state;
@override
Widget build(BuildContext context) {
return Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
state.toLowerCase() == 'motion'
? Assets.assetsIconsPresenceSensorAssetsPresenceSensorMotion
: Assets.assetsIconsPresenceSensorAssetsPresence,
width: 100,
height: 100,
),
const SizedBox(
height: 10,
),
BodyMedium(
text: StringHelpers.toTitleCase(state),
style: context.bodyMedium.copyWith(fontWeight: FontsManager.bold),
),
],
),
);
}
}
@@ -13,6 +13,7 @@ import 'package:syncrow_app/features/devices/view/widgets/ACs/acs_view.dart';
import 'package:syncrow_app/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/curtains/curtain_view.dart';
import 'package:syncrow_app/features/devices/view/widgets/door_sensor/door_sensor_screen.dart';
import 'package:syncrow_app/features/devices/view/widgets/flush_sensor/flush_sensor_interface.dart';
import 'package:syncrow_app/features/devices/view/widgets/four_scene_switch/four_scene_screen.dart';
import 'package:syncrow_app/features/devices/view/widgets/garage_door/garage_door_screen.dart';
import 'package:syncrow_app/features/devices/view/widgets/gateway/gateway_view.dart';
@@ -300,6 +301,13 @@ Future<void> showDeviceInterface(
pageBuilder: (context, animation1, animation2) =>
SosScreen(device: device)));
case DeviceType.FlushMountedSensor:
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation1, animation2) =>
FlushMountedInterface(deviceModel: device)));
break;
default:
}
+7
View File
@@ -1148,4 +1148,11 @@ class Assets {
static const String refreshStatusIcon =
'assets/icons/refresh_status_icon.svg';
static const String detectionDistanceIcon =
'assets/icons/detection_distance_icon.svg';
static const String triggerLevelIcon = 'assets/icons/trigger_level_icon.svg';
static const String indentLevelIcon = 'assets/icons/indent_level_icon.svg';
static const String targetConfirmTimeIcon =
'assets/icons/target_confirm_time_icon.svg';
static const String disappeDelayIcon = 'assets/icons/disappe_delay_icon.svg';
}
+1 -4
View File
@@ -595,10 +595,7 @@ class DevicesAPI {
final result = <DeviceModel>[];
for (final device in data) {
final mappedDevice = DeviceModel.fromJson(device);
if (mappedDevice.productType?.name !=
DeviceType.FlushMountedSensor.name) {
result.add(mappedDevice);
}
result.add(mappedDevice);
}
return result;
},
@@ -36,7 +36,5 @@ abstract class ColorsManager {
static const Color grayButtonColors = Color(0xffCCCCCC);
static const Color blueButton = Color(0xff85BDFF);
static const Color backgroundGrey = Color(0xFFF3F3F3);
static const Color grayTextColor = Color(0xFFC4C4C4);
}
//background: #F5F5F5;background: #85BDFF;