import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:real_estate_mobile/core/constants/app_colors.dart'; import 'package:real_estate_mobile/features/profile/presentation/providers/profile_provider.dart'; class ProfileSettingsScreen extends ConsumerStatefulWidget { const ProfileSettingsScreen({super.key}); @override ConsumerState createState() => _ProfileSettingsScreenState(); } class _ProfileSettingsScreenState extends ConsumerState { int _selectedTab = 0; // Profile form controllers late TextEditingController _fullNameController; late TextEditingController _titleController; late TextEditingController _emailController; late TextEditingController _phoneController; late TextEditingController _locationController; // Notification preferences final Map> _notificationPrefs = { 'newLeadAlerts': {'email': true, 'inApp': true}, 'messageUpdates': {'email': true, 'inApp': true}, 'urgentRequests': {'email': true, 'inApp': true}, }; String _digestFrequency = 'instant'; bool _controllersInitialized = false; @override void initState() { super.initState(); _fullNameController = TextEditingController(); _titleController = TextEditingController(); _emailController = TextEditingController(); _phoneController = TextEditingController(); _locationController = TextEditingController(); } @override void dispose() { _fullNameController.dispose(); _titleController.dispose(); _emailController.dispose(); _phoneController.dispose(); _locationController.dispose(); super.dispose(); } void _initControllersFromProfile(ProfileState profileState) { if (_controllersInitialized) return; _controllersInitialized = true; final profile = profileState.profile; final isAgent = profileState.isAgent; final firstName = profile['firstName'] as String? ?? ''; final lastName = profile['lastName'] as String? ?? ''; _fullNameController.text = '$firstName $lastName'.trim(); _emailController.text = profile['email'] as String? ?? (profile['user'] is Map ? (profile['user'] as Map)['email'] as String? ?? '' : ''); _phoneController.text = profile['phone'] as String? ?? ''; if (isAgent) { // Agent: headline from agentType.name or bio final agentType = profile['agentType'] as Map?; _titleController.text = agentType?['name'] as String? ?? profile['bio'] as String? ?? ''; // Agent: serviceAreas for location final serviceAreas = profile['serviceAreas']; if (serviceAreas is List && serviceAreas.isNotEmpty) { _locationController.text = serviceAreas.first.toString(); } } else { // User: headline _titleController.text = profile['headline'] as String? ?? ''; // User: city/state/country final city = profile['city'] as String? ?? ''; final stateName = profile['state'] as String? ?? ''; final country = profile['country'] as String? ?? ''; final parts = [city, stateName, country].where((s) => s.isNotEmpty); _locationController.text = parts.join(', '); } } @override Widget build(BuildContext context) { final profileState = ref.watch(profileProvider); if (!profileState.isLoading && profileState.profile.isNotEmpty) { _initControllersFromProfile(profileState); } // Show success/error snackbars ref.listen(profileProvider, (prev, next) { if (next.successMessage != null && prev?.successMessage == null) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(next.successMessage!), backgroundColor: const Color(0xFF638559), ), ); } if (next.errorMessage != null && prev?.errorMessage == null) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(next.errorMessage!), backgroundColor: Colors.red, ), ); } }); if (profileState.isLoading) { return const Center( child: CircularProgressIndicator( color: AppColors.accentOrange, ), ); } return Column( children: [ const SizedBox(height: 16), _buildTabBar(), _buildProgressBar(), Expanded( child: _selectedTab == 0 ? _buildProfileSettingsTab(profileState) : _selectedTab == 1 ? _buildNotificationsTab() : _buildPrivacyTab(), ), ], ); } // ── Tab Bar ── Widget _buildTabBar() { return SizedBox( height: 46, child: ListView( scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: 7), children: [ _buildTab(0, 'Profile Settings', Icons.person), const SizedBox(width: 6), _buildTab(1, 'Notifications', Icons.notifications), const SizedBox(width: 6), _buildTab(2, 'Privacy', Icons.person), ], ), ); } Widget _buildTab(int index, String label, IconData icon) { final isActive = _selectedTab == index; return GestureDetector( onTap: () => setState(() => _selectedTab = index), child: Container( width: 162, height: 46, decoration: BoxDecoration( color: isActive ? AppColors.accentOrange : Colors.transparent, borderRadius: BorderRadius.circular(15), border: Border.all( color: isActive ? AppColors.accentOrange : AppColors.primaryDark.withValues(alpha: 0.2), width: 0.1, ), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, size: 24, color: AppColors.primaryDark), const SizedBox(width: 8), Text( label, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ], ), ), ); } Widget _buildProgressBar() { final progress = (_selectedTab + 1) / 3; return Padding( padding: const EdgeInsets.symmetric(horizontal: 11, vertical: 16), child: Container( height: 5, decoration: BoxDecoration( color: const Color(0xFFD9D9D9), borderRadius: BorderRadius.circular(15), ), child: Align( alignment: Alignment.centerLeft, child: FractionallySizedBox( widthFactor: progress, child: Container( decoration: BoxDecoration( color: AppColors.primaryDark.withValues(alpha: 0.75), borderRadius: BorderRadius.circular(15), ), ), ), ), ), ); } // ── Profile Settings Tab ── Widget _buildProfileSettingsTab(ProfileState profileState) { final profile = profileState.profile; final isAgent = profileState.isAgent; // Get avatar URL — agent avatar might be an S3 key or full URL String? avatarUrl = profile['avatar'] as String?; if (avatarUrl == null || avatarUrl.isEmpty) { // Try nested user object (agent profile structure) final user = profile['user'] as Map?; avatarUrl = user?['avatar'] as String?; } final descriptionText = isAgent ? 'This information will be displayed on your public agent page.' : 'This information will be displayed on your profile.'; return ListView( padding: const EdgeInsets.fromLTRB(31, 10, 31, 30), children: [ // Section title const Text( 'Public Profile', style: TextStyle( fontFamily: 'Fractul', fontSize: 15, fontWeight: FontWeight.w700, color: AppColors.primaryDark, ), ), const SizedBox(height: 8), Text( descriptionText, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), const SizedBox(height: 39), // Avatar Center( child: Column( children: [ Container( width: 71, height: 69, decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( color: AppColors.accentOrange, width: 2, ), ), child: ClipOval( child: avatarUrl != null && avatarUrl.isNotEmpty ? Image.network( avatarUrl, fit: BoxFit.cover, errorBuilder: (_, __, ___) => const Icon( Icons.person, size: 36, color: AppColors.primaryDark, ), ) : const Icon( Icons.person, size: 36, color: AppColors.primaryDark, ), ), ), const SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ GestureDetector( onTap: _pickAvatar, child: Container( width: 79, height: 24, decoration: BoxDecoration( color: AppColors.accentOrange, borderRadius: BorderRadius.circular(7), ), alignment: Alignment.center, child: const Text( 'Upload Now', style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 11, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ), ), const SizedBox(width: 6), GestureDetector( onTap: () { ref .read(profileProvider.notifier) .updateProfile({'avatar': ''}); }, child: Container( width: 79, height: 24, decoration: BoxDecoration( borderRadius: BorderRadius.circular(7), border: Border.all( color: AppColors.primaryDark, width: 0.1, ), ), alignment: Alignment.center, child: const Text( 'Delete', style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 11, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ), ), ], ), ], ), ), const SizedBox(height: 49), // Form fields _buildFormField('Full Name', _fullNameController), const SizedBox(height: 24), _buildFormField( isAgent ? 'Career / Agent Type' : 'Professional Title', _titleController, enabled: !isAgent, // Agent type is read-only ), const SizedBox(height: 24), _buildFormField('Email Address', _emailController, enabled: false), const SizedBox(height: 24), _buildFormField('Phone Number', _phoneController, keyboardType: TextInputType.phone), const SizedBox(height: 24), _buildFormField( isAgent ? 'Service Areas' : 'Location', _locationController, prefixIcon: Icons.location_on, ), const SizedBox(height: 30), // Action buttons _buildActionButtons(onSave: _saveProfileSettings), ], ); } Widget _buildFormField( String label, TextEditingController controller, { bool enabled = true, TextInputType? keyboardType, IconData? prefixIcon, }) { final borderSide = BorderSide( color: AppColors.primaryDark.withValues(alpha: 0.1), width: 1, ); final border = OutlineInputBorder( borderRadius: BorderRadius.circular(7), borderSide: borderSide, ); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: const TextStyle( fontFamily: 'Fractul', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), const SizedBox(height: 14), SizedBox( height: 38, child: TextField( controller: controller, enabled: enabled, keyboardType: keyboardType, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), decoration: InputDecoration( contentPadding: const EdgeInsets.symmetric( horizontal: 26, vertical: 8, ), border: border, enabledBorder: border, focusedBorder: border, disabledBorder: border, prefixIcon: prefixIcon != null ? Icon(prefixIcon, size: 18, color: AppColors.accentOrange) : null, prefixIconConstraints: prefixIcon != null ? const BoxConstraints(minWidth: 40) : null, ), ), ), ], ); } void _pickAvatar() { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Avatar upload coming soon')), ); } void _saveProfileSettings() { final profileState = ref.read(profileProvider); final isAgent = profileState.isAgent; final nameParts = _fullNameController.text.trim().split(RegExp(r'\s+')); final firstName = nameParts.first; final lastName = nameParts.length > 1 ? nameParts.sublist(1).join(' ') : ''; final data = { 'firstName': firstName, 'lastName': lastName, 'phone': _phoneController.text.trim(), }; if (isAgent) { // Agent: serviceAreas as array final location = _locationController.text.trim(); if (location.isNotEmpty) { data['serviceAreas'] = [location]; } } else { // User: city/state/country + headline data['headline'] = _titleController.text.trim(); final locationParts = _locationController.text .split(',') .map((s) => s.trim()) .where((s) => s.isNotEmpty) .toList(); if (locationParts.isNotEmpty) data['city'] = locationParts[0]; if (locationParts.length > 1) data['state'] = locationParts[1]; if (locationParts.length > 2) data['country'] = locationParts[2]; } ref.read(profileProvider.notifier).updateProfile(data); } // ── Notifications Tab ── Widget _buildNotificationsTab() { return ListView( padding: const EdgeInsets.fromLTRB(27, 10, 27, 30), children: [ // Title — Figma: Fractul Bold 15px, y=263 const Text( 'Notification Preferences', style: TextStyle( fontFamily: 'Fractul', fontSize: 15, fontWeight: FontWeight.w700, color: AppColors.primaryDark, ), ), const SizedBox(height: 8), // Description — Figma: SourceSerif4 Regular 14px, y=291 const Text( 'Choose what you want to be notified about and how.', style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), const SizedBox(height: 20), // Divider — Figma: y=322 _buildSectionDivider(), const SizedBox(height: 19), // New Lead Alerts — Figma: title y=341 _buildNotificationCategory( title: 'New Lead Alerts', description: 'Choose what you want to be notified about and how.', prefKey: 'newLeadAlerts', ), // Divider — Figma: y=416 _buildSectionDivider(), const SizedBox(height: 13), // Message Updates — Figma: title y=429 _buildNotificationCategory( title: 'Message Updates', description: 'Notices about new messages from clients or other agents.', prefKey: 'messageUpdates', ), // Divider — Figma: y=511 _buildSectionDivider(), const SizedBox(height: 16), // Urgent Requests & Tours — Figma: title y=527 _buildNotificationCategory( title: 'Urgent Requests & Tours', description: 'Immediate notifications for tour requests and time-sensitive items.', prefKey: 'urgentRequests', ), // Divider — Figma: y=621 _buildSectionDivider(), const SizedBox(height: 20), // Email Digest Frequency — Figma: Fractul Bold 14px, y=641 const Text( 'Email Digest Frequency', style: TextStyle( fontFamily: 'Fractul', fontSize: 14, fontWeight: FontWeight.w700, color: AppColors.primaryDark, ), ), const SizedBox(height: 8), // Description — Figma: y=667 const Text( 'Choose how often you receive non-urgent email summaries', style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), const SizedBox(height: 16), // Frequency selector — Figma: y=731 _buildFrequencySelector(), const SizedBox(height: 40), // Action buttons — Figma: y=879, rounded-[15px] _buildNotificationActionButtons(), ], ); } Widget _buildSectionDivider() { return Divider( color: AppColors.primaryDark.withValues(alpha: 0.15), height: 1, thickness: 1, ); } Widget _buildNotificationCategory({ required String title, required String description, required String prefKey, }) { final prefs = _notificationPrefs[prefKey]!; return Padding( padding: const EdgeInsets.only(bottom: 16), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Left: title + description (max width ~220 from Figma) Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: const TextStyle( fontFamily: 'Fractul', fontSize: 14, fontWeight: FontWeight.w700, color: AppColors.primaryDark, ), ), const SizedBox(height: 4), Text( description, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ], ), ), const SizedBox(width: 20), // Right: checkbox labels — Figma: x=321, Email/In-App stacked ~26px apart Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildCheckboxRow( label: 'Email', value: prefs['email']!, onChanged: (val) { setState(() { _notificationPrefs[prefKey]!['email'] = val ?? false; }); }, ), const SizedBox(height: 6), _buildCheckboxRow( label: 'In-App', value: prefs['inApp']!, onChanged: (val) { setState(() { _notificationPrefs[prefKey]!['inApp'] = val ?? false; }); }, ), ], ), ], ), ); } Widget _buildCheckboxRow({ required String label, required bool value, required ValueChanged onChanged, }) { return GestureDetector( onTap: () => onChanged(!value), behavior: HitTestBehavior.opaque, child: Row( mainAxisSize: MainAxisSize.min, children: [ SizedBox( width: 18, height: 18, child: Checkbox( value: value, onChanged: onChanged, activeColor: AppColors.primaryDark, checkColor: Colors.white, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, visualDensity: VisualDensity.compact, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(3), ), side: BorderSide( color: AppColors.primaryDark.withValues(alpha: 0.4), width: 1, ), ), ), const SizedBox(width: 4), Text( label, style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ], ), ); } /// Figma: h=32, w=278, rounded-[7px], with vertical dividers and inset orange pill Widget _buildFrequencySelector() { const options = ['instant', 'daily', 'weekly', 'off']; const labels = ['Instant', 'Daily', 'Weekly', 'Off']; return Container( height: 32, width: 278, decoration: BoxDecoration( borderRadius: BorderRadius.circular(7), border: Border.all( color: AppColors.primaryDark.withValues(alpha: 0.15), width: 1, ), ), child: ClipRRect( borderRadius: BorderRadius.circular(6), child: Row( children: List.generate(options.length * 2 - 1, (index) { // Odd indices = vertical dividers if (index.isOdd) { return Container( width: 1, height: 32, color: AppColors.primaryDark.withValues(alpha: 0.15), ); } final i = index ~/ 2; final isSelected = _digestFrequency == options[i]; return Expanded( child: GestureDetector( onTap: () => setState(() => _digestFrequency = options[i]), child: Center( child: isSelected ? Container( height: 22, margin: const EdgeInsets.symmetric(horizontal: 4), decoration: BoxDecoration( color: AppColors.accentOrange, borderRadius: BorderRadius.circular(15), ), alignment: Alignment.center, child: Text( labels[i], style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ) : Text( labels[i], style: const TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ), ), ); }), ), ), ); } /// Notification tab buttons — Figma: rounded-[15px] (different from profile tab) Widget _buildNotificationActionButtons() { final isSaving = ref.watch(profileProvider).isSaving; return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), border: Border.all( color: AppColors.primaryDark.withValues(alpha: 0.15), width: 1, ), ), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), child: Row( children: [ Expanded( child: GestureDetector( onTap: () => context.pop(), child: Container( height: 31, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), border: Border.all( color: AppColors.primaryDark.withValues(alpha: 0.15), width: 1, ), ), alignment: Alignment.center, child: const Text( 'Cancel', style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ), ), ), const SizedBox(width: 10), Expanded( child: GestureDetector( onTap: isSaving ? null : _saveNotificationSettings, child: Container( height: 31, decoration: BoxDecoration( color: AppColors.accentOrange, borderRadius: BorderRadius.circular(15), ), alignment: Alignment.center, child: isSaving ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, color: AppColors.primaryDark, ), ) : const Text( 'Save Changes', style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ), ), ), ], ), ); } void _saveNotificationSettings() { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Notification settings saved'), backgroundColor: Color(0xFF638559), ), ); } // ── Privacy Tab ── Widget _buildPrivacyTab() { return const Center( child: Padding( padding: EdgeInsets.all(24), child: Text( 'Privacy settings coming soon', style: TextStyle( fontFamily: 'SourceSerif4', fontSize: 16, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ), ); } // ── Shared Action Buttons ── Widget _buildActionButtons({required VoidCallback onSave}) { final isSaving = ref.watch(profileProvider).isSaving; return Container( height: 71, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), border: Border.all( color: AppColors.primaryDark.withValues(alpha: 0.1), width: 1, ), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ GestureDetector( onTap: () => context.pop(), child: Container( width: 143, height: 31, decoration: BoxDecoration( borderRadius: BorderRadius.circular(7), border: Border.all( color: AppColors.primaryDark.withValues(alpha: 0.1), width: 1, ), ), alignment: Alignment.center, child: const Text( 'Cancel', style: TextStyle( fontFamily: 'Fractul', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ), ), const SizedBox(width: 10), GestureDetector( onTap: isSaving ? null : onSave, child: Container( width: 143, height: 31, decoration: BoxDecoration( color: AppColors.accentOrange, borderRadius: BorderRadius.circular(7), ), alignment: Alignment.center, child: isSaving ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, color: AppColors.primaryDark, ), ) : const Text( 'Save Changes', style: TextStyle( fontFamily: 'Fractul', fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.primaryDark, ), ), ), ), ], ), ); } }