mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-09 22:57:21 +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
|
||||
static const String ac = "assets/icons/AC.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
|
||||
static const String doorLock = "assets/icons/doorLock.svg";
|
||||
//assets/icons/Gateway.svg
|
||||
|
Reference in New Issue
Block a user