Chepuhagram/lib/presentation/screens/settings_screen.dart

392 lines
13 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:chepuhagram/presentation/screens/account_settings_screen.dart';
import 'package:chepuhagram/presentation/screens/login_screen.dart';
import 'package:chepuhagram/presentation/screens/privacy_settings_menu_screen.dart';
import 'package:chepuhagram/presentation/screens/appearance_settings_screen.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '/logic/auth_provider.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
import 'admin_panel_screen.dart';
class SettingsScreen extends StatefulWidget {
final bool isFromList;
const SettingsScreen({super.key, this.isFromList = true});
@override
State<SettingsScreen> createState() => _SettingsScreenState();
}
class _SettingsScreenState extends State<SettingsScreen> {
String? versionCode;
final ImagePicker _picker = ImagePicker();
bool _isAvatarExpanded = false;
@override
void initState() {
super.initState();
_loadVersion();
}
void _loadVersion() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
if (mounted) {
setState(() {
versionCode = packageInfo.version;
});
}
}
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
Widget build(BuildContext context) {
final authProv = context.watch<AuthProvider>();
final colorScheme = Theme.of(context).colorScheme;
final screenWidth = MediaQuery.of(context).size.width;
String platformName = Platform.isAndroid
? 'Android'
: Platform.isIOS
? 'iOS'
: Platform.isWindows
? 'Windows'
: Platform.isLinux
? 'Linux'
: Platform.isMacOS
? 'macOS'
: 'Unknown';
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: [
// Анимированный интерактивный аватар как в MyProfileScreen
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
: (authProv.displayName.isNotEmpty
? authProv.displayName
: 'Имя не указано'),
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,
),
)
else
const SizedBox(height: 24),
// Секция навигации меню (Сгруппированный контейнер)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
decoration: BoxDecoration(
color: colorScheme.surfaceVariant.withOpacity(0.2),
borderRadius: BorderRadius.circular(24),
border: Border.all(
color: colorScheme.outlineVariant.withOpacity(0.1),
),
),
child: Column(
children: [
_buildMenuTile(
context,
Icons.person_outline_rounded,
'Аккаунт',
'Имя, телефон, почта, о себе',
() => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const AccountSettingsScreen(),
),
),
),
_buildDivider(context),
_buildMenuTile(
context,
Icons.shield_outlined,
'Конфиденциальность',
'Безопасность и видимость данных',
() => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const PrivacySettingsMenuScreen(),
),
),
),
_buildDivider(context),
_buildMenuTile(
context,
Icons.palette_outlined,
'Оформление',
'Тема, цвета, обои чата',
() => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const AppearanceSettingsScreen(),
),
),
),
],
),
),
),
if (authProv.currentUserId == 1) ...[
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
decoration: BoxDecoration(
color: colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(24),
border: Border.all(
color: colorScheme.primary.withOpacity(0.2),
),
),
child: ListTile(
contentPadding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 4,
),
leading: Icon(
Icons.admin_panel_settings_rounded,
color: colorScheme.primary,
),
title: Text(
"Админ-панель",
style: TextStyle(
color: colorScheme.primary,
fontWeight: FontWeight.bold,
),
),
subtitle: const Text("Управление пользователями системы"),
trailing: Icon(
Icons.chevron_right_rounded,
color: colorScheme.primary,
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const AdminPanelScreen(),
),
);
},
),
),
),
],
const SizedBox(height: 16),
// Кнопка выхода из аккаунта
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
decoration: BoxDecoration(
color: colorScheme.errorContainer.withOpacity(0.15),
borderRadius: BorderRadius.circular(24),
border: Border.all(
color: colorScheme.errorContainer.withOpacity(0.2),
),
),
child: ListTile(
contentPadding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 4,
),
leading: Icon(Icons.logout_rounded, color: colorScheme.error),
title: Text(
"Выйти из аккаунта",
style: TextStyle(
color: colorScheme.error,
fontWeight: FontWeight.w600,
),
),
trailing: Icon(
Icons.chevron_right_rounded,
color: colorScheme.error,
),
onTap: () async {
await authProv.logout();
if (context.mounted) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => const LoginScreen()),
(r) => false,
);
}
},
),
),
),
const SizedBox(height: 40),
Center(
child: Text(
"Chepuhagram for $platformName v${versionCode ?? '1.0.0'}",
style: TextStyle(color: colorScheme.outline, fontSize: 12),
),
),
const SizedBox(height: 4),
Center(
child: Text(
"Made by ArturKarasevich",
style: TextStyle(
color: colorScheme.outline.withOpacity(0.6),
fontSize: 11,
),
),
),
const SizedBox(height: 40),
],
),
);
}
Widget _buildMenuTile(
BuildContext context,
IconData icon,
String title,
String subtitle,
VoidCallback onTap,
) {
final colorScheme = Theme.of(context).colorScheme;
return ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 6),
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: 22),
),
title: Text(
title,
style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16),
),
subtitle: Text(
subtitle,
style: TextStyle(color: colorScheme.outline, fontSize: 13),
),
trailing: Icon(Icons.chevron_right_rounded, color: colorScheme.outline),
onTap: onTap,
);
}
Widget _buildDivider(BuildContext context) => Divider(
height: 1,
indent: 68,
color: Theme.of(context).colorScheme.outlineVariant.withOpacity(0.15),
);
}