mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-08-25 14:19:40 +00:00
build UI and integrate with back
This commit is contained in:
@ -0,0 +1,71 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/batch/curtain_module_batch_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/curtain_movment_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/icon_name_status_container.dart';
|
||||||
|
import 'package:syncrow_web/services/control_device_service.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
|
class CurtainModuleBatchView extends StatelessWidget {
|
||||||
|
final List<String> devicesIds;
|
||||||
|
const CurtainModuleBatchView({
|
||||||
|
super.key,
|
||||||
|
required this.devicesIds,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => CurtainModuleBatchBloc(RemoteControlDeviceService())
|
||||||
|
..add(CutrainModuleFetchBatchStatusEvent(devicesIds: devicesIds)),
|
||||||
|
child: _buildStatusControls(context),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusControls(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 30),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
ControlCurtainMovementWidget(
|
||||||
|
deviceId: devicesIds.first,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 120,
|
||||||
|
width: 350,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: IconNameStatusContainer(
|
||||||
|
isFullIcon: false,
|
||||||
|
name: 'Factory Reset',
|
||||||
|
icon: Assets.factoryReset,
|
||||||
|
onTap: () {},
|
||||||
|
status: false,
|
||||||
|
textColor: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: IconNameStatusContainer(
|
||||||
|
isFullIcon: false,
|
||||||
|
name: 'Firmware Update',
|
||||||
|
icon: Assets.firmware,
|
||||||
|
onTap: () {},
|
||||||
|
status: false,
|
||||||
|
textColor: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/curtain_movment_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/prefrences_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/icon_name_status_container.dart';
|
||||||
|
import 'package:syncrow_web/services/control_device_service.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
|
class CurtainModuleItems extends StatelessWidget with HelperResponsiveLayout {
|
||||||
|
final String deviceId;
|
||||||
|
const CurtainModuleItems({
|
||||||
|
super.key,
|
||||||
|
required this.deviceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => CurtainModuleBloc(RemoteControlDeviceService())
|
||||||
|
..add(FetchCurtainModuleStatusEvent(deviceId: deviceId)),
|
||||||
|
child: _buildStatusControls(context),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusControls(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 30),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
ControlCurtainMovementWidget(
|
||||||
|
deviceId: deviceId,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 120,
|
||||||
|
width: 350,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: IconNameStatusContainer(
|
||||||
|
isFullIcon: false,
|
||||||
|
name: 'Schedules',
|
||||||
|
icon: Assets.schedule,
|
||||||
|
onTap: () {},
|
||||||
|
status: false,
|
||||||
|
textColor: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 10,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: BlocBuilder<CurtainModuleBloc, CurtainModuleState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is CurtainModuleLoading) {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
} else if (state is CurtainModuleStatusLoaded) {
|
||||||
|
return IconNameStatusContainer(
|
||||||
|
isFullIcon: false,
|
||||||
|
name: 'Preferences',
|
||||||
|
icon: Assets.preferences,
|
||||||
|
onTap: () => showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => BlocProvider.value(
|
||||||
|
value: context.watch<CurtainModuleBloc>(),
|
||||||
|
child: CurtainModulePrefrencesDialog(
|
||||||
|
deviceId: deviceId,
|
||||||
|
curtainModuleStatusModel:
|
||||||
|
state.curtainModuleStatus,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
status: false,
|
||||||
|
textColor: ColorsManager.blackColor,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/calibrate_completed_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/normal_text_body_for_dialog.dart';
|
||||||
|
|
||||||
|
class AccurteCalibratingDialog extends StatelessWidget {
|
||||||
|
final String deviceId;
|
||||||
|
final BuildContext parentContext;
|
||||||
|
const AccurteCalibratingDialog({
|
||||||
|
super.key,
|
||||||
|
required this.deviceId,
|
||||||
|
required this.parentContext,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(_) {
|
||||||
|
return AlertDialog(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
content: AccurateDialogWidget(
|
||||||
|
title: 'Calibrating',
|
||||||
|
body: const NormalTextBodyForDialog(
|
||||||
|
title: '',
|
||||||
|
step1:
|
||||||
|
'1. Click Close Button to make the Curtain run to Full Close and Position.',
|
||||||
|
step2: '2. click Next to complete the Calibration.',
|
||||||
|
),
|
||||||
|
leftOnTap: () => Navigator.of(parentContext).pop(),
|
||||||
|
rightOnTap: () {
|
||||||
|
parentContext.read<CurtainModuleBloc>().add(
|
||||||
|
CurCalibrationEvent(
|
||||||
|
deviceId: deviceId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Navigator.of(parentContext).pop();
|
||||||
|
showDialog(
|
||||||
|
context: parentContext,
|
||||||
|
builder: (_) => CalibrateCompletedDialog(
|
||||||
|
parentContext: parentContext,
|
||||||
|
deviceId: deviceId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_calibrating_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/normal_text_body_for_dialog.dart';
|
||||||
|
|
||||||
|
class AccurateCalibrationDialog extends StatelessWidget {
|
||||||
|
final String deviceId;
|
||||||
|
final BuildContext parentContext;
|
||||||
|
const AccurateCalibrationDialog({
|
||||||
|
super.key,
|
||||||
|
required this.deviceId,
|
||||||
|
required this.parentContext,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(_) {
|
||||||
|
return AlertDialog(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
content: AccurateDialogWidget(
|
||||||
|
title: 'Accurate Calibration',
|
||||||
|
body: const NormalTextBodyForDialog(
|
||||||
|
title: 'Prepare Calibration:',
|
||||||
|
step1: '1. Run The Curtain to the Fully Open Position,and pause.',
|
||||||
|
step2: '2. click Next to Start accurate calibration.',
|
||||||
|
),
|
||||||
|
leftOnTap: () => Navigator.of(parentContext).pop(),
|
||||||
|
rightOnTap: () {
|
||||||
|
Navigator.of(parentContext).pop();
|
||||||
|
showDialog(
|
||||||
|
context: parentContext,
|
||||||
|
builder: (_) => AccurteCalibratingDialog(
|
||||||
|
deviceId: deviceId,
|
||||||
|
parentContext: parentContext,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class AccurateDialogWidget extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final Widget body;
|
||||||
|
final void Function() leftOnTap;
|
||||||
|
final void Function() rightOnTap;
|
||||||
|
const AccurateDialogWidget({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.body,
|
||||||
|
required this.leftOnTap,
|
||||||
|
required this.rightOnTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 300,
|
||||||
|
width: 400,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
child: Text(
|
||||||
|
title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: ColorsManager.blueColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
const Divider(
|
||||||
|
indent: 10,
|
||||||
|
endIndent: 10,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
child: body,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
const Spacer(),
|
||||||
|
const Divider(),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: leftOnTap,
|
||||||
|
child: Container(
|
||||||
|
height: 60,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
right: BorderSide(
|
||||||
|
color: ColorsManager.grayBorder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
'Cancel',
|
||||||
|
style: TextStyle(color: ColorsManager.grayBorder),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: rightOnTap,
|
||||||
|
child: Container(
|
||||||
|
height: 60,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
right: BorderSide(
|
||||||
|
color: ColorsManager.grayBorder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
'Next',
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorsManager.blueColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class CalibrateCompletedDialog extends StatelessWidget {
|
||||||
|
final BuildContext parentContext;
|
||||||
|
final String deviceId;
|
||||||
|
const CalibrateCompletedDialog({
|
||||||
|
super.key,
|
||||||
|
required this.parentContext,
|
||||||
|
required this.deviceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(_) {
|
||||||
|
return AlertDialog(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
content: SizedBox(
|
||||||
|
height: 250,
|
||||||
|
width: 400,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
child: Text(
|
||||||
|
'Calibration Completed',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: ColorsManager.blueColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
const Divider(
|
||||||
|
indent: 10,
|
||||||
|
endIndent: 10,
|
||||||
|
),
|
||||||
|
const Icon(
|
||||||
|
Icons.check_circle,
|
||||||
|
size: 100,
|
||||||
|
color: ColorsManager.blueColor,
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
const Divider(
|
||||||
|
indent: 10,
|
||||||
|
endIndent: 10,
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
parentContext.read<CurtainModuleBloc>().add(
|
||||||
|
FetchCurtainModuleStatusEvent(
|
||||||
|
deviceId: deviceId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Navigator.of(parentContext).pop();
|
||||||
|
Navigator.of(parentContext).pop();
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
height: 40,
|
||||||
|
width: double.infinity,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: const Text(
|
||||||
|
'Close',
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorsManager.grayBorder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class CurtainActionWidget extends StatelessWidget {
|
||||||
|
final String icon;
|
||||||
|
final void Function() onTap;
|
||||||
|
const CurtainActionWidget({
|
||||||
|
super.key,
|
||||||
|
required this.icon,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
child: ClipOval(
|
||||||
|
child: Container(
|
||||||
|
height: 60,
|
||||||
|
width: 60,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
child: ClipOval(
|
||||||
|
child: Container(
|
||||||
|
height: 60,
|
||||||
|
width: 60,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
color: ColorsManager.graysColor,
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
icon,
|
||||||
|
width: 35,
|
||||||
|
height: 35,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,190 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/models/curtain_module_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/curtain_action_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
|
class ControlCurtainMovementWidget extends StatelessWidget {
|
||||||
|
final String deviceId;
|
||||||
|
const ControlCurtainMovementWidget({
|
||||||
|
super.key,
|
||||||
|
required this.deviceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
width: 550,
|
||||||
|
child: DeviceControlsContainer(
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
CurtainActionWidget(
|
||||||
|
icon: Assets.openCurtain,
|
||||||
|
onTap: () {
|
||||||
|
context.read<CurtainModuleBloc>().add(
|
||||||
|
OpenCurtainEvent(deviceId: deviceId),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 30,
|
||||||
|
),
|
||||||
|
CurtainActionWidget(
|
||||||
|
icon: Assets.pauseCurtain,
|
||||||
|
onTap: () {
|
||||||
|
context.read<CurtainModuleBloc>().add(
|
||||||
|
StopCurtainEvent(deviceId: deviceId),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 30,
|
||||||
|
),
|
||||||
|
CurtainActionWidget(
|
||||||
|
icon: Assets.closeCurtain,
|
||||||
|
onTap: () {
|
||||||
|
context.read<CurtainModuleBloc>().add(
|
||||||
|
CloseCurtainEvent(deviceId: deviceId),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
BlocBuilder<CurtainModuleBloc, CurtainModuleState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is CurtainModuleError) {
|
||||||
|
return Center(
|
||||||
|
child: Text(
|
||||||
|
state.message,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.minBlueDot,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (state is CurtainModuleLoading) {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: ColorsManager.minBlueDot,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (state is CurtainModuleInitial) {
|
||||||
|
return const Center(
|
||||||
|
child: Text(
|
||||||
|
'No data available',
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorsManager.minBlueDot,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (state is CurtainModuleStatusLoaded) {
|
||||||
|
return CurtainSliderWidget(
|
||||||
|
status: state.curtainModuleStatus,
|
||||||
|
deviceId: deviceId,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const Center(
|
||||||
|
child: Text(
|
||||||
|
'Unknown state',
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorsManager.minBlueDot,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CurtainSliderWidget extends StatefulWidget {
|
||||||
|
final CurtainModuleStatusModel status;
|
||||||
|
final String deviceId;
|
||||||
|
|
||||||
|
const CurtainSliderWidget({
|
||||||
|
super.key,
|
||||||
|
required this.status,
|
||||||
|
required this.deviceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CurtainSliderWidget> createState() => _CurtainSliderWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CurtainSliderWidgetState extends State<CurtainSliderWidget> {
|
||||||
|
double? _localValue; // For temporary drag state
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// If user is dragging, use local value. Otherwise, use Firebase-synced state
|
||||||
|
final double currentSliderValue =
|
||||||
|
_localValue ?? widget.status.percentControl / 100;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'${(currentSliderValue * 100).round()}%',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.minBlueDot,
|
||||||
|
fontSize: 25,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Slider(
|
||||||
|
value: currentSliderValue,
|
||||||
|
min: 0,
|
||||||
|
max: 1,
|
||||||
|
divisions: 10, // 10% step
|
||||||
|
activeColor: ColorsManager.minBlueDot,
|
||||||
|
thumbColor: ColorsManager.primaryColor,
|
||||||
|
inactiveColor: ColorsManager.whiteColors,
|
||||||
|
|
||||||
|
// Start dragging — use local control
|
||||||
|
onChangeStart: (_) {
|
||||||
|
setState(() {
|
||||||
|
_localValue = currentSliderValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// While dragging — update temporary value
|
||||||
|
onChanged: (value) {
|
||||||
|
final steppedValue = (value * 10).roundToDouble() / 10;
|
||||||
|
setState(() {
|
||||||
|
_localValue = steppedValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// On release — send API and return to Firebase-controlled state
|
||||||
|
onChangeEnd: (value) {
|
||||||
|
final int targetPercent = (value * 100).round();
|
||||||
|
|
||||||
|
// Dispatch API call
|
||||||
|
context.read<CurtainModuleBloc>().add(
|
||||||
|
SendCurtainPercentToApiEvent(
|
||||||
|
deviceId: widget.deviceId,
|
||||||
|
status: Status(
|
||||||
|
code: 'percent_control',
|
||||||
|
value: targetPercent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Revert back to Firebase-synced stream
|
||||||
|
setState(() {
|
||||||
|
_localValue = null;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class NormalTextBodyForDialog extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final String step1;
|
||||||
|
final String step2;
|
||||||
|
|
||||||
|
const NormalTextBodyForDialog({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.step1,
|
||||||
|
required this.step2,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
step1,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
step2,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class NumberInputField extends StatelessWidget {
|
||||||
|
final TextEditingController controller;
|
||||||
|
|
||||||
|
const NumberInputField({super.key, required this.controller});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return TextField(
|
||||||
|
controller: controller,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
isDense: true,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
import 'package:syncrow_web/web_layout/default_container.dart';
|
||||||
|
|
||||||
|
class PrefReversCardWidget extends StatelessWidget {
|
||||||
|
final void Function() onTap;
|
||||||
|
final String title;
|
||||||
|
final String body;
|
||||||
|
const PrefReversCardWidget({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.body,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DefaultContainer(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 8,
|
||||||
|
child: Text(
|
||||||
|
title,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.grayBorder,
|
||||||
|
fontSize: 15,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 20,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(3),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
borderRadius: const BorderRadius.horizontal(
|
||||||
|
left: Radius.circular(10),
|
||||||
|
right: Radius.circular(10)),
|
||||||
|
border: Border.all(color: ColorsManager.grayBorder)),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
Assets.reverseArrows,
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 100,
|
||||||
|
child: Text(
|
||||||
|
body,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 18,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,137 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/models/curtain_module_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_calibration_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/pref_revers_card_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/quick_calibration_dialog.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/web_layout/default_container.dart';
|
||||||
|
|
||||||
|
class CurtainModulePrefrencesDialog extends StatelessWidget {
|
||||||
|
final CurtainModuleStatusModel curtainModuleStatusModel;
|
||||||
|
final String deviceId;
|
||||||
|
|
||||||
|
const CurtainModulePrefrencesDialog({
|
||||||
|
super.key,
|
||||||
|
required this.curtainModuleStatusModel,
|
||||||
|
required this.deviceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(_) {
|
||||||
|
return AlertDialog(
|
||||||
|
backgroundColor: ColorsManager.CircleImageBackground,
|
||||||
|
contentPadding: const EdgeInsets.all(30),
|
||||||
|
title: const Center(
|
||||||
|
child: Text(
|
||||||
|
'Preferences',
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorsManager.blueColor,
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
content: BlocBuilder<CurtainModuleBloc, CurtainModuleState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is CurtainModuleLoading) {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
} else if (state is CurtainModuleStatusLoaded) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 300,
|
||||||
|
width: 400,
|
||||||
|
child: GridView(
|
||||||
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
childAspectRatio: 1.5,
|
||||||
|
mainAxisSpacing: 10,
|
||||||
|
crossAxisSpacing: 10,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
PrefReversCardWidget(
|
||||||
|
title: state.curtainModuleStatus.controlBack,
|
||||||
|
body: 'Motor Steering',
|
||||||
|
onTap: () {
|
||||||
|
context.read<CurtainModuleBloc>().add(
|
||||||
|
ChangeControlBackEvent(
|
||||||
|
deviceId: deviceId,
|
||||||
|
controlBack:
|
||||||
|
state.curtainModuleStatus.controlBack ==
|
||||||
|
'forward'
|
||||||
|
? 'back'
|
||||||
|
: 'forward',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
PrefReversCardWidget(
|
||||||
|
title: state.curtainModuleStatus.elecMachineryMode,
|
||||||
|
body: 'Motor Mode',
|
||||||
|
onTap: () => context.read<CurtainModuleBloc>().add(
|
||||||
|
ChangeElecMachineryModeEvent(
|
||||||
|
deviceId: deviceId,
|
||||||
|
elecMachineryMode:
|
||||||
|
state.curtainModuleStatus.elecMachineryMode ==
|
||||||
|
'dry_contact'
|
||||||
|
? 'strong_power'
|
||||||
|
: 'dry_contact',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
DefaultContainer(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => AccurateCalibrationDialog(
|
||||||
|
deviceId: deviceId,
|
||||||
|
parentContext: context,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text('Accurte Calibration',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
DefaultContainer(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => QuickCalibrationDialog(
|
||||||
|
timControl: state.curtainModuleStatus.trTimeControl,
|
||||||
|
deviceId: deviceId,
|
||||||
|
parentContext: context),
|
||||||
|
),
|
||||||
|
child: const Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text('Quick Calibration',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,122 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/bloc/curtain_module_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/calibrate_completed_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/number_input_textfield.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class QuickCalibratingDialog extends StatefulWidget {
|
||||||
|
final int timControl;
|
||||||
|
final String deviceId;
|
||||||
|
final BuildContext parentContext;
|
||||||
|
const QuickCalibratingDialog({
|
||||||
|
super.key,
|
||||||
|
required this.timControl,
|
||||||
|
required this.deviceId,
|
||||||
|
required this.parentContext,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<QuickCalibratingDialog> createState() => _QuickCalibratingDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _QuickCalibratingDialogState extends State<QuickCalibratingDialog> {
|
||||||
|
late TextEditingController _controller;
|
||||||
|
String? _errorText;
|
||||||
|
|
||||||
|
void _onRightTap() {
|
||||||
|
final value = int.tryParse(_controller.text);
|
||||||
|
|
||||||
|
if (value == null || value < 10 || value > 120) {
|
||||||
|
setState(() {
|
||||||
|
_errorText = 'Number should be between 10 and 120';
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_errorText = null;
|
||||||
|
});
|
||||||
|
widget.parentContext.read<CurtainModuleBloc>().add(
|
||||||
|
ChangeTimerControlEvent(
|
||||||
|
deviceId: widget.deviceId,
|
||||||
|
timControl: value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Navigator.of(widget.parentContext).pop();
|
||||||
|
showDialog(
|
||||||
|
context: widget.parentContext,
|
||||||
|
builder: (_) => CalibrateCompletedDialog(
|
||||||
|
parentContext: widget.parentContext,
|
||||||
|
deviceId: widget.deviceId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_controller = TextEditingController(text: widget.timControl.toString());
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(_) {
|
||||||
|
return AlertDialog(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
content: AccurateDialogWidget(
|
||||||
|
title: 'Calibrating',
|
||||||
|
body: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'1. please Enter the Travel Time:',
|
||||||
|
style: TextStyle(color: ColorsManager.grayBorder),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Container(
|
||||||
|
width: 150,
|
||||||
|
height: 40,
|
||||||
|
padding: const EdgeInsets.all(5),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: NumberInputField(controller: _controller),
|
||||||
|
),
|
||||||
|
const Expanded(
|
||||||
|
child: Text(
|
||||||
|
'seconds',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
color: ColorsManager.blueColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_errorText != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
|
child: Text(
|
||||||
|
_errorText!,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.red,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
leftOnTap: () => Navigator.of(widget.parentContext).pop(),
|
||||||
|
rightOnTap: _onRightTap,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/accurate_dialog_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/normal_text_body_for_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain_module/widgets/quick_calibrating_dialog.dart';
|
||||||
|
|
||||||
|
class QuickCalibrationDialog extends StatelessWidget {
|
||||||
|
final int timControl;
|
||||||
|
final String deviceId;
|
||||||
|
final BuildContext parentContext;
|
||||||
|
const QuickCalibrationDialog({
|
||||||
|
super.key,
|
||||||
|
required this.timControl,
|
||||||
|
required this.deviceId,
|
||||||
|
required this.parentContext,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(_) {
|
||||||
|
return AlertDialog(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
content: AccurateDialogWidget(
|
||||||
|
title: 'Quick Calibration',
|
||||||
|
body: const NormalTextBodyForDialog(
|
||||||
|
title: 'Prepare Calibration:',
|
||||||
|
step1:
|
||||||
|
'1. Confirm that the curtain is in the fully closed and suspended state.',
|
||||||
|
step2: '2. click Next to Start calibration.',
|
||||||
|
),
|
||||||
|
leftOnTap: () => Navigator.of(parentContext).pop(),
|
||||||
|
rightOnTap: () {
|
||||||
|
Navigator.of(parentContext).pop();
|
||||||
|
showDialog(
|
||||||
|
context: parentContext,
|
||||||
|
builder: (_) => QuickCalibratingDialog(
|
||||||
|
timControl: timControl,
|
||||||
|
deviceId: deviceId,
|
||||||
|
parentContext: parentContext,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -124,7 +124,11 @@ class Assets {
|
|||||||
//assets/icons/AC.svg
|
//assets/icons/AC.svg
|
||||||
static const String ac = "assets/icons/AC.svg";
|
static const String ac = "assets/icons/AC.svg";
|
||||||
//assets/icons/Curtain.svg
|
//assets/icons/Curtain.svg
|
||||||
static const String curtain = "assets/icons/Curtain.svg";
|
static const String curtain = 'assets/icons/Curtain.svg';
|
||||||
|
static const String openCurtain = 'assets/icons/open_curtain.svg';
|
||||||
|
static const String pauseCurtain = 'assets/icons/pause_curtain.svg';
|
||||||
|
static const String closeCurtain = 'assets/icons/close_curtain.svg';
|
||||||
|
static const String reverseArrows = 'assets/icons/reverse_arrows.svg';
|
||||||
//assets/icons/doorLock.svg
|
//assets/icons/doorLock.svg
|
||||||
static const String doorLock = "assets/icons/doorLock.svg";
|
static const String doorLock = "assets/icons/doorLock.svg";
|
||||||
//assets/icons/Gateway.svg
|
//assets/icons/Gateway.svg
|
||||||
|
Reference in New Issue
Block a user