Chepuhagram/lib/presentation/screens/my_profile_screen.dart

339 lines
11 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import '/logic/auth_provider.dart';
import 'account_settings_screen.dart';
import 'settings_screen.dart';
class MyProfileScreen extends StatefulWidget {
const MyProfileScreen({super.key, required this.isFromList});
final bool isFromList;
@override
State<MyProfileScreen> createState() => _MyProfileScreenState();
}
class _MyProfileScreenState extends State<MyProfileScreen> {
final ImagePicker _picker = ImagePicker();
bool _isAvatarExpanded = false;
String? privKey;
Future<void> _pickAvatar() async {
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
if (image != null) {
final success = await context.read<AuthProvider>().updateAvatar(
image.path,
);
if (!success && mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Ошибка загрузки аватарки')),
);
}
}
}
@override
void initState() {
super.initState();
_loadPrivKey();
}
Future<void> _loadPrivKey() async {
final storage = const FlutterSecureStorage();
privKey = await storage.read(key: 'private_key');
if (mounted) {
setState(() {});
}
}
@override
Widget build(BuildContext context) {
final authProv = context.watch<AuthProvider>();
final colorScheme = Theme.of(context).colorScheme;
final screenWidth = MediaQuery.of(context).size.width;
final String fullName =
'${authProv.firstName ?? ''} ${authProv.lastName ?? ''}'.trim();
final String username = authProv.username ?? '';
ImageProvider? avatarImage;
if (authProv.avatarUrl != null) {
avatarImage = NetworkImage(authProv.avatarUrl!);
} else if (authProv.avatarPath != null) {
avatarImage = FileImage(File(authProv.avatarPath!));
}
final initials =
(authProv.displayName.isNotEmpty
? authProv.displayName
: (username.isNotEmpty ? username : 'U'))
.trim()
.split(RegExp(r'\s+'))
.where((p) => p.isNotEmpty)
.take(2)
.map((p) => p[0].toUpperCase())
.join();
return Scaffold(
backgroundColor: colorScheme.background,
appBar: (Platform.isWindows || !widget.isFromList)
? AppBar(
title: const Text(
'Профиль',
style: TextStyle(fontWeight: FontWeight.bold),
),
elevation: 0,
backgroundColor: Colors.transparent,
)
: null,
body: ListView(
physics: const BouncingScrollPhysics(),
padding: EdgeInsets.zero,
children: [
// Анимированный интерактивный аватар
GestureDetector(
onTap: () => setState(() => _isAvatarExpanded = !_isAvatarExpanded),
child: AnimatedContainer(
duration: const Duration(milliseconds: 350),
curve: Curves.fastOutSlowIn,
width: _isAvatarExpanded ? screenWidth : 130.0,
height: _isAvatarExpanded ? screenWidth : 130.0,
margin: _isAvatarExpanded
? EdgeInsets.zero
: const EdgeInsets.only(top: 16, bottom: 8),
decoration: BoxDecoration(
shape: _isAvatarExpanded ? BoxShape.rectangle : BoxShape.circle,
color: colorScheme.primaryContainer.withOpacity(0.4),
boxShadow: _isAvatarExpanded
? []
: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 20,
offset: const Offset(0, 10),
),
],
image: avatarImage != null
? DecorationImage(image: avatarImage, fit: BoxFit.cover)
: null,
),
child: avatarImage == null
? Center(
child: Text(
initials.isEmpty ? 'U' : initials,
style: TextStyle(
fontSize: _isAvatarExpanded ? 80 : 38,
fontWeight: FontWeight.w700,
color: colorScheme.onPrimaryContainer,
),
),
)
: null,
),
),
// Имя пользователя
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Text(
fullName.isNotEmpty ? fullName : 'Имя не указано',
style: const TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
letterSpacing: -0.5,
),
textAlign: TextAlign.center,
),
),
if (username.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: 4, bottom: 24),
child: Text(
'@$username',
style: TextStyle(
color: colorScheme.primary,
fontSize: 16,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
),
// Блок кнопок (Выбрать фото, Изменить, Настройки)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Expanded(
child: _buildActionButton(
icon: Icons.photo_camera_rounded,
label: 'Фото',
onTap: _pickAvatar,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildActionButton(
icon: Icons.edit_note_rounded,
label: 'Изменить',
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const AccountSettingsScreen(),
),
),
),
),
const SizedBox(width: 8),
Expanded(
child: _buildActionButton(
icon: Icons.settings_suggest_rounded,
label: 'Настройки',
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const SettingsScreen(isFromList: false),
),
),
),
),
],
),
),
const SizedBox(height: 32),
// Блок с личной информацией
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
decoration: BoxDecoration(
color: colorScheme.surfaceVariant.withOpacity(0.3),
borderRadius: BorderRadius.circular(24),
border: Border.all(
color: colorScheme.outlineVariant.withOpacity(0.2),
),
),
child: Column(
children: [
_buildInfoRow(
context,
Icons.fingerprint_rounded,
authProv.currentUserId.toString(),
'ID пользователя',
true,
),
_buildInfoRow(
context,
Icons.info_outline_rounded,
authProv.about,
'О себе',
true,
),
_buildInfoRow(
context,
Icons.phone_android_rounded,
authProv.phone,
'Номер телефона',
true,
),
_buildInfoRow(
context,
Icons.mail_outline_rounded,
authProv.email,
'Электронная почта',
true,
),
_buildInfoRow(
context,
Icons.key_rounded,
privKey ?? 'Отсутствует',
'Публичный E2EE ключ',
false,
),
],
),
),
),
const SizedBox(height: 20),
],
),
);
}
Widget _buildActionButton({
required IconData icon,
required String label,
required VoidCallback onTap,
}) {
final colorScheme = Theme.of(context).colorScheme;
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(16),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration(
color: colorScheme.surfaceVariant.withOpacity(0.4),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: colorScheme.outlineVariant.withOpacity(0.1),
),
),
child: Column(
children: [
Icon(icon, color: colorScheme.primary, size: 22),
const SizedBox(height: 4),
Text(
label,
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600),
),
],
),
),
);
}
Widget _buildInfoRow(
BuildContext context,
IconData icon,
String? value,
String label,
bool showDivider,
) {
final colorScheme = Theme.of(context).colorScheme;
return Column(
children: [
ListTile(
contentPadding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 4,
),
leading: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: colorScheme.primary.withOpacity(0.08),
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, color: colorScheme.primary, size: 20),
),
title: Text(
value?.isNotEmpty == true ? value! : 'Не указано',
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
),
subtitle: Text(
label,
style: TextStyle(fontSize: 12, color: colorScheme.outline),
),
),
if (showDivider)
Divider(
height: 1,
indent: 70,
color: colorScheme.outlineVariant.withOpacity(0.2),
),
],
);
}
}