Signup screen and functionality

This commit is contained in:
Shivering Ocelot committed 2025-05-26 11:22:31 -05:00
1 parent 08f0668997
commit c3e55e89f2
6 files changed
+155 -11

No files matched your search

+2
View File
@@ -7,6 +7,8 @@ class ConfigField {
final String name;
final String type;
final TextEditingController controller;
// There should be an additional property to indicate whether this field is deactivated/removed/reset
// meaning it will be set as 'null' (default) rather than any string, otherwise only an empty string can be set
ConfigField({required this.name, required this.type, required this.controller});
}
+20 -2
View File
@@ -1,7 +1,8 @@
import 'package:http/http.dart' as http;
import 'dart:html';
import 'dart:convert';
import 'dart:async'; // Import for handling Completers
import 'dart:async';
class ChatInfo {
int id;
@@ -31,6 +32,23 @@ class Backend {
connect();
}
static Future<void> signup(String username, String email, String password) async {
final response = await http.post(Uri.parse("$_url/user/signup"),
headers: {
'Content-Type': 'application/json',
},
body: jsonEncode({
'username': username,
'email': email,
'password': password,
})
);
if (response.statusCode < 200 || response.statusCode >= 300) {
throw Exception("Signup Status Code ${response.statusCode}");
}
}
static Future<void> auth(String username, String password) {
_socket!.send(json.encode({'username': username, 'password': password}));
_credentials = (username: username, password: password);
@@ -43,7 +61,7 @@ class Backend {
}
static void connect() {
_socket = WebSocket(_url!);
_socket = WebSocket(_url! + "/ws");
_socket!.onOpen.listen((event) {
print('Connected to the WebSocket server');
Backend.invalidateCache(chat: true);
+99 -8
View File
@@ -1,24 +1,52 @@
import 'package:flutter/material.dart';
import 'backend.dart';
class LoginScreen extends StatelessWidget {
final Function(String, String) onLogin;
final bool isSignupMode; // Changed to a boolean
LoginScreen({required this.onLogin});
LoginScreen({required this.onLogin, this.isSignupMode = false}); // Default to false (login mode)
final TextEditingController usernameController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
final TextEditingController emailController = TextEditingController();
final TextEditingController confirmPasswordController = TextEditingController();
final FocusNode usernameFocusNode = FocusNode();
final FocusNode passwordFocusNode = FocusNode();
final FocusNode emailFocusNode = FocusNode();
final FocusNode confirmPasswordFocusNode = FocusNode();
void _handleLogin() {
onLogin(usernameController.text, passwordController.text);
}
void _handleSignup(BuildContext context) async {
final username = usernameController.text;
final password = passwordController.text;
final email = emailController.text;
if (password != confirmPasswordController.text) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Passwords don't match")),
);
return;
}
try {
await Backend.signup(username, email, password);
_handleLogin();
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Could not signup: $e')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Login')),
appBar: AppBar(title: Text(isSignupMode ? 'Signup' : 'Login')), // Dynamic title
body: Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
@@ -31,24 +59,87 @@ class LoginScreen extends StatelessWidget {
decoration: InputDecoration(labelText: 'Username'),
textInputAction: TextInputAction.next,
onSubmitted: (_) {
FocusScope.of(context).requestFocus(passwordFocusNode);
if (!isSignupMode) { // If not in signup mode, move to password
FocusScope.of(context).requestFocus(passwordFocusNode);
} else { // In signup mode, move to email
FocusScope.of(context).requestFocus(emailFocusNode);
}
},
),
SizedBox(height: 20),
if (isSignupMode) ...[ // Conditionally show email field
TextField(
controller: emailController,
focusNode: emailFocusNode,
decoration: InputDecoration(labelText: 'Email'),
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.next,
onSubmitted: (_) {
FocusScope.of(context).requestFocus(passwordFocusNode);
},
),
SizedBox(height: 20),
],
TextField(
controller: passwordController,
focusNode: passwordFocusNode,
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
textInputAction: TextInputAction.done,
textInputAction: isSignupMode ? TextInputAction.next : TextInputAction.done, // Adjust text action
onSubmitted: (_) {
_handleLogin();
if (!isSignupMode) { // If not in signup mode, handle login
_handleLogin();
} else { // In signup mode, move to confirm password
FocusScope.of(context).requestFocus(confirmPasswordFocusNode);
}
},
),
SizedBox(height: 20),
if (isSignupMode) ...[ // Conditionally show confirm password field
TextField(
controller: confirmPasswordController,
focusNode: confirmPasswordFocusNode,
decoration: InputDecoration(labelText: 'Confirm Password'),
obscureText: true,
textInputAction: TextInputAction.done,
onSubmitted: (_) {
_handleSignup(context);
},
),
SizedBox(height: 20),
],
ElevatedButton(
onPressed: _handleLogin,
child: Text('Login'),
onPressed: () {
if (!isSignupMode) { // If not in signup mode, handle login
_handleLogin();
} else { // In signup mode, handle signup
_handleSignup(context);
}
},
child: Text(isSignupMode ? 'Signup' : 'Login'), // Dynamic button text
),
SizedBox(height: 20),
TextButton(
onPressed: () {
if (!isSignupMode) { // If in login mode, navigate to signup
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => LoginScreen(
onLogin: onLogin,
isSignupMode: true, // Push with signup mode
),
),
);
} else { // If in signup mode, pop back to login
Navigator.of(context).pop();
}
},
child: Text(
isSignupMode
? 'Already have an account? Login' // Text for signup mode
: 'Don\'t have an account? Sign Up', // Text for login mode
style: TextStyle(color: Colors.blue),
),
),
],
),
@@ -56,4 +147,4 @@ class LoginScreen extends StatelessWidget {
),
);
}
}
}
+1 -1
View File
@@ -30,7 +30,7 @@ void main() {
print("Main");
Config.loadConfig(); // Load the configuration before running the app
Backend.setup('${Config.backendUrl}/ws');
Backend.setup(Config.backendUrl!);
runApp(HubApp());
}
+32
View File
@@ -136,6 +136,22 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
http:
dependency: "direct main"
description:
name: http
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
url: "https://pub.dev"
source: hosted
version: "1.4.0"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.1.2"
js:
dependency: transitive
description:
@@ -325,6 +341,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.4"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
vector_math:
dependency: transitive
description:
@@ -341,6 +365,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "14.3.1"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
win32:
dependency: transitive
description:
+1
View File
@@ -37,6 +37,7 @@ dependencies:
# For user credentials and sensitive data
flutter_secure_storage: ^9.2.4
http: ^1.4.0
dev_dependencies:
flutter_test: