Implemented home management and user invitation flows

This commit is contained in:
Abdullah Alassaf
2024-06-05 03:42:00 +03:00
parent 6a9c2967d2
commit 631ba53d7a
29 changed files with 968 additions and 288 deletions

View File

@ -27,7 +27,7 @@ if (flutterVersionName == null) {
}
android {
namespace "com.example.syncrow.app"
namespace "com.example.syncrow_application"
compileSdkVersion 34
ndkVersion flutter.ndkVersion
@ -46,7 +46,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.syncrow.app"
applicationId "com.example.syncrow_application"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 21

View File

@ -9,7 +9,7 @@
"client_info": {
"mobilesdk_app_id": "1:427332280600:android:550f67441246cb1a0c7e6d",
"android_client_info": {
"package_name": "com.example.syncrow.app"
"package_name": "com.example.syncrow_application"
}
},
"oauth_client": [],

View File

@ -1,4 +1,4 @@
package com.example.syncrow.app
package com.example.syncrow_application
import io.flutter.embedding.android.FlutterActivity

View File

@ -0,0 +1,3 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 20.3799C5.52381 20.3799 1 15.8561 1 10.3799C1 4.90369 5.51613 0.379883 10.9923 0.379883C16.4685 0.379883 21 4.90369 21 10.3799C21 15.8561 16.4762 20.3799 11 20.3799ZM9.77112 15.4029C10.0553 15.4029 10.3011 15.2724 10.4854 14.9882L15.6467 6.90062C15.7619 6.73165 15.8618 6.52428 15.8618 6.33994C15.8618 5.93288 15.5008 5.67942 15.1244 5.67942C14.9017 5.67942 14.679 5.80999 14.51 6.07113L9.73272 13.6671L7.19048 10.4797C6.99078 10.2263 6.78341 10.1341 6.53763 10.1341C6.16129 10.1341 5.84639 10.4336 5.84639 10.8407C5.84639 11.0327 5.9232 11.2401 6.05376 11.4014L9.01843 14.9959C9.25653 15.2877 9.48694 15.4029 9.77112 15.4029Z" fill="#023DFE" fill-opacity="0.6"/>
</svg>

After

Width:  |  Height:  |  Size: 820 B

View File

@ -0,0 +1,3 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="20.62" height="20.62" rx="10.31" stroke="#AEAEB2"/>
</svg>

After

Width:  |  Height:  |  Size: 184 B

View File

@ -6,6 +6,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:onesignal_flutter/onesignal_flutter.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:share_plus/share_plus.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/app_layout/view/widgets/app_bar_home_dropdown.dart';
import 'package:syncrow_app/features/auth/model/user_model.dart';
@ -22,6 +23,7 @@ import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/navigation/navigation_service.dart';
import 'package:syncrow_app/services/api/spaces_api.dart';
import 'package:syncrow_app/utils/helpers/custom_page_route.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';
@ -31,11 +33,12 @@ class HomeCubit extends Cubit<HomeState> {
HomeCubit._() : super(HomeInitial()) {
checkIfNotificationPermissionGranted();
if (selectedSpace == null) {
fetchUnitsByUserId().then((value) {
if (selectedSpace != null) {
fetchRoomsByUnitId(selectedSpace!);
}
});
fetchUnitsByUserId();
// .then((value) {
// if (selectedSpace != null) {
// fetchRoomsByUnitId(selectedSpace!);
// }
// });
}
}
@ -143,6 +146,7 @@ class HomeCubit extends Cubit<HomeState> {
changeSelectedSpace(SpaceModel space) {
selectedSpace = space;
emitSafe(SpaceSelected(space));
fetchRoomsByUnitId(space);
}
roomSliderPageChanged(int index) {
@ -193,6 +197,39 @@ class HomeCubit extends Cubit<HomeState> {
}
//////////////////////////////////////// API ////////////////////////////////////////
generateInvitation(String unitId) async {
try {
final invitationCode = await SpacesAPI.generateInvitationCode(unitId);
if (invitationCode.isNotEmpty) {
Share.share('The invitation code is $invitationCode');
CustomSnackBar.displaySnackBar(
'Invitation code generated successfully the code is: $invitationCode');
} else {
CustomSnackBar.displaySnackBar('Please try again!');
}
} catch (failure) {
CustomSnackBar.displaySnackBar('Something went wrong');
return;
}
}
Future<bool> joinAUnit(String code) async {
try {
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? '';
Map<String, String> body = {'userUuid': uuid, 'inviteCode': code};
final success = await SpacesAPI.joinUnit(body);
if (success) {
await fetchUnitsByUserId();
CustomSnackBar.displaySnackBar('Done successfully');
}
return true;
} catch (failure) {
CustomSnackBar.displaySnackBar('Something went wrong');
return false;
}
}
fetchUnitsByUserId() async {
emitSafe(GetSpacesLoading());
try {
@ -204,8 +241,8 @@ class HomeCubit extends Cubit<HomeState> {
if (spaces != null && spaces!.isNotEmpty) {
selectedSpace = spaces!.first;
await fetchRoomsByUnitId(selectedSpace!);
emitSafe(GetSpacesSuccess(spaces!));
// fetchRoomsByUnitId(selectedSpace!);
} else {
emitSafe(GetSpacesError("No spaces found"));
}

View File

@ -20,11 +20,9 @@ class DefaultAppBar extends StatelessWidget implements PreferredSizeWidget {
leadingWidth: 150,
toolbarHeight: Constants.appBarHeight,
leading: HomeCubit.getInstance().spaces!.isNotEmpty
? HomeCubit.appBarLeading[
HomeCubit.bottomNavItems[HomeCubit.pageIndex].label]
? HomeCubit.appBarLeading[HomeCubit.bottomNavItems[HomeCubit.pageIndex].label]
: null,
actions: HomeCubit.appBarActions[
HomeCubit.bottomNavItems[HomeCubit.pageIndex].label],
// actions: HomeCubit.appBarActions[HomeCubit.bottomNavItems[HomeCubit.pageIndex].label],
));
},
);

View File

@ -1,4 +1,3 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
@ -9,6 +8,7 @@ import 'package:syncrow_app/features/auth/model/user_model.dart';
import 'package:syncrow_app/navigation/navigation_service.dart';
import 'package:syncrow_app/navigation/routing_constants.dart';
import 'package:syncrow_app/services/api/authentication_api.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
part 'auth_state.dart';
class AuthCubit extends Cubit<AuthState> {
@ -26,6 +26,7 @@ class AuthCubit extends Cubit<AuthState> {
final loginFormKey = GlobalKey<FormState>();
final signUpFormKey = GlobalKey<FormState>();
bool isPasswordVisible = false;
bool showValidationMessage = false;
static GlobalKey<FormState> formKey = GlobalKey<FormState>();
@ -58,7 +59,7 @@ class AuthCubit extends Cubit<AuthState> {
if (value.isNotEmpty) {
if (!RegExp(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$')
.hasMatch(value)) {
return 'Password must contain at least one uppercase letter, one lowercase letter, one number and one special character';
return 'Password must contain at least:\n - one uppercase letter.\n - one lowercase letter.\n - one number. \n - special character';
}
}
}
@ -147,6 +148,12 @@ class AuthCubit extends Cubit<AuthState> {
login() async {
emit(AuthLoginLoading());
try {
if (emailController.text.isEmpty || passwordController.text.isEmpty) {
CustomSnackBar.displaySnackBar('Please enter your credentials');
emit(AuthLoginError(message: 'Something went wrong'));
return;
}
token = await AuthenticationAPI.loginWithEmail(
model: LoginWithEmailModel(
email: emailController.text.toLowerCase(),

View File

@ -86,7 +86,7 @@ class SignUpView extends StatelessWidget {
),
Form(
key: formKey,
autovalidateMode: AutovalidateMode.disabled,
// autovalidateMode: AutovalidateMode.disabled,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -96,14 +96,15 @@ class SignUpView extends StatelessWidget {
fontColor: Colors.white,
),
TextFormField(
autovalidateMode: AutovalidateMode.disabled,
// autovalidateMode: AutovalidateMode.disabled,
textInputAction: TextInputAction.done,
keyboardType: TextInputType.name,
scrollPadding: EdgeInsets.zero,
autocorrect: false,
autofillHints: const [AutofillHints.name],
// controller: AuthCubit.get(context).fullNameController,
validator: AuthCubit.get(context).fullNameValidator,
validator: (value) =>
AuthCubit.get(context).fullNameValidator(value),
onTapOutside: (event) {
FocusScope.of(context).unfocus();
},
@ -119,7 +120,7 @@ class SignUpView extends StatelessWidget {
fontColor: Colors.white,
),
TextFormField(
autovalidateMode: AutovalidateMode.disabled,
// autovalidateMode: AutovalidateMode.disabled,
textInputAction: TextInputAction.done,
keyboardType: TextInputType.text,
scrollPadding: EdgeInsets.zero,
@ -141,7 +142,7 @@ class SignUpView extends StatelessWidget {
fontColor: Colors.white,
),
TextFormField(
autovalidateMode: AutovalidateMode.disabled,
// autovalidateMode: AutovalidateMode.disabled,
textInputAction: TextInputAction.done,
keyboardType: TextInputType.text,
scrollPadding: EdgeInsets.zero,
@ -199,6 +200,7 @@ class SignUpView extends StatelessWidget {
'Sign up',
),
onPressed: () {
AuthCubit.get(context).showValidationMessage = true;
if (formKey.currentState!.validate()) {
if ((state is! AuthLoading)) {
AuthCubit.get(context).signUp();

View File

@ -65,7 +65,7 @@ class LoginForm extends StatelessWidget {
if (state is AuthTokenError && !pressed) {
return null;
}
return AuthCubit.get(context).passwordValidator(value);
// return AuthCubit.get(context).passwordValidator(value);
},
onTapOutside: (event) {
FocusScope.of(context).unfocus();

View File

@ -1,9 +1,11 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/auth/model/user_model.dart';
import 'package:syncrow_app/features/menu/bloc/create_unit_bloc/create_unit_event.dart';
import 'package:syncrow_app/features/menu/bloc/create_unit_bloc/create_unit_state.dart';
import 'package:syncrow_app/services/api/home_creation_api.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
class CreateUnitBloc extends Bloc<CreateUnitEvent, CreateUnitState> {
List<String> rooms = [];
@ -45,11 +47,16 @@ class CreateUnitBloc extends Bloc<CreateUnitEvent, CreateUnitState> {
void _save(SaveRooms event, Emitter<CreateUnitState> emit) async {
try {
emit(LoadingState());
if (rooms.isEmpty) {
const ErrorState(message: 'Please add at least one room!');
if (event.unitName.isEmpty) {
CustomSnackBar.displaySnackBar('Please enter the unit name');
return;
}
if (rooms.isEmpty) {
CustomSnackBar.displaySnackBar('Please add at least one room!');
return;
}
emit(LoadingState());
var storage = const FlutterSecureStorage();
var userId = await storage.read(key: UserModel.userUuidKey) ?? '';
@ -83,6 +90,7 @@ class CreateUnitBloc extends Bloc<CreateUnitEvent, CreateUnitState> {
}
// }
}
await HomeCubit.getInstance().fetchUnitsByUserId();
emit(RoomsSavedSuccessfully());
} catch (_) {
emit(const ErrorState(message: 'Something went wrong'));

View File

@ -0,0 +1,112 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_event.dart';
import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_state.dart';
import 'package:syncrow_app/services/api/devices_api.dart';
import 'package:syncrow_app/services/api/home_creation_api.dart';
import 'package:syncrow_app/services/api/home_management_api.dart';
import 'package:syncrow_app/services/api/spaces_api.dart';
class ManageUnitBloc extends Bloc<ManageUnitEvent, ManageUnitState> {
List<DeviceModel> allDevices = [];
String unitName = '';
ManageUnitBloc() : super(InitialState()) {
on<FetchRoomsEvent>(_fetchRoomsAndDevices);
on<FetchDevicesByRoomIdEvent>(_fetchDevicesByRoomId);
on<AssignRoomEvent>(_assignDevice);
on<AddNewRoom>(_addNewRoom);
}
void _fetchRoomsAndDevices(FetchRoomsEvent event, Emitter<ManageUnitState> emit) async {
try {
emit(LoadingState());
final roomsList = await SpacesAPI.getRoomsBySpaceId(event.unitId);
emit(FetchRoomsState(devicesList: allDevices, roomsList: roomsList));
} catch (e) {
emit(const ErrorState(message: 'Something went wrong'));
return;
}
}
void _fetchDevicesByRoomId(FetchDevicesByRoomIdEvent event, Emitter<ManageUnitState> emit) async {
try {
Map<String, bool> roomDevicesId = {};
emit(LoadingState());
final devicesList = await DevicesAPI.getDevicesByRoomId(event.roomId);
allDevices = await HomeManagementAPI.fetchDevicesByUserId();
List<String> allDevicesIds = [];
allDevices.forEach((element) {
allDevicesIds.add(element.uuid!);
});
devicesList.forEach((e) {
if (allDevicesIds.contains(e.uuid!)) {
roomDevicesId[e.uuid!] = true;
} else {
roomDevicesId[e.uuid!] = false;
}
});
emit(FetchDeviceByRoomIdState(
roomDevices: devicesList,
allDevices: allDevices,
roomDevicesId: roomDevicesId,
roomId: event.roomId));
} catch (e) {
emit(const ErrorState(message: 'Something went wrong'));
return;
}
}
void _assignDevice(AssignRoomEvent event, Emitter<ManageUnitState> emit) async {
try {
Map<String, bool> roomDevicesId = {};
emit(LoadingState());
Map<String, String> body = {"deviceUuid": event.deviceId, "roomUuid": event.roomId};
await HomeManagementAPI.assignDeviceToRoom(body);
final devicesList = await DevicesAPI.getDevicesByRoomId(event.roomId);
List<String> allDevicesIds = [];
allDevices.forEach((element) {
allDevicesIds.add(element.uuid!);
});
devicesList.forEach((e) {
if (allDevicesIds.contains(e.uuid!)) {
roomDevicesId[e.uuid!] = true;
} else {
roomDevicesId[e.uuid!] = false;
}
});
emit(FetchDeviceByRoomIdState(
roomDevices: devicesList,
allDevices: allDevices,
roomDevicesId: roomDevicesId,
roomId: event.roomId));
} catch (e) {
emit(const ErrorState(message: 'Something went wrong'));
return;
}
}
_addNewRoom(AddNewRoom event, Emitter<ManageUnitState> emit) async {
Map<String, String> body = {'roomName': event.roomName, 'unitUuid': event.unitId};
try {
emit(LoadingState());
final response = await HomeCreation.createRoom(body);
if (response['data']['uuid'] != '') {
final roomsList = await SpacesAPI.getRoomsBySpaceId(event.unitId);
allDevices = await HomeManagementAPI.fetchDevicesByUserId();
emit(FetchRoomsState(devicesList: allDevices, roomsList: roomsList));
} else {
emit(const ErrorState(message: 'Something went wrong'));
}
} catch (_) {
emit(const ErrorState(message: 'Something went wrong'));
return;
}
}
}

View File

@ -0,0 +1,50 @@
import 'package:equatable/equatable.dart';
abstract class ManageUnitEvent extends Equatable {
const ManageUnitEvent();
@override
List<Object> get props => [];
}
class InitialEvent extends ManageUnitEvent {}
class LoadingEvent extends ManageUnitEvent {}
class FetchRoomsEvent extends ManageUnitEvent {
final String unitId;
const FetchRoomsEvent({required this.unitId});
@override
List<Object> get props => [unitId];
}
class FetchDevicesByRoomIdEvent extends ManageUnitEvent {
final String roomId;
const FetchDevicesByRoomIdEvent({required this.roomId});
@override
List<Object> get props => [roomId];
}
class AddNewRoom extends ManageUnitEvent {
final String roomName;
final String unitId;
const AddNewRoom({required this.roomName, required this.unitId});
@override
List<Object> get props => [roomName, unitId];
}
class AssignRoomEvent extends ManageUnitEvent {
final String roomId;
final String deviceId;
const AssignRoomEvent({required this.roomId, required this.deviceId});
@override
List<Object> get props => [roomId];
}

View File

@ -0,0 +1,51 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/room_model.dart';
abstract class ManageUnitState extends Equatable {
const ManageUnitState();
@override
List<Object> get props => [];
}
class InitialState extends ManageUnitState {}
class LoadingState extends ManageUnitState {}
class FetchRoomsState extends ManageUnitState {
final List<RoomModel> roomsList;
final List<DeviceModel> devicesList;
const FetchRoomsState({required this.devicesList, required this.roomsList});
@override
List<Object> get props => [devicesList];
}
class FetchDeviceByRoomIdState extends ManageUnitState {
final List<DeviceModel> roomDevices;
final List<DeviceModel> allDevices;
final Map<String, bool> roomDevicesId;
final String roomId;
const FetchDeviceByRoomIdState(
{required this.roomDevices,
required this.allDevices,
required this.roomDevicesId,
required this.roomId});
@override
List<Object> get props => [roomDevices, allDevices, roomDevicesId, roomId];
}
class AssignRoomState extends ManageUnitState {}
class ErrorState extends ManageUnitState {
final String message;
const ErrorState({required this.message});
@override
List<Object> get props => [message];
}

View File

@ -1,7 +1,7 @@
// ignore_for_file: unnecessary_const
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/menu/bloc/create_unit_bloc/create_unit_bloc.dart';
import 'package:syncrow_app/features/menu/bloc/create_unit_bloc/create_unit_event.dart';
import 'package:syncrow_app/features/menu/bloc/create_unit_bloc/create_unit_state.dart';
@ -23,245 +23,257 @@ class CreateUnitView extends StatelessWidget {
@override
Widget build(BuildContext context) {
String unitName = '';
String location = '';
TextEditingController textEditingController = TextEditingController();
return BlocProvider(
create: (context) => CreateUnitBloc(),
child: BlocConsumer<CreateUnitBloc, CreateUnitState>(listener: (context, state) {
if (state is RoomsSavedSuccessfully) {
CustomSnackBar.displaySnackBar('Saved successfully');
NavigationService.navigatorKey.currentState!.pushNamedAndRemoveUntil(
Routes.homeRoute,
(Route route) => false,
);
}
child: BlocConsumer<CreateUnitBloc, CreateUnitState>(
listener: (context, state) {
if (state is RoomsSavedSuccessfully) {
CustomSnackBar.displaySnackBar('Unit created successfully');
Navigator.of(context).pop();
}
if (state is ErrorState) {
CustomSnackBar.displaySnackBar(state.message);
}
}, builder: (context, state) {
textEditingController.text = '';
return DefaultScaffold(
actions: [
TextButton(
onPressed: () {
if (unitName.isNotEmpty) {
if (state is ErrorState) {
CustomSnackBar.displaySnackBar(state.message);
}
},
builder: (context, state) {
textEditingController.text = '';
return DefaultScaffold(
actions: [
TextButton(
onPressed: () {
BlocProvider.of<CreateUnitBloc>(context).add(SaveRooms(
communityName: 'Community Test',
buildingName: 'Building Test',
floorName: 'Floor Test',
unitName: unitName));
} else {
CustomSnackBar.displaySnackBar('Please enter the unit name');
}
},
child: const BodyLarge(
text: 'Save',
fontWeight: FontWeight.bold,
communityName: 'Community Test',
buildingName: 'Building Test',
floorName: 'Floor Test',
unitName: unitName,
));
},
child: const BodyLarge(
text: 'Save',
fontWeight: FontWeight.bold,
),
),
),
],
title: 'Create a Unit',
child: Container(
padding: const EdgeInsets.symmetric(vertical: 40),
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
child: SingleChildScrollView(
child: Column(
children: [
//Home Info
DefaultContainer(
padding: const EdgeInsets.symmetric(
horizontal: 25,
vertical: 5,
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const BodyMedium(text: 'Unit Name'),
Flexible(
child: TextField(
textAlign: TextAlign.end,
onChanged: (value) {
unitName = value;
},
decoration: InputDecoration(
hintText: 'Enter Name',
hintStyle: context.bodyMedium.copyWith(color: Colors.grey),
border: InputBorder.none,
),
),
],
title: 'Create a Unit',
child: Container(
padding: const EdgeInsets.symmetric(vertical: 40),
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
child: state is LoadingState
? const Center(
child: CircularProgressIndicator(),
)
: SingleChildScrollView(
child: Column(
children: [
// Home Info
DefaultContainer(
padding: const EdgeInsets.symmetric(
horizontal: 25,
vertical: 5,
),
],
),
const Divider(
color: ColorsManager.greyColor,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const BodyMedium(text: 'Location '),
Flexible(
child: TextField(
textAlign: TextAlign.end,
onChanged: (value) {
location = value;
},
decoration: InputDecoration(
hintText: 'Set',
hintStyle: context.bodyMedium.copyWith(color: Colors.grey),
border: InputBorder.none,
),
),
),
],
),
],
),
),
//Rooms Info
const SizedBox(height: 10),
const Row(
children: [
SizedBox(
width: 25,
),
BodySmall(
text: 'Spaces',
fontWeight: FontWeight.bold,
),
],
),
const SizedBox(
height: 4,
),
DefaultContainer(
padding: const EdgeInsets.symmetric(
horizontal: 25,
vertical: 5,
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
if (state is CreateRoomState)
if (state.roomList.isNotEmpty)
Column(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
height: (state.roomList.length) * 55,
child: ListView.separated(
physics:
const NeverScrollableScrollPhysics(), // Disable scrolling
separatorBuilder: (context, index) => const Divider(
color: ColorsManager.greyColor,
),
itemCount: state.roomList.length,
itemBuilder: (context, index) {
return Dismissible(
key: Key(state.roomList[index]),
background: Container(
padding: const EdgeInsets.only(right: 10),
alignment: AlignmentDirectional.centerEnd,
decoration: ShapeDecoration(
color: const Color(0xFFFF0000),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(index == 0 ? 20 : 0),
topRight: Radius.circular(index == 0 ? 20 : 0),
),
),
),
child: SvgPicture.asset(
Assets.assetsDeleteIcon,
width: 20,
height: 22,
),
),
direction: DismissDirection.endToStart,
onDismissed: (direction) {
String removedUnit = state.roomList[index];
BlocProvider.of<CreateUnitBloc>(context)
.add(RemoveRoomEvent(roomName: removedUnit));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('$removedUnit removed')),
);
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const BodyMedium(text: 'Unit Name'),
Flexible(
child: TextField(
textAlign: TextAlign.end,
onChanged: (value) {
unitName = value;
},
child: Container(
height: 50,
padding: const EdgeInsets.symmetric(vertical: 16),
child: Text(
state.roomList[index],
style: const TextStyle(
color: Color(0xFF5D5D5D),
fontSize: 15,
fontWeight: FontWeight.w400,
),
),
decoration: InputDecoration(
hintText: 'Enter Name',
hintStyle:
context.bodyMedium.copyWith(color: Colors.grey),
border: InputBorder.none,
),
);
},
),
),
),
],
),
const Divider(
color: ColorsManager.greyColor,
),
],
),
Row(
children: [
TextButton(
onPressed: () {
if (textEditingController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Please add the room name')),
);
return;
}
BlocProvider.of<CreateUnitBloc>(context)
.add(CreateRoomEvent(roomName: textEditingController.text));
},
child: const Row(
mainAxisAlignment: MainAxisAlignment.start,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BodyMedium(
text: 'Add Space',
fontColor: ColorsManager.primaryColor,
const BodyMedium(text: 'Location '),
Flexible(
child: TextField(
textAlign: TextAlign.end,
onChanged: (value) {},
decoration: InputDecoration(
hintText: 'Set',
hintStyle:
context.bodyMedium.copyWith(color: Colors.grey),
border: InputBorder.none,
),
),
),
],
)),
Flexible(
child: TextField(
textAlign: TextAlign.end,
controller: textEditingController,
onChanged: (value) {
textEditingController.text = value;
},
decoration: InputDecoration(
hintText: 'Set',
hintStyle: context.bodyMedium.copyWith(color: Colors.grey),
border: InputBorder.none,
),
],
),
),
// Rooms Info
const SizedBox(height: 10),
const Row(
children: [
SizedBox(
width: 25,
),
BodySmall(
text: 'Spaces',
fontWeight: FontWeight.bold,
),
],
),
const SizedBox(
height: 4,
),
Container(
// margin: EdgeInsets.zero,
// margin: const EdgeInsets.symmetric(
// horizontal: 25,
// vertical: 5,
// ),
decoration: const ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20)),
),
),
],
)
],
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
if (state is CreateRoomState)
if (state.roomList.isNotEmpty)
Column(
children: [
ListView.separated(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
separatorBuilder: (context, index) => const Divider(
color: ColorsManager.greyColor,
),
itemCount: state.roomList.length,
itemBuilder: (context, index) {
return Dismissible(
key: Key(state.roomList[index]),
background: Container(
padding: const EdgeInsets.only(right: 10),
alignment: AlignmentDirectional.centerEnd,
decoration: ShapeDecoration(
color: const Color(0xFFFF0000),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(index == 0 ? 20 : 0),
topRight:
Radius.circular(index == 0 ? 20 : 0),
),
),
),
child: SvgPicture.asset(
Assets.assetsDeleteIcon,
width: 20,
height: 22,
),
),
direction: DismissDirection.endToStart,
onDismissed: (direction) {
String removedUnit = state.roomList[index];
BlocProvider.of<CreateUnitBloc>(context)
.add(RemoveRoomEvent(roomName: removedUnit));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('$removedUnit removed')),
);
},
child: Container(
height: 50,
padding: const EdgeInsets.symmetric(
vertical: 16, horizontal: 25),
child: Text(
state.roomList[index],
style: const TextStyle(
color: Color(0xFF5D5D5D),
fontSize: 15,
fontWeight: FontWeight.w400,
),
),
),
);
},
),
const Divider(
color: ColorsManager.greyColor,
),
],
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 5),
child: Row(
children: [
TextButton(
onPressed: () {
if (textEditingController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please add the room name')),
);
return;
}
BlocProvider.of<CreateUnitBloc>(context).add(
CreateRoomEvent(
roomName: textEditingController.text));
textEditingController.clear();
},
child: const Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
BodyMedium(
text: 'Add custom Space',
fontColor: ColorsManager.primaryColor,
),
],
),
),
Flexible(
child: TextField(
textAlign: TextAlign.end,
controller: textEditingController,
decoration: InputDecoration(
hintText: 'Set',
hintStyle:
context.bodyMedium.copyWith(color: Colors.grey),
border: InputBorder.none,
),
),
),
],
),
),
],
),
),
],
),
),
),
],
),
),
),
);
}),
);
},
),
);
}
}

View File

@ -1,16 +1,19 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/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/helpers/snack_bar.dart';
class JoinHomeView extends StatelessWidget {
const JoinHomeView({super.key});
@override
Widget build(BuildContext context) {
TextEditingController textEditingController = TextEditingController();
return DefaultScaffold(
title: "Join a Home",
child: Column(
@ -38,16 +41,27 @@ class JoinHomeView extends StatelessWidget {
children: [
Flexible(
child: TextField(
controller: textEditingController,
decoration: InputDecoration(
hintText: 'Invitatoin code',
hintStyle:
context.bodyMedium.copyWith(color: Colors.grey),
hintStyle: context.bodyMedium.copyWith(color: Colors.grey),
border: InputBorder.none,
),
),
),
IconButton(
onPressed: () {},
onPressed: () async {
if (textEditingController.text.isEmpty) {
CustomSnackBar.displaySnackBar('Please enter the invitation code');
return;
}
if (await HomeCubit.getInstance().joinAUnit(textEditingController.text)) {
CustomSnackBar.displaySnackBar('Done successfully');
Navigator.of(context).pop();
} else {
CustomSnackBar.displaySnackBar('Wrong code!');
}
},
icon: const Icon(
Icons.arrow_right_alt,
),

View File

@ -0,0 +1,92 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_bloc.dart';
import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_event.dart';
import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_state.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/generated/assets.dart';
class AssignDeviceView extends StatelessWidget {
final String unitId;
final String roomId;
const AssignDeviceView({super.key, required this.unitId, required this.roomId});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => ManageUnitBloc()..add(FetchDevicesByRoomIdEvent(roomId: roomId)),
child: BlocConsumer<ManageUnitBloc, ManageUnitState>(
listener: (context, state) {},
builder: (context, state) {
return DefaultScaffold(
title: 'Space Setting',
child: state is LoadingState
? const Center(child: RefreshProgressIndicator())
: state is FetchDeviceByRoomIdState
? Container(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0,
childAspectRatio: 1.0,
),
itemCount: state.allDevices.length,
itemBuilder: (context, index) {
return Container(
width: MediaQuery.sizeOf(context).width,
padding: const EdgeInsets.all(16),
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SvgPicture.asset(
state.allDevices[index].icon!,
fit: BoxFit.contain,
),
GestureDetector(
onTap: () {
if (state.roomDevicesId[
state.allDevices[index].uuid!] ??
false == false) {
BlocProvider.of<ManageUnitBloc>(context).add(
AssignRoomEvent(
deviceId:
state.allDevices[index].uuid ??
'',
roomId: roomId));
}
},
child: SvgPicture.asset(
state.roomDevicesId[
state.allDevices[index].uuid!] ??
false
? Assets.blueCheckboxIcon
: Assets.emptyCheckboxIcon,
width: 22,
height: 22,
))
],
),
Text(state.allDevices[index].name ?? ''),
],
),
);
}),
)
: Container());
}));
}
}

View File

@ -1,11 +1,15 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/menu/view/widgets/manage_home/room_screen.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/helpers/custom_page_route.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class HomeSettingsView extends StatelessWidget {
@ -38,8 +42,7 @@ class HomeSettingsView extends StatelessWidget {
textAlign: TextAlign.end,
decoration: InputDecoration(
hintText: 'Enter Name',
hintStyle:
context.bodyMedium.copyWith(color: Colors.grey),
hintStyle: context.bodyMedium.copyWith(color: Colors.grey),
border: InputBorder.none,
),
),
@ -52,16 +55,27 @@ class HomeSettingsView extends StatelessWidget {
height: 1,
color: ColorsManager.greyColor,
),
const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BodyMedium(text: 'Rooms'),
Icon(
Icons.arrow_forward_ios,
color: ColorsManager.greyColor,
size: 15,
)
],
Container(
width: MediaQuery.sizeOf(context).width,
child: GestureDetector(
onTap: () {
Navigator.of(context).push(CustomPageRoute(
builder: (context) => RoomsView(
unitId: space?.id ?? '',
)));
},
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BodyMedium(text: 'Rooms'),
Icon(
Icons.arrow_forward_ios,
color: ColorsManager.greyColor,
size: 15,
)
],
),
),
),
//Divider
Container(
@ -78,14 +92,39 @@ class HomeSettingsView extends StatelessWidget {
textAlign: TextAlign.end,
decoration: InputDecoration(
hintText: 'Set',
hintStyle:
context.bodyMedium.copyWith(color: Colors.grey),
hintStyle: context.bodyMedium.copyWith(color: Colors.grey),
border: InputBorder.none,
),
),
),
],
),
//Divider
Container(
margin: const EdgeInsets.only(bottom: 10),
height: 1,
color: ColorsManager.greyColor,
),
Container(
width: MediaQuery.sizeOf(context).width,
padding: const EdgeInsets.only(bottom: 10),
child: GestureDetector(
onTap: () async {
await HomeCubit.getInstance().generateInvitation(space?.id ?? '');
},
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BodyMedium(text: 'Invite a member'),
Icon(
Icons.arrow_forward_ios,
color: ColorsManager.greyColor,
size: 15,
)
],
),
),
),
],
),
),
@ -104,14 +143,13 @@ class HomeSettingsView extends StatelessWidget {
crossAxisCount: 2,
crossAxisSpacing: 10,
),
itemCount: 4,
itemCount: 2,
itemBuilder: (context, index) => Stack(
alignment: Alignment.topCenter,
children: [
DefaultContainer(
margin: const EdgeInsets.only(top: 20),
padding:
const EdgeInsets.symmetric(vertical: 15, horizontal: 40),
padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 40),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [

View File

@ -0,0 +1,137 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_bloc.dart';
import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_event.dart';
import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_state.dart';
import 'package:syncrow_app/features/menu/view/widgets/manage_home/assign_devices.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class RoomsView extends StatelessWidget {
final String unitId;
const RoomsView({super.key, required this.unitId});
@override
Widget build(BuildContext context) {
TextEditingController textEditingController = TextEditingController();
return BlocProvider(
create: (context) => ManageUnitBloc()..add(FetchRoomsEvent(unitId: unitId)),
child: BlocConsumer<ManageUnitBloc, ManageUnitState>(
listener: (context, state) {},
builder: (context, state) {
return DefaultScaffold(
title: 'Space Management',
child: state is LoadingState
? const Center(child: RefreshProgressIndicator())
: Container(
margin: const EdgeInsets.only(top: 32),
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
child: Column(
children: [
if (state is FetchRoomsState)
Container(
decoration: const ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20)),
),
),
child: Column(
children: [
if (state.roomsList.isNotEmpty)
ListView.separated(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
separatorBuilder: (context, index) => const Divider(
color: ColorsManager.greyColor,
),
itemCount: state.roomsList.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
// BlocProvider.of<ManageUnitBloc>(context).add(
// FetchDevicesByRoomIdEvent(roomId: roomId));
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => AssignDeviceView(
roomId: state.roomsList[index].id ?? '',
unitId: unitId,
)),
);
},
child: Container(
height: 50,
padding: const EdgeInsets.symmetric(
vertical: 16, horizontal: 25),
child: Text(
state.roomsList[index].name ?? '',
style: const TextStyle(
color: Color(0xFF5D5D5D),
fontSize: 15,
fontWeight: FontWeight.w400,
),
),
),
);
},
),
const Divider(
color: ColorsManager.greyColor,
),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 25, vertical: 5),
child: Row(
children: [
TextButton(
onPressed: () {
if (textEditingController.text.isEmpty) {
CustomSnackBar.displaySnackBar(
'Please add the room name');
return;
}
BlocProvider.of<ManageUnitBloc>(context).add(
AddNewRoom(
roomName: textEditingController.text,
unitId: unitId));
textEditingController.clear();
},
child: const Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
BodyMedium(
text: 'Add Space',
fontColor: ColorsManager.primaryColor,
),
],
),
),
Flexible(
child: TextField(
textAlign: TextAlign.end,
controller: textEditingController,
decoration: InputDecoration(
hintText: 'Set',
hintStyle:
context.bodyMedium.copyWith(color: Colors.grey),
border: InputBorder.none,
),
),
),
],
),
),
],
),
)
],
),
),
);
}));
}
}

View File

@ -8,12 +8,7 @@ import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
class DefaultScaffold extends StatelessWidget {
const DefaultScaffold(
{super.key,
required this.child,
this.title,
this.actions,
this.appBar,
this.bottomNavBar});
{super.key, required this.child, this.title, this.actions, this.appBar, this.bottomNavBar});
final Widget child;
final String? title;
@ -46,8 +41,7 @@ class DefaultScaffold extends StatelessWidget {
body: Container(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
padding:
const EdgeInsets.symmetric(horizontal: Constants.defaultPadding),
padding: const EdgeInsets.symmetric(horizontal: Constants.defaultPadding),
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(

View File

@ -647,6 +647,9 @@ class Assets {
static const String assetsDeleteIcon = "assets/icons/delete_room_ic.svg";
static const String blueCheckboxIcon = "assets/icons/blue_checkbox_ic.svg";
static const String emptyCheckboxIcon = "assets/icons/empty_checkbox_ic.svg";
/// Assets for assetsImagesAutomation
/// assets/images/automation.jpg
static const String assetsImagesAutomation = "assets/images/automation.jpg";

View File

@ -6,7 +6,7 @@ import 'package:syncrow_app/features/auth/view/sign_up_view.dart';
import 'package:syncrow_app/features/dashboard/view/dashboard_view.dart';
import 'package:syncrow_app/features/layout/view/layout_view.dart';
import 'package:syncrow_app/features/menu/view/menu_view.dart';
import 'package:syncrow_app/features/menu/view/widgets/home%20management/create_home_view.dart';
import 'package:syncrow_app/features/menu/view/widgets/create_home/create_home_view.dart';
import 'package:syncrow_app/features/menu/view/widgets/profile/profile_view.dart';
import 'package:syncrow_app/features/scene/view/scene_view.dart';
import 'package:syncrow_app/features/splash/view/splash_view.dart';

View File

@ -57,6 +57,9 @@ abstract class ApiEndpoints {
static const String unitChild = '$baseUrl/unit/child/';
static const String unitParent = '$baseUrl/unit/parent/{unitUuid}';
static const String unitUser = '$baseUrl/unit/user/';
static const String invitationCode = '$baseUrl/unit/{unitUuid}/invitation-code';
static const String verifyInvitationCode = '$baseUrl/unit/user/verify-code';
//PUT
static const String renameUnit = '$baseUrl/unit/rename/{unitUuid}';
@ -87,6 +90,8 @@ abstract class ApiEndpoints {
static const String addDeviceToRoom = '$baseUrl/device/room';
static const String addDeviceToGroup = '$baseUrl/device/group';
static const String controlDevice = '$baseUrl/device/{deviceUuid}/control';
static const String getDevicesByUserId = '$baseUrl/device/user/{userId}';
//GET
static const String deviceByRoom = '$baseUrl/device/room';
static const String deviceByUuid = '$baseUrl/device/{deviceUuid}';
@ -100,4 +105,6 @@ abstract class ApiEndpoints {
static const String devicePermissionList = '$baseUrl/device-permission/list';
//PUT
static const String editDevicePermission = '$baseUrl/device-permission/edit/{userId}';
static const String assignDeviceToRoom = '$baseUrl/device/room';
}

View File

@ -0,0 +1,40 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:syncrow_app/features/auth/model/user_model.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/services/api/api_links_endpoints.dart';
import 'package:syncrow_app/services/api/http_service.dart';
class HomeManagementAPI {
static final HTTPService _httpService = HTTPService();
static Future<List<DeviceModel>> fetchDevicesByUserId() async {
var storage = const FlutterSecureStorage();
var userId = await storage.read(key: UserModel.userUuidKey) ?? '';
List<DeviceModel> list = [];
await _httpService.get(
path: ApiEndpoints.getDevicesByUserId.replaceAll("{userId}", userId),
showServerMessage: false,
expectedResponseModel: (json) {
json.forEach((value) {
list.add(DeviceModel.fromJson(value));
});
});
return list;
}
static Future<Map<String, dynamic>> assignDeviceToRoom(Map<String, String> body) async {
try {
final response = await _httpService.put(
path: ApiEndpoints.assignDeviceToRoom,
body: body,
expectedResponseModel: (json) {
return json;
},
);
return response;
} catch (e) {
rethrow;
}
}
}

View File

@ -79,6 +79,23 @@ class HTTPService {
}
}
Future<T> put<T>(
{required String path,
Map<String, dynamic>? queryParameters,
dynamic body,
required T Function(dynamic) expectedResponseModel}) async {
try {
final response = await client.put(
path,
data: body,
queryParameters: queryParameters,
);
return expectedResponseModel(response.data);
} catch (error) {
rethrow;
}
}
Future<T> download<T>(
{required String path,
required String savePath,

View File

@ -9,8 +9,7 @@ class SpacesAPI {
static final HTTPService _httpService = HTTPService();
static Future<List<SpaceModel>> getUnitsByUserId() async {
var uuid =
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
final response = await _httpService.get(
path: "${ApiEndpoints.unitUser}$uuid",
showServerMessage: false,
@ -34,4 +33,27 @@ class SpacesAPI {
);
return response;
}
static Future<String> generateInvitationCode(
String unitId,
) async {
final response = await _httpService.get(
path: ApiEndpoints.invitationCode.replaceAll('{unitUuid}', unitId),
showServerMessage: false,
expectedResponseModel: (json) => json['invitationCode'],
);
return response;
}
static Future<bool> joinUnit(
Map<String, String> body,
) async {
final response = await _httpService.post(
path: ApiEndpoints.verifyInvitationCode,
showServerMessage: false,
body: body,
expectedResponseModel: (json) => json['success'],
);
return response;
}
}

View File

@ -2,7 +2,7 @@
import 'dart:ui';
import 'package:syncrow_app/features/devices/model/function_model.dart';
import 'package:syncrow_app/features/menu/view/widgets/home%20management/create_home_view.dart';
import 'package:syncrow_app/features/menu/view/widgets/create_home/create_home_view.dart';
import 'package:syncrow_app/features/menu/view/widgets/join_home/join_home_view.dart';
import 'package:syncrow_app/features/menu/view/widgets/manage_home/manage_home_view.dart';
import 'package:syncrow_app/features/menu/view/widgets/privacy/privacy_view.dart';

View File

@ -89,6 +89,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.18.0"
cross_file:
dependency: transitive
description:
name: cross_file
sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32"
url: "https://pub.dev"
source: hosted
version: "0.3.4+1"
crypto:
dependency: transitive
description:
@ -197,10 +205,10 @@ packages:
dependency: transitive
description:
name: firebase_core_web
sha256: c8e1d59385eee98de63c92f961d2a7062c5d9a65e7f45bdc7f1b0b205aab2492
sha256: "22fcb352744908224fc7be3caae254836099786acfe5df6e9fe901e9c2575a41"
url: "https://pub.dev"
source: hosted
version: "2.11.5"
version: "2.17.1"
firebase_crashlytics:
dependency: "direct main"
description:
@ -377,10 +385,10 @@ packages:
dependency: transitive
description:
name: http
sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba
sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
version: "1.2.1"
http_parser:
dependency: transitive
description:
@ -461,6 +469,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.11.0"
mime:
dependency: transitive
description:
name: mime
sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2"
url: "https://pub.dev"
source: hosted
version: "1.0.5"
nested:
dependency: transitive
description:
@ -645,6 +661,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.27.7"
share_plus:
dependency: "direct main"
description:
name: share_plus
sha256: ef3489a969683c4f3d0239010cc8b7a2a46543a8d139e111c06c558875083544
url: "https://pub.dev"
source: hosted
version: "9.0.0"
share_plus_platform_interface:
dependency: transitive
description:
name: share_plus_platform_interface
sha256: "0f9e4418835d1b2c3ae78fdb918251959106cefdbc4dd43526e182f80e82f6d4"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
shared_preferences:
dependency: "direct main"
description:
@ -689,10 +721,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_web
sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21"
sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a"
url: "https://pub.dev"
source: hosted
version: "2.2.2"
version: "2.3.0"
shared_preferences_windows:
dependency: transitive
description:
@ -862,10 +894,10 @@ packages:
dependency: transitive
description:
name: url_launcher_web
sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b
sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a"
url: "https://pub.dev"
source: hosted
version: "2.2.3"
version: "2.3.1"
url_launcher_windows:
dependency: transitive
description:
@ -926,10 +958,10 @@ packages:
dependency: transitive
description:
name: web
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
url: "https://pub.dev"
source: hosted
version: "0.3.0"
version: "0.5.1"
win32:
dependency: transitive
description:
@ -955,5 +987,5 @@ packages:
source: hosted
version: "6.5.0"
sdks:
dart: ">=3.2.0 <4.0.0"
flutter: ">=3.16.0"
dart: ">=3.3.0 <4.0.0"
flutter: ">=3.19.0"

View File

@ -4,7 +4,7 @@ description: This is the mobile application project, developed with Flutter for
# 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
version: 1.0.1+2
version: 1.0.2+4
environment:
sdk: ">=3.0.6 <4.0.0"
@ -41,6 +41,7 @@ dependencies:
onesignal_flutter: ^5.2.0
permission_handler: ^11.3.1
pin_code_fields: ^8.0.1
share_plus: ^9.0.0
dev_dependencies:
flutter_test: