import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:tp5/core/core.dart'; class LoginOtp extends ConsumerStatefulWidget { const LoginOtp({super.key}); @override ConsumerState createState() => _LoginOtpState(); } class _LoginOtpState extends ConsumerState { final TextEditingController _emailController = TextEditingController(); final TextEditingController _otpController = TextEditingController(); bool _isOtpSent = false; bool _isLoading = false; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Sign In'), backgroundColor: Colors.blueAccent, ), body: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SizedBox(height: 50), Image( image: AssetImage('assets/logo.png'), width: 100, height: 100, ), Gap(20), Text( 'Welcome to TAR Pilot v5', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Colors.blueAccent, ), textAlign: TextAlign.center, ), SizedBox(height: 20), Text( 'Please enter your email to sign in.', style: TextStyle( fontSize: 16, color: Colors.grey[700], ), textAlign: TextAlign.center, ), SizedBox(height: 40), TextField( controller: _emailController, decoration: InputDecoration( labelText: 'Email', hintText: 'Enter your email', border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), ), prefixIcon: Icon(Icons.email), ), keyboardType: TextInputType.emailAddress, ), SizedBox(height: 20), if (_isOtpSent) ...[ TextField( controller: _otpController, decoration: InputDecoration( labelText: 'OTP', hintText: 'Enter OTP sent to your email', border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), ), prefixIcon: Icon(Icons.lock_outline), ), keyboardType: TextInputType.number, ), SizedBox(height: 20), ], ElevatedButton( onPressed: _isLoading ? null : () async { final email = _emailController.text; setState(() => _isLoading = true); try { if (!_isOtpSent) { await Supabase.instance.client.auth.signInWithOtp( email: email, shouldCreateUser: false, emailRedirectTo: 'com.example.tp5://magic-link'); setState(() => _isOtpSent = true); } else { final response = await Supabase.instance.client.auth.verifyOTP( email: email, token: _otpController.text, type: OtpType.email, ); if (response.session == null) { _otpController.clear(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Invalid OTP. Please try again.'), backgroundColor: Colors.red, ), ); } else { //logged in print(response.session?.accessToken); context.go("/home"); } } } catch (e) { if (e is AuthApiException && !_isOtpSent) { //signup print("loginotp: new signup"); setState(() => _isLoading = true); await Supabase.instance.client.auth.signInWithOtp( email: email, shouldCreateUser: true, emailRedirectTo: 'com.example.tp5://auth/magic-link'); setState(() => _isOtpSent = true); //go profile after new registration context.go('/home'); } else { //error _otpController.clear(); context.showError('Error: ${e.toString()}'); } } finally { if (mounted) { setState(() => _isLoading = false); } } }, style: ElevatedButton.styleFrom( padding: EdgeInsets.symmetric(vertical: 15), backgroundColor: Colors.blueAccent, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), child: _isLoading ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : Text( _isOtpSent ? 'Verify OTP' : 'Send OTP', style: TextStyle(fontSize: 18), ), ), SizedBox(height: 20), if (_isOtpSent) TextButton( onPressed: () { // Handle use another email _emailController.clear(); setState(() => _isOtpSent = false); }, child: Text( 'Use another mail?', style: TextStyle( color: Colors.blueAccent, fontSize: 16, ), ), ), ], ), ), ), ); } @override void dispose() { _emailController.dispose(); _otpController.dispose(); super.dispose(); } }