import 'dart:async'; import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:go_router/go_router.dart'; import 'package:google_sign_in/google_sign_in.dart'; import 'package:google_sign_in_web/web_only.dart' as web; import 'package:qpay/models/responsive_policy.dart'; import 'package:qpay/screens/onboarding/login/login_controller.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:skeletonizer/skeletonizer.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @override State createState() => _LoginScreenState(); } class _LoginScreenState extends State { late SharedPreferences prefs; late GoogleSignIn googleSignIn; final _formKey = GlobalKey(); bool _loading = false; List scopes = [ 'https://www.googleapis.com/auth/contacts.readonly', ]; late LoginController _loginController; StreamSubscription? _authSubscription; @override void initState() { super.initState(); _loginController = LoginController(context); googleSignIn = GoogleSignIn.instance; if (kIsWeb) { googleSignIn.initialize(); _authSubscription = googleSignIn.authenticationEvents .handleError(_handleAuthenticationError) .listen(_handleAuthenticationEvent); } else { unawaited( googleSignIn.initialize(serverClientId: dotenv.env['CLIENTID']), ); } } Future signInWithGoogle() async { if (GoogleSignIn.instance.supportsAuthenticate()) { _authSubscription?.cancel(); unawaited( GoogleSignIn.instance.authenticate().then((value) async { _authSubscription = googleSignIn.authenticationEvents .handleError(_handleAuthenticationError) .listen(_handleAuthenticationEvent); }), ); } } void saveUser(GoogleSignInAccount user) async { final Map data = { 'displayName': user.displayName, 'email': user.email, 'id': user.id, 'photoUrl': user.photoUrl, }; prefs = await SharedPreferences.getInstance(); if (prefs.getString("googleUser") == null) { prefs.setString("googleUser", jsonEncode(data)); } } Future _handleAuthenticationEvent( GoogleSignInAuthenticationEvent event, ) async { if (!mounted) return; // #docregion CheckAuthorization final GoogleSignInAccount? user = // ... // #enddocregion CheckAuthorization switch (event) { GoogleSignInAuthenticationEventSignIn() => event.user, GoogleSignInAuthenticationEventSignOut() => null, }; if (user == null || !mounted) return; // Check for existing authorization. // #docregion CheckAuthorization final GoogleSignInClientAuthorization? authorization = await user ?.authorizationClient .authorizationForScopes(scopes); // #enddocregion CheckAuthorization print(user); saveUser(user); if (!mounted) return; final response = await _loginController.login(user.email, user.id); if (response.isSuccess) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text("Login successful!"), behavior: SnackBarBehavior.floating, backgroundColor: Colors.green, ), ); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(response.error ?? "Login failed"), behavior: SnackBarBehavior.floating, backgroundColor: Colors.deepOrange, ), ); } if (!mounted) return; context.go('/'); } Future _handleAuthenticationError(Object e) async { print(e.toString()); } @override void dispose() { _authSubscription?.cancel(); _loginController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(25), child: Form( key: _formKey, child: Center( child: LayoutBuilder( builder: (context, constraints) { final maxWidth = constraints.maxWidth > ResponsivePolicy.md ? ResponsivePolicy.md.toDouble() : constraints.maxWidth; return SizedBox( width: maxWidth, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox(height: 75), Text( 'Login', style: TextStyle( fontSize: 30, fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.primary, ), ), SizedBox(height: 5), Text( 'Welcome back to Velocity', style: TextStyle(fontSize: 18), textAlign: TextAlign.center, ), SizedBox(height: 30), Image.asset('assets/password.png', width: 300), Text( 'Tap on your preferred social media platform to get started', style: TextStyle(fontSize: 18), textAlign: TextAlign.center, ), // Username Field SizedBox(height: 20), // Forgot Password Link // Divider with "or" text Row( children: [ Expanded( child: Divider(color: Colors.grey.shade300), ), Padding( padding: EdgeInsets.symmetric(horizontal: 16), child: Text( 'choose from the options below to continue', style: TextStyle( color: Colors.grey.shade600, fontSize: 14, ), textAlign: TextAlign.center, ), ), Expanded( child: Divider(color: Colors.grey.shade300), ), ], ), SizedBox(height: 30), // Social Login Buttons if (GoogleSignIn.instance.supportsAuthenticate()) _buildGoogleButton() else ...[if (kIsWeb) web.renderButton()], SizedBox(height: 40), TextButton( onPressed: () { context.go('/'); }, child: Text( 'Continue as guest?', style: TextStyle(fontSize: 14), ), ), ], ), ); }, ), ), ), ), ), ); } Widget _buildGoogleButton() { return Skeletonizer( enabled: _loading, child: Row( children: [ Expanded( child: OutlinedButton.icon( onPressed: () { signInWithGoogle(); }, icon: Image.asset('assets/google.png', width: 20, height: 20), label: Text('Google', style: TextStyle(fontSize: 16)), style: OutlinedButton.styleFrom( padding: EdgeInsets.symmetric(vertical: 16), side: BorderSide(color: Colors.grey.shade300), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ), ), SizedBox(width: 16), ], ), ); } }