/// Edit Profile Dialog Widget library; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:image_picker/image_picker.dart'; import '../../../core/theme/colors.dart'; import '../../../core/theme/text_styles.dart'; import '../../../domain/entities/user.dart'; import '../../providers/settings_provider.dart'; /// Edit profile dialog class EditProfileDialog extends ConsumerStatefulWidget { const EditProfileDialog({ super.key, required this.user, }); final User user; @override ConsumerState createState() => _EditProfileDialogState(); } class _EditProfileDialogState extends ConsumerState { late final TextEditingController _displayNameController; final ImagePicker _imagePicker = ImagePicker(); String? _avatarUrl; @override void initState() { super.initState(); _displayNameController = TextEditingController( text: widget.user.displayName ?? widget.user.username, ); _avatarUrl = widget.user.avatarUrl; } @override void dispose() { _displayNameController.dispose(); super.dispose(); } Future _pickImage() async { try { final XFile? image = await _imagePicker.pickImage( source: ImageSource.gallery, maxWidth: 512, maxHeight: 512, imageQuality: 85, ); if (image != null && mounted) { // For now, just show the selected image // In production, you would upload this to your server setState(() { _avatarUrl = image.path; }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Image selected. Note: Avatar upload requires server implementation.', style: AppTextStyles.body.copyWith( color: Colors.white, ), ), backgroundColor: AppColors.info, behavior: SnackBarBehavior.floating, duration: const Duration(seconds: 3), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Failed to pick image: ${e.toString()}', style: AppTextStyles.body.copyWith( color: Colors.white, ), ), backgroundColor: AppColors.error, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), ); } } } Future _saveProfile() async { final displayName = _displayNameController.text.trim(); if (displayName.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Display name cannot be empty', style: AppTextStyles.body.copyWith( color: Colors.white, ), ), backgroundColor: AppColors.error, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), ); return; } Navigator.pop(context); try { await ref.read(settingsProvider.notifier).updateProfile( displayName: displayName, avatarUrl: _avatarUrl, ); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Profile updated successfully', style: AppTextStyles.body.copyWith( color: Colors.white, ), ), backgroundColor: AppColors.vert, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Failed to update profile: ${e.toString()}', style: AppTextStyles.body.copyWith( color: Colors.white, ), ), backgroundColor: AppColors.error, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), ); } } } @override Widget build(BuildContext context) { return Dialog( backgroundColor: AppColors.surface, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), side: BorderSide( color: AppColors.cyan.withOpacity(0.2), width: 1, ), ), child: Container( constraints: const BoxConstraints(maxWidth: 400), padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Header Row( children: [ Text( 'Edit Profile', style: AppTextStyles.h3.copyWith( color: AppColors.onBackground, fontWeight: FontWeight.w700, ), ), const Spacer(), IconButton( onPressed: () => Navigator.pop(context), icon: const Icon(Icons.close), color: AppColors.muted, ), ], ), const SizedBox(height: 24), // Avatar Center( child: GestureDetector( onTap: _pickImage, child: Stack( children: [ Container( width: 100, height: 100, decoration: BoxDecoration( shape: BoxShape.circle, gradient: AppColors.primaryGradient, boxShadow: AppColors.cyanGlow, ), child: ClipOval( child: _avatarUrl != null // Check if it's a network URL or local file path ? (_avatarUrl!.startsWith('http') ? Image.network( _avatarUrl!, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return _buildDefaultAvatar(); }, ) : Image.file( // Use File for local path // ignore: unnecessary_null_comparison _avatarUrl != null ? _avatarUrl as Object : Object(), fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return _buildDefaultAvatar(); }, )) : _buildDefaultAvatar(), ), ), Positioned( bottom: 0, right: 0, child: Container( width: 36, height: 36, decoration: BoxDecoration( color: AppColors.cyan, shape: BoxShape.circle, border: Border.all( color: AppColors.surface, width: 3, ), ), child: const Icon( Icons.camera_alt_outlined, color: AppColors.primary, size: 18, ), ), ), ], ), ), ), const SizedBox(height: 16), Center( child: Text( 'Tap to change photo', style: AppTextStyles.bodySmall.copyWith( color: AppColors.cyan, ), ), ), const SizedBox(height: 24), // Display name field Text( 'Display Name', style: AppTextStyles.label.copyWith( color: AppColors.onSurface, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 8), TextField( controller: _displayNameController, style: AppTextStyles.body.copyWith( color: AppColors.onBackground, ), decoration: InputDecoration( hintText: 'Enter display name', hintStyle: AppTextStyles.body.copyWith( color: AppColors.muted, ), filled: true, fillColor: AppColors.surfaceVariant.withOpacity(0.5), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: AppColors.cyan.withOpacity(0.2), width: 2, ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: AppColors.cyan.withOpacity(0.2), width: 2, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide( color: AppColors.cyan, width: 2, ), ), ), ), const SizedBox(height: 24), // Buttons Row( children: [ Expanded( child: OutlinedButton( onPressed: () => Navigator.pop(context), style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 14), side: BorderSide( color: AppColors.muted.withOpacity(0.5), width: 1.5, ), foregroundColor: AppColors.muted, ), child: Text( 'Cancel', style: AppTextStyles.button, ), ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton( onPressed: _saveProfile, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 14), backgroundColor: AppColors.cyan, foregroundColor: AppColors.primary, ), child: Text( 'Save', style: AppTextStyles.button, ), ), ), ], ), ], ), ), ); } Widget _buildDefaultAvatar() { final firstLetter = (widget.user.displayName ?? widget.user.username) .substring(0, 1) .toUpperCase(); return Container( color: AppColors.surfaceVariant, child: Center( child: Text( firstLetter, style: AppTextStyles.h2.copyWith( color: AppColors.cyan, fontWeight: FontWeight.w700, ), ), ), ); } }