Fixed design issues, added updated read me file

This commit is contained in:
Abdullah Alassaf
2024-08-01 12:57:13 +03:00
parent 296726ac82
commit 8d6da30d09
6 changed files with 297 additions and 214 deletions

View File

@ -23,13 +23,12 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
String timeZoneSelected = '';
String regionSelected = '';
final TextEditingController searchController = TextEditingController();
final TextEditingController nameController = TextEditingController(text: '${HomeCubit.user!.firstName} ${HomeCubit.user!.lastName}');
final TextEditingController nameController =
TextEditingController(text: '${HomeCubit.user!.firstName} ${HomeCubit.user!.lastName}');
List<RegionModel> allRegions = [];
List<TimeZone> allTimeZone = [];
ProfileBloc() : super(InitialState()) {
on<InitialProfileEvent>(_fetchUserInfo);
on<TimeZoneInitialEvent>(_fetchTimeZone);
@ -53,7 +52,7 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
final nameParts = fullName.split(' ');
final firstName = nameParts[0];
final lastName = nameParts.length > 1 ? nameParts[1] : '';
var response = await ProfileApi.saveName(firstName: firstName, lastName: lastName);
await ProfileApi.saveName(firstName: firstName, lastName: lastName);
add(InitialProfileEvent());
await HomeCubit.getInstance().fetchUserInfo();
CustomSnackBar.displaySnackBar('Save Successfully');
@ -72,7 +71,7 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
Future.delayed(const Duration(milliseconds: 500), () {
focusNode.requestFocus();
});
}else {
} else {
focusNode.unfocus();
}
emit(NameEditingState(editName: editName));
@ -116,7 +115,7 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
Future selectRegion(SelectRegionEvent event, Emitter<ProfileState> emit) async {
try {
emit(LoadingInitialState());
await ProfileApi.saveRegion(regionUuid:event.val );
await ProfileApi.saveRegion(regionUuid: event.val);
CustomSnackBar.displaySnackBar('Save Successfully');
emit(SaveState());
} catch (e) {
@ -125,7 +124,6 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
}
}
Future<void> searchRegion(SearchRegionEvent event, Emitter<ProfileState> emit) async {
emit(LoadingInitialState());
final query = event.query.toLowerCase();
@ -158,7 +156,6 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
}
}
void _fetchRegion(RegionInitialEvent event, Emitter<ProfileState> emit) async {
try {
emit(LoadingInitialState());
@ -170,36 +167,37 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
}
Future<void> _selectImage(SelectImageEvent event, Emitter<ProfileState> emit) async {
if (await _requestPermission()) {
emit(ChangeImageState());
final pickedFile = await _picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
image = File(pickedFile.path);
final bytes = image!.readAsBytesSync().lengthInBytes;
final kb = bytes / 1024;
final mb = kb / 1024;
if(mb>1){
image=null;
CustomSnackBar.displaySnackBar('Image size must be 1 MB or less');
}else{
await _saveImage();
try {
if (await _requestPermission()) {
emit(ChangeImageState());
final pickedFile = await _picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
image = File(pickedFile.path);
final bytes = image!.readAsBytesSync().lengthInBytes;
final kb = bytes / 1024;
final mb = kb / 1024;
if (mb > 1) {
image = null;
CustomSnackBar.displaySnackBar('Image size must be 1 MB or less');
} else {
emit(LoadingInitialState());
await _saveImage();
emit(ImageSelectedState());
}
}
emit(ImageSelectedState());
} else {
print('No image selected.');
_showPermissionDeniedDialog(event.context);
}
emit(ImageSelectedState());
} else {
_showPermissionDeniedDialog(event.context);
} catch (_) {
emit(const FailedState(errorMessage: 'Something went wrong'));
}
}
Future<void> _saveImage() async {
emit(LoadingInitialState());
List<int> imageBytes = image!.readAsBytesSync();
String base64Image = base64Encode(imageBytes);
print(base64Image);
var response = await ProfileApi.saveImage(base64Image);
emit(ImageSelectedState());
await ProfileApi.saveImage(base64Image);
}
void _showPermissionDeniedDialog(BuildContext context) {
@ -229,9 +227,6 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
);
}
bool _validateInputs() {
final nameError = fullNameValidator(nameController.text);
if (nameError != null) {
@ -241,7 +236,6 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
return false;
}
String? fullNameValidator(String? value) {
if (value == null) return 'Full name is required';
final withoutExtraSpaces = value.replaceAll(RegExp(r"\s+"), ' ').trim();
@ -261,13 +255,12 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
return null;
}
Future<bool> _requestPermission() async {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
if (Platform.isAndroid ) {
if (Platform.isAndroid) {
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
PermissionStatus status = await Permission.photos.status;
if(androidInfo.version.sdkInt<33){
if (androidInfo.version.sdkInt < 33) {
if (status.isDenied) {
PermissionStatus status = await Permission.storage.request();
if (status.isGranted) {
@ -276,7 +269,7 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
return false;
}
}
}else{
} else {
if (status.isGranted) {
return true;
} else if (status.isDenied) {
@ -301,5 +294,4 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
}
}
}
}

View File

@ -21,12 +21,11 @@ class MenuView extends StatelessWidget {
builder: (context, state) {
return BlocBuilder<AuthCubit, AuthState>(
builder: (context, state) {
final profileBloc = BlocProvider.of<MenuCubit>(context);
return SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
children: [
ProfileTab(),
const ProfileTab(),
for (var section in menuSections)
MenuList(
section: section,

View File

@ -7,20 +7,23 @@ import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dar
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
class ProfileTab extends StatelessWidget {
const ProfileTab({super.key,});
const ProfileTab({
super.key,
});
@override
Widget build(BuildContext context) {
return BlocBuilder<HomeCubit, HomeState>(
builder: (context, state) {
return _buildProfileContent(context );
return _buildProfileContent(context);
},
);
}
Widget _buildProfileContent(BuildContext context) {
final homeCubit = context.read<HomeCubit>();
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10,),
padding: const EdgeInsets.symmetric(
vertical: 10,
),
child: InkWell(
onTap: () {
Navigator.of(context)
@ -28,7 +31,8 @@ class ProfileTab extends StatelessWidget {
MaterialPageRoute(
builder: (context) => const ProfileView(),
),
).then((result) {
)
.then((result) {
context.read<HomeCubit>().fetchUserInfo();
});
},
@ -47,7 +51,7 @@ class ProfileTab extends StatelessWidget {
Row(
children: [
BodyMedium(
text: '${HomeCubit.user!.firstName ?? ''} ',
text: '${HomeCubit.user?.firstName ?? ''} ',
fontWeight: FontWeight.bold,
),
BodyMedium(
@ -56,7 +60,9 @@ class ProfileTab extends StatelessWidget {
),
],
),
SizedBox(height: 5,),
const SizedBox(
height: 5,
),
const BodySmall(text: "Syncrow Account"),
],
),
@ -76,12 +82,12 @@ class ProfileTab extends StatelessWidget {
child: ClipOval(
child: HomeCubit.user?.profilePicture != null
? Image.memory(
HomeCubit.user!.profilePicture!,
fit: BoxFit.cover,
width: 110,
height: 110,
)
: Icon(Icons.person, size: 70), // Fallback if no image
HomeCubit.user!.profilePicture!,
fit: BoxFit.cover,
width: 110,
height: 110,
)
: const Icon(Icons.person, size: 70), // Fallback if no image
),
),
),

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/menu/bloc/profile_bloc/profile_bloc.dart';
@ -25,175 +24,180 @@ class ProfileView extends StatelessWidget {
final profileBloc = BlocProvider.of<ProfileBloc>(context);
return DefaultScaffold(
title: 'Syncrow Account',
child:
state is LoadingInitialState
? const Center(child: CircularProgressIndicator()):
Column(
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.05,
),
InkWell(
onTap: () {
profileBloc.add(SelectImageEvent(context: context, isSelected: false));
},
child: SizedBox.square(
dimension: 125,
child: CircleAvatar(
backgroundColor: Colors.white,
child: SizedBox.square(
dimension: 120,
child: CircleAvatar(
backgroundColor: Colors.white,
backgroundImage: profileBloc.image == null
? null
: FileImage(profileBloc.image!),
child: profileBloc.image != null
? null
:HomeCubit.user!.profilePicture != null
? ClipOval(
child: Image.memory(
HomeCubit.user!.profilePicture!,
fit: BoxFit.cover,
width: 120,
height: 120,
),
)
: null,
child: state is LoadingInitialState
? const Center(child: CircularProgressIndicator())
: SizedBox(
height: MediaQuery.sizeOf(context).height,
child: ListView(
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.05,
),
),
),
),
),
const SizedBox(height: 20),
SizedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IntrinsicWidth(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 200),
child: TextFormField(
maxLength: 30,
style: const TextStyle(
color: Colors.black,
),
textAlign: TextAlign.center,
focusNode: profileBloc.focusNode,
controller: profileBloc.nameController,
enabled: profileBloc.editName,
onEditingComplete: () {
profileBloc.add(SaveNameEvent(context: context));
},
decoration: const InputDecoration(
hintText: "Your Name",
border: InputBorder.none,
fillColor: Colors.white10,
counterText: '',
InkWell(
onTap: () {
profileBloc.add(SelectImageEvent(context: context, isSelected: false));
},
child: SizedBox.square(
dimension: 125,
child: CircleAvatar(
backgroundColor: Colors.white,
child: SizedBox.square(
dimension: 120,
child: CircleAvatar(
backgroundColor: Colors.white,
backgroundImage: profileBloc.image == null
? null
: FileImage(profileBloc.image!),
child: profileBloc.image != null
? null
: HomeCubit.user!.profilePicture != null
? ClipOval(
child: Image.memory(
HomeCubit.user!.profilePicture!,
fit: BoxFit.cover,
width: 120,
height: 120,
),
)
: null,
),
),
),
),
),
),
const SizedBox(width: 5),
InkWell(
onTap: () {
profileBloc.add(const ChangeNameEvent(value: true));
},
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Icon(
Icons.edit_outlined,
size: 20,
color: ColorsManager.textPrimaryColor,
),
),
),
],
),
),
const SizedBox(height: 10),
DefaultContainer(
padding: const EdgeInsets.symmetric(
horizontal: 25,
vertical: 5,
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 20, bottom: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const BodyMedium(text: 'email '),
Flexible(child: BodyMedium(text: HomeCubit.user!.email ?? 'No Email')),
],
),
),
Container(
height: 1,
color: ColorsManager.greyColor,
),
InkWell(
onTap: () {
profileBloc.add(const ChangeNameEvent(value: false));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const RegionPage(),
),
).then((result) {
profileBloc.add(InitialProfileEvent());
});
},
child: Padding(
padding: const EdgeInsets.only(top: 20, bottom: 20),
const SizedBox(height: 20),
SizedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const BodyMedium(text: 'Region '),
Flexible(child: BodyMedium(text: HomeCubit.user!.regionName ?? 'No Region')),
],
),
),
),
Container(
height: 1,
color: ColorsManager.greyColor,
),
InkWell(
onTap: () {
profileBloc.add(const ChangeNameEvent(value: false));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const TimeZoneScreenPage(),
),
).then((result) {
profileBloc.add(InitialProfileEvent());
});
},
child: Padding(
padding: const EdgeInsets.only(top: 15, bottom: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const BodyMedium(text: 'Time Zone '),
Flexible(
child: BodyMedium(text: HomeCubit.user!.timeZone ?? "No Time Zone"),
IntrinsicWidth(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 200),
child: TextFormField(
maxLength: 30,
style: const TextStyle(
color: Colors.black,
),
textAlign: TextAlign.center,
focusNode: profileBloc.focusNode,
controller: profileBloc.nameController,
enabled: profileBloc.editName,
onEditingComplete: () {
profileBloc.add(SaveNameEvent(context: context));
},
decoration: const InputDecoration(
hintText: "Your Name",
border: InputBorder.none,
fillColor: Colors.white10,
counterText: '',
),
),
),
),
const SizedBox(width: 5),
InkWell(
onTap: () {
profileBloc.add(const ChangeNameEvent(value: true));
},
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Icon(
Icons.edit_outlined,
size: 20,
color: ColorsManager.textPrimaryColor,
),
),
),
],
),
),
),
],
)
),
],
),
const SizedBox(height: 10),
DefaultContainer(
padding: const EdgeInsets.symmetric(
horizontal: 25,
vertical: 5,
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 20, bottom: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const BodyMedium(text: 'email '),
Flexible(
child: BodyMedium(
text: HomeCubit.user!.email ?? 'No Email')),
],
),
),
Container(
height: 1,
color: ColorsManager.greyColor,
),
InkWell(
onTap: () {
profileBloc.add(const ChangeNameEvent(value: false));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const RegionPage(),
),
).then((result) {
profileBloc.add(InitialProfileEvent());
});
},
child: Padding(
padding: const EdgeInsets.only(top: 20, bottom: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const BodyMedium(text: 'Region '),
Flexible(
child: BodyMedium(
text: HomeCubit.user!.regionName ?? 'No Region')),
],
),
),
),
Container(
height: 1,
color: ColorsManager.greyColor,
),
InkWell(
onTap: () {
profileBloc.add(const ChangeNameEvent(value: false));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const TimeZoneScreenPage(),
),
).then((result) {
profileBloc.add(InitialProfileEvent());
});
},
child: Padding(
padding: const EdgeInsets.only(top: 15, bottom: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const BodyMedium(text: 'Time Zone '),
Flexible(
child: BodyMedium(
text: HomeCubit.user!.timeZone ?? "No Time Zone"),
),
],
),
),
),
],
)),
],
),
),
);
},
),