mirror of
https://github.com/SyncrowIOT/web.git
synced 2026-10-23 09:17:57 +00:00
Merge pull request #224 from SyncrowIOT/SP-1597-FE-Add-Device-Settings-Column-and-Build-Device-Settings-Dialog-UI
Sp 1597 fe add device settings column and build device settings dialog UI
This commit is contained in:
@@ -211,6 +211,7 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
onChanged: widget.withSelectAll && widget.data.isNotEmpty
|
||||
? _toggleSelectAll
|
||||
: null,
|
||||
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -281,6 +282,7 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: index == widget.headers.length - 1 ? 12 : 8.0,
|
||||
vertical: 4),
|
||||
|
||||
child: Text(
|
||||
title,
|
||||
style: context.textTheme.titleSmall!.copyWith(
|
||||
@@ -301,6 +303,7 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
required int rowIndex,
|
||||
required int columnIndex,
|
||||
}) {
|
||||
|
||||
bool isBatteryLevel = content.endsWith('%');
|
||||
double? batteryLevel;
|
||||
|
||||
@@ -312,6 +315,7 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
if (isSettingsColumn) {
|
||||
return _buildSettingsIcon(rowIndex, size);
|
||||
}
|
||||
|
||||
|
||||
Color? statusColor;
|
||||
switch (content) {
|
||||
|
||||
@@ -95,7 +95,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
||||
return const RoutinesView();
|
||||
}
|
||||
if (state.createRoutineView) {
|
||||
return CreateNewRoutineView();
|
||||
return const CreateNewRoutineView();
|
||||
}
|
||||
|
||||
return BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
|
||||
|
||||
@@ -6,9 +6,11 @@ import 'package:syncrow_web/pages/common/filter/filter_widget.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_search_filters.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/device_settings_panel.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/device_batch_control_dialog.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/device_control_dialog.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/format_date_time.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
@@ -58,7 +60,8 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
'Low Battery ($lowBatteryCount)',
|
||||
];
|
||||
|
||||
final buttonLabel = (selectedDevices.length > 1) ? 'Batch Control' : 'Control';
|
||||
final buttonLabel =
|
||||
(selectedDevices.length > 1) ? 'Batch Control' : 'Control';
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
@@ -105,18 +108,23 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
if (selectedDevices.length == 1) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => DeviceControlDialog(
|
||||
builder: (context) =>
|
||||
DeviceControlDialog(
|
||||
device: selectedDevices.first,
|
||||
),
|
||||
);
|
||||
} else if (selectedDevices.length > 1) {
|
||||
final productTypes = selectedDevices
|
||||
.map((device) => device.productType)
|
||||
.toSet();
|
||||
} else if (selectedDevices.length >
|
||||
1) {
|
||||
final productTypes =
|
||||
selectedDevices
|
||||
.map((device) =>
|
||||
device.productType)
|
||||
.toSet();
|
||||
if (productTypes.length == 1) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => DeviceBatchControlDialog(
|
||||
builder: (context) =>
|
||||
DeviceBatchControlDialog(
|
||||
devices: selectedDevices,
|
||||
),
|
||||
);
|
||||
@@ -130,7 +138,9 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: isControlButtonEnabled ? Colors.white : Colors.grey,
|
||||
color: isControlButtonEnabled
|
||||
? Colors.white
|
||||
: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -166,29 +176,40 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
'Installation Date and Time',
|
||||
'Status',
|
||||
'Last Offline Date and Time',
|
||||
'Settings'
|
||||
],
|
||||
data: devicesToShow.map((device) {
|
||||
final combinedSpaceNames = device.spaces != null
|
||||
? device.spaces!.map((space) => space.spaceName).join(' > ') +
|
||||
? device.spaces!
|
||||
.map((space) => space.spaceName)
|
||||
.join(' > ') +
|
||||
(device.community != null
|
||||
? ' > ${device.community!.name}'
|
||||
: '')
|
||||
: (device.community != null ? device.community!.name : '');
|
||||
: (device.community != null
|
||||
? device.community!.name
|
||||
: '');
|
||||
|
||||
return [
|
||||
device.name ?? '',
|
||||
device.productName ?? '',
|
||||
device.uuid ?? '',
|
||||
(device.spaces != null && device.spaces!.isNotEmpty)
|
||||
(device.spaces != null &&
|
||||
device.spaces!.isNotEmpty)
|
||||
? device.spaces![0].spaceName
|
||||
: '',
|
||||
combinedSpaceNames,
|
||||
device.batteryLevel != null ? '${device.batteryLevel}%' : '-',
|
||||
formatDateTime(DateTime.fromMillisecondsSinceEpoch(
|
||||
(device.createTime ?? 0) * 1000)),
|
||||
device.batteryLevel != null
|
||||
? '${device.batteryLevel}%'
|
||||
: '-',
|
||||
formatDateTime(
|
||||
DateTime.fromMillisecondsSinceEpoch(
|
||||
(device.createTime ?? 0) * 1000)),
|
||||
device.online == true ? 'Online' : 'Offline',
|
||||
formatDateTime(DateTime.fromMillisecondsSinceEpoch(
|
||||
(device.updateTime ?? 0) * 1000)),
|
||||
formatDateTime(
|
||||
DateTime.fromMillisecondsSinceEpoch(
|
||||
(device.updateTime ?? 0) * 1000)),
|
||||
'Settings',
|
||||
];
|
||||
}).toList(),
|
||||
onSelectionChanged: (selectedRows) {
|
||||
@@ -202,6 +223,10 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
.map((device) => device.uuid!)
|
||||
.toList(),
|
||||
isEmpty: devicesToShow.isEmpty,
|
||||
onSettingsPressed: (rowIndex) {
|
||||
final device = devicesToShow[rowIndex];
|
||||
showDeviceSettingsSidebar(context, device);
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -213,4 +238,37 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void showDeviceSettingsSidebar(BuildContext context, AllDevicesModel device) {
|
||||
showGeneralDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierLabel: "Device Settings",
|
||||
transitionDuration: const Duration(milliseconds: 300),
|
||||
pageBuilder: (context, anim1, anim2) {
|
||||
return Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Material(
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width * 0.3,
|
||||
color: ColorsManager.whiteColors,
|
||||
child: DeviceSettingsPanel(
|
||||
device: device,
|
||||
onClose: () => Navigator.of(context).pop(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
transitionBuilder: (context, anim1, anim2, child) {
|
||||
return SlideTransition(
|
||||
position: Tween<Offset>(
|
||||
begin: const Offset(1, 0),
|
||||
end: Offset.zero,
|
||||
).animate(anim1),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/device_info_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
|
||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||
import 'package:syncrow_web/services/space_mana_api.dart';
|
||||
import 'package:syncrow_web/utils/snack_bar.dart';
|
||||
part 'setting_bloc_event.dart';
|
||||
|
||||
class SettingDeviceBloc extends Bloc<DeviceSettingEvent, DeviceSettingsState> {
|
||||
final String deviceId;
|
||||
SettingDeviceBloc({
|
||||
required this.deviceId,
|
||||
}) : super(const DeviceSettingsInitial()) {
|
||||
on<DeviceSettingInitialInfo>(_fetchDeviceInfo);
|
||||
on<SettingBlocSaveName>(_saveName);
|
||||
on<ChangeNameEvent>(_changeName);
|
||||
on<SettingBlocDeleteDevice>(_deleteDevice);
|
||||
on<SettingBlocFetchRooms>(_fetchRooms);
|
||||
on<SettingBlocAssignRoom>(_onAssignDevice);
|
||||
}
|
||||
final TextEditingController nameController = TextEditingController();
|
||||
List<SubSpaceModel> roomsList = [];
|
||||
bool isEditingName = false;
|
||||
|
||||
bool _validateInputs() {
|
||||
final nameError = _fullNameValidator(nameController.text);
|
||||
if (nameError != null) {
|
||||
CustomSnackBar.displaySnackBar(nameError);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String? _fullNameValidator(String? value) {
|
||||
if (value == null) return 'name is required';
|
||||
final withoutExtraSpaces = value.replaceAll(RegExp(r"\s+"), ' ').trim();
|
||||
if (withoutExtraSpaces.length < 2 || withoutExtraSpaces.length > 30) {
|
||||
return 'name must be between 2 and 30 characters long';
|
||||
}
|
||||
if (RegExp(r"/[^ a-zA-Z0-9-\']/").hasMatch(withoutExtraSpaces)) {
|
||||
return 'Only alphanumeric characters, space, dash and single quote are allowed';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> _saveName(
|
||||
SettingBlocSaveName event, Emitter<DeviceSettingsState> emit) async {
|
||||
if (_validateInputs()) return;
|
||||
try {
|
||||
emit(DeviceSettingsLoading());
|
||||
await DevicesManagementApi.putDeviceName(
|
||||
deviceId: deviceId, deviceName: nameController.text);
|
||||
add(DeviceSettingInitialInfo());
|
||||
CustomSnackBar.displaySnackBar('Save Successfully');
|
||||
emit(DeviceSettingsUpdate(deviceName: nameController.text));
|
||||
} catch (e) {
|
||||
emit(DeviceSettingsError(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetchDeviceInfo(
|
||||
DeviceSettingInitialInfo event, Emitter<DeviceSettingsState> emit) async {
|
||||
try {
|
||||
emit(DeviceSettingsLoading());
|
||||
var response = await DevicesManagementApi.getDeviceInfo(deviceId);
|
||||
DeviceInfoModel deviceInfo = DeviceInfoModel.fromJson(response);
|
||||
nameController.text = deviceInfo.name;
|
||||
emit(DeviceSettingsUpdate(
|
||||
deviceName: nameController.text,
|
||||
deviceInfo: deviceInfo,
|
||||
roomsList: roomsList,
|
||||
));
|
||||
} catch (e) {
|
||||
emit(DeviceSettingsError(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
bool editName = false;
|
||||
final FocusNode focusNode = FocusNode();
|
||||
|
||||
void _changeName(ChangeNameEvent event, Emitter<DeviceSettingsState> emit) {
|
||||
emit(DeviceSettingsInitial(
|
||||
deviceName: nameController.text,
|
||||
deviceId: deviceId,
|
||||
isEditingName: event.value ?? false,
|
||||
editingNameValue: event.value?.toString() ?? '',
|
||||
deviceInfo: state.deviceInfo,
|
||||
));
|
||||
editName = event.value!;
|
||||
if (editName) {
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
focusNode.requestFocus();
|
||||
});
|
||||
} else {
|
||||
add(const SettingBlocSaveName());
|
||||
focusNode.unfocus();
|
||||
}
|
||||
emit(DeviceSettingsUpdate(
|
||||
deviceName: nameController.text,
|
||||
deviceInfo: state.deviceInfo,
|
||||
roomsList: roomsList,
|
||||
));
|
||||
}
|
||||
|
||||
void _deleteDevice(
|
||||
SettingBlocDeleteDevice event, Emitter<DeviceSettingsState> emit) async {
|
||||
try {
|
||||
emit(DeviceSettingsLoading());
|
||||
await DevicesManagementApi.resetDevice(devicesUuid: deviceId);
|
||||
CustomSnackBar.displaySnackBar('Reset Successfully');
|
||||
emit(DeviceSettingsUpdate(
|
||||
deviceName: nameController.text,
|
||||
deviceInfo: state.deviceInfo,
|
||||
roomsList: roomsList,
|
||||
));
|
||||
} catch (e) {
|
||||
emit(DeviceSettingsError(message: e.toString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void _onAssignDevice(
|
||||
SettingBlocAssignRoom event, Emitter<DeviceSettingsState> emit) async {
|
||||
try {
|
||||
emit(DeviceSettingsLoading());
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
await CommunitySpaceManagementApi.assignDeviceToRoom(
|
||||
communityId: event.communityUuid,
|
||||
spaceId: event.spaceUuid,
|
||||
subSpaceId: event.subSpaceUuid,
|
||||
deviceId: deviceId,
|
||||
projectId: projectUuid);
|
||||
add(DeviceSettingInitialInfo());
|
||||
CustomSnackBar.displaySnackBar('Save Successfully');
|
||||
emit(DeviceSettingsSaveSelectionSuccess());
|
||||
} catch (e) {
|
||||
emit(DeviceSettingsError(message: e.toString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void _fetchRooms(
|
||||
SettingBlocFetchRooms event, Emitter<DeviceSettingsState> emit) async {
|
||||
try {
|
||||
emit(DeviceSettingsLoading());
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
roomsList = await CommunitySpaceManagementApi.getSubSpaceBySpaceId(
|
||||
communityId: event.communityUuid,
|
||||
spaceId: event.spaceUuid,
|
||||
projectId: projectUuid);
|
||||
emit(DeviceSettingsUpdate(
|
||||
deviceName: nameController.text,
|
||||
deviceInfo: state.deviceInfo,
|
||||
roomsList: roomsList,
|
||||
));
|
||||
} catch (e) {
|
||||
emit(DeviceSettingsError(message: e.toString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
part of 'setting_bloc_bloc.dart';
|
||||
|
||||
abstract class DeviceSettingEvent extends Equatable {
|
||||
const DeviceSettingEvent();
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class SettingBlocSaveDeviceName extends DeviceSettingEvent {
|
||||
final String deviceName;
|
||||
final String deviceId;
|
||||
|
||||
const SettingBlocSaveDeviceName(
|
||||
{required this.deviceName, required this.deviceId});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [deviceName, deviceId];
|
||||
}
|
||||
|
||||
class SettingBlocStartEditingName extends DeviceSettingEvent {}
|
||||
|
||||
class SettingBlocCancelEditingName extends DeviceSettingEvent {}
|
||||
|
||||
class SettingBlocChangeEditingNameValue extends DeviceSettingEvent {
|
||||
final String value;
|
||||
const SettingBlocChangeEditingNameValue(this.value);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [value];
|
||||
}
|
||||
|
||||
class SettingBlocFetchRooms extends DeviceSettingEvent {
|
||||
final String communityUuid;
|
||||
final String spaceUuid;
|
||||
|
||||
const SettingBlocFetchRooms(
|
||||
{required this.communityUuid, required this.spaceUuid});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [communityUuid, spaceUuid];
|
||||
}
|
||||
|
||||
class SettingBlocSaveName extends DeviceSettingEvent {
|
||||
const SettingBlocSaveName();
|
||||
}
|
||||
|
||||
class DeviceSettingInitialInfo extends DeviceSettingEvent {}
|
||||
|
||||
class ChangeNameEvent extends DeviceSettingEvent {
|
||||
final bool? value;
|
||||
const ChangeNameEvent({this.value});
|
||||
}
|
||||
|
||||
class SettingBlocDeleteDevice extends DeviceSettingEvent {}
|
||||
|
||||
class SettingBlocAssignRoom extends DeviceSettingEvent {
|
||||
final String communityUuid;
|
||||
final String spaceUuid;
|
||||
final String subSpaceUuid;
|
||||
|
||||
const SettingBlocAssignRoom({
|
||||
required this.communityUuid,
|
||||
required this.spaceUuid,
|
||||
required this.subSpaceUuid,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [spaceUuid, communityUuid, subSpaceUuid];
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/device_info_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
|
||||
|
||||
abstract class DeviceSettingsState extends Equatable {
|
||||
const DeviceSettingsState({this.deviceInfo});
|
||||
|
||||
final DeviceInfoModel? deviceInfo;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [deviceInfo];
|
||||
}
|
||||
|
||||
class DeviceSettingsInitial extends DeviceSettingsState {
|
||||
final String deviceName;
|
||||
final String deviceId;
|
||||
final bool isEditingName;
|
||||
final String editingNameValue;
|
||||
|
||||
const DeviceSettingsInitial({
|
||||
this.deviceName = '',
|
||||
this.deviceId = '',
|
||||
this.isEditingName = false,
|
||||
this.editingNameValue = '',
|
||||
super.deviceInfo,
|
||||
});
|
||||
|
||||
DeviceSettingsInitial copyWith({
|
||||
String? deviceName,
|
||||
String? deviceId,
|
||||
bool? isEditingName,
|
||||
String? editingNameValue,
|
||||
}) =>
|
||||
DeviceSettingsInitial(
|
||||
deviceName: deviceName ?? this.deviceName,
|
||||
deviceId: deviceId ?? this.deviceId,
|
||||
isEditingName: isEditingName ?? this.isEditingName,
|
||||
editingNameValue: editingNameValue ?? this.editingNameValue,
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
[deviceName, deviceId, isEditingName, editingNameValue];
|
||||
}
|
||||
|
||||
class DeviceSettingsLoading extends DeviceSettingsState {}
|
||||
|
||||
class DeviceSettingsUpdate extends DeviceSettingsState {
|
||||
final String? deviceName;
|
||||
final List<SubSpaceModel> roomsList;
|
||||
|
||||
const DeviceSettingsUpdate({
|
||||
this.deviceName,
|
||||
super.deviceInfo,
|
||||
this.roomsList = const [],
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [deviceName, deviceInfo, roomsList];
|
||||
}
|
||||
|
||||
class DeviceSettingsError extends DeviceSettingsState {
|
||||
final String message;
|
||||
|
||||
const DeviceSettingsError({required this.message});
|
||||
@override
|
||||
List<Object?> get props => [message];
|
||||
}
|
||||
|
||||
class DeviceSettingsFetchRooms extends DeviceSettingsState {
|
||||
final List<SubSpaceModel> roomsList;
|
||||
|
||||
const DeviceSettingsFetchRooms({required this.roomsList});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [roomsList];
|
||||
}
|
||||
|
||||
class DeviceSettingsSaveSelectionSuccess extends DeviceSettingsState {}
|
||||
|
||||
class ChangeNameState extends DeviceSettingsState {}
|
||||
@@ -0,0 +1,28 @@
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class DeviceIconTypeHelper {
|
||||
static const Map<String, String> _iconMap = {
|
||||
'AC': Assets.ac,
|
||||
'GW': Assets.gateway,
|
||||
'CPS': Assets.sensors,
|
||||
'DL': Assets.doorLock,
|
||||
'WPS': Assets.sensors,
|
||||
'3G': Assets.gangSwitch,
|
||||
'2G': Assets.twoGang,
|
||||
'1G': Assets.oneGang,
|
||||
'CUR': Assets.curtain,
|
||||
'WH': Assets.waterHeater,
|
||||
'DS': Assets.doorSensor,
|
||||
'1GT': Assets.oneTouchSwitch,
|
||||
'2GT': Assets.twoTouchSwitch,
|
||||
'3GT': Assets.threeTouchSwitch,
|
||||
'GD': Assets.garageDoor,
|
||||
'WL': Assets.waterLeakNormal,
|
||||
'NCPS': Assets.sensors,
|
||||
};
|
||||
|
||||
static String getDeviceIconByTypeCode(String? typeCode) {
|
||||
if (typeCode == null) return Assets.logoHorizontal;
|
||||
return _iconMap[typeCode] ?? Assets.logoHorizontal;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/device_info_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/sub_space_dialog.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/web_layout/default_container.dart';
|
||||
|
||||
class DeviceManagementContent extends StatelessWidget {
|
||||
const DeviceManagementContent({
|
||||
super.key,
|
||||
required this.device,
|
||||
required this.subSpaces,
|
||||
required this.deviceInfo,
|
||||
});
|
||||
|
||||
final AllDevicesModel device;
|
||||
final List<SubSpaceModel> subSpaces;
|
||||
final DeviceInfoModel deviceInfo;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget infoRow(
|
||||
{required String label,
|
||||
required String value,
|
||||
Widget? trailing,
|
||||
required Color? valueColor}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 6.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: context.theme.textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 14,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
value,
|
||||
textAlign: TextAlign.end,
|
||||
style: context.theme.textTheme.bodyMedium!
|
||||
.copyWith(fontSize: 14, color: valueColor),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
trailing ?? const SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return DefaultContainer(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 5),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
showSubSpaceDialog(
|
||||
context,
|
||||
communityUuid: device.community!.uuid!,
|
||||
spaceUuid: device.spaces!.first.uuid!,
|
||||
subSpaces: subSpaces,
|
||||
selected: device.subspace!.uuid,
|
||||
);
|
||||
},
|
||||
child: infoRow(
|
||||
label: 'Sub-Space:',
|
||||
value: deviceInfo.subspace.subspaceName,
|
||||
valueColor: ColorsManager.textGray,
|
||||
trailing: const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 16,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(color: ColorsManager.dividerColor),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: infoRow(
|
||||
label: 'Virtual Address:',
|
||||
value: deviceInfo.productUuid,
|
||||
valueColor: ColorsManager.blackColor,
|
||||
trailing: InkWell(
|
||||
onTap: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(text: device.productUuid ?? ''),
|
||||
);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Virtual Address copied to clipboard'),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.copy,
|
||||
size: 16,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(color: ColorsManager.dividerColor),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: infoRow(
|
||||
label: 'MAC Address:',
|
||||
valueColor: ColorsManager.blackColor,
|
||||
value: deviceInfo.macAddress,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/device_icon_type_helper.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/device_management_content.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/remove_device_widget.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/device_info_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/web_layout/default_container.dart';
|
||||
|
||||
class DeviceSettingsPanel extends StatelessWidget {
|
||||
final VoidCallback? onClose;
|
||||
final AllDevicesModel device;
|
||||
const DeviceSettingsPanel({super.key, this.onClose, required this.device});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final sectionTitle = context.theme.textTheme.titleMedium!.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: ColorsManager.grayColor,
|
||||
);
|
||||
return BlocProvider(
|
||||
create: (context) => SettingDeviceBloc(
|
||||
deviceId: device.uuid ?? '',
|
||||
)
|
||||
..add(DeviceSettingInitialInfo())
|
||||
..add(SettingBlocFetchRooms(
|
||||
communityUuid: device.community!.uuid!,
|
||||
spaceUuid: device.spaces!.first.uuid!,
|
||||
)),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return BlocBuilder<SettingDeviceBloc, DeviceSettingsState>(
|
||||
builder: (context, state) {
|
||||
final _bloc = context.read<SettingDeviceBloc>();
|
||||
final iconPath = DeviceIconTypeHelper.getDeviceIconByTypeCode(
|
||||
device.productType);
|
||||
final deviceInfo = state is DeviceSettingsUpdate
|
||||
? state.deviceInfo ?? DeviceInfoModel.empty()
|
||||
: DeviceInfoModel.empty();
|
||||
final subSpaces =
|
||||
state is DeviceSettingsUpdate ? state.roomsList ?? [] : [];
|
||||
return Stack(
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width * 0.3,
|
||||
color: ColorsManager.grey25,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20, vertical: 24),
|
||||
child: ListView(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: SvgPicture.asset(Assets.closeSettingsIcon),
|
||||
onPressed:
|
||||
onClose ?? () => Navigator.of(context).pop(),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Device Settings',
|
||||
style:
|
||||
context.theme.textTheme.titleLarge!.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: ColorsManager.primaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
DefaultContainer(
|
||||
child: Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 40,
|
||||
backgroundColor:
|
||||
const Color.fromARGB(177, 213, 213, 213),
|
||||
child: CircleAvatar(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
radius: 36,
|
||||
child: SvgPicture.asset(
|
||||
iconPath,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Device Name:',
|
||||
style: context.textTheme.bodyMedium!
|
||||
.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
TextFormField(
|
||||
maxLength: 30,
|
||||
style: const TextStyle(
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
textAlign: TextAlign.start,
|
||||
focusNode: _bloc.focusNode,
|
||||
controller: _bloc.nameController,
|
||||
enabled: _bloc.editName,
|
||||
onFieldSubmitted: (value) {
|
||||
_bloc.add(const ChangeNameEvent(
|
||||
value: false));
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
border: InputBorder.none,
|
||||
fillColor: Colors.white10,
|
||||
counterText: '',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Visibility(
|
||||
visible: _bloc.editName != true,
|
||||
replacement: const SizedBox(),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
_bloc.add(
|
||||
const ChangeNameEvent(value: true));
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
Assets.editNameIconSettings,
|
||||
color: ColorsManager.grayColor,
|
||||
height: 20,
|
||||
width: 20,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
Text('Device Management', style: sectionTitle),
|
||||
DeviceManagementContent(
|
||||
device: device,
|
||||
subSpaces: subSpaces.cast<SubSpaceModel>(),
|
||||
deviceInfo: deviceInfo,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
RemoveDeviceWidget(bloc: _bloc),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (state is DeviceSettingsLoading)
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
child: const Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: ColorsManager.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/web_layout/default_container.dart';
|
||||
|
||||
class RemoveDeviceWidget extends StatelessWidget {
|
||||
const RemoveDeviceWidget({
|
||||
super.key,
|
||||
required SettingDeviceBloc bloc,
|
||||
}) : _bloc = bloc;
|
||||
|
||||
final SettingDeviceBloc _bloc;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
'Remove Device',
|
||||
style: context.textTheme.bodyMedium!.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: ColorsManager.red,
|
||||
),
|
||||
),
|
||||
content: Text(
|
||||
'Are you sure you want to remove this device?',
|
||||
style: context.textTheme.bodyMedium!.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(
|
||||
'Cancel',
|
||||
style: context.textTheme.bodyMedium!.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
_bloc.add(SettingBlocDeleteDevice());
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(
|
||||
'Remove',
|
||||
style: context.textTheme.bodyMedium!.copyWith(
|
||||
color: ColorsManager.red,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: DefaultContainer(
|
||||
padding: const EdgeInsets.all(25),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Remove Device',
|
||||
style: context.textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 14,
|
||||
color: ColorsManager.red,
|
||||
fontWeight: FontWeight.w700),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
class DeviceInfoModel {
|
||||
final int activeTime;
|
||||
final String category;
|
||||
final String categoryName;
|
||||
final int createTime;
|
||||
final String gatewayId;
|
||||
final String icon;
|
||||
final String ip;
|
||||
final String lat;
|
||||
final String localKey;
|
||||
final String lon;
|
||||
final String model;
|
||||
final String name;
|
||||
final String nodeId;
|
||||
final bool online;
|
||||
final String ownerId;
|
||||
final String productName;
|
||||
final bool sub;
|
||||
final String timeZone;
|
||||
final int updateTime;
|
||||
final String uuid;
|
||||
final String productUuid;
|
||||
final String productType;
|
||||
final String permissionType;
|
||||
final String macAddress;
|
||||
final Subspace subspace;
|
||||
|
||||
DeviceInfoModel({
|
||||
required this.activeTime,
|
||||
required this.category,
|
||||
required this.categoryName,
|
||||
required this.createTime,
|
||||
required this.gatewayId,
|
||||
required this.icon,
|
||||
required this.ip,
|
||||
required this.lat,
|
||||
required this.localKey,
|
||||
required this.lon,
|
||||
required this.model,
|
||||
required this.name,
|
||||
required this.nodeId,
|
||||
required this.online,
|
||||
required this.ownerId,
|
||||
required this.productName,
|
||||
required this.sub,
|
||||
required this.timeZone,
|
||||
required this.updateTime,
|
||||
required this.uuid,
|
||||
required this.productUuid,
|
||||
required this.productType,
|
||||
required this.permissionType,
|
||||
required this.macAddress,
|
||||
required this.subspace,
|
||||
});
|
||||
|
||||
factory DeviceInfoModel.fromJson(Map<String, dynamic> json) {
|
||||
return DeviceInfoModel(
|
||||
activeTime: json['activeTime'] as int? ?? 0,
|
||||
category: json['category'] ?? '',
|
||||
categoryName: json['categoryName'] as String? ?? '',
|
||||
createTime: json['createTime'] as int? ?? 0,
|
||||
gatewayId: json['gatewayId'] as String? ?? '',
|
||||
icon: json['icon'] as String? ?? '',
|
||||
ip: json['ip'] as String? ?? '',
|
||||
lat: json['lat'] as String? ?? '',
|
||||
localKey: json['localKey'] as String? ?? '',
|
||||
lon: json['lon'] as String? ?? '',
|
||||
model: json['model'] as String? ?? '',
|
||||
name: json['name'] as String? ?? '',
|
||||
nodeId: json['nodeId'] as String? ?? '',
|
||||
online: json['online'] as bool? ?? false,
|
||||
ownerId: json['ownerId'] as String? ?? '',
|
||||
productName: json['productName'] as String? ?? '',
|
||||
sub: json['sub'] as bool? ?? false,
|
||||
timeZone: json['timeZone'] as String? ?? '',
|
||||
updateTime: json['updateTime'] as int? ?? 0,
|
||||
uuid: json['uuid'] as String? ?? '',
|
||||
productUuid: json['productUuid'] as String? ?? '',
|
||||
productType: json['productType'] as String? ?? '',
|
||||
permissionType: json['permissionType'] as String? ?? '',
|
||||
macAddress: json['macAddress'] as String? ?? '',
|
||||
subspace:
|
||||
Subspace.fromJson(json['subspace'] as Map<String, dynamic>? ?? {}),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'activeTime': activeTime,
|
||||
'category': category,
|
||||
'categoryName': categoryName,
|
||||
'createTime': createTime,
|
||||
'gatewayId': gatewayId,
|
||||
'icon': icon,
|
||||
'ip': ip,
|
||||
'lat': lat,
|
||||
'localKey': localKey,
|
||||
'lon': lon,
|
||||
'model': model,
|
||||
'name': name,
|
||||
'nodeId': nodeId,
|
||||
'online': online,
|
||||
'ownerId': ownerId,
|
||||
'productName': productName,
|
||||
'sub': sub,
|
||||
'timeZone': timeZone,
|
||||
'updateTime': updateTime,
|
||||
'uuid': uuid,
|
||||
'productUuid': productUuid,
|
||||
'productType': productType,
|
||||
'permissionType': permissionType,
|
||||
'macAddress': macAddress,
|
||||
'subspace': subspace.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
static DeviceInfoModel empty() {
|
||||
return DeviceInfoModel(
|
||||
activeTime: 0,
|
||||
category: '',
|
||||
categoryName: '',
|
||||
createTime: 0,
|
||||
gatewayId: '',
|
||||
icon: '',
|
||||
ip: '',
|
||||
lat: '',
|
||||
localKey: '',
|
||||
lon: '',
|
||||
model: '',
|
||||
name: '',
|
||||
nodeId: '',
|
||||
online: false,
|
||||
ownerId: '',
|
||||
productName: '',
|
||||
sub: false,
|
||||
timeZone: '',
|
||||
updateTime: 0,
|
||||
uuid: '',
|
||||
productUuid: '',
|
||||
productType: '',
|
||||
permissionType: '',
|
||||
macAddress: '',
|
||||
subspace: Subspace(
|
||||
uuid: '',
|
||||
createdAt: '',
|
||||
updatedAt: '',
|
||||
subspaceName: '',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Subspace {
|
||||
final String uuid;
|
||||
final String createdAt;
|
||||
final String updatedAt;
|
||||
final String subspaceName;
|
||||
|
||||
Subspace({
|
||||
required this.uuid,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
required this.subspaceName,
|
||||
});
|
||||
|
||||
factory Subspace.fromJson(Map<String, dynamic> json) {
|
||||
return Subspace(
|
||||
uuid: json['uuid'] as String? ?? '',
|
||||
createdAt: json['createdAt'] as String? ?? '',
|
||||
updatedAt: json['updatedAt'] as String? ?? '',
|
||||
subspaceName: json['subspaceName'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'uuid': uuid,
|
||||
'createdAt': createdAt,
|
||||
'updatedAt': updatedAt,
|
||||
'subspaceName': subspaceName,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import 'package:syncrow_web/pages/visitor_password/model/device_model.dart';
|
||||
|
||||
class SubSpaceModel {
|
||||
final String? id;
|
||||
final String? name;
|
||||
List<DeviceModel>? devices;
|
||||
|
||||
SubSpaceModel({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.devices,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'devices': devices?.map((device) => device.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
factory SubSpaceModel.fromJson(Map<String, dynamic> json) {
|
||||
List<DeviceModel> devices = [];
|
||||
if (json['devices'] != null) {
|
||||
for (var device in json['devices']) {
|
||||
devices.add(DeviceModel.fromJson(device));
|
||||
}
|
||||
}
|
||||
return SubSpaceModel(
|
||||
id: json['uuid'] as String? ?? '',
|
||||
name: json['subspaceName'] as String? ?? '',
|
||||
devices: devices.isNotEmpty ? devices : null as List<DeviceModel>?,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/subspace_dialog_buttons.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class SubSpaceDialog extends StatefulWidget {
|
||||
final List<SubSpaceModel> subSpaces;
|
||||
final String? selected;
|
||||
final void Function(SubSpaceModel?) onConfirmed;
|
||||
|
||||
const SubSpaceDialog({
|
||||
Key? key,
|
||||
required this.subSpaces,
|
||||
this.selected,
|
||||
required this.onConfirmed,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SubSpaceDialog> createState() => _SubSpaceDialogState();
|
||||
}
|
||||
|
||||
class _SubSpaceDialogState extends State<SubSpaceDialog> {
|
||||
String? _selectedId;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedId = widget.selected;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
insetPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 60),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
),
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width * 0.35,
|
||||
padding: const EdgeInsets.fromLTRB(0, 24, 0, 0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'Sub-Space',
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: ColorsManager.blueColor,
|
||||
fontSize: 20),
|
||||
),
|
||||
const Divider(),
|
||||
const SizedBox(height: 10),
|
||||
...widget.subSpaces.map((space) {
|
||||
return RadioListTile<String>(
|
||||
value: space.id!,
|
||||
groupValue: _selectedId,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedId = value;
|
||||
});
|
||||
},
|
||||
activeColor: Color(0xFF2962FF),
|
||||
title: Text(
|
||||
space.name ?? 'Unnamed Sub-Space',
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 15,
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
);
|
||||
}).toList(),
|
||||
const SizedBox(height: 12),
|
||||
const Divider(height: 1, thickness: 1),
|
||||
SubSpaceDialogButtons(selectedId: _selectedId, widget: widget),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void showSubSpaceDialog(
|
||||
BuildContext context, {
|
||||
required List<SubSpaceModel> subSpaces,
|
||||
String? selected,
|
||||
required String communityUuid,
|
||||
required String spaceUuid,
|
||||
}) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
builder: (ctx) => SubSpaceDialog(
|
||||
subSpaces: subSpaces,
|
||||
selected: selected,
|
||||
onConfirmed: (selectedModel) {
|
||||
if (selectedModel != null) {
|
||||
context.read<SettingDeviceBloc>().add(
|
||||
SettingBlocAssignRoom(
|
||||
communityUuid: communityUuid,
|
||||
spaceUuid: spaceUuid,
|
||||
subSpaceUuid: selectedModel.id ?? '',
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/sub_space_dialog.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class SubSpaceDialogButtons extends StatelessWidget {
|
||||
const SubSpaceDialogButtons({
|
||||
super.key,
|
||||
required String? selectedId,
|
||||
required this.widget,
|
||||
}) : _selectedId = selectedId;
|
||||
|
||||
final String? _selectedId;
|
||||
final SubSpaceDialog widget;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 50,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
right: BorderSide(
|
||||
color: ColorsManager.dividerColor,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(
|
||||
'Cancel',
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
color: ColorsManager.textGray,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
color: ColorsManager.dividerColor,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: TextButton(
|
||||
onPressed: _selectedId == null
|
||||
? null
|
||||
: () {
|
||||
final selectedModel = widget.subSpaces.firstWhere(
|
||||
(space) => space.id == _selectedId,
|
||||
orElse: () =>
|
||||
SubSpaceModel(id: null, name: '', devices: []));
|
||||
widget.onConfirmed(selectedModel);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(
|
||||
'Confirm',
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
color: ColorsManager.secondaryColor,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void showSubSpaceDialog(
|
||||
BuildContext context, {
|
||||
required List<SubSpaceModel> subSpaces,
|
||||
String? selected,
|
||||
required String communityUuid,
|
||||
required String spaceUuid,
|
||||
}) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
builder: (ctx) => SubSpaceDialog(
|
||||
subSpaces: subSpaces,
|
||||
selected: selected,
|
||||
onConfirmed: (selectedModel) {
|
||||
if (selectedModel != null) {
|
||||
context.read<SettingDeviceBloc>().add(
|
||||
SettingBlocAssignRoom(
|
||||
communityUuid: communityUuid,
|
||||
spaceUuid: spaceUuid,
|
||||
subSpaceUuid: selectedModel.id ?? '',
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -91,7 +91,8 @@ class DevicesManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> deviceBatchControl(List<String> uuids, String code, dynamic value) async {
|
||||
Future<bool> deviceBatchControl(
|
||||
List<String> uuids, String code, dynamic value) async {
|
||||
try {
|
||||
final body = {
|
||||
'devicesUuid': uuids,
|
||||
@@ -116,7 +117,8 @@ class DevicesManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<List<DeviceModel>> getDevicesByGatewayId(String gatewayId) async {
|
||||
static Future<List<DeviceModel>> getDevicesByGatewayId(
|
||||
String gatewayId) async {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.gatewayApi.replaceAll('{gatewayUuid}', gatewayId),
|
||||
showServerMessage: false,
|
||||
@@ -150,7 +152,9 @@ class DevicesManagementApi {
|
||||
String code,
|
||||
) async {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.getDeviceLogs.replaceAll('{uuid}', uuid).replaceAll('{code}', code),
|
||||
path: ApiEndpoints.getDeviceLogs
|
||||
.replaceAll('{uuid}', uuid)
|
||||
.replaceAll('{code}', code),
|
||||
showServerMessage: false,
|
||||
expectedResponseModel: (json) {
|
||||
return DeviceReport.fromJson(json['data']);
|
||||
@@ -223,7 +227,8 @@ class DevicesManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> addScheduleRecord(ScheduleEntry sendSchedule, String uuid) async {
|
||||
Future<bool> addScheduleRecord(
|
||||
ScheduleEntry sendSchedule, String uuid) async {
|
||||
try {
|
||||
final response = await HTTPService().post(
|
||||
path: ApiEndpoints.scheduleByDeviceId.replaceAll('{deviceUuid}', uuid),
|
||||
@@ -240,7 +245,8 @@ class DevicesManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<ScheduleModel>> getDeviceSchedules(String uuid, String category) async {
|
||||
Future<List<ScheduleModel>> getDeviceSchedules(
|
||||
String uuid, String category) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.getScheduleByDeviceId
|
||||
@@ -263,7 +269,9 @@ class DevicesManagementApi {
|
||||
}
|
||||
|
||||
Future<bool> updateScheduleRecord(
|
||||
{required bool enable, required String uuid, required String scheduleId}) async {
|
||||
{required bool enable,
|
||||
required String uuid,
|
||||
required String scheduleId}) async {
|
||||
try {
|
||||
final response = await HTTPService().put(
|
||||
path: ApiEndpoints.updateScheduleByDeviceId
|
||||
@@ -284,7 +292,8 @@ class DevicesManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> editScheduleRecord(String uuid, ScheduleEntry newSchedule) async {
|
||||
Future<bool> editScheduleRecord(
|
||||
String uuid, ScheduleEntry newSchedule) async {
|
||||
try {
|
||||
final response = await HTTPService().put(
|
||||
path: ApiEndpoints.scheduleByDeviceId.replaceAll('{deviceUuid}', uuid),
|
||||
@@ -335,4 +344,46 @@ class DevicesManagementApi {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> putDeviceName(
|
||||
{required String deviceId, required String deviceName}) async {
|
||||
try {
|
||||
final response = await HTTPService().put(
|
||||
path: ApiEndpoints.deviceByUuid.replaceAll('{deviceUuid}', deviceId),
|
||||
body: {"deviceName": deviceName},
|
||||
expectedResponseModel: (json) {
|
||||
return json['data'];
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
static Future getDeviceInfo(String deviceId) async {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.deviceByUuid.replaceAll('{deviceUuid}', deviceId),
|
||||
showServerMessage: false,
|
||||
expectedResponseModel: (json) {
|
||||
return json['data'] as Map<String, dynamic>;
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
static Future resetDevice({
|
||||
String? devicesUuid,
|
||||
}) async {
|
||||
final response = await HTTPService().post(
|
||||
path: ApiEndpoints.resetDevice.replaceAll('{deviceUuid}', devicesUuid!),
|
||||
showServerMessage: false,
|
||||
body: {
|
||||
"devicesUuid": [devicesUuid]
|
||||
},
|
||||
expectedResponseModel: (json) {
|
||||
return json;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/model/pagination_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subspace_model.dart';
|
||||
@@ -12,14 +13,16 @@ import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||
|
||||
class CommunitySpaceManagementApi {
|
||||
// Community Management APIs
|
||||
Future<List<CommunityModel>> fetchCommunities(String projectId, {int page = 1}) async {
|
||||
Future<List<CommunityModel>> fetchCommunities(String projectId,
|
||||
{int page = 1}) async {
|
||||
try {
|
||||
List<CommunityModel> allCommunities = [];
|
||||
bool hasNext = true;
|
||||
|
||||
while (hasNext) {
|
||||
await HTTPService().get(
|
||||
path: ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
|
||||
path: ApiEndpoints.getCommunityList
|
||||
.replaceAll('{projectId}', projectId),
|
||||
queryParameters: {
|
||||
'page': page,
|
||||
},
|
||||
@@ -55,8 +58,14 @@ class CommunitySpaceManagementApi {
|
||||
try {
|
||||
bool hasNext = false;
|
||||
await HTTPService().get(
|
||||
path: ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
|
||||
queryParameters: {'page': page, 'includeSpaces': true, 'size': 25, 'search': search},
|
||||
path:
|
||||
ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
|
||||
queryParameters: {
|
||||
'page': page,
|
||||
'includeSpaces': true,
|
||||
'size': 25,
|
||||
'search': search
|
||||
},
|
||||
expectedResponseModel: (json) {
|
||||
try {
|
||||
List<dynamic> jsonData = json['data'] ?? [];
|
||||
@@ -68,7 +77,10 @@ class CommunitySpaceManagementApi {
|
||||
|
||||
page = currentPage + 1;
|
||||
paginationModel = PaginationModel(
|
||||
pageNum: page, hasNext: hasNext, size: 25, communities: communityList);
|
||||
pageNum: page,
|
||||
hasNext: hasNext,
|
||||
size: 25,
|
||||
communities: communityList);
|
||||
return paginationModel;
|
||||
} catch (_) {
|
||||
hasNext = false;
|
||||
@@ -83,7 +95,8 @@ class CommunitySpaceManagementApi {
|
||||
Future<CommunityModel?> getCommunityById(String communityId) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.getCommunityById.replaceAll('{communityId}', communityId),
|
||||
path: ApiEndpoints.getCommunityById
|
||||
.replaceAll('{communityId}', communityId),
|
||||
expectedResponseModel: (json) {
|
||||
return CommunityModel.fromJson(json['data']);
|
||||
},
|
||||
@@ -95,7 +108,8 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<CommunityModel?> createCommunity(String name, String description, String projectId) async {
|
||||
Future<CommunityModel?> createCommunity(
|
||||
String name, String description, String projectId) async {
|
||||
try {
|
||||
final response = await HTTPService().post(
|
||||
path: ApiEndpoints.createCommunity.replaceAll('{projectId}', projectId),
|
||||
@@ -114,7 +128,8 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> updateCommunity(String communityId, String name, String projectId) async {
|
||||
Future<bool> updateCommunity(
|
||||
String communityId, String name, String projectId) async {
|
||||
try {
|
||||
final response = await HTTPService().put(
|
||||
path: ApiEndpoints.updateCommunity
|
||||
@@ -151,7 +166,8 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<SpacesResponse> fetchSpaces(String communityId, String projectId) async {
|
||||
Future<SpacesResponse> fetchSpaces(
|
||||
String communityId, String projectId) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.listSpaces
|
||||
@@ -177,7 +193,8 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<SpaceModel?> getSpace(String communityId, String spaceId, String projectId) async {
|
||||
Future<SpaceModel?> getSpace(
|
||||
String communityId, String spaceId, String projectId) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.getSpace
|
||||
@@ -289,7 +306,8 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> deleteSpace(String communityId, String spaceId, String projectId) async {
|
||||
Future<bool> deleteSpace(
|
||||
String communityId, String spaceId, String projectId) async {
|
||||
try {
|
||||
final response = await HTTPService().delete(
|
||||
path: ApiEndpoints.deleteSpace
|
||||
@@ -307,15 +325,17 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<SpaceModel>> getSpaceHierarchy(String communityId, String projectId) async {
|
||||
Future<List<SpaceModel>> getSpaceHierarchy(
|
||||
String communityId, String projectId) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.getSpaceHierarchy
|
||||
.replaceAll('{communityId}', communityId)
|
||||
.replaceAll('{projectId}', projectId),
|
||||
expectedResponseModel: (json) {
|
||||
final spaceModels =
|
||||
(json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList();
|
||||
final spaceModels = (json['data'] as List)
|
||||
.map((spaceJson) => SpaceModel.fromJson(spaceJson))
|
||||
.toList();
|
||||
|
||||
return spaceModels;
|
||||
},
|
||||
@@ -327,15 +347,17 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<SpaceModel>> getSpaceOnlyWithDevices({String? communityId, String? projectId}) async {
|
||||
Future<List<SpaceModel>> getSpaceOnlyWithDevices(
|
||||
{String? communityId, String? projectId}) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.spaceOnlyWithDevices
|
||||
.replaceAll('{communityId}', communityId!)
|
||||
.replaceAll('{projectId}', projectId!),
|
||||
expectedResponseModel: (json) {
|
||||
final spaceModels =
|
||||
(json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList();
|
||||
final spaceModels = (json['data'] as List)
|
||||
.map((spaceJson) => SpaceModel.fromJson(spaceJson))
|
||||
.toList();
|
||||
return spaceModels;
|
||||
},
|
||||
);
|
||||
@@ -345,4 +367,59 @@ class CommunitySpaceManagementApi {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static Future<List<SubSpaceModel>> getSubSpaceBySpaceId(
|
||||
{required String communityId,
|
||||
required String spaceId,
|
||||
required String projectId}) async {
|
||||
try {
|
||||
final path = ApiEndpoints.listSubspace
|
||||
.replaceFirst('{communityUuid}', communityId)
|
||||
.replaceFirst('{spaceUuid}', spaceId)
|
||||
.replaceAll('{projectUuid}', projectId);
|
||||
|
||||
final response = await HTTPService().get(
|
||||
path: path,
|
||||
queryParameters: {"page": 1, "pageSize": 10},
|
||||
showServerMessage: false,
|
||||
expectedResponseModel: (json) {
|
||||
List<SubSpaceModel> rooms = [];
|
||||
if (json['data'] != null) {
|
||||
for (var subspace in json['data']) {
|
||||
rooms.add(SubSpaceModel.fromJson(subspace));
|
||||
}
|
||||
}
|
||||
return rooms;
|
||||
},
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (error, stackTrace) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> assignDeviceToRoom(
|
||||
{required String communityId,
|
||||
required String spaceId,
|
||||
required String subSpaceId,
|
||||
required String deviceId,
|
||||
required String projectId}) async {
|
||||
try {
|
||||
final response = await HTTPService().post(
|
||||
path: ApiEndpoints.assignDeviceToRoom
|
||||
.replaceAll('{projectUuid}', projectId)
|
||||
.replaceAll('{communityUuid}', communityId)
|
||||
.replaceAll('{spaceUuid}', spaceId)
|
||||
.replaceAll('{subSpaceUuid}', subSpaceId)
|
||||
.replaceAll('{deviceUuid}', deviceId),
|
||||
expectedResponseModel: (json) {
|
||||
return json;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,4 +83,7 @@ abstract class ColorsManager {
|
||||
static const Color maxPurpleDot = Color(0xFF5F00BD);
|
||||
static const Color minBlue = Color(0xFF93AAFD);
|
||||
static const Color minBlueDot = Color(0xFF023DFE);
|
||||
static const Color grey25 = Color(0xFFF9F9F9);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -60,9 +60,12 @@ abstract class ApiEndpoints {
|
||||
'/devices/{uuid}/report-logs?code={code}&startTime={startTime}&endTime={endTime}';
|
||||
|
||||
static const String scheduleByDeviceId = '/schedule/{deviceUuid}';
|
||||
static const String getScheduleByDeviceId = '/schedule/{deviceUuid}?category={category}';
|
||||
static const String deleteScheduleByDeviceId = '/schedule/{deviceUuid}/{scheduleUuid}';
|
||||
static const String updateScheduleByDeviceId = '/schedule/enable/{deviceUuid}';
|
||||
static const String getScheduleByDeviceId =
|
||||
'/schedule/{deviceUuid}?category={category}';
|
||||
static const String deleteScheduleByDeviceId =
|
||||
'/schedule/{deviceUuid}/{scheduleUuid}';
|
||||
static const String updateScheduleByDeviceId =
|
||||
'/schedule/enable/{deviceUuid}';
|
||||
static const String factoryReset = '/devices/batch';
|
||||
|
||||
//product
|
||||
@@ -124,4 +127,13 @@ abstract class ApiEndpoints {
|
||||
'/projects/{projectId}/communities/{communityId}/spaces/{unitUuid}/automations';
|
||||
static const String spaceOnlyWithDevices =
|
||||
'/projects/{projectId}/communities/{communityId}/spaces?onlyWithDevices=true';
|
||||
|
||||
static const String listSubspace =
|
||||
'/projects/{projectUuid}/communities/{communityUuid}/spaces/{spaceUuid}/subspaces';
|
||||
static const String deviceByUuid = '/devices/{deviceUuid}';
|
||||
|
||||
static const String resetDevice = '/factory/reset/{deviceUuid}';
|
||||
|
||||
static const String assignDeviceToRoom =
|
||||
'/projects/{projectUuid}/communities/{communityUuid}/spaces/{spaceUuid}/subspaces/{subSpaceUuid}/devices/{deviceUuid}';
|
||||
}
|
||||
|
||||
@@ -452,6 +452,13 @@ class Assets {
|
||||
'assets/icons/refresh_status_icon.svg';
|
||||
static const String energyConsumedIcon =
|
||||
'assets/icons/energy_consumed_icon.svg';
|
||||
|
||||
static const String closeSettingsIcon =
|
||||
'assets/icons/close_settings_icon.svg';
|
||||
|
||||
static const String editNameIconSettings =
|
||||
'assets/icons/edit_name_icon_settings.svg';
|
||||
|
||||
static const String locationPin = 'assets/icons/location_pin.svg';
|
||||
static const String aqiTemperature = 'assets/icons/aqi_temperature.svg';
|
||||
static const String aqiHumidity = 'assets/icons/aqi_humidity.svg';
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DefaultContainer extends StatelessWidget {
|
||||
const DefaultContainer({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.height,
|
||||
this.width,
|
||||
this.color,
|
||||
this.boxConstraints,
|
||||
this.margin,
|
||||
this.padding,
|
||||
this.onTap,
|
||||
this.borderRadius,
|
||||
});
|
||||
|
||||
final double? height;
|
||||
final double? width;
|
||||
final Widget child;
|
||||
final BoxConstraints? boxConstraints;
|
||||
final EdgeInsets? margin;
|
||||
final EdgeInsets? padding;
|
||||
final Color? color;
|
||||
final Function()? onTap;
|
||||
final BorderRadius? borderRadius;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Container(
|
||||
height: height,
|
||||
width: width,
|
||||
margin: margin ?? const EdgeInsets.only(right: 3, bottom: 3),
|
||||
constraints: boxConstraints,
|
||||
decoration: BoxDecoration(
|
||||
color: color ?? Colors.white,
|
||||
borderRadius: borderRadius ?? BorderRadius.circular(20),
|
||||
),
|
||||
padding: padding ?? const EdgeInsets.all(10),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user