Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions devtools_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
230 changes: 230 additions & 0 deletions lib/login.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@


import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import '/main.dart';

class Loginscreen extends StatefulWidget {
@override
_Loginscreen createState() => _Loginscreen();
}

class _Loginscreen extends State<Loginscreen> {
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _usernameController = TextEditingController();

bool _isLogin = true;
bool _obscurePassword = true;
bool _isLoading = false;

Future<void> _authenticate() async {
if (!_formKey.currentState!.validate()) return;

final email = _emailController.text.trim();
final password = _passwordController.text.trim();
final username = _usernameController.text.trim();

setState(() => _isLoading = true);

try {
if (_isLogin) {
final response = await Supabase.instance.client.auth
.signInWithPassword(email: email, password: password);
if (response.user != null) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => MainScreen()),
);
}
} else {
final response = await Supabase.instance.client.auth.signUp(
email: email,
password: password,
data: {'username': username},
);
if (response.user != null) {
setState(() => _isLogin = true);
}
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Invalid credentials or error occurred.')),
);
} finally {
setState(() => _isLoading = false);
}
}

Widget _buildForm() {
return Form(
key: _formKey,
child: Column(
key: ValueKey(_isLogin),
children: [
TextFormField(
controller: _emailController,
decoration: InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.email),
),
validator: (value) {
if (value == null || value.isEmpty || !value.contains('@')) {
return 'Enter a valid email';
}
return null;
},
),
SizedBox(height: 16),
TextFormField(
controller: _passwordController,
obscureText: _obscurePassword,
decoration: InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.lock),
suffixIcon: IconButton(
icon: Icon(
_obscurePassword ? Icons.visibility_off : Icons.visibility,
),
onPressed: () {
setState(() => _obscurePassword = !_obscurePassword);
},
),
),
validator: (value) {
if (value == null || value.length < 6) {
return 'Password must be at least 6 characters';
}
return null;
},
),
if (!_isLogin) ...[
SizedBox(height: 16),
TextFormField(
controller: _usernameController,
decoration: InputDecoration(
labelText: 'Username',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.person),
),
validator: (value) {
if (!_isLogin && (value == null || value.isEmpty)) {
return 'Please enter a username';
}
return null;
},
),
],
SizedBox(height: 24),
AnimatedOpacity(
opacity: _isLoading ? 0.6 : 1,
duration: Duration(milliseconds: 300),
child: ElevatedButton.icon(
onPressed: _isLoading ? null : _authenticate,
icon: _isLoading
? SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Icon(_isLogin ? Icons.login : Icons.person_add),
label: Text(
_isLogin ? 'Login' : 'Sign Up',
style: TextStyle(fontSize: 18),
),
style: ElevatedButton.styleFrom(
backgroundColor: _isLogin ? Colors.indigo : Colors.green[600],
padding: EdgeInsets.symmetric(horizontal: 32, vertical: 14),
foregroundColor: Colors.white,
),
),
),
SizedBox(height: 12),
TextButton(
onPressed: () {
setState(() => _isLogin = !_isLogin);
},
child: Text(
_isLogin
? "Don't have an account? Sign Up"
: "Already have an account? Login",
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: _isLogin ? Colors.indigo : Colors.green[800],
decoration: TextDecoration.underline,
),
),
),
],
),
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.indigo[50],
appBar: AppBar(
backgroundColor: _isLogin ? Colors.indigo : Colors.green[600],
title: Text(
_isLogin ? 'Login' : 'Sign Up',
style: TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
color: Colors.white,
letterSpacing: 1.2,
),
),
centerTitle: true,
),
body: Center(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 500),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16)),
elevation: 8,
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ShaderMask(
shaderCallback: (bounds) => LinearGradient(
colors: _isLogin
? [Colors.indigo, Colors.blue]
: [Colors.green, Colors.lightGreen],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
).createShader(
Rect.fromLTWH(0, 0, bounds.width, bounds.height),
),
child: Text(
'Student Grievance Portal',
style: TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
color: Colors.white,
letterSpacing: 1.3,
),
),
),
SizedBox(height: 20),
_buildForm(),
],
),
),
),
),
),
),
),
);
}
}
Loading