import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/assets.dart'; import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/providers/theme.dart'; import 'package:didvan/providers/user.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/storage/storage.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/views/profile/general_settings/settings_state.dart'; import 'package:didvan/views/widgets/animated_visibility.dart'; import 'package:didvan/views/widgets/didvan/radial_button.dart'; import 'package:didvan/views/widgets/menu_item.dart'; import 'package:didvan/views/widgets/didvan/divider.dart'; import 'package:didvan/views/widgets/didvan/scaffold.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher_string.dart'; class ProfilePage extends StatefulWidget { const ProfilePage({Key? key}) : super(key: key); @override State createState() => _ProfilePageState(); } class _ProfilePageState extends State with SingleTickerProviderStateMixin { late AnimationController _animationController; String fontScaleSuffix(double fontSizeScale) { final fontScale = fontSizeScale; if (fontScale == 1) return 'متوسط'; if (fontScale == 1.15) return 'بزرگ'; return 'کوچک'; } @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: const Duration(milliseconds: 1200), ); context.read().init(); final state = context.read(); Future.delayed( Duration.zero, () { state.getTime(); _animationController.forward(); }, ); } @override void dispose() { _animationController.dispose(); super.dispose(); } Widget _buildAnimatedSection({required Widget child, required int index}) { final double start = (index * 0.15).clamp(0.0, 1.0); final double end = (start + 0.4).clamp(0.0, 1.0); final animation = CurvedAnimation( parent: _animationController, curve: Interval(start, end, curve: Curves.easeOutQuint), ); return FadeTransition( opacity: animation, child: SlideTransition( position: Tween( begin: const Offset(0, 0.1), end: Offset.zero, ).animate(animation), child: child, ), ); } @override Widget build(BuildContext context) { final user = context.watch().user; print('DEBUG - User Photo URL: ${user.photo}'); return Consumer( builder: (context, state, child) => StateHandler( onRetry: context.read().init, state: context.read(), builder: (context, state) => DidvanScaffold( padding: const EdgeInsets.fromLTRB(0, 0, 0, 20), appBarData: null, showSliversFirst: true, slivers: [ SliverAppBar( pinned: true, backgroundColor: Theme.of(context).colorScheme.surface, automaticallyImplyLeading: false, leadingWidth: 200, scrolledUnderElevation: 0, surfaceTintColor: Colors.transparent, leading: Padding( padding: const EdgeInsetsDirectional.only(start: 0.0), child: SvgPicture.asset( Assets.horizontalLogoWithText, fit: BoxFit.contain, height: 80, ), ), actions: [ IconButton( onPressed: () { Navigator.of(context).pushNamed(Routes.bookmarks); }, icon: SvgPicture.asset( 'lib/assets/icons/hugeicons_telescope-01.svg', color: Theme.of(context).colorScheme.caption, )), IconButton( onPressed: () => Navigator.of(context).pop(), icon: SvgPicture.asset( 'lib/assets/icons/arrow-left.svg', color: Theme.of(context).colorScheme.caption, )), const SizedBox(width: 8), ], ), ], children: [ const SizedBox(height: 24), _buildAnimatedSection( index: 0, child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 100, height: 100, decoration: BoxDecoration( shape: BoxShape.circle, color: Theme.of(context) .colorScheme .surfaceContainerHighest, border: Border.all( color: const Color.fromARGB(255, 184, 184, 184), width: 1.0, ), image: (user.photo != null && user.photo!.isNotEmpty) ? DecorationImage( image: NetworkImage('https://api.didvan.app${user.photo!}', headers: { 'Authorization': 'Bearer ${RequestService.token}', }), fit: BoxFit.cover, ) : null, ), alignment: Alignment.center, child: (user.photo == null || user.photo!.isEmpty) ? Icon( Icons.person_rounded, size: 60, color: Theme.of(context) .colorScheme .onSurfaceVariant .withOpacity(0.5), ) : null, ), const SizedBox(height: 16), DidvanText( user.fullName, style: Theme.of(context) .textTheme .titleLarge ?.copyWith( fontWeight: FontWeight.bold, color: const Color.fromARGB(255, 0, 126, 167)), ), ], ), ), ), const SizedBox(height: 32), _buildAnimatedSection( index: 1, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 15.0), child: Container( decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(16), border: Border.all( color: const Color.fromARGB(255, 224, 224, 224), width: 1, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.03), blurRadius: 10, offset: const Offset(0, 4), ) ], ), padding: const EdgeInsets.all(10.0), child: Column( children: [ Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 8), decoration: BoxDecoration( color: Theme.of(context).colorScheme.focused, borderRadius: BorderRadius.circular(16), ), child: Center( child: DidvanText( 'حساب کاربری', style: Theme.of(context) .textTheme .titleMedium ?.copyWith( color: DesignConfig.isDark ? Colors.white : Colors.black, fontWeight: FontWeight.normal, fontSize: 15), ), ), ), const SizedBox(height: 15), MenuOption( titleWidget: DidvanText('ویرایش پروفایل', style: TextStyle( color: Theme.of(context) .colorScheme .caption)), onTap: () => Navigator.of(context) .pushNamed(Routes.editProfile), iconWidget: SvgPicture.asset( 'lib/assets/icons/user-edit.svg'), ), const DidvanDivider(), MenuOption( titleWidget: DidvanText('تغییر رمز عبور ', style: TextStyle( color: Theme.of(context) .colorScheme .caption)), iconWidget: SvgPicture.asset('lib/assets/icons/lock.svg'), onTap: () => Navigator.of(context).pushNamed( Routes.changePassword, ), ), const DidvanDivider(), MenuOption( titleWidget: const DidvanText( 'خروج از حساب کاربری', style: TextStyle( color: Color.fromARGB(255, 178, 4, 54))), iconWidget: SvgPicture.asset( 'lib/assets/icons/logout.svg'), onTap: () async { StorageService.delete(key: 'token'); Navigator.of(context).pushNamedAndRemoveUntil( Routes.splash, (_) => false, ); }, ), const SizedBox( height: 7, ) ], ), ), ), ), const SizedBox(height: 32), _buildAnimatedSection( index: 2, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 15.0), child: Container( decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(16), border: Border.all( color: const Color.fromARGB(255, 224, 224, 224), width: 1, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.03), blurRadius: 10, offset: const Offset(0, 4), ) ], ), padding: const EdgeInsets.all(10.0), child: Column( children: [ Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 8), decoration: BoxDecoration( color: Theme.of(context).colorScheme.focused, borderRadius: BorderRadius.circular(16), ), child: Center( child: DidvanText( 'تنظیمات اپلیکیشن', style: Theme.of(context) .textTheme .titleMedium ?.copyWith( color: DesignConfig.isDark ? Colors.white : Colors.black, fontWeight: FontWeight.normal, fontSize: 15), ), ), ), const SizedBox(height: 15), MenuOption( titleWidget: DidvanText('تنظیمات اعلان‌ها', style: TextStyle( color: Theme.of(context) .colorScheme .caption)), onTap: () => Navigator.of(context) .pushNamed(Routes.notificationSettings), iconWidget: SvgPicture.asset( 'lib/assets/icons/notification-bing.svg'), ), const DidvanDivider(), MenuOption( titleWidget: DidvanText('ظاهر اپلیکیشن', style: TextStyle( color: Theme.of(context) .colorScheme .caption)), iconWidget: SvgPicture.asset( 'lib/assets/icons/fluent_paint-brush-16-regular.svg'), onTap: () { state.showSettings = !state.showSettings; state.update(); }, trailing: SvgPicture.asset( state.showSettings ? 'lib/assets/icons/arrow-up2.svg' : 'lib/assets/icons/arrow-down.svg', height: 25, color: Theme.of(context).colorScheme.caption, )), const SizedBox(height: 8), AnimatedVisibility( duration: DesignConfig.lowAnimationDuration, isVisible: state.showSettings, child: Padding( padding: const EdgeInsets.only(right: 8.0, top: 8), child: Column( children: [ Padding( padding: const EdgeInsets.symmetric( vertical: 12.0), child: MenuOption( suffix: state.fontFamily == 'Dana-FA' ? 'دانا' : state.fontFamily == 'Iransans-FA' ? 'ایران سنس' : 'ایران یکان', titleWidget: DidvanText('فونت', style: TextStyle( color: Theme.of(context) .colorScheme .caption)), onTap: _showFontFamilyBottomSheet, iconWidget: SvgPicture.asset( 'lib/assets/icons/hugeicons_edit-01.svg'), ), ), Padding( padding: const EdgeInsets.symmetric( vertical: 12.0), child: MenuOption( suffix: fontScaleSuffix( state.fontSizeScale), titleWidget: DidvanText('اندازه متن', style: TextStyle( color: Theme.of(context) .colorScheme .caption)), onTap: _showFontScaleBottomSheet, iconWidget: SvgPicture.asset( 'lib/assets/icons/smallcaps.svg'), )), Padding( padding: const EdgeInsets.symmetric( vertical: 12.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ SvgPicture.asset( 'lib/assets/icons/moon.svg'), const SizedBox(width: 4), DidvanText('تم برنامه', style: TextStyle( color: Theme.of(context) .colorScheme .caption)), ], ), const SizedBox(height: 16), Align( alignment: Alignment.centerLeft, child: Padding( padding: const EdgeInsets.only( left: 8.0), child: _buildThemeSwitch( context, state), ), ), ], )), ], ), ), ), ], ), ), ), ), const SizedBox(height: 32), _buildAnimatedSection( index: 3, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 15.0), child: Container( decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(16), border: Border.all( color: const Color.fromARGB(255, 224, 224, 224), width: 1, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.03), blurRadius: 10, offset: const Offset(0, 4), ) ], ), padding: const EdgeInsets.all(10.0), child: Column( children: [ Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 8), decoration: BoxDecoration( color: Theme.of(context).colorScheme.focused, borderRadius: BorderRadius.circular(16), ), child: Center( child: DidvanText( 'پشتیبانی', style: Theme.of(context) .textTheme .titleMedium ?.copyWith( color: DesignConfig.isDark ? Colors.white : Colors.black, fontWeight: FontWeight.normal, fontSize: 15), ), ), ), const SizedBox(height: 15), MenuOption( iconWidget: SvgPicture.asset( 'lib/assets/icons/info-circle.svg'), titleWidget: DidvanText('درباره دیدوان', style: TextStyle( color: Theme.of(context) .colorScheme .caption)), onTap: () => launchUrlString( 'https://didvan.com/#info', mode: LaunchMode.inAppWebView), ), const DidvanDivider(), MenuOption( iconWidget: SvgPicture.asset( 'lib/assets/icons/call-calling.svg'), titleWidget: DidvanText('تماس با ما', style: TextStyle( color: Theme.of(context) .colorScheme .caption)), onTap: () { Navigator.of(context) .pushNamed(Routes.directList) .then( (value) => state.init(), ); }, trailing: state.unread > 0 ? Container( height: 18, padding: const EdgeInsets.symmetric( horizontal: 4), decoration: BoxDecoration( borderRadius: DesignConfig.lowBorderRadius, color: Theme.of(context) .colorScheme .secondary, ), alignment: Alignment.center, child: DidvanText( state.unread.toString(), color: Colors.white, fontSize: 12, ), ) : null), AnimatedVisibility( isVisible: state.showContactUs, duration: DesignConfig.lowAnimationDuration, fadeMode: FadeMode.vertical, child: Padding( padding: const EdgeInsets.only(right: 8.0, top: 8), child: Column( children: [ Padding( padding: const EdgeInsets.symmetric( vertical: 12), child: MenuOption( icon: DidvanIcons.support_regular, title: 'پیام به پشتیبانی', onTap: () { Navigator.of(context).pushNamed( Routes.direct, arguments: { 'type': 'پشتیبانی اپلیکیشن' }, ); }, ), ), Padding( padding: const EdgeInsets.only(top: 12), child: MenuOption( icon: DidvanIcons.chats_regular, title: 'صندوق پیام', onTap: () { Navigator.of(context) .pushNamed(Routes.directList) .then( (value) => state.init(), ); }, trailing: Row( children: [ if (state.unread > 0) Container( height: 18, padding: const EdgeInsets .symmetric(horizontal: 4), decoration: BoxDecoration( borderRadius: DesignConfig .lowBorderRadius, color: Theme.of(context) .colorScheme .secondary, ), alignment: Alignment.center, child: DidvanText( state.unread.toString(), color: Colors.white, fontSize: 12, ), ), const SizedBox( width: 12, ), Icon( DidvanIcons.angle_left_regular, size: 18, color: Theme.of(context) .colorScheme .onSurface, ) ], ), ), ), ], ), )), const DidvanDivider(), MenuOption( iconWidget: SvgPicture.asset( 'lib/assets/icons/security-safe.svg'), titleWidget: DidvanText('حریم خصوصی', style: TextStyle( color: Theme.of(context) .colorScheme .caption)), onTap: () => launchUrlString( 'https://didvan.com/terms-of-use#privacy', mode: LaunchMode.inAppWebView), ), const SizedBox( height: 7, ) ], ), ), ), ), const SizedBox(height: 20), _buildAnimatedSection( index: 4, child: FutureBuilder( future: PackageInfo.fromPlatform(), builder: (context, snapshot) { String version = '...'; if (snapshot.hasData && snapshot.data != null) { version = snapshot.data!.version; } return Center( child: DidvanText( 'نسخه نرم‌افزار: $version', style: Theme.of(context) .textTheme .bodySmall ?.copyWith( color: const Color.fromARGB( 255, 102, 102, 102), ), ), ); }), ) ], ), )); } Future _showFontFamilyBottomSheet() async { final themeProvider = context.read(); final state = context.read(); final family = state.fontFamily; await ActionSheetUtils(context).showBottomSheet( data: ActionSheetData( content: StatefulBuilder( builder: (context, setState) => Column( children: [ DidvanRadialButton( title: 'دانا', fontFamily: 'Dana-FA', onSelected: () { state.fontFamily = 'Dana-FA'; setState(() {}); }, value: state.fontFamily == 'Dana-FA', ), const SizedBox(height: 24), DidvanRadialButton( title: 'ایران سنس', fontFamily: 'Iransans-FA', onSelected: () { state.fontFamily = 'Iransans-FA'; setState(() {}); }, value: state.fontFamily == 'Iransans-FA', ), const SizedBox(height: 24), DidvanRadialButton( title: 'ایران یکان', fontFamily: 'IranYekan', onSelected: () { state.fontFamily = 'IranYekan'; setState(() {}); }, value: state.fontFamily == 'IranYekan', ), ], ), ), title: 'انتخاب فونت برنامه', titleIconWidget: SvgPicture.asset('lib/assets/icons/hugeicons_edit-01.svg'), onDismissed: () => state.fontFamily = family, onConfirmed: () => themeProvider.fontFamily = state.fontFamily, ), ); } Future _showFontScaleBottomSheet() async { final themeProvider = context.read(); final state = context.read(); final scale = state.fontSizeScale; await ActionSheetUtils(context).showBottomSheet( data: ActionSheetData( content: StatefulBuilder( builder: (context, setState) => Column( children: [ DidvanRadialButton( title: 'بزرگ', fontSize: 15 * 1.15, onSelected: () { state.fontSizeScale = 1.15; setState(() {}); }, value: state.fontSizeScale == 1.15, ), const SizedBox(height: 24), DidvanRadialButton( title: 'متوسط', onSelected: () { state.fontSizeScale = 1; setState(() {}); }, value: state.fontSizeScale == 1, ), const SizedBox(height: 24), DidvanRadialButton( title: 'کوچک', fontSize: 15 * 0.85, onSelected: () { state.fontSizeScale = 0.85; setState(() {}); }, value: state.fontSizeScale == 0.85, ), ], ), ), title: 'انتخاب اندازه متن', titleIconWidget: SvgPicture.asset('lib/assets/icons/smallcaps.svg'), onDismissed: () => state.fontSizeScale = scale, onConfirmed: () => themeProvider.fontScale = state.fontSizeScale, ), ); } Widget _buildThemeSwitch(BuildContext context, GeneralSettingsState state) { final isDarkTheme = DesignConfig.isDark; return GestureDetector( onTap: () { if (isDarkTheme) { context.read().themeMode = ThemeMode.light; state.brightness = 'light'; } else { context.read().themeMode = ThemeMode.dark; state.brightness = 'dark'; } DesignConfig.updateSystemUiOverlayStyle(); state.update(); }, child: Container( width: 70, height: 37, decoration: BoxDecoration( color: const Color.fromARGB(255, 200, 224, 244), borderRadius: BorderRadius.circular(22), ), child: AnimatedAlign( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, alignment: isDarkTheme ? Alignment.centerRight : Alignment.centerLeft, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 3), child: Container( width: 34, height: 34, padding: const EdgeInsets.all(0), child: SvgPicture.asset( isDarkTheme ? 'lib/assets/icons/dark.svg' : 'lib/assets/icons/light.svg', fit: BoxFit.contain, ), ), ), ), ), ); } }