Added create a unit screen, and implemented otp screen bloc and view

This commit is contained in:
Abdullah Alassaf
2024-05-30 10:59:22 +03:00
parent 28576f2cd5
commit 9877dde78e
22 changed files with 789 additions and 348 deletions
+2 -1
View File
@@ -22,6 +22,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';
@@ -198,7 +199,7 @@ class HomeCubit extends Cubit<HomeState> {
try {
spaces = await SpacesAPI.getUnitsByUserId();
} catch (failure) {
emitSafe(GetSpacesError(failure.toString()));
emitSafe(GetSpacesError("No units found"));
return;
}
+45 -22
View File
@@ -19,11 +19,6 @@ class AuthCubit extends Cubit<AuthState> {
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
// final TextEditingController emailSignUpController = TextEditingController();
// final TextEditingController fullNameController = TextEditingController();
// final TextEditingController passwordSignUpController = TextEditingController();
// final TextEditingController reEnterPasswordSignUpController = TextEditingController();
String fullName = '';
String email = '';
String signUpPassword = '';
@@ -51,6 +46,10 @@ class AuthCubit extends Cubit<AuthState> {
static Token token = Token.emptyConstructor();
setOtpCode(String value) {
otpCode = value;
}
/////////////////////////////////////VALIDATORS/////////////////////////////////////
String? passwordValidator(String? value) {
if (value != null) {
@@ -64,9 +63,6 @@ class AuthCubit extends Cubit<AuthState> {
}
}
}
if (signUpFormKey.currentState != null) {
signUpFormKey.currentState!.save();
}
return null;
}
@@ -94,19 +90,16 @@ class AuthCubit extends Cubit<AuthState> {
if (parts.any((part) => part.length < 2 || part.length > 30)) {
return 'Full name parts must be between 2 and 30 characters long';
}
if (signUpFormKey.currentState != null) {
signUpFormKey.currentState!.save();
}
return null;
}
String reEnterPasswordCheck(String? value) {
String? reEnterPasswordCheck(String? value) {
passwordValidator(value);
if (signUpPassword == value) {
if (signUpFormKey.currentState != null) {
signUpFormKey.currentState!.save();
}
return '';
// if (signUpFormKey.currentState != null) {
// signUpFormKey.currentState!.save();
// }
return null;
} else {
return 'Passwords do not match';
}
@@ -115,9 +108,6 @@ class AuthCubit extends Cubit<AuthState> {
String? emailAddressValidator(String? value) {
if (value != null && value.isNotEmpty && value != "") {
if (checkValidityOfEmail(value)) {
if (signUpFormKey.currentState != null) {
signUpFormKey.currentState!.save();
}
return null;
} else {
return 'Please enter a valid email';
@@ -204,14 +194,47 @@ class AuthCubit extends Cubit<AuthState> {
}
if (response) {
maskedEmail = maskEmail(email);
final response = await AuthenticationAPI.sendOtp(body: {'email': email, 'type': 'PASSWORD'});
otpCode = response['otp'];
emit(AuthSignUpSuccess());
await sendOtp();
} else {
emit(AuthLoginError(message: 'Something went wrong'));
}
}
sendOtp() async {
try {
final response =
await AuthenticationAPI.sendOtp(body: {'email': email, 'type': 'VERIFICATION'});
// otpCode = response['otp'];
emit(AuthSignUpSuccess());
} catch (_) {
emit(AuthLoginError(message: 'Something went wrong'));
}
}
verifyOtp() async {
emit(AuthLoginLoading());
try {
final response = await AuthenticationAPI.verifyPassCode(
body: {'email': email, 'type': 'VERIFICATION', 'otpCode': otpCode});
if (response['statusCode'] == 200) {
emailController.text = email;
passwordController.text = signUpPassword;
await login();
emit(AuthOtpSuccess());
} else {
emit(AuthLoginError(message: 'Something went wrong'));
}
// if (otpCode == '654321') {
// emit(AuthOtpSuccess());
// } else {
// emit(AuthLoginError(message: 'Otp is not correct'));
// }
} catch (failure) {
emit(AuthLoginError(message: failure.toString()));
return;
}
}
logout() async {
emit(AuthLogoutLoading());
try {
+6 -3
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
@@ -18,6 +19,8 @@ class LoginView extends StatelessWidget {
@override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarBrightness: Brightness.light, statusBarIconBrightness: Brightness.light));
return BlocConsumer<AuthCubit, AuthState>(
listener: (context, state) {
if (state is AuthError) {
@@ -31,9 +34,9 @@ class LoginView extends StatelessWidget {
}
},
builder: (context, state) {
return SafeArea(
child: Scaffold(
body: Stack(
return Scaffold(
body: SafeArea(
child: Stack(
children: [
Container(
width: MediaQuery.sizeOf(context).width,
+59 -34
View File
@@ -1,35 +1,33 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:pin_code_fields/pin_code_fields.dart';
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
import 'package:syncrow_app/features/shared_widgets/default_button.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/navigation/routing_constants.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
import 'package:syncrow_app/utils/resource_manager/styles_manager.dart';
class OtpView extends StatelessWidget {
const OtpView({super.key});
@override
Widget build(BuildContext context) {
final formKey = AuthCubit.get(context).signUpFormKey;
String maskedEmail = AuthCubit.get(context).maskEmail(AuthCubit.get(context).email);
return BlocConsumer<AuthCubit, AuthState>(
listener: (context, state) {
if (state is AuthError) {
} else if (state is AuthOtpSuccess) {
Navigator.popAndPushNamed(context, Routes.homeRoute);
if (state is AuthOtpSuccess) {
Navigator.pushNamedAndRemoveUntil(context, Routes.homeRoute, (value) => false);
}
},
builder: (context, state) {
return SafeArea(
child: Scaffold(
body: Stack(
return Scaffold(
body: SafeArea(
child: Stack(
children: [
Container(
width: MediaQuery.sizeOf(context).width,
@@ -96,7 +94,7 @@ class OtpView extends StatelessWidget {
),
children: [
TextSpan(
text: ' ex******[email protected]',
text: ' $maskedEmail',
style: Theme.of(context).textTheme.titleSmall!.copyWith(
color: Colors.black,
fontWeight: FontsManager.bold,
@@ -116,20 +114,19 @@ class OtpView extends StatelessWidget {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
const SizedBox(
height: 40,
),
PinCodeTextField(
key: const Key('pin_code_text_field'),
appContext: context,
length: 4,
length: 6,
obscuringCharacter: '-',
cursorHeight: 25,
backgroundColor: Colors.transparent,
keyboardType: TextInputType.number,
autoFocus: true,
backgroundColor: Colors.transparent,
animationDuration: const Duration(milliseconds: 30),
// controller: AuthCubit.get(context).emailController,
// cursorColor: KeysperColors.primaryBase,
beforeTextPaste: (text) {
// Allow pasting only if all characters are numeric
return int.tryParse(text!) != null;
@@ -137,11 +134,11 @@ class OtpView extends StatelessWidget {
textStyle: Theme.of(context)
.textTheme
.headlineMedium!
.copyWith(color: Colors.black),
.copyWith(color: Colors.white),
hintStyle: Theme.of(context)
.textTheme
.headlineMedium!
.copyWith(color: Colors.grey),
.copyWith(color: Colors.white),
enablePinAutofill: true,
pinTheme: PinTheme(
borderRadius: BorderRadius.circular(8),
@@ -152,24 +149,28 @@ class OtpView extends StatelessWidget {
errorBorderWidth: 1,
borderWidth: 1,
errorBorderColor: Colors.red,
activeColor: Colors.white,
inactiveColor: Colors.white,
activeFillColor: Colors.white,
inactiveFillColor: Colors.white,
selectedFillColor: Colors.white,
activeColor: state is AuthLoginError ? Colors.red : Colors.white,
inactiveColor:
state is AuthLoginError ? Colors.red : Colors.white,
activeFillColor:
state is AuthLoginError ? Colors.red : Colors.white,
inactiveFillColor:
state is AuthLoginError ? Colors.red : Colors.white,
selectedFillColor:
state is AuthLoginError ? Colors.red : Colors.white,
disabledColor: Colors.white,
fieldHeight: 50,
fieldWidth: 50,
fieldHeight: 56,
fieldWidth: MediaQuery.sizeOf(context).width > 340 ? 40 : 20,
// fieldWidth: 40,
selectedColor: Colors.white,
shape: PinCodeFieldShape.box,
),
onChanged: (value) async {
// otpCode = value;
// await bloc.onOTPChanged(value);
onChanged: (value) {
AuthCubit.get(context).setOtpCode(value);
},
onCompleted: (value) {},
onSubmitted: (value) async {
// await bloc.onOTPSubmitted();
onSubmitted: (value) {
// AuthCubit.get(context).setOtpCode(value);
},
),
const SizedBox(height: 40),
@@ -192,11 +193,35 @@ class OtpView extends StatelessWidget {
'Verify',
),
onPressed: () {
if (formKey.currentState!.validate()) {
if ((state is! AuthLoading)) {
AuthCubit.get(context).signUp();
FocusScope.of(context).unfocus();
}
if ((state is! AuthLoading)) {
AuthCubit.get(context).verifyOtp();
FocusScope.of(context).unfocus();
}
},
),
),
const SizedBox(
width: 4,
),
Expanded(
child: DefaultButton(
isDone: state is AuthLoginSuccess,
isLoading: state is AuthLoading,
customButtonStyle: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Colors.black.withOpacity(.25),
),
foregroundColor: MaterialStateProperty.all(
Colors.white,
),
),
child: const Text(
'Resend',
),
onPressed: () async {
if ((state is! AuthLoading)) {
await AuthCubit.get(context).sendOtp();
FocusScope.of(context).unfocus();
}
},
),
+10 -6
View File
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
import 'package:syncrow_app/features/auth/view/widgets/login_form.dart';
import 'package:syncrow_app/features/shared_widgets/default_button.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart';
@@ -26,13 +25,14 @@ class SignUpView extends StatelessWidget {
}
},
builder: (context, state) {
return SafeArea(
child: Scaffold(
body: Stack(
return Scaffold(
body: SafeArea(
child: Stack(
children: [
Container(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
padding: const EdgeInsets.symmetric(vertical: 24),
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(
@@ -86,6 +86,7 @@ class SignUpView extends StatelessWidget {
),
Form(
key: formKey,
autovalidateMode: AutovalidateMode.disabled,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -95,7 +96,7 @@ class SignUpView extends StatelessWidget {
fontColor: Colors.white,
),
TextFormField(
// autovalidateMode: AutovalidateMode.onUserInteraction,
autovalidateMode: AutovalidateMode.disabled,
textInputAction: TextInputAction.done,
keyboardType: TextInputType.name,
scrollPadding: EdgeInsets.zero,
@@ -109,6 +110,7 @@ class SignUpView extends StatelessWidget {
onChanged: (value) {
AuthCubit.get(context).fullName = value;
},
onTap: () {},
decoration: defaultInputDecoration(context, hint: "Full Name"),
),
const SizedBox(height: 16),
@@ -117,6 +119,7 @@ class SignUpView extends StatelessWidget {
fontColor: Colors.white,
),
TextFormField(
autovalidateMode: AutovalidateMode.disabled,
textInputAction: TextInputAction.done,
keyboardType: TextInputType.text,
scrollPadding: EdgeInsets.zero,
@@ -138,6 +141,7 @@ class SignUpView extends StatelessWidget {
fontColor: Colors.white,
),
TextFormField(
autovalidateMode: AutovalidateMode.disabled,
textInputAction: TextInputAction.done,
keyboardType: TextInputType.text,
scrollPadding: EdgeInsets.zero,
@@ -160,6 +164,7 @@ class SignUpView extends StatelessWidget {
fontColor: Colors.white,
),
TextFormField(
autovalidateMode: AutovalidateMode.disabled,
textInputAction: TextInputAction.done,
keyboardType: TextInputType.text,
scrollPadding: EdgeInsets.zero,
@@ -220,6 +225,5 @@ class SignUpView extends StatelessWidget {
);
},
);
;
}
}
+33 -31
View File
@@ -1,9 +1,9 @@
import 'package:flutter/cupertino.dart';
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/dashboard/view/widgets/carbon_emission.dart';
import 'package:syncrow_app/features/dashboard/view/widgets/consumption.dart';
import 'package:syncrow_app/features/dashboard/view/widgets/live_monitor_tab.dart';
import 'package:syncrow_app/features/shared_widgets/create_unit.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart';
import 'package:syncrow_app/utils/resource_manager/strings_manager.dart';
@@ -14,37 +14,39 @@ class DashboardView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const TitleMedium(
text: StringsManager.dashboard,
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
),
),
const LiveMonitorTab(),
const SizedBox(height: 10),
const EnergyUsage(),
Container(
padding: const EdgeInsets.only(top: 20),
constraints: const BoxConstraints(
minHeight: 220,
maxHeight: 240,
),
child: const Column(
mainAxisAlignment: MainAxisAlignment.start,
return HomeCubit.getInstance().spaces?.isEmpty ?? true
? const CreateUnitWidget()
: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Consumption(),
SizedBox(height: 20),
CarbonEmission(),
const TitleMedium(
text: StringsManager.dashboard,
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
),
),
const LiveMonitorTab(),
const SizedBox(height: 10),
const EnergyUsage(),
Container(
padding: const EdgeInsets.only(top: 20),
constraints: const BoxConstraints(
minHeight: 220,
maxHeight: 240,
),
child: const Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Consumption(),
SizedBox(height: 20),
CarbonEmission(),
],
),
),
],
),
),
],
),
);
);
}
}
@@ -7,6 +7,7 @@ import 'package:syncrow_app/features/devices/view/widgets/devices_view_header.da
import 'package:syncrow_app/features/devices/view/widgets/room_page.dart';
import 'package:syncrow_app/features/devices/view/widgets/rooms_slider.dart';
import 'package:syncrow_app/features/devices/view/widgets/wizard_page.dart';
import 'package:syncrow_app/features/shared_widgets/create_unit.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
class DevicesViewBody extends StatelessWidget {
@@ -23,59 +24,56 @@ class DevicesViewBody extends StatelessWidget {
state is DevicesCategoriesLoading) {
return const Center(child: CircularProgressIndicator());
} else {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const DevicesViewHeader(),
const RoomsSlider(),
const SizedBox(
height: 10,
),
Expanded(
child: PageView(
controller: HomeCubit.getInstance().devicesPageController,
onPageChanged: (index) {
HomeCubit.getInstance().devicesPageChanged(index);
},
return HomeCubit.getInstance().spaces?.isEmpty ?? true
? const CreateUnitWidget()
: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const WizardPage(),
if (HomeCubit.getInstance().selectedSpace != null)
if (HomeCubit.getInstance().selectedSpace!.rooms != null)
...HomeCubit.getInstance().selectedSpace!.rooms!.map(
(room) {
return RoomPage(
room: room,
);
},
)
],
),
),
HomeCubit.getInstance().selectedSpace != null
? Padding(
padding: const EdgeInsets.symmetric(
vertical: 7,
),
child: SmoothPageIndicator(
controller:
HomeCubit.getInstance().devicesPageController,
count: HomeCubit.getInstance()
.selectedSpace!
.rooms!
.length +
1,
effect: const WormEffect(
paintStyle: PaintingStyle.stroke,
dotHeight: 8,
dotWidth: 8,
),
),
)
: const Center(
child: BodyLarge(text: 'No Home Found'),
const DevicesViewHeader(),
const RoomsSlider(),
const SizedBox(
height: 10,
),
],
);
Expanded(
child: PageView(
controller: HomeCubit.getInstance().devicesPageController,
onPageChanged: (index) {
HomeCubit.getInstance().devicesPageChanged(index);
},
children: [
const WizardPage(),
if (HomeCubit.getInstance().selectedSpace != null)
if (HomeCubit.getInstance().selectedSpace!.rooms != null)
...HomeCubit.getInstance().selectedSpace!.rooms!.map(
(room) {
return RoomPage(
room: room,
);
},
)
],
),
),
HomeCubit.getInstance().selectedSpace != null
? Padding(
padding: const EdgeInsets.symmetric(
vertical: 7,
),
child: SmoothPageIndicator(
controller: HomeCubit.getInstance().devicesPageController,
count: HomeCubit.getInstance().selectedSpace!.rooms!.length + 1,
effect: const WormEffect(
paintStyle: PaintingStyle.stroke,
dotHeight: 8,
dotWidth: 8,
),
),
)
: const Center(
child: BodyLarge(text: 'No Home Found'),
),
],
);
}
},
);
@@ -0,0 +1,42 @@
import 'package:flutter_bloc/flutter_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';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
class CreateUnitBloc extends Bloc<CreateUnitEvent, CreateUnitState> {
List<String> rooms = [];
String unitName = '';
String locationName = '';
CreateUnitBloc() : super(InitialState()) {
on<CreateRoomEvent>(_createRoom);
on<RemoveRoomEvent>(_removeRoom);
}
void _createRoom(CreateRoomEvent event, Emitter<CreateUnitState> emit) async {
try {
emit(LoadingState());
if (rooms.contains(event.roomName)) {
emit(const ErrorState(message: 'This name is already exist'));
emit(CreateRoomState(roomList: rooms));
return;
}
rooms.add(event.roomName);
emit(CreateRoomState(roomList: rooms));
} catch (e) {
emit(const ErrorState(message: 'Something went wrong'));
return;
}
}
void _removeRoom(RemoveRoomEvent event, Emitter<CreateUnitState> emit) {
try {
emit(LoadingState());
rooms.removeWhere((element) => element == event.roomName);
emit(CreateRoomState(roomList: rooms));
} catch (e) {
emit(const ErrorState(message: 'Something went wrong'));
return;
}
}
}
@@ -0,0 +1,30 @@
import 'package:equatable/equatable.dart';
abstract class CreateUnitEvent extends Equatable {
const CreateUnitEvent();
@override
List<Object> get props => [];
}
class InitialEvent extends CreateUnitEvent {}
class LoadingEvent extends CreateUnitEvent {}
class CreateRoomEvent extends CreateUnitEvent {
final String roomName;
const CreateRoomEvent({required this.roomName});
@override
List<Object> get props => [roomName];
}
class RemoveRoomEvent extends CreateUnitEvent {
final String roomName;
const RemoveRoomEvent({required this.roomName});
@override
List<Object> get props => [roomName];
}
@@ -0,0 +1,30 @@
import 'package:equatable/equatable.dart';
abstract class CreateUnitState extends Equatable {
const CreateUnitState();
@override
List<Object> get props => [];
}
class InitialState extends CreateUnitState {}
class LoadingState extends CreateUnitState {}
class CreateRoomState extends CreateUnitState {
final List<String> roomList;
const CreateRoomState({required this.roomList});
@override
List<Object> get props => [roomList];
}
class ErrorState extends CreateUnitState {
final String message;
const ErrorState({required this.message});
@override
List<Object> get props => [message];
}
@@ -1,124 +1,247 @@
// 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/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';
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/generated/assets.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 CreateHomeView extends StatelessWidget {
const CreateHomeView({super.key});
class CreateUnitView extends StatelessWidget {
const CreateUnitView({super.key});
@override
Widget build(BuildContext context) {
return DefaultScaffold(
actions: [
TextButton(
onPressed: () {},
child: const BodyLarge(
text: 'Save',
fontWeight: FontWeight.bold,
),
),
],
title: 'Create Home',
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: 'Home Name '),
Flexible(
child: TextField(
textAlign: TextAlign.end,
decoration: InputDecoration(
hintText: 'Enter Name',
hintStyle:
context.bodyMedium.copyWith(color: Colors.grey),
border: InputBorder.none,
),
),
),
],
),
//Divider
Container(
height: 1,
color: ColorsManager.greyColor,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const BodyMedium(text: 'Location '),
Flexible(
child: TextField(
textAlign: TextAlign.end,
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: 'Rooms',
String unitName = '';
String location = '';
TextEditingController textEditingController = TextEditingController();
return BlocProvider(
create: (context) => CreateUnitBloc(),
child: BlocConsumer<CreateUnitBloc, CreateUnitState>(listener: (context, state) {
if (state is ErrorState) {
CustomSnackBar.displaySnackBar(state.message);
}
}, builder: (context, state) {
textEditingController.text = '';
return DefaultScaffold(
actions: [
TextButton(
onPressed: () {},
child: const BodyLarge(
text: 'Save',
fontWeight: FontWeight.bold,
),
],
),
DefaultContainer(
padding: const EdgeInsets.symmetric(
horizontal: 25,
vertical: 5,
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
TextButton(
onPressed: () {
//TODO add room
},
child: const Row(
],
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: [
BodyMedium(
text: 'Add Room',
fontColor: ColorsManager.primaryColor,
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,
),
),
),
],
),
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(
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')),
);
},
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,
),
),
),
);
},
),
),
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,
children: [
BodyMedium(
text: 'Add Space',
fontColor: ColorsManager.primaryColor,
),
],
)),
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,
),
),
),
],
)
],
),
),
],
),
),
),
],
),
);
}),
);
}
}
+95 -91
View File
@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/scene/bloc/scene_cubit.dart';
import 'package:syncrow_app/features/shared_widgets/create_unit.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/features/shared_widgets/text_widgets/body_small.dart';
@@ -17,100 +19,102 @@ class SceneView extends StatelessWidget {
create: (BuildContext context) => SceneCubit(),
child: BlocBuilder<SceneCubit, SceneState>(
builder: (context, state) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const TitleMedium(
text: StringsManager.routine,
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
const BodySmall(
text: StringsManager.tapToRunRoutine,
),
Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 10,
),
child: DefaultContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset(
height: 50,
width: 50,
Assets.assetsIconsHot1,
fit: BoxFit.contain,
),
const Icon(
Icons.play_circle,
size: 40,
color: Colors.grey,
)
],
),
const BodyMedium(
text: StringsManager.summerMode,
fontWeight: FontWeight.bold,
fontSize: 16,
)
],
),
return HomeCubit.getInstance().spaces?.isEmpty ?? true
? const CreateUnitWidget()
: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const TitleMedium(
text: StringsManager.routine,
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(width: 10),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 10,
),
child: DefaultContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset(
height: 50,
width: 50,
Assets.assetsIconsWinter1,
fit: BoxFit.contain,
),
const Icon(
Icons.play_circle,
size: 40,
color: Colors.grey,
)
],
),
const BodyMedium(
text: StringsManager.winterMode,
fontWeight: FontWeight.bold,
fontSize: 16,
)
],
),
),
const SizedBox(height: 20),
const BodySmall(
text: StringsManager.tapToRunRoutine,
),
),
],
)
],
);
Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 10,
),
child: DefaultContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset(
height: 50,
width: 50,
Assets.assetsIconsHot1,
fit: BoxFit.contain,
),
const Icon(
Icons.play_circle,
size: 40,
color: Colors.grey,
)
],
),
const BodyMedium(
text: StringsManager.summerMode,
fontWeight: FontWeight.bold,
fontSize: 16,
)
],
),
),
),
),
const SizedBox(width: 10),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 10,
),
child: DefaultContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset(
height: 50,
width: 50,
Assets.assetsIconsWinter1,
fit: BoxFit.contain,
),
const Icon(
Icons.play_circle,
size: 40,
color: Colors.grey,
)
],
),
const BodyMedium(
text: StringsManager.winterMode,
fontWeight: FontWeight.bold,
fontSize: 16,
)
],
),
),
),
),
],
)
],
);
},
),
);
@@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/navigation/routing_constants.dart';
class CreateUnitWidget extends StatelessWidget {
const CreateUnitWidget({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Assets.noUnitsIconDashboard,
width: 100,
height: 100,
),
const SizedBox(
height: 50,
),
Flexible(
child: GestureDetector(
onTap: () {
Navigator.pushNamed(context, Routes.createUnit);
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 34, vertical: 14),
decoration: ShapeDecoration(
color: const Color(0x99023DFE),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
child: const TitleMedium(
text: 'Create a unit',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w400, color: Colors.white),
),
),
),
),
],
),
);
}
}