import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../core/l10n/app_strings.dart'; import '../../core/providers/auth_provider.dart'; import '../../core/providers/locale_provider.dart'; import '../../core/router/app_router.dart'; import '../../core/theme/app_theme.dart'; import '../../core/widgets/tooth_logo.dart'; class SignInScreen extends ConsumerStatefulWidget { const SignInScreen({super.key}); @override ConsumerState createState() => _SignInScreenState(); } class _SignInScreenState extends ConsumerState { final _formKey = GlobalKey(); final _emailCtrl = TextEditingController(); final _passCtrl = TextEditingController(); bool _obscure = true; @override void dispose() { _emailCtrl.dispose(); _passCtrl.dispose(); super.dispose(); } Future _submit() async { if (!_formKey.currentState!.validate()) return; await ref .read(authProvider.notifier) .signIn(_emailCtrl.text.trim(), _passCtrl.text); } @override Widget build(BuildContext context) { final auth = ref.watch(authProvider); final s = ref.watch(stringsProvider); final locale = ref.watch(localeProvider); final isDesktop = MediaQuery.sizeOf(context).width > 800; return Scaffold( backgroundColor: AppColors.background, body: isDesktop ? _buildDesktop(context, auth, s, locale) : _buildMobile(context, auth, s, locale), ); } // ── Mobile ───────────────────────────────────────────────────────────────── Widget _buildMobile( BuildContext context, dynamic auth, AppStrings s, Locale locale) { return Stack( children: [ SafeArea( child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 24), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const SizedBox(height: 56), // Logo mark Center( child: Container( width: 68, height: 68, decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFF0B1D35), Color(0xFF1A5C8A)], ), borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: const Color(0xFF0B1D35).withValues(alpha: 0.3), blurRadius: 20, offset: const Offset(0, 8), ), ], ), child: const Center(child: ToothLogo(size: 34, color: Colors.white)), ), ).animate().fadeIn(duration: 400.ms).scale(begin: const Offset(0.8, 0.8)), const SizedBox(height: 24), Center( child: Text( s.signInWelcome, style: const TextStyle( fontSize: 26, fontWeight: FontWeight.w800, color: AppColors.textPrimary, letterSpacing: -0.5, ), ), ).animate(delay: 60.ms).fadeIn(duration: 400.ms).slideY(begin: 0.1), const SizedBox(height: 6), Center( child: Text( s.signInSubtitle, style: const TextStyle( fontSize: 14, color: AppColors.textSecondary), ), ).animate(delay: 100.ms).fadeIn(duration: 400.ms), const SizedBox(height: 36), _buildFormFields(auth, s), const SizedBox(height: 24), _buildSignUpLink(context, s), const SizedBox(height: 32), ], ), ), ), Positioned( top: MediaQuery.paddingOf(context).top + 12, right: 12, child: _LanguageButton(locale: locale, s: s, ref: ref), ), ], ); } // ── Desktop ──────────────────────────────────────────────────────────────── Widget _buildDesktop( BuildContext context, dynamic auth, AppStrings s, Locale locale) { return Row( children: [ // LEFT PANEL Expanded( flex: 55, child: Stack( fit: StackFit.expand, children: [ Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, stops: [0.0, 0.55, 1.0], colors: [ Color(0xFF080F1E), Color(0xFF0D2D58), Color(0xFF0E4A82), ], ), ), ), const Positioned(top: -140, left: -140, child: _Ring(size: 520, opacity: 0.06)), const Positioned(bottom: -100, right: -100, child: _Ring(size: 400, opacity: 0.05)), const Positioned(top: 160, right: 60, child: _Ring(size: 100, opacity: 0.09)), const Positioned(bottom: 220, left: 60, child: _Ring(size: 70, opacity: 0.07)), Padding( padding: const EdgeInsets.symmetric(horizontal: 64, vertical: 52), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 38, height: 38, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(10), border: Border.all( color: Colors.white.withValues(alpha: 0.2), ), ), child: const Center( child: ToothLogo(size: 20, color: Colors.white)), ), const SizedBox(width: 12), const Text( 'DLS', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.w800, letterSpacing: 1.5, ), ), ], ).animate().fadeIn(duration: 500.ms), const Spacer(), Text( s.signInHeadline, style: const TextStyle( color: Colors.white, fontSize: 46, fontWeight: FontWeight.w800, height: 1.1, letterSpacing: -1.0, ), ) .animate(delay: 100.ms) .fadeIn(duration: 500.ms) .slideY(begin: 0.1, end: 0), const SizedBox(height: 18), Text( s.signInTagline, style: TextStyle( color: Colors.white.withValues(alpha: 0.6), fontSize: 16, height: 1.6, ), ).animate(delay: 160.ms).fadeIn(duration: 500.ms), const SizedBox(height: 44), const _DashboardPreviewCard() .animate(delay: 220.ms) .fadeIn(duration: 600.ms) .slideY(begin: 0.12, end: 0), const Spacer(), Text( s.footerCopyright, style: TextStyle( color: Colors.white.withValues(alpha: 0.3), fontSize: 12, ), ).animate(delay: 300.ms).fadeIn(duration: 500.ms), ], ), ), ], ), ), // RIGHT PANEL Stack( children: [ Container( width: 460, color: Colors.white, child: SafeArea( child: LayoutBuilder( builder: (context, constraints) => SingleChildScrollView( child: ConstrainedBox( constraints: BoxConstraints(minHeight: constraints.maxHeight), child: IntrinsicHeight( child: Padding( padding: const EdgeInsets.symmetric( horizontal: 52, vertical: 40), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Container( width: 44, height: 44, decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color(0xFF0B1D35), Color(0xFF1A5C8A) ], ), borderRadius: BorderRadius.circular(12), ), child: const Center( child: ToothLogo( size: 24, color: Colors.white)), ).animate().fadeIn(duration: 400.ms), const SizedBox(height: 32), Text( s.signInWelcome, style: const TextStyle( fontSize: 28, fontWeight: FontWeight.w800, color: AppColors.textPrimary, letterSpacing: -0.5, ), ) .animate(delay: 60.ms) .fadeIn(duration: 400.ms) .slideY(begin: 0.08, end: 0), const SizedBox(height: 6), Text( s.signInSubtitle, style: const TextStyle( fontSize: 15, color: AppColors.textSecondary, ), ).animate(delay: 100.ms).fadeIn(duration: 400.ms), const SizedBox(height: 40), _buildFormFields(auth, s) .animate(delay: 140.ms) .fadeIn(duration: 400.ms) .slideY(begin: 0.08, end: 0), const SizedBox(height: 28), _buildSignUpLink(context, s) .animate(delay: 200.ms) .fadeIn(duration: 400.ms), ], ), ), ), ), ), ), ), ), Positioned( top: MediaQuery.paddingOf(context).top + 16, right: 16, child: _LanguageButton(locale: locale, s: s, ref: ref), ), ], ), ], ); } // ── Form fields (shared) ──────────────────────────────────────────────────── Widget _buildFormFields(dynamic auth, AppStrings s) { return Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _Field( controller: _emailCtrl, label: s.emailAddress, icon: Icons.email_outlined, keyboardType: TextInputType.emailAddress, textInputAction: TextInputAction.next, validator: (v) => (v == null || v.trim().isEmpty) ? s.emailRequired : null, ), const SizedBox(height: 14), _Field( controller: _passCtrl, label: s.password, icon: Icons.lock_outline_rounded, obscureText: _obscure, textInputAction: TextInputAction.done, onFieldSubmitted: (_) => _submit(), suffixIcon: IconButton( icon: Icon( _obscure ? Icons.visibility_outlined : Icons.visibility_off_outlined, size: 20, color: AppColors.textSecondary, ), onPressed: () => setState(() => _obscure = !_obscure), ), validator: (v) => (v == null || v.isEmpty) ? s.passwordRequired : null, ), if (auth.error != null) ...[ const SizedBox(height: 14), Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), decoration: BoxDecoration( color: const Color(0xFFFEF2F2), borderRadius: BorderRadius.circular(10), border: Border.all( color: AppColors.cancelled.withValues(alpha: 0.25)), ), child: Row( children: [ const Icon(Icons.error_outline_rounded, color: AppColors.cancelled, size: 16), const SizedBox(width: 8), Expanded( child: Text( auth.error!, style: const TextStyle( color: AppColors.cancelled, fontSize: 13), ), ), ], ), ), ], const SizedBox(height: 24), DecoratedBox( decoration: BoxDecoration( gradient: const LinearGradient( colors: [Color(0xFF0B1D35), Color(0xFF1A5C8A)], ), borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: const Color(0xFF0B1D35).withValues(alpha: 0.35), blurRadius: 16, offset: const Offset(0, 6), ), ], ), child: FilledButton( onPressed: auth.isLoading ? null : _submit, style: FilledButton.styleFrom( backgroundColor: Colors.transparent, foregroundColor: Colors.white, disabledForegroundColor: Colors.white.withValues(alpha: 0.5), disabledBackgroundColor: Colors.transparent, shadowColor: Colors.transparent, minimumSize: const Size.fromHeight(52), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12)), ), child: auth.isLoading ? const SizedBox( width: 22, height: 22, child: CircularProgressIndicator( strokeWidth: 2.5, color: Colors.white), ) : Text( s.signIn, style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w600), ), ), ), ], ), ); } // ── Sign-up link ─────────────────────────────────────────────────────────── Widget _buildSignUpLink(BuildContext context, AppStrings s) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( s.noAccount, style: const TextStyle(color: AppColors.textSecondary, fontSize: 14), ), TextButton( onPressed: () => context.go(routeSignUp), style: TextButton.styleFrom( foregroundColor: const Color(0xFF0D4C85), padding: const EdgeInsets.symmetric(horizontal: 8), ), child: Text( s.signUp, style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 14), ), ), ], ); } } // ── Language button ─────────────────────────────────────────────────────────── class _LanguageButton extends StatelessWidget { const _LanguageButton( {required this.locale, required this.s, required this.ref}); final Locale locale; final AppStrings s; final WidgetRef ref; static const _flags = { 'tr': '🇹🇷', 'en': '🇬🇧', 'ru': '🇷🇺', 'ar': '🇸🇦', 'de': '🇩🇪', }; @override Widget build(BuildContext context) { final flag = _flags[locale.languageCode] ?? '🌐'; return GestureDetector( onTap: () => _showPicker(context), child: Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(20), border: Border.all(color: AppColors.border), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.06), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text(flag, style: const TextStyle(fontSize: 15)), const SizedBox(width: 4), const Icon(Icons.expand_more_rounded, size: 14, color: AppColors.textSecondary), ], ), ), ); } void _showPicker(BuildContext context) { final options = [ ('tr', '🇹🇷', s.languageTurkish), ('en', '🇬🇧', s.languageEnglish), ('ru', '🇷🇺', s.languageRussian), ('ar', '🇸🇦', s.languageArabic), ('de', '🇩🇪', s.languageGerman), ]; showModalBottomSheet( context: context, backgroundColor: Colors.transparent, builder: (_) => Container( decoration: const BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), padding: const EdgeInsets.all(20), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Center( child: Container( width: 40, height: 4, decoration: BoxDecoration( color: AppColors.border, borderRadius: BorderRadius.circular(2), ), ), ), const SizedBox(height: 16), Text( s.languageSelection, style: const TextStyle( fontSize: 17, fontWeight: FontWeight.w700, color: AppColors.textPrimary, ), ), const SizedBox(height: 12), for (final (code, flag, label) in options) ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 4), leading: Text(flag, style: const TextStyle(fontSize: 24)), title: Text( label, style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w500, color: AppColors.textPrimary, ), ), trailing: locale.languageCode == code ? const Icon(Icons.check_circle_rounded, color: AppColors.accent) : null, onTap: () { ref.read(localeProvider.notifier).setLocale(Locale(code)); Navigator.pop(context); }, ), SizedBox(height: MediaQuery.paddingOf(context).bottom + 4), ], ), ), ); } } // ── Decorative ring ─────────────────────────────────────────────────────────── class _Ring extends StatelessWidget { const _Ring({required this.size, required this.opacity}); final double size; final double opacity; @override Widget build(BuildContext context) { return Container( width: size, height: size, decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( color: Colors.white.withValues(alpha: opacity), width: 1.5, ), ), ); } } // ── Dashboard preview card (glassmorphism) ──────────────────────────────────── class _DashboardPreviewCard extends StatelessWidget { const _DashboardPreviewCard(); @override Widget build(BuildContext context) { return ClipRRect( borderRadius: BorderRadius.circular(20), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 16, sigmaY: 16), child: Container( width: 340, padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.08), borderRadius: BorderRadius.circular(20), border: Border.all( color: Colors.white.withValues(alpha: 0.12), ), ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 28, height: 28, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(8), ), child: const Icon( Icons.bar_chart_rounded, color: Colors.white, size: 15, ), ), const SizedBox(width: 10), Text( 'Bugünkü Durum', style: TextStyle( color: Colors.white.withValues(alpha: 0.9), fontWeight: FontWeight.w600, fontSize: 13, ), ), const Spacer(), Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(20), ), child: Text( 'Canlı', style: TextStyle( color: Colors.white.withValues(alpha: 0.7), fontSize: 11, fontWeight: FontWeight.w500, ), ), ), ], ), const SizedBox(height: 18), const Row( children: [ _StatChip(value: '24', label: 'Aktif', color: Color(0xFF60A5FA)), SizedBox(width: 8), _StatChip( value: '8', label: 'Bekliyor', color: Color(0xFFFBBF24)), SizedBox(width: 8), _StatChip( value: '142', label: 'Bu ay', color: Color(0xFF34D399)), ], ), const SizedBox(height: 18), const _PreviewBar( label: 'Zirkon', value: 0.76, color: Color(0xFF60A5FA)), const SizedBox(height: 10), const _PreviewBar( label: 'Metal alt.', value: 0.48, color: Color(0xFFFBBF24)), const SizedBox(height: 10), const _PreviewBar( label: 'Porselen', value: 0.62, color: Color(0xFF34D399)), ], ), ), ), ); } } class _StatChip extends StatelessWidget { const _StatChip({ required this.value, required this.label, required this.color, }); final String value; final String label; final Color color; @override Widget build(BuildContext context) { return Expanded( child: Container( padding: const EdgeInsets.symmetric(vertical: 10), decoration: BoxDecoration( color: color.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(10), border: Border.all(color: color.withValues(alpha: 0.2)), ), child: Column( children: [ Text( value, style: TextStyle( color: color, fontSize: 18, fontWeight: FontWeight.w800, ), ), const SizedBox(height: 2), Text( label, style: TextStyle( color: Colors.white.withValues(alpha: 0.55), fontSize: 11, ), ), ], ), ), ); } } class _PreviewBar extends StatelessWidget { const _PreviewBar({ required this.label, required this.value, required this.color, }); final String label; final double value; final Color color; @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( label, style: TextStyle( color: Colors.white.withValues(alpha: 0.65), fontSize: 12, ), ), Text( '${(value * 100).toInt()}%', style: TextStyle( color: Colors.white.withValues(alpha: 0.65), fontSize: 12, fontWeight: FontWeight.w600, ), ), ], ), const SizedBox(height: 5), LayoutBuilder( builder: (_, constraints) => Stack( children: [ Container( height: 5, width: constraints.maxWidth, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.08), borderRadius: BorderRadius.circular(10), ), ), Container( height: 5, width: constraints.maxWidth * value, decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(10), ), ), ], ), ), ], ); } } // ── Form field ──────────────────────────────────────────────────────────────── class _Field extends StatelessWidget { const _Field({ required this.controller, required this.label, required this.icon, this.keyboardType, this.textInputAction, this.obscureText = false, this.suffixIcon, this.onFieldSubmitted, this.validator, }); final TextEditingController controller; final String label; final IconData icon; final TextInputType? keyboardType; final TextInputAction? textInputAction; final bool obscureText; final Widget? suffixIcon; final ValueChanged? onFieldSubmitted; final FormFieldValidator? validator; @override Widget build(BuildContext context) { return TextFormField( controller: controller, keyboardType: keyboardType, textInputAction: textInputAction, obscureText: obscureText, onFieldSubmitted: onFieldSubmitted, validator: validator, style: const TextStyle(fontSize: 15, color: AppColors.textPrimary), decoration: InputDecoration( labelText: label, prefixIcon: Icon(icon, size: 20, color: AppColors.textSecondary), suffixIcon: suffixIcon, filled: true, fillColor: const Color(0xFFF8FAFC), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: AppColors.border), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: AppColors.border), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Color(0xFF0D4C85), width: 2), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: AppColors.cancelled, width: 1.5), ), focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: AppColors.cancelled, width: 2), ), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), labelStyle: const TextStyle( color: AppColors.textSecondary, fontSize: 14), ), ); } }