// Author: fruit.com.cn import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../core/data/services/auth_controller.dart'; import '../home/home_view.dart'; /// 登录页:Google / 手机号 / 邮箱 三种登录入口 + 游客浏览。 class LoginPage extends StatefulWidget { const LoginPage({super.key}); @override State createState() => _LoginPageState(); } class _LoginPageState extends State { static const _orange = Color(0xFFFF5B3D); static const _purple = Color(0xFF9278EA); void _enterHome() { Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (_) => const HomePage()), ); } Future _showLoginSheet(String targetType) async { final auth = context.read(); final targetCtrl = TextEditingController(); final codeCtrl = TextEditingController(); final isPhone = targetType == 'PHONE'; await showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: const Color(0xFF161822), builder: (sheetContext) => Padding( padding: EdgeInsets.only( bottom: MediaQuery.of(sheetContext).viewInsets.bottom, ), child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( isPhone ? 'Phone Login' : 'Email Login', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.w700, color: Colors.white, ), ), const SizedBox(height: 16), TextField( controller: targetCtrl, keyboardType: isPhone ? TextInputType.phone : TextInputType.emailAddress, style: const TextStyle(color: Colors.white), decoration: InputDecoration( hintText: isPhone ? 'Phone number' : 'Email', hintStyle: const TextStyle(color: Colors.white38), filled: true, fillColor: Colors.white.withValues(alpha: 0.05), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, ), ), ), const SizedBox(height: 12), Row( children: [ Expanded( child: TextField( controller: codeCtrl, keyboardType: TextInputType.number, style: const TextStyle(color: Colors.white), decoration: InputDecoration( hintText: 'Code', hintStyle: const TextStyle(color: Colors.white38), filled: true, fillColor: Colors.white.withValues(alpha: 0.05), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, ), ), ), ), const SizedBox(width: 12), TextButton( onPressed: () async { try { await auth.sendOtp( scene: 'LOGIN', targetType: targetType, target: targetCtrl.text.trim(), ); if (sheetContext.mounted) { ScaffoldMessenger.of(sheetContext).showSnackBar( const SnackBar(content: Text('Code sent')), ); } } catch (e) { if (sheetContext.mounted) { ScaffoldMessenger.of(sheetContext).showSnackBar( SnackBar(content: Text('$e')), ); } } }, child: const Text('Send Code'), ), ], ), const SizedBox(height: 20), FilledButton( onPressed: () async { try { await auth.loginWithCode( targetType: targetType, target: targetCtrl.text.trim(), code: codeCtrl.text.trim(), ); if (sheetContext.mounted) { Navigator.of(sheetContext).pop(); } _enterHome(); } catch (e) { if (sheetContext.mounted) { ScaffoldMessenger.of(sheetContext).showSnackBar( SnackBar(content: Text('$e')), ); } } }, style: FilledButton.styleFrom( backgroundColor: _orange, padding: const EdgeInsets.symmetric(vertical: 14), ), child: const Text('Sign In'), ), ], ), ), ), ); } @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: Column( children: [ // 顶部:关闭 + 游客浏览 Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _IconButton(icon: Icons.close, onTap: () {}), TextButton( onPressed: _enterHome, style: TextButton.styleFrom( foregroundColor: Colors.white54, backgroundColor: Colors.white.withValues(alpha: 0.05), padding: const EdgeInsets.symmetric( horizontal: 14, vertical: 8, ), shape: const StadiumBorder(), ), child: const Text('Guest Explore'), ), ], ), ), // 中央内容 Expanded( child: Center( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 28), child: Column( mainAxisSize: MainAxisSize.min, children: [ const _BrandLogo(), const SizedBox(height: 20), const Text( 'DramaPulse', style: TextStyle( fontSize: 30, fontWeight: FontWeight.w800, color: Colors.white, ), ), const SizedBox(height: 8), const Text( 'Sign in to sync your progress and coins', textAlign: TextAlign.center, style: TextStyle( fontSize: 14, color: Colors.white54, ), ), const SizedBox(height: 36), _GoogleButton(onTap: _enterHome), const SizedBox(height: 14), _DarkButton( icon: Icons.phone_iphone, iconColor: _orange, label: 'Continue with Phone Number', onTap: () => _showLoginSheet('PHONE'), ), const SizedBox(height: 14), _DarkButton( icon: Icons.email_outlined, iconColor: _purple, label: 'Continue with Email', onTap: () => _showLoginSheet('EMAIL'), ), const SizedBox(height: 32), const _BonusPill(), ], ), ), ), ), // 底部协议 const Padding( padding: EdgeInsets.only(bottom: 24), child: Text( 'By continuing, you agree to DramaPulse\'s\n' 'Terms of Service · Privacy Policy · Content Policy', textAlign: TextAlign.center, style: TextStyle(fontSize: 11, color: Colors.white38), ), ), ], ), ), ); } } class _IconButton extends StatelessWidget { const _IconButton({required this.icon, required this.onTap}); final IconData icon; final VoidCallback onTap; @override Widget build(BuildContext context) { return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(24), child: Container( width: 40, height: 40, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.05), border: Border.all(color: Colors.white12), shape: BoxShape.circle, ), child: Icon(icon, color: Colors.white54, size: 20), ), ); } } class _BrandLogo extends StatelessWidget { const _BrandLogo(); @override Widget build(BuildContext context) { return Container( width: 80, height: 80, decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFFFF5B3D), Color(0xFFD8536C), Color(0xFF9278EA)], ), borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.white24), ), child: const Icon(Icons.play_arrow_rounded, color: Colors.white, size: 40), ); } } class _GoogleButton extends StatelessWidget { const _GoogleButton({required this.onTap}); final VoidCallback onTap; @override Widget build(BuildContext context) { return _AuthButton( onTap: onTap, background: Colors.white, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const _GoogleG(), const SizedBox(width: 12), Text( 'Continue with Google', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: Colors.grey.shade900, ), ), ], ), ); } } /// 简化版 Google「G」logo。 class _GoogleG extends StatelessWidget { const _GoogleG(); @override Widget build(BuildContext context) { return Container( width: 20, height: 20, decoration: const BoxDecoration( shape: BoxShape.circle, color: Color(0xFF4285F4), ), child: const Center( child: Text( 'G', style: TextStyle( color: Colors.white, fontSize: 13, fontWeight: FontWeight.w700, ), ), ), ); } } class _DarkButton extends StatelessWidget { const _DarkButton({ required this.icon, required this.iconColor, required this.label, required this.onTap, }); final IconData icon; final Color iconColor; final String label; final VoidCallback onTap; @override Widget build(BuildContext context) { return _AuthButton( onTap: onTap, background: const Color(0xFF161822), border: const Color(0xFF232634), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, color: iconColor, size: 20), const SizedBox(width: 12), Text( label, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white, ), ), ], ), ); } } class _AuthButton extends StatelessWidget { const _AuthButton({ required this.onTap, required this.background, required this.child, this.border, }); final VoidCallback onTap; final Color background; final Color? border; final Widget child; @override Widget build(BuildContext context) { return SizedBox( width: double.infinity, height: 52, child: Material( color: background, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), side: border == null ? BorderSide.none : BorderSide(color: border!), ), child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(16), child: child, ), ), ); } } class _BonusPill extends StatelessWidget { const _BonusPill(); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.04), border: Border.all(color: Colors.white12), borderRadius: BorderRadius.circular(999), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 6, height: 6, decoration: const BoxDecoration( color: Color(0xFF34D399), shape: BoxShape.circle, ), ), const SizedBox(width: 8), const Text( 'Get ', style: TextStyle(fontSize: 11, color: Colors.white54), ), const Text( '100 Free Bonus Coins', style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: Colors.white, ), ), const Text( ' upon first sign-in', style: TextStyle(fontSize: 11, color: Colors.white54), ), ], ), ); } }