2024-05-31 18:31:37 +02:00
|
|
|
import 'package:cofounderella/enumerations.dart';
|
|
|
|
import 'package:cofounderella/forms/skills_form.dart';
|
2024-05-24 00:30:08 +02:00
|
|
|
import 'package:flutter/material.dart';
|
2024-05-30 01:10:53 +02:00
|
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
2024-05-30 16:37:34 +02:00
|
|
|
|
2024-05-24 00:30:08 +02:00
|
|
|
import '../constants.dart';
|
2024-05-30 16:37:34 +02:00
|
|
|
import '../models/user_profile.dart';
|
|
|
|
import '../services/user_service.dart';
|
2024-05-31 03:20:42 +02:00
|
|
|
import '../utils/helper.dart';
|
|
|
|
import '../utils/math.dart';
|
2024-05-30 01:10:53 +02:00
|
|
|
import 'edit_profile_page.dart';
|
2024-05-31 03:20:42 +02:00
|
|
|
import 'user_data_page.dart';
|
2024-05-24 00:30:08 +02:00
|
|
|
|
|
|
|
class UserProfilePage extends StatefulWidget {
|
|
|
|
const UserProfilePage({super.key});
|
|
|
|
|
|
|
|
@override
|
2024-05-30 01:10:53 +02:00
|
|
|
State<UserProfilePage> createState() => _UserProfilePageState();
|
2024-05-24 00:30:08 +02:00
|
|
|
}
|
|
|
|
|
2024-05-30 01:10:53 +02:00
|
|
|
class _UserProfilePageState extends State<UserProfilePage> {
|
|
|
|
String? profileImageUrl; // Track the profile image URL
|
2024-05-30 16:37:34 +02:00
|
|
|
late UserProfile myData;
|
2024-05-30 01:10:53 +02:00
|
|
|
bool isLoading = true;
|
2024-05-24 00:30:08 +02:00
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
2024-05-30 16:37:34 +02:00
|
|
|
// Load user data on initialization
|
|
|
|
_loadUserData();
|
2024-05-24 00:30:08 +02:00
|
|
|
}
|
|
|
|
|
2024-05-30 01:10:53 +02:00
|
|
|
Future<void> _loadUserData() async {
|
2024-05-30 16:37:34 +02:00
|
|
|
myData = await UserService.getUserProfileById(
|
|
|
|
FirebaseAuth.instance.currentUser!.uid);
|
2024-05-26 01:44:49 +02:00
|
|
|
|
2024-05-26 17:33:28 +02:00
|
|
|
setState(() {
|
2024-05-30 01:10:53 +02:00
|
|
|
// Initialize the profile image URL
|
2024-05-30 16:37:34 +02:00
|
|
|
profileImageUrl = myData.profilePictureUrl;
|
2024-05-30 01:10:53 +02:00
|
|
|
// Set loading to false once data is loaded
|
|
|
|
isLoading = false;
|
2024-05-26 17:33:28 +02:00
|
|
|
});
|
2024-05-24 00:30:08 +02:00
|
|
|
}
|
|
|
|
|
2024-05-30 01:10:53 +02:00
|
|
|
void editNameInfo() async {
|
|
|
|
final updatedUserData = await Navigator.push(
|
2024-05-25 13:56:45 +02:00
|
|
|
context,
|
|
|
|
MaterialPageRoute(
|
2024-05-30 16:37:34 +02:00
|
|
|
builder: (context) => EditProfilePage(userData: myData),
|
2024-05-25 13:56:45 +02:00
|
|
|
),
|
|
|
|
);
|
2024-05-30 01:10:53 +02:00
|
|
|
|
|
|
|
if (updatedUserData != null) {
|
|
|
|
setState(() {
|
2024-05-30 16:37:34 +02:00
|
|
|
// above Type of updatedUserData is dynamic, so check EditProfilePage
|
2024-05-30 01:10:53 +02:00
|
|
|
profileImageUrl = updatedUserData[Constants.dbFieldUsersProfilePic];
|
2024-05-30 16:37:34 +02:00
|
|
|
myData.profilePictureUrl =
|
|
|
|
updatedUserData[Constants.dbFieldUsersProfilePic];
|
|
|
|
myData.name = updatedUserData[Constants.dbFieldUsersName];
|
|
|
|
myData.bio = updatedUserData[Constants.dbFieldUsersBio];
|
2024-05-30 01:10:53 +02:00
|
|
|
});
|
|
|
|
}
|
2024-05-24 00:30:08 +02:00
|
|
|
}
|
|
|
|
|
2024-05-31 03:20:42 +02:00
|
|
|
void editUserDataInfo() async {
|
|
|
|
final updatedUserData = await Navigator.push(
|
|
|
|
context,
|
|
|
|
MaterialPageRoute(
|
|
|
|
builder: (context) =>
|
|
|
|
const UserDataPage(isRegProcess: false, isEditMode: true),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
if (updatedUserData != null) {
|
|
|
|
setState(() {
|
|
|
|
// above Type of updatedUserData is dynamic, so check UserDataPage
|
|
|
|
myData.born = updatedUserData[Constants.dbFieldUsersYearBorn];
|
|
|
|
myData.gender = updatedUserData[Constants.dbFieldUsersGender];
|
|
|
|
myData.languages = updatedUserData[Constants.dbCollectionLanguages];
|
|
|
|
myData.locations = updatedUserData[Constants.dbCollectionLocations];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-31 18:31:37 +02:00
|
|
|
void _editSkills({required bool skillsSought}) async {
|
|
|
|
final updatedUserData = await Navigator.push(
|
|
|
|
context,
|
|
|
|
MaterialPageRoute(
|
|
|
|
builder: (context) => SkillsForm(
|
|
|
|
isRegProcess: false,
|
|
|
|
skillsSought: skillsSought,
|
|
|
|
isEditMode: true,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
if (updatedUserData != null) {
|
|
|
|
setState(() {
|
|
|
|
// above Type of updatedUserData is dynamic, so convert
|
|
|
|
if (skillsSought) {
|
|
|
|
List<dynamic> dynamicList =
|
|
|
|
updatedUserData[Constants.dbFieldUsersSkillsSought];
|
|
|
|
myData.skillsSought =
|
|
|
|
dynamicList.map((e) => e as SkillOption).toList();
|
|
|
|
} else {
|
|
|
|
List<dynamic> dynamicList =
|
|
|
|
updatedUserData[Constants.dbFieldUsersSkills];
|
|
|
|
myData.skills = dynamicList.map((e) => e as SkillOption).toList();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void editUserSkillsInfo() async {
|
|
|
|
_editSkills(skillsSought: false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void editUserSkillsSoughtInfo() async {
|
|
|
|
_editSkills(skillsSought: true);
|
|
|
|
}
|
|
|
|
|
2024-05-24 00:30:08 +02:00
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return Scaffold(
|
2024-05-30 01:10:53 +02:00
|
|
|
appBar: AppBar(
|
|
|
|
title: const Text('User Profile'),
|
|
|
|
),
|
|
|
|
body: isLoading
|
|
|
|
? const Center(child: CircularProgressIndicator())
|
|
|
|
: Padding(
|
|
|
|
padding: const EdgeInsets.all(16.0),
|
|
|
|
child: SingleChildScrollView(
|
|
|
|
child: Column(
|
|
|
|
children: [
|
2024-05-31 03:20:42 +02:00
|
|
|
_buildAvatar(context),
|
2024-05-30 01:10:53 +02:00
|
|
|
const SizedBox(height: 16),
|
2024-05-31 03:20:42 +02:00
|
|
|
Divider(color: Theme.of(context).colorScheme.primary),
|
2024-05-30 01:10:53 +02:00
|
|
|
const SizedBox(height: 16),
|
2024-05-31 03:20:42 +02:00
|
|
|
_buildLocation(context),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
Divider(color: Theme.of(context).colorScheme.primary),
|
2024-05-30 01:10:53 +02:00
|
|
|
const SizedBox(height: 16),
|
2024-05-31 18:31:37 +02:00
|
|
|
_buildSkills(context),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
Divider(color: Theme.of(context).colorScheme.primary),
|
|
|
|
const SizedBox(height: 16),
|
2024-05-30 01:10:53 +02:00
|
|
|
],
|
|
|
|
),
|
2024-05-24 00:30:08 +02:00
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
2024-05-31 03:20:42 +02:00
|
|
|
|
2024-05-31 18:31:37 +02:00
|
|
|
Widget _buildSkills(BuildContext context) {
|
|
|
|
return Column(
|
|
|
|
children: [
|
|
|
|
Align(
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
children: [
|
|
|
|
Expanded(
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Text(
|
|
|
|
'Skills offered',
|
|
|
|
style: TextStyle(
|
|
|
|
color: Theme.of(context).colorScheme.primary,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Text(myData.skills.map((x) => x.displayName).join(', '),
|
|
|
|
style: const TextStyle(fontSize: 16)),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Align(
|
|
|
|
alignment: Alignment.topRight,
|
|
|
|
child: OutlinedButton.icon(
|
|
|
|
label: const Text('Edit'),
|
|
|
|
icon: const Icon(Icons.edit),
|
|
|
|
onPressed: editUserSkillsInfo,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
children: [
|
|
|
|
Expanded(
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Text(
|
|
|
|
'Skills sought',
|
|
|
|
style: TextStyle(
|
|
|
|
color: Theme.of(context).colorScheme.primary,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Text(
|
|
|
|
myData.skillsSought
|
|
|
|
.map((x) => x.displayName)
|
|
|
|
.join(', '),
|
|
|
|
style: const TextStyle(fontSize: 16)),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Align(
|
|
|
|
alignment: Alignment.topRight,
|
|
|
|
child: OutlinedButton.icon(
|
|
|
|
label: const Text('Edit'),
|
|
|
|
icon: const Icon(Icons.edit),
|
|
|
|
onPressed: editUserSkillsSoughtInfo,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-05-31 03:20:42 +02:00
|
|
|
Widget _buildLocation(BuildContext context) {
|
|
|
|
int age = calcAge(myData.born);
|
|
|
|
return Column(
|
|
|
|
children: [
|
|
|
|
Align(
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
children: [
|
|
|
|
Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Text(
|
|
|
|
'Age',
|
|
|
|
style: TextStyle(
|
|
|
|
color: Theme.of(context).colorScheme.primary,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Text(
|
|
|
|
(age > 0
|
|
|
|
? '$age years old, born ${myData.born}'
|
|
|
|
: 'n/a'),
|
|
|
|
style: const TextStyle(fontSize: 16)),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
Align(
|
|
|
|
alignment: Alignment.bottomRight,
|
|
|
|
child: OutlinedButton.icon(
|
|
|
|
label: const Text('Edit'),
|
|
|
|
icon: const Icon(Icons.edit),
|
|
|
|
onPressed: editUserDataInfo,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
Text(
|
|
|
|
'Gender',
|
|
|
|
style: TextStyle(
|
|
|
|
color: Theme.of(context).colorScheme.primary,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Text(getDisplayText(myData.gender),
|
|
|
|
style: const TextStyle(fontSize: 16)),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
Text(
|
|
|
|
'Spoken languages',
|
|
|
|
style: TextStyle(
|
|
|
|
color: Theme.of(context).colorScheme.primary,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Text(
|
|
|
|
myData.languages.map((lang) => lang.name).join(', '),
|
|
|
|
style: const TextStyle(fontSize: 16),
|
|
|
|
),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
Text(
|
|
|
|
'Locations',
|
|
|
|
style: TextStyle(
|
|
|
|
color: Theme.of(context).colorScheme.primary,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
if (myData.locations.isEmpty)
|
|
|
|
const Text('n/a', style: TextStyle(fontSize: 16)),
|
|
|
|
if (myData.locations.containsKey(Constants.dbDocMainLocation))
|
|
|
|
Text(
|
|
|
|
myData.locations[Constants.dbDocMainLocation].toString(),
|
|
|
|
style: const TextStyle(fontSize: 16),
|
|
|
|
),
|
|
|
|
if (myData.locations.containsKey(Constants.dbDocSecondLocation))
|
|
|
|
Text(
|
|
|
|
myData.locations[Constants.dbDocSecondLocation].toString(),
|
|
|
|
style: const TextStyle(fontSize: 16),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _buildAvatar(BuildContext context) {
|
|
|
|
return Column(
|
|
|
|
children: [
|
|
|
|
Align(
|
|
|
|
alignment: Alignment.bottomRight,
|
|
|
|
child: OutlinedButton.icon(
|
|
|
|
label: const Text('Edit'),
|
|
|
|
icon: const Icon(Icons.edit),
|
|
|
|
onPressed: editNameInfo,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
CircleAvatar(
|
|
|
|
radius: 50,
|
|
|
|
backgroundImage:
|
|
|
|
profileImageUrl != null ? NetworkImage(profileImageUrl!) : null,
|
|
|
|
child: profileImageUrl == null
|
|
|
|
? const Icon(Icons.person, size: 50)
|
|
|
|
: null,
|
|
|
|
),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
Text(myData.name, style: const TextStyle(fontSize: 24)),
|
|
|
|
Text(myData.email, style: const TextStyle(fontSize: 16)),
|
|
|
|
const SizedBox(height: 32),
|
|
|
|
Align(
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Text(
|
|
|
|
'Short description of yourself',
|
|
|
|
style: TextStyle(
|
|
|
|
color: Theme.of(context).colorScheme.primary,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Text(myData.bio ?? 'n/a', style: const TextStyle(fontSize: 16)),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
2024-05-24 00:30:08 +02:00
|
|
|
}
|