finished adding tasks and removing them , added error handling

This commit is contained in:
ashrafzarkanisala
2024-06-26 20:32:34 +03:00
parent ae47e48832
commit 9fe25b9bd3
22 changed files with 605 additions and 231 deletions

View File

@ -1,7 +1,7 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/device_category_model.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/functions.dart';
import 'package:syncrow_app/features/devices/model/function_model.dart';
class DeviceManagerState extends Equatable {
final bool loading;
@ -11,7 +11,7 @@ class DeviceManagerState extends Equatable {
final bool categoryChanged;
final bool deviceSelected;
final bool functionsLoading;
final FunctionsEntity? deviceFunctions;
final FunctionModel? deviceFunctions;
const DeviceManagerState({
required this.loading,
@ -45,7 +45,7 @@ class DeviceManagerState extends Equatable {
bool? categoryChanged,
bool? deviceSelected,
bool? functionsLoading,
FunctionsEntity? deviceFunctions,
FunctionModel? deviceFunctions,
}) {
return DeviceManagerState(
loading: loading ?? this.loading,

View File

@ -2,6 +2,8 @@ import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/features/scene/model/scene_static_function.dart';
part 'create_scene_event.dart';
part 'create_scene_state.dart';
@ -9,5 +11,51 @@ part 'create_scene_state.dart';
class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState> {
CreateSceneBloc() : super(CreateSceneInitial()) {
on<CreateSceneEvent>((event, emit) => null as FutureOr<void>);
on<AddTaskEvent>(_onAddSceneTask);
on<SelectedValueEvent>(_selectedValue);
on<RemoveTaskEvent>(_removeTaskById);
}
List<SceneStaticFunction> tasksList = [];
String selectedValue = '';
FutureOr<void> _onAddSceneTask(
AddTaskEvent event, Emitter<CreateSceneState> emit) {
tasksList.add(
SceneStaticFunction(
operationName: event.operation,
deviceName: event.deviceName,
icon: event.icon,
code: event.deviceControlModel.code ?? '',
deviceId: event.deviceId,
operationalValues: [
SceneOperationalValue(
value: event.deviceControlModel.value,
icon: '',
),
],
),
);
emit(AddSceneTask(tasksList: tasksList));
}
FutureOr<void> _selectedValue(
SelectedValueEvent event, Emitter<CreateSceneState> emit) {
selectedValue = event.value;
emit(SelectedTaskValueState(value: event.value));
}
FutureOr<void> _removeTaskById(
RemoveTaskEvent event, Emitter<CreateSceneState> emit) {
emit(CreateSceneLoading());
for (var element in tasksList) {
if (element.uniqueCustomId == event.taskId) {
tasksList.remove(element);
emit(AddSceneTask(tasksList: tasksList));
break;
}
}
}
}

View File

@ -7,4 +7,40 @@ sealed class CreateSceneEvent extends Equatable {
List<Object> get props => [];
}
class AddTaskEvent extends CreateSceneEvent {
final DeviceControlModel deviceControlModel;
final String deviceId;
final String icon;
final String operation;
final String deviceName;
const AddTaskEvent({
required this.deviceControlModel,
required this.deviceId,
required this.icon,
required this.operation,
required this.deviceName,
});
@override
List<Object> get props =>
[deviceControlModel, deviceId, deviceName, icon, operation];
}
class SelectedValueEvent extends CreateSceneEvent {
final String value;
const SelectedValueEvent({required this.value});
@override
List<Object> get props => [value];
}
class RemoveTaskEvent extends CreateSceneEvent {
final String taskId;
const RemoveTaskEvent({required this.taskId});
@override
List<Object> get props => [taskId];
}

View File

@ -11,4 +11,18 @@ final class CreateSceneInitial extends CreateSceneState {}
class CreateSceneLoading extends CreateSceneState {}
class AddSceneTask extends CreateSceneState {
final List<SceneStaticFunction> tasksList;
const AddSceneTask({required this.tasksList});
@override
List<Object> get props => [tasksList];
}
class SelectedTaskValueState extends CreateSceneState {
final String value;
const SelectedTaskValueState({required this.value});
@override
List<Object> get props => [value];
}

View File

@ -5,27 +5,40 @@ import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';
mixin SceneHelper {
List<SceneStaticFunction> getFunctionsWithIcons(
{DeviceType? type, required List<FunctionModel> functions}) {
List<SceneStaticFunction> getFunctionsWithIcons({
DeviceType? type,
required List<FunctionModel> functions,
required String deviceId,
required String deviceName,
}) {
switch (type) {
case DeviceType.LightBulb:
return lightBulbFunctions(functions);
return lightBulbFunctions(
functions: functions, deviceId: deviceId, deviceName: deviceName);
case DeviceType.CeilingSensor:
return ceilingSensorFunctions(functions);
return ceilingSensorFunctions(
functions: functions, deviceId: deviceId, deviceName: deviceName);
case DeviceType.WallSensor:
return wallSensorFunctions(functions);
return wallSensorFunctions(
functions: functions, deviceId: deviceId, deviceName: deviceName);
case DeviceType.AC:
return acFunctions(functions);
return acFunctions(
functions: functions, deviceId: deviceId, deviceName: deviceName);
case DeviceType.DoorLock:
return doorLockFunctions(functions);
return doorLockFunctions(
functions: functions, deviceId: deviceId, deviceName: deviceName);
case DeviceType.Curtain:
return curtainFunctions(functions);
return curtainFunctions(
functions: functions, deviceId: deviceId, deviceName: deviceName);
case DeviceType.ThreeGang:
return threeGangFunctions(functions);
return threeGangFunctions(
functions: functions, deviceId: deviceId, deviceName: deviceName);
case DeviceType.Gateway:
return gatewayFunctions(functions);
return gatewayFunctions(
functions: functions, deviceId: deviceId, deviceName: deviceName);
default:
return lightBulbFunctions(functions);
return lightBulbFunctions(
functions: functions, deviceId: deviceId, deviceName: deviceName);
}
}
@ -54,32 +67,36 @@ mixin SceneHelper {
/// presence sensor
List<SceneStaticFunction> ceilingSensorFunctions(
List<FunctionModel> functions) {
{required List<FunctionModel> functions,
required String deviceId,
required String deviceName}) {
return [
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsSensitivityFunction,
name: 'Sensitivity',
operationName: 'Sensitivity',
code: 'sensitivity',
operationalValues: [
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "1"),
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "2"),
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "3"),
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "4"),
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "5"),
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "6"),
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "7"),
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "8"),
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "9"),
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "10"),
],
),
@ -87,165 +104,204 @@ mixin SceneHelper {
}
}
List<SceneStaticFunction> curtainFunctions(List<FunctionModel> functions) {
List<SceneStaticFunction> curtainFunctions(
{required List<FunctionModel> functions,
required String deviceId,
required String deviceName}) {
return [];
}
List<SceneStaticFunction> doorLockFunctions(List<FunctionModel> functions) {
List<SceneStaticFunction> doorLockFunctions(
{required List<FunctionModel> functions,
required String deviceId,
required String deviceName}) {
return [];
}
List<SceneStaticFunction> wallSensorFunctions(List<FunctionModel> functions) {
List<SceneStaticFunction> wallSensorFunctions(
{required List<FunctionModel> functions,
required String deviceId,
required String deviceName}) {
return [];
}
List<SceneStaticFunction> lightBulbFunctions(List<FunctionModel> functions) {
List<SceneStaticFunction> lightBulbFunctions(
{required List<FunctionModel> functions,
required String deviceId,
required String deviceName}) {
return [];
}
List<SceneStaticFunction> gatewayFunctions(List<FunctionModel> functions) {
List<SceneStaticFunction> gatewayFunctions(
{required List<FunctionModel> functions,
required String deviceId,
required String deviceName}) {
return [];
}
List<SceneStaticFunction> threeGangFunctions(List<FunctionModel> functions) {
List<SceneStaticFunction> threeGangFunctions(
{required List<FunctionModel> functions,
required String deviceId,
required String deviceName}) {
return [
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
name: 'Light 1 Switch',
operationName: 'Light 1 Switch',
code: 'switch_1',
operationalValues: [
StaticFunctionOperationHelper(icon: Assets.assetsAcPower, value: "ON"),
StaticFunctionOperationHelper(
icon: Assets.assetsAcPowerOFF, value: "OFF"),
StaticFunctionOperationHelper(
SceneOperationalValue(icon: Assets.assetsAcPower, value: "ON"),
SceneOperationalValue(icon: Assets.assetsAcPowerOFF, value: "OFF"),
SceneOperationalValue(
icon: Assets.assetsSceneRefresh, value: "Reverse Switch"),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
name: 'Light 2 Switch',
operationName: 'Light 2 Switch',
code: 'switch_2',
operationalValues: [
StaticFunctionOperationHelper(icon: Assets.assetsAcPower, value: "ON"),
StaticFunctionOperationHelper(
icon: Assets.assetsAcPowerOFF, value: "OFF"),
StaticFunctionOperationHelper(
SceneOperationalValue(icon: Assets.assetsAcPower, value: "ON"),
SceneOperationalValue(icon: Assets.assetsAcPowerOFF, value: "OFF"),
SceneOperationalValue(
icon: Assets.assetsSceneRefresh, value: "Reverse Switch"),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
name: 'Light 3 Switch',
operationName: 'Light 3 Switch',
code: 'switch_3',
operationalValues: [
StaticFunctionOperationHelper(icon: Assets.assetsAcPower, value: "ON"),
StaticFunctionOperationHelper(
icon: Assets.assetsAcPowerOFF, value: "OFF"),
StaticFunctionOperationHelper(
SceneOperationalValue(icon: Assets.assetsAcPower, value: "ON"),
SceneOperationalValue(icon: Assets.assetsAcPowerOFF, value: "OFF"),
SceneOperationalValue(
icon: Assets.assetsSceneRefresh, value: "Reverse Switch"),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsLightCountdown,
name: 'Light 1 CountDown',
operationName: 'Light 1 CountDown',
code: 'countdown_1',
operationalValues: [
StaticFunctionOperationHelper(icon: '', value: "12610"),
SceneOperationalValue(icon: '', value: "0"),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsLightCountdown,
name: 'Light 2 CountDown',
operationName: 'Light 2 CountDown',
code: 'countdown_1',
operationalValues: [
StaticFunctionOperationHelper(icon: '', value: "0"),
SceneOperationalValue(icon: '', value: "0"),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsLightCountdown,
name: 'Light 3 CountDown',
operationName: 'Light 3 CountDown',
code: 'countdown_1',
operationalValues: [
StaticFunctionOperationHelper(icon: '', value: "0"),
SceneOperationalValue(icon: '', value: "0"),
],
),
];
}
/// smart ac thermostat
List<SceneStaticFunction> acFunctions(List<FunctionModel> functions) {
List<SceneStaticFunction> acFunctions(
{required List<FunctionModel> functions,
required String deviceId,
required String deviceName}) {
return [
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsAcPower,
name: 'Power',
operationName: 'Power',
code: 'switch',
operationalValues: [
StaticFunctionOperationHelper(icon: Assets.assetsAcPower, value: "ON"),
StaticFunctionOperationHelper(
icon: Assets.assetsAcPowerOFF, value: "OFF"),
SceneOperationalValue(icon: Assets.assetsAcPower, value: "ON"),
SceneOperationalValue(icon: Assets.assetsAcPowerOFF, value: "OFF"),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsFreezing,
name: 'Mode',
operationName: 'Mode',
code: 'mode',
operationalValues: [
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsAcCooling,
value: AcValuesEnums.Cooling.name,
),
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsAcHeating,
value: AcValuesEnums.Heating.name,
),
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsFanSpeed,
value: AcValuesEnums.Ventilation.name,
),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsTempreture,
name: 'Set Temperature',
operationName: 'Set Temperature',
code: 'temp_set',
operationalValues: [
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsCelsiusDegrees, value: "COOL TO"),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsFanSpeed,
name: 'Fan Speed',
operationName: 'Fan Speed',
code: 'level',
operationalValues: [
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsAcFanLow,
value: ValueACRange.LOW.name,
),
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsAcFanMiddle,
value: ValueACRange.MIDDLE.name,
),
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsAcFanHigh,
value: ValueACRange.HIGH.name,
),
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsAcFanAuto,
value: ValueACRange.AUTO.name,
),
],
),
SceneStaticFunction(
deviceId: deviceId,
deviceName: deviceName,
icon: Assets.assetsChildLock,
name: 'Child Lock',
operationName: 'Child Lock',
code: 'child_lock',
operationalValues: [
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsSceneChildLock,
value: 'Lock',
),
StaticFunctionOperationHelper(
SceneOperationalValue(
icon: Assets.assetsSceneChildUnlock,
value: 'Unlock',
),

View File

@ -1,51 +1,65 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:uuid/uuid.dart';
class SceneStaticFunction {
final String icon;
final String name;
final String deviceName;
final String code;
final List<StaticFunctionOperationHelper> operationalValues;
final List<SceneOperationalValue> operationalValues;
final String deviceId;
final String operationName;
final String uniqueCustomId;
SceneStaticFunction({
required this.icon,
required this.name,
required this.deviceName,
required this.code,
required this.operationalValues,
});
required this.deviceId,
required this.operationName,
}) : uniqueCustomId = const Uuid().v4();
SceneStaticFunction copyWith({
String? icon,
String? name,
String? code,
List<StaticFunctionOperationHelper>? operationalValues,
List<SceneOperationalValue>? operationalValues,
String? deviceId,
String? operationName,
}) {
return SceneStaticFunction(
icon: icon ?? this.icon,
name: name ?? this.name,
deviceName: name ?? this.deviceName,
code: code ?? this.code,
operationalValues: operationalValues ?? this.operationalValues,
deviceId: deviceId ?? this.deviceId,
operationName: operationName ?? this.operationName,
);
}
Map<String, dynamic> toMap() {
return {
'icon': icon,
'name': name,
'name': deviceName,
'code': code,
'operationalValues': operationalValues.map((x) => x.toMap()).toList(),
'deviceId': deviceId,
'operationName': operationName
};
}
factory SceneStaticFunction.fromMap(Map<String, dynamic> map) {
return SceneStaticFunction(
icon: map['icon'] ?? '',
name: map['name'] ?? '',
deviceName: map['name'] ?? '',
code: map['code'] ?? '',
operationalValues: List<StaticFunctionOperationHelper>.from(
map['operationalValues']
?.map((x) => StaticFunctionOperationHelper.fromMap(x))),
operationalValues: List<SceneOperationalValue>.from(
map['operationalValues']?.map((x) => SceneOperationalValue.fromMap(x)),
),
deviceId: map['deviceId'] ?? '',
operationName: map['operationName'] ?? '',
);
}
@ -56,7 +70,7 @@ class SceneStaticFunction {
@override
String toString() {
return 'SceneStaticFunction(icon: $icon, name: $name, code: $code, operationalValues: $operationalValues)';
return 'SceneStaticFunction(icon: $icon, name: $deviceName, code: $code, operationalValues: $operationalValues, deviceId: $deviceId, operationName: $operationName)';
}
@override
@ -65,34 +79,38 @@ class SceneStaticFunction {
return other is SceneStaticFunction &&
other.icon == icon &&
other.name == name &&
other.deviceName == deviceName &&
other.code == code &&
listEquals(other.operationalValues, operationalValues);
other.operationName == operationName &&
listEquals(other.operationalValues, operationalValues) &&
other.deviceId == deviceId;
}
@override
int get hashCode {
return icon.hashCode ^
name.hashCode ^
deviceName.hashCode ^
code.hashCode ^
deviceId.hashCode ^
operationName.hashCode ^
operationalValues.hashCode;
}
}
class StaticFunctionOperationHelper {
class SceneOperationalValue {
final String icon;
final String value;
StaticFunctionOperationHelper({
SceneOperationalValue({
required this.icon,
required this.value,
});
StaticFunctionOperationHelper copyWith({
SceneOperationalValue copyWith({
String? icon,
String? value,
}) {
return StaticFunctionOperationHelper(
return SceneOperationalValue(
icon: icon ?? this.icon,
value: value ?? this.value,
);
@ -105,8 +123,8 @@ class StaticFunctionOperationHelper {
};
}
factory StaticFunctionOperationHelper.fromMap(Map<String, dynamic> map) {
return StaticFunctionOperationHelper(
factory SceneOperationalValue.fromMap(Map<String, dynamic> map) {
return SceneOperationalValue(
icon: map['icon'] ?? '',
value: map['value'] ?? '',
);
@ -114,8 +132,8 @@ class StaticFunctionOperationHelper {
String toJson() => json.encode(toMap());
factory StaticFunctionOperationHelper.fromJson(String source) =>
StaticFunctionOperationHelper.fromMap(json.decode(source));
factory SceneOperationalValue.fromJson(String source) =>
SceneOperationalValue.fromMap(json.decode(source));
@override
String toString() =>
@ -125,7 +143,7 @@ class StaticFunctionOperationHelper {
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is StaticFunctionOperationHelper &&
return other is SceneOperationalValue &&
other.icon == icon &&
other.value == value;
}

View File

@ -0,0 +1,16 @@
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/services/api/devices_api.dart';
class SceneRepo {
Future<bool> deviceControl({
required DeviceControlModel controlModel,
required String deviceId,
}) async {
final response = await DevicesAPI.controlDevice(controlModel, deviceId);
if (response['success'] == true) {
return true;
} else {
return false;
}
}
}

View File

@ -1,4 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart';
import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
@ -27,11 +29,14 @@ class CreateSceneView extends StatelessWidget {
titleString: StringsManager.tapToRun,
subtitleString: StringsManager.turnOffAllLights,
),
onTap: () => Navigator.pushNamed(
onTap: () {
Navigator.pushNamed(
context,
Routes.sceneTasksRoute,
arguments: CreateSceneEnum.tabToRun,
),
);
context.read<CreateSceneBloc>().tasksList.clear();
},
),
DefaultContainer(
width: double.infinity,

View File

@ -1,5 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
import 'package:syncrow_app/features/scene/helper/scene_helper.dart';
import 'package:syncrow_app/features/scene/model/scene_static_function.dart';
import 'package:syncrow_app/features/scene/widgets/alert_dialog_countdown.dart';
@ -10,6 +13,7 @@ import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/features/shared_widgets/light_divider.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/navigation/routing_constants.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
@ -29,14 +33,22 @@ class DeviceFunctionsView extends StatelessWidget with SceneHelper {
List<SceneStaticFunction> functions = [];
if (device.functions.isNotEmpty) {
functions = getFunctionsWithIcons(
type: device.productType, functions: device.functions);
type: device.productType,
functions: device.functions,
deviceId: device.uuid ?? '',
deviceName: device.name ?? '',
);
}
return DefaultScaffold(
title: getTitle(type: device.productType),
actions: [
TextButton(
onPressed: () {},
onPressed: () {
Navigator.popUntil(context, (route) {
return route.settings.name == Routes.sceneTasksRoute;
});
},
child: BodyMedium(
text: 'Save',
fontWeight: FontWeight.normal,
@ -81,11 +93,13 @@ class DeviceFunctionsView extends StatelessWidget with SceneHelper {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SceneListTile(
BlocBuilder<CreateSceneBloc, CreateSceneState>(
builder: (context, state) {
return SceneListTile(
iconsSize: 32,
minLeadingWidth: 20,
assetPath: functions[index].icon,
titleString: functions[index].name,
titleString: functions[index].operationName,
trailingWidget: const Row(
mainAxisSize: MainAxisSize.min,
children: [
@ -111,8 +125,28 @@ class DeviceFunctionsView extends StatelessWidget with SceneHelper {
.value)
: AlertDialogFunctionsOperationsBody(
index: index, functions: functions),
title: functions[index].name,
onConfirm: () {},
title: functions[index].operationName,
onConfirm: () {
final selectedValue = context
.read<CreateSceneBloc>()
.selectedValue;
context
.read<CreateSceneBloc>()
.add(AddTaskEvent(
deviceControlModel: DeviceControlModel(
deviceId: device.uuid,
code: functions[index].code,
value: selectedValue,
),
deviceId: device.uuid ?? '',
operation: functions[index].operationName,
icon: device.icon ?? '',
deviceName: device.name ?? '',
));
Navigator.pop(context);
},
);
},
);
},
),

View File

@ -12,15 +12,16 @@ import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';
import 'package:syncrow_app/utils/resource_manager/strings_manager.dart';
class SceneControlDevicesView extends StatefulWidget {
const SceneControlDevicesView({super.key});
class SceneRoomsTabBarDevicesView extends StatefulWidget {
const SceneRoomsTabBarDevicesView({super.key});
@override
State<SceneControlDevicesView> createState() =>
_SceneControlDevicesViewState();
State<SceneRoomsTabBarDevicesView> createState() =>
_SceneRoomsTabBarDevicesViewState();
}
class _SceneControlDevicesViewState extends State<SceneControlDevicesView>
class _SceneRoomsTabBarDevicesViewState
extends State<SceneRoomsTabBarDevicesView>
with SingleTickerProviderStateMixin {
late final TabController _tabController;
List<RoomModel>? rooms = [];

View File

@ -1,7 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
import 'package:syncrow_app/features/scene/widgets/if_then_containers/if_container.dart';
import 'package:syncrow_app/features/scene/widgets/if_then_containers/then_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_button.dart';
@ -12,8 +10,8 @@ import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/strings_manager.dart';
class SceneAddTasksView extends StatelessWidget {
const SceneAddTasksView({super.key});
class SceneTasksView extends StatelessWidget {
const SceneTasksView({super.key});
@override
Widget build(BuildContext context) {
@ -36,10 +34,8 @@ class SceneAddTasksView extends StatelessWidget {
],
child: Stack(
children: [
SingleChildScrollView(
child: BlocProvider(
create: (context) => CreateSceneBloc(),
child: const Column(
const SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
@ -58,7 +54,6 @@ class SceneAddTasksView extends StatelessWidget {
],
),
),
),
Positioned(
bottom: 16,
right: 40,

View File

@ -1,6 +1,7 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
class AlertDialogCountdown extends StatefulWidget {
const AlertDialogCountdown({super.key, required this.durationValue});
@ -29,6 +30,9 @@ class _AlertDialogCountdownState extends State<AlertDialogCountdown> {
setState(() {
durationInSeconds = newDuration.inSeconds;
});
context
.read<CreateSceneBloc>()
.add(SelectedValueEvent(value: newDuration.inSeconds.toString()));
},
),
);

View File

@ -1,4 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
import 'package:syncrow_app/features/scene/model/scene_static_function.dart';
import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart';
@ -27,7 +29,9 @@ class _AlertDialogFunctionsOperationsBodyState
mainAxisSize: MainAxisSize.min,
children: [
...widget.functions[widget.index].operationalValues.map(
(operation) => SceneListTile(
(operation) => BlocBuilder<CreateSceneBloc, CreateSceneState>(
builder: (context, state) {
return SceneListTile(
iconsSize: 25,
minLeadingWidth: 15,
padding: const EdgeInsets.symmetric(horizontal: 16),
@ -36,17 +40,27 @@ class _AlertDialogFunctionsOperationsBodyState
textAlign: TextAlign.start,
trailingWidget: Radio(
value: operation.value,
groupValue: groupValue,
groupValue: (state is SelectedTaskValueState)
? state.value
: groupValue,
onChanged: (value) {
setState(() {
groupValue = value;
});
context
.read<CreateSceneBloc>()
.add(SelectedValueEvent(value: value!));
},
),
onPressed: () {
setState(() {
groupValue = operation.value;
});
context
.read<CreateSceneBloc>()
.add(SelectedValueEvent(value: groupValue!));
},
);
},
),
),

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
import 'package:syncrow_app/features/scene/model/scene_static_function.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart';
@ -24,7 +26,7 @@ class AlertDialogTemperatureBody extends StatefulWidget {
class _AlertDialogTemperatureBodyState
extends State<AlertDialogTemperatureBody> {
double temperature = 16;
int temperature = 24;
@override
Widget build(BuildContext context) {
return ListTile(
@ -32,8 +34,13 @@ class _AlertDialogTemperatureBodyState
leading: IconButton(
onPressed: () {
setState(() {
if (temperature > 20) {
temperature--;
}
});
context
.read<CreateSceneBloc>()
.add(SelectedValueEvent(value: temperature.toString()));
},
icon: const Icon(
Icons.remove,
@ -69,8 +76,13 @@ class _AlertDialogTemperatureBodyState
trailing: IconButton(
onPressed: () {
setState(() {
if (temperature < 30) {
temperature++;
}
});
context
.read<CreateSceneBloc>()
.add(SelectedValueEvent(value: temperature.toString()));
},
icon: const Icon(
Icons.add,

View File

@ -0,0 +1,99 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
import 'package:syncrow_app/features/scene/model/scene_static_function.dart';
import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class ThenAddedTasksContainer extends StatelessWidget {
const ThenAddedTasksContainer({
super.key,
required this.taskList,
});
final SceneStaticFunction taskList;
@override
Widget build(BuildContext context) {
String operationValue = '';
if (taskList.code.contains('countdown')) {
final duration = Duration(
seconds:
int.tryParse(taskList.operationalValues.first.value.toString()) ??
0);
operationValue =
"${duration.inHours}h ${duration.inMinutes.remainder(60)}m ";
} else {
operationValue = taskList.operationalValues.first.value.toString();
}
return DefaultContainer(
padding: EdgeInsets.zero,
child: Dismissible(
key: Key(taskList.uniqueCustomId.toString()),
background: Container(
padding: const EdgeInsets.only(right: 10),
alignment: AlignmentDirectional.centerEnd,
decoration: const ShapeDecoration(
color: Color(0xFFFF0000),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
bottomRight: Radius.circular(20),
),
),
),
child: SvgPicture.asset(
Assets.assetsDeleteIcon,
width: 20,
height: 22,
),
),
direction: DismissDirection.endToStart,
onDismissed: (direction) {
String removeFunctionById = taskList.uniqueCustomId;
context
.read<CreateSceneBloc>()
.add(RemoveTaskEvent(taskId: removeFunctionById));
String removeFunction =
"${taskList.operationName} with value ${taskList.operationalValues.first.value}";
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('$removeFunction removed')),
);
},
child: SceneListTile(
padding: EdgeInsets.zero,
assetPath: taskList.icon,
iconsSize: 32,
titleWidget: BodyMedium(
text: taskList.deviceName,
style: context.bodyMedium.copyWith(
fontWeight: FontWeight.bold,
),
),
subtitleWidget: Row(
children: [
BodyMedium(
text: "${taskList.operationName}: ",
fontColor: ColorsManager.secondaryTextColor,
fontWeight: FontWeight.normal,
),
BodyMedium(
text: operationValue,
fontColor: ColorsManager.secondaryTextColor,
fontWeight: FontWeight.normal,
),
],
),
),
),
);
}
}

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
import 'package:syncrow_app/features/scene/widgets/if_then_containers/then_added_tasks.dart';
import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart';
import 'package:syncrow_app/features/scene/widgets/bottom_sheet_widget.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
@ -48,6 +49,28 @@ class ThenDefaultContainer extends StatelessWidget {
const LightDivider(),
BlocBuilder<CreateSceneBloc, CreateSceneState>(
builder: (context, state) {
if (state is AddSceneTask) {
final taskLists = state.tasksList;
if (taskLists.isNotEmpty) {
return ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: taskLists.length,
itemBuilder: (context, index) {
return ThenAddedTasksContainer(
taskList: taskLists[index],
);
},
);
}
return SceneListTile(
titleString: '+ Add Task',
textAlign: TextAlign.center,
onPressed: () => context.customBottomSheet(
child: const CustomBottomSheetWidget(),
),
);
}
return SceneListTile(
titleString: '+ Add Task',
textAlign: TextAlign.center,

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
import 'package:syncrow_app/navigation/navigation_service.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';
@ -19,8 +20,11 @@ class MyApp extends StatelessWidget {
MediaQuery.sizeOf(context).height * Constants.appBarHeightPercentage;
Constants.bottomNavBarHeight = MediaQuery.sizeOf(context).height *
Constants.bottomNavBarHeightPercentage;
return BlocProvider(
create: (context) => AuthCubit(),
return MultiBlocProvider(
providers: [
BlocProvider(create: (context) => AuthCubit()),
BlocProvider(create: (context) => CreateSceneBloc())
],
child: MaterialApp(
navigatorKey: NavigationService.navigatorKey,
scaffoldMessengerKey: NavigationService.snackbarKey,

View File

@ -14,8 +14,8 @@ import 'package:syncrow_app/features/menu/view/widgets/profile/profile_view.dart
import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_event.dart';
import 'package:syncrow_app/features/scene/view/device_functions_view.dart';
import 'package:syncrow_app/features/scene/view/scene_add_tasks.dart';
import 'package:syncrow_app/features/scene/view/scene_control_devices.dart';
import 'package:syncrow_app/features/scene/view/scene_tasks_view.dart';
import 'package:syncrow_app/features/scene/view/scene_rooms_tabbar.dart';
import 'package:syncrow_app/features/scene/view/scene_view.dart';
import 'package:syncrow_app/features/splash/view/splash_view.dart';
import 'routing_constants.dart';
@ -72,7 +72,7 @@ class Router {
builder: (_) => const CreateUnitView(), settings: settings);
case Routes.sceneTasksRoute:
return MaterialPageRoute(
builder: (_) => const SceneAddTasksView(), settings: settings);
builder: (_) => const SceneTasksView(), settings: settings);
case Routes.sceneControlDevicesRoute:
return MaterialPageRoute(
builder: (_) => MultiBlocProvider(
@ -87,7 +87,7 @@ class Router {
..add(const TabChanged(selectedIndex: 0, roomId: '-1')),
),
],
child: const SceneControlDevicesView(),
child: const SceneRoomsTabBarDevicesView(),
),
settings: settings);
case Routes.deviceFunctionsRoute:

View File

@ -3,7 +3,7 @@ import 'dart:async';
import 'package:syncrow_app/features/devices/model/device_category_model.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/functions.dart';
import 'package:syncrow_app/features/devices/model/function_model.dart';
import 'package:syncrow_app/services/api/api_links_endpoints.dart';
import 'package:syncrow_app/services/api/http_service.dart';
@ -57,12 +57,12 @@ class DevicesAPI {
}
/// Get Device Functions
static Future<FunctionsEntity> deviceFunctions(String deviceId) async {
static Future<FunctionModel> deviceFunctions(String deviceId) async {
final response = await _httpService.get(
path: ApiEndpoints.deviceFunctions.replaceAll('{deviceUuid}', deviceId),
showServerMessage: false,
expectedResponseModel: (json) {
final functions = FunctionsEntity.fromJson(json);
final functions = FunctionModel.fromJson(json);
return functions;
},
);

View File

@ -50,7 +50,7 @@ extension ContextExtension on BuildContext {
void customAlertDialog(
{required Widget alertBody,
required String title,
required Function()? onConfirm}) {
required VoidCallback onConfirm}) {
showDialog(
context: this,
builder: (BuildContext context) {
@ -98,7 +98,7 @@ extension ContextExtension on BuildContext {
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
InkWell(
GestureDetector(
onTap: () {
Navigator.pop(context);
},
@ -115,7 +115,7 @@ extension ContextExtension on BuildContext {
width: 1,
color: ColorsManager.greyColor,
),
InkWell(
GestureDetector(
onTap: onConfirm,
child: Center(
child: BodyMedium(

View File

@ -907,13 +907,13 @@ packages:
source: hosted
version: "3.1.1"
uuid:
dependency: transitive
dependency: "direct main"
description:
name: uuid
sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8
sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8"
url: "https://pub.dev"
source: hosted
version: "4.3.3"
version: "4.4.0"
vector_graphics:
dependency: transitive
description:

View File

@ -1,5 +1,6 @@
name: syncrow_app
description: This is the mobile application project, developed with Flutter for Syncrow IOT Project.
description: This is the mobile application project, developed with Flutter for
Syncrow IOT Project.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: "none" # Remove this line if you wish to publish to pub.dev
@ -10,44 +11,39 @@ environment:
sdk: ">=3.0.6 <4.0.0"
dependencies:
cached_network_image: ^3.3.1
cupertino_icons: ^1.0.6
dio: ^5.4.1
equatable: ^2.0.5
firebase_analytics: ^10.8.7
firebase_core: ^2.25.5
firebase_crashlytics: ^3.4.16
fl_chart: ^0.66.2
flutter:
sdk: flutter
#UI Packages
cupertino_icons: ^1.0.6
shared_preferences: ^2.2.2
flutter_animated_dialog: ^2.0.1
flutter_svg: ^2.0.10+1
fl_chart: ^0.66.2
sleek_circular_slider: ^2.0.1
# Utility Packages
flutter_secure_storage: ^9.0.0
cached_network_image: ^3.3.1
flutter_dotenv: ^5.1.0
# noinspection YAMLSchemaValidation
intl: ^0.19.0
get_it: ^7.6.7
url_launcher: ^6.2.5
dio: ^5.4.1
flutter_localization: ^0.2.0
flutter_bloc: ^8.1.4
firebase_core: ^2.25.5
firebase_analytics: ^10.8.7
firebase_crashlytics: ^3.4.16
smooth_page_indicator: ^1.1.0
flutter_dotenv: ^5.1.0
flutter_localization: ^0.2.0
flutter_secure_storage: ^9.0.0
flutter_svg: ^2.0.10+1
get_it: ^7.6.7
html: ^0.15.4
equatable: ^2.0.5
intl: ^0.19.0
onesignal_flutter: ^5.2.0
permission_handler: ^11.3.1
pin_code_fields: ^8.0.1
share_plus: ^9.0.0
shared_preferences: ^2.2.2
sleek_circular_slider: ^2.0.1
smooth_page_indicator: ^1.1.0
url_launcher: ^6.2.5
uuid: ^4.4.0
dev_dependencies:
flutter_lints: ^3.0.1
flutter_test:
sdk: flutter
flutter_lints: ^3.0.1
flutter_assets:
assets_path: assets/
@ -78,7 +74,6 @@ flutter:
- family: Aftika
fonts:
- asset: assets/fonts/AftikaRegular.ttf
#
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf