cofounderella/lib/pages/user_profile_page.dart

759 lines
25 KiB
Dart
Raw Normal View History

import 'package:expandable_text/expandable_text.dart';
2024-05-24 00:30:08 +02:00
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
2024-05-30 16:37:34 +02:00
import '../components/external_link_widget.dart';
2024-06-14 14:28:59 +02:00
import '../components/language_list.dart';
2024-05-24 00:30:08 +02:00
import '../constants.dart';
2024-05-31 23:16:15 +02:00
import '../enumerations.dart';
import '../forms/corporate_culture_form.dart';
2024-05-31 23:55:33 +02:00
import '../forms/risks_form.dart';
2024-06-21 02:42:45 +02:00
import '../forms/sectors_form.dart';
2024-05-31 23:16:15 +02:00
import '../forms/skills_form.dart';
2024-05-30 16:37:34 +02:00
import '../models/user_profile.dart';
import '../services/user_service.dart';
import '../utils/helper.dart';
2024-06-21 02:42:45 +02:00
import '../utils/list_utils.dart';
import '../utils/math.dart';
import 'edit_profile_page.dart';
import 'user_data_page.dart';
2024-05-31 23:16:15 +02:00
import 'user_vision_page.dart';
2024-05-24 00:30:08 +02:00
class UserProfilePage extends StatefulWidget {
2024-07-12 22:32:07 +02:00
const UserProfilePage({super.key, this.userId, this.titleText});
final String? userId;
2024-07-12 22:32:07 +02:00
final String? titleText;
2024-05-24 00:30:08 +02:00
@override
State<UserProfilePage> createState() => _UserProfilePageState();
2024-05-24 00:30:08 +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;
bool isLoading = true;
late bool isOwner;
late String _userId;
2024-07-12 22:32:07 +02:00
late String _titleText;
2024-05-24 00:30:08 +02:00
@override
void initState() {
super.initState();
// Determine the userId to use, then check if user is the current user
_userId = widget.userId ?? FirebaseAuth.instance.currentUser!.uid;
isOwner = (_userId == FirebaseAuth.instance.currentUser!.uid);
2024-05-30 16:37:34 +02:00
// Load user data on initialization
_loadUserData();
2024-07-12 22:32:07 +02:00
_titleText = (widget.titleText ??
(isOwner ? 'My Profile Information' : 'Profile Information'));
2024-05-24 00:30:08 +02:00
}
Future<void> _loadUserData() async {
myData = await UserService.getUserProfileById(_userId);
2024-05-26 01:44:49 +02:00
setState(() {
// Initialize the profile image URL
2024-05-30 16:37:34 +02:00
profileImageUrl = myData.profilePictureUrl;
// Set loading to false once data is loaded
isLoading = false;
});
2024-05-24 00:30:08 +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
),
);
if (updatedUserData != null) {
setState(() {
2024-05-30 16:37:34 +02:00
// above Type of updatedUserData is dynamic, so check EditProfilePage
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];
myData.urlFacebook = updatedUserData[Constants.dbFieldUsersUrlFacebook];
myData.urlLinkedIn = updatedUserData[Constants.dbFieldUsersUrlLinkedIn];
myData.urlXing = updatedUserData[Constants.dbFieldUsersUrlXing];
});
}
2024-05-24 00:30:08 +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-06-21 02:42:45 +02:00
void editUserSectorsInfo() async {
final updatedUserData = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SectorsForm(
isRegProcess: false,
isEditMode: true,
),
),
);
if (updatedUserData != null) {
setState(() {
// above Type of updatedUserData is dynamic, so convert
List<dynamic> dynamicList =
updatedUserData[Constants.dbFieldUsersSectors];
myData.sectors = dynamicList.map((e) => e as SectorOption).toList();
});
}
}
2024-05-31 23:16:15 +02:00
void editUserVisionInfo() async {
final updatedUserData = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const UserVisionPage(
isRegProcess: false,
isEditMode: true,
),
),
);
if (updatedUserData != null) {
setState(() {
// above Type of updatedUserData is dynamic, so convert
List<dynamic> dynamicList =
updatedUserData[Constants.dbFieldUsersVisions];
myData.visions = dynamicList.map((e) => e as VisionOption).toList();
myData.availability =
updatedUserData[Constants.dbFieldUsersAvailability];
});
}
}
void editUserWorkCultureInfo() async {
final updatedUserData = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const CultureValuesFormPage(
isRegProcess: false,
isEditMode: true,
),
),
);
if (updatedUserData != null) {
setState(() {
// above Type of updatedUserData is dynamic, so convert
List<dynamic> dynamicList =
updatedUserData[Constants.dbFieldUsersWorkValues];
myData.workValues =
dynamicList.map((e) => e as WorkValueOption).toList();
myData.culture = updatedUserData[Constants.dbFieldUsersCorpCulture];
});
}
}
void editUserRiskInfo() async {
2024-05-31 23:55:33 +02:00
final updatedUserData = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const RisksFormPage(
isRegProcess: false,
isEditMode: true,
),
),
);
if (updatedUserData != null) {
setState(() {
// above Type of updatedUserData is dynamic, so convert
myData.risk = updatedUserData[Constants.dbFieldUsersRiskTolerance];
myData.communication =
updatedUserData[Constants.dbFieldUsersCommunication];
});
}
}
2024-05-24 00:30:08 +02:00
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
2024-07-12 22:32:07 +02:00
title: Text(_titleText),
),
body: isLoading
? const Center(child: CircularProgressIndicator())
: Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
children: [
_buildAvatar(context),
const SizedBox(height: 16),
if (isOwner)
Divider(color: Theme.of(context).colorScheme.primary),
if (isOwner) const SizedBox(height: 16),
_buildLocation(context),
const SizedBox(height: 16),
if (isOwner)
Divider(color: Theme.of(context).colorScheme.primary),
if (isOwner) const SizedBox(height: 16),
2024-05-31 18:31:37 +02:00
_buildSkills(context),
const SizedBox(height: 16),
if (isOwner)
Divider(color: Theme.of(context).colorScheme.primary),
if (isOwner) const SizedBox(height: 16),
2024-06-21 02:42:45 +02:00
_buildSectors(context),
const SizedBox(height: 16),
if (isOwner)
Divider(color: Theme.of(context).colorScheme.primary),
if (isOwner) const SizedBox(height: 16),
_buildRisks(context),
2024-05-31 23:16:15 +02:00
const SizedBox(height: 16),
if (isOwner)
Divider(color: Theme.of(context).colorScheme.primary),
if (isOwner) const SizedBox(height: 16),
_buildVision(context),
2024-05-31 23:16:15 +02:00
const SizedBox(height: 16),
if (isOwner)
Divider(color: Theme.of(context).colorScheme.primary),
if (isOwner) const SizedBox(height: 16),
_buildWorkCulture(context),
2024-05-31 23:55:33 +02:00
const SizedBox(height: 16),
if (isOwner)
Divider(color: Theme.of(context).colorScheme.primary),
if (isOwner) const SizedBox(height: 16),
],
),
2024-05-24 00:30:08 +02:00
),
),
);
}
Widget _buildAvatar(BuildContext context) {
Widget genderIcon = const Icon(null);
if (myData.gender == Gender.male) {
genderIcon = const Padding(
padding: EdgeInsets.only(left: 4.0),
child: Icon(Icons.male, color: Colors.blue),
);
} else if (myData.gender == Gender.female) {
genderIcon = const Icon(Icons.female, color: Colors.pink);
}
2024-08-13 14:12:24 +02:00
Widget verifiedIcon = const Icon(null);
if (myData.verified != null &&
myData.verified!.toDate().isAfter(DateTime(2024, 8, 1))) {
verifiedIcon = const Padding(
padding: EdgeInsets.only(right: 4.0),
child: Icon(Icons.verified, color: Colors.blueGrey),
);
}
return Column(
children: [
if (isOwner)
_editButton(context, editNameInfo, alignment: Alignment.bottomRight),
CircleAvatar(
radius: 80,
backgroundImage:
(profileImageUrl != null && profileImageUrl!.isNotEmpty)
? NetworkImage(profileImageUrl!)
: null,
child: (profileImageUrl == null || profileImageUrl!.isEmpty)
? const Icon(Icons.person, size: 80)
: null,
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
2024-08-13 14:12:24 +02:00
verifiedIcon,
Flexible(
child: Text(
isOwner
? myData.name
: '${myData.name} ${ageInfo(myData.born)}'.trim(),
style: const TextStyle(fontSize: 24),
),
),
genderIcon,
],
),
if (isOwner) Text(myData.email, style: const TextStyle(fontSize: 16)),
if (myData.urlXing != null && myData.urlXing!.isNotEmpty)
ExternalLinkWidget(url: myData.urlXing!, context: context),
if (myData.urlLinkedIn != null && myData.urlLinkedIn!.isNotEmpty)
ExternalLinkWidget(url: myData.urlLinkedIn!, context: context),
if (myData.urlFacebook != null && myData.urlFacebook!.isNotEmpty)
ExternalLinkWidget(url: myData.urlFacebook!, context: context),
const SizedBox(height: 32),
Align(
alignment: Alignment.centerLeft,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
isOwner ? 'Short description of yourself' : 'Short description',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
ExpandableText(
linkEllipsis: false,
collapseOnTextTap: true,
expandOnTextTap: true,
myData.bio ?? 'n/a',
expandText: '[Expand]',
collapseText: '[Collapse]',
maxLines: 3,
linkColor: Colors.blue,
style: const TextStyle(fontSize: 16),
),
],
),
),
],
);
}
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(
myData.locations.length > 1 ? 'Locations' : 'Location',
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),
),
],
),
if (isOwner)
_editButton(context, editUserDataInfo,
alignment: Alignment.bottomRight),
],
),
if (isOwner) const SizedBox(height: 16),
if (isOwner)
Text(
'Age',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
if (isOwner)
Text(
(age > 0 ? '$age years old, born ${myData.born}' : 'n/a'),
style: const TextStyle(fontSize: 16),
),
if (isOwner) const SizedBox(height: 16),
if (isOwner)
Text(
'Gender',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
if (isOwner)
Text(getDisplayText(myData.gender),
style: const TextStyle(fontSize: 16)),
const SizedBox(height: 16),
Text(
'Spoken languages',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
MyLanguageList(
langList: myData.languages,
textSize: 16,
),
],
),
),
],
);
}
Widget _buildSkills(BuildContext context) {
2024-05-31 23:55:33 +02:00
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',
2024-05-31 23:55:33 +02:00
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
Text(myData.skills.map((x) => x.displayName).join(', '),
2024-05-31 23:55:33 +02:00
style: const TextStyle(fontSize: 16)),
],
),
),
if (isOwner) _editButton(context, editUserSkillsInfo),
2024-05-31 23:55:33 +02:00
],
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Skills sought',
2024-05-31 23:55:33 +02:00
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
Text(
myData.skillsSought
.map((x) => x.displayName)
.join(', '),
2024-05-31 23:55:33 +02:00
style: const TextStyle(fontSize: 16)),
],
),
),
if (isOwner) _editButton(context, editUserSkillsSoughtInfo),
2024-05-31 23:55:33 +02:00
],
),
],
),
),
],
);
}
Widget _buildSectors(BuildContext context) {
2024-05-31 23:16:15 +02:00
return Column(
children: [
Align(
alignment: Alignment.centerLeft,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
2024-05-31 23:16:15 +02:00
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Sectors of Interest',
2024-05-31 23:16:15 +02:00
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
Text(
sortSectorsList(myData.sectors)
2024-05-31 23:16:15 +02:00
.map((x) => x.displayName)
.join('\n'),
2024-05-31 23:16:15 +02:00
style: const TextStyle(fontSize: 16)),
],
),
),
if (isOwner) _editButton(context, editUserSectorsInfo),
2024-05-31 23:16:15 +02:00
],
),
],
),
),
],
);
}
Widget _buildRisks(BuildContext context) {
2024-05-31 23:16:15 +02:00
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(
'Availability',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
Text(myData.availability.displayName,
style: const TextStyle(fontSize: 16)),
],
),
),
if (isOwner) _editButton(context, editUserRiskInfo),
2024-05-31 23:16:15 +02:00
],
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Risk profile',
2024-05-31 23:16:15 +02:00
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
Text(myData.risk.displayName,
2024-05-31 23:16:15 +02:00
style: const TextStyle(fontSize: 16)),
],
),
),
],
),
],
),
),
],
);
}
Widget _buildVision(BuildContext context) {
2024-06-21 02:42:45 +02:00
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(
'Communication profile',
2024-06-21 02:42:45 +02:00
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
Text(myData.communication.displayName,
2024-06-21 02:42:45 +02:00
style: const TextStyle(fontSize: 16)),
],
),
),
if (isOwner)
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: _editButton(context, editUserVisionInfo),
),
2024-06-21 02:42:45 +02:00
],
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Vision and Goals',
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
Text(
myData.visions
.map((x) => x.displayName)
.join(',\n'),
style: const TextStyle(fontSize: 16)),
],
),
),
],
),
2024-06-21 02:42:45 +02:00
],
),
),
],
);
}
Widget _buildWorkCulture(BuildContext context) {
2024-05-31 18:31:37 +02:00
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(
'Corporate culture',
2024-05-31 18:31:37 +02:00
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
Text(myData.culture.displayName,
2024-05-31 18:31:37 +02:00
style: const TextStyle(fontSize: 16)),
],
),
),
if (isOwner)
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: _editButton(context, editUserWorkCultureInfo),
),
2024-05-31 18:31:37 +02:00
],
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Corporate values',
2024-05-31 18:31:37 +02:00
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
Text(
myData.workValues
2024-05-31 18:31:37 +02:00
.map((x) => x.displayName)
.join(',\n'),
2024-05-31 18:31:37 +02:00
style: const TextStyle(fontSize: 16)),
],
),
),
2024-06-14 14:28:59 +02:00
],
),
],
),
),
],
);
}
2024-06-10 17:38:47 +02:00
Widget _editButton(BuildContext context, void Function()? onPressedFunction,
{Alignment alignment = Alignment.topRight}) {
return Align(
alignment: alignment,
2024-06-14 14:28:59 +02:00
child: IconButton(
2024-06-10 17:38:47 +02:00
icon: const Icon(Icons.edit),
onPressed: onPressedFunction,
),
);
}
2024-05-24 00:30:08 +02:00
}