Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SCRUM-3] Implement login page ui #1

Merged
merged 2 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ migrate_working_dir/
.pub/
/build/
/frontend/build/
devtools_options.yaml

# Symbolication related
app.*.symbols
Expand Down
7 changes: 7 additions & 0 deletions frontend/lib/common/theme.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import 'package:flutter/material.dart';

final appTheme = ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.lightBlue),
useMaterial3: true,
scaffoldBackgroundColor: Colors.white,
);
81 changes: 27 additions & 54 deletions frontend/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,70 +1,43 @@
import 'package:flutter/material.dart';
// import 'package:go_router/go_router.dart';
import 'package:go_router/go_router.dart';
// import 'package:provider/provider.dart';

import 'package:utm_marketplace/common/theme.dart';
import 'package:utm_marketplace/views/login.dart';

// TODO: These are just for testing, remove when done/replace with actual views
import 'package:utm_marketplace/views/model_view.dart';
import 'package:utm_marketplace/models/model.dart';
ziadzananiri marked this conversation as resolved.
Show resolved Hide resolved

void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
GoRouter router() {
return GoRouter(
ziadzananiri marked this conversation as resolved.
Show resolved Hide resolved
initialLocation: '/login',
routes: [
GoRoute(
path: '/login',
builder: (context, state) => const Login(),
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;

@override
State<MyHomePage> createState() => _MyHomePageState();
GoRoute(
path: '/temp_view',
builder: (context, state) => ModelView(model: Model(attribute1: 'Attribute 1', attribute2: 2)),
),
],
);
}

class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;

void _incrementCounter() {
setState(() {
_counter++;
});
}
class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
return MaterialApp.router(
title: 'UTM Marketplace',
theme: appTheme,
routerConfig: router(),
);
}
}
149 changes: 149 additions & 0 deletions frontend/lib/views/login.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

// Note: There's a small issue regarding the flex Spacer widget, it's not too important
// but would be nice to find a way that makes it so the UI elements
// don't shift when a validator fails.

class Login extends StatefulWidget {
const Login({super.key});

@override
LoginState createState() => LoginState();
}

class LoginState extends State<Login> {
final _formKey = GlobalKey<FormState>();
bool _obscureText = true;

@override
Widget build(BuildContext context) {
return Scaffold(
ziadzananiri marked this conversation as resolved.
Show resolved Hide resolved
resizeToAvoidBottomInset: false,
body: Container(
padding: EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Spacer(flex: 1),
Text(
'UTMarketplace',
style: TextStyle(
fontSize: 40.0,
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.w900,
letterSpacing: 2,
),
),
Spacer(flex: 1),
Align(
alignment: Alignment.centerLeft,
child: Text(
'UofT Email Address',
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w700,
),
),
),
TextFormField(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Enter your UofT email',
errorMaxLines: 1,
errorStyle: TextStyle(
height: 0.0,
),
),
// TODO: REGEX for valid UofT domain email, uncomment when ready
// validator: (value) {
// if (value == null || value.isEmpty) {
// return 'Please a valid UofT email address';
// }
// return null;
// },
),
SizedBox(height: 16.0),
Align(
alignment: Alignment.centerLeft,
child: Text(
'Password',
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w700,
),
),
ziadzananiri marked this conversation as resolved.
Show resolved Hide resolved
),
TextFormField(
obscureText: _obscureText,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Enter your password',
ziadzananiri marked this conversation as resolved.
Show resolved Hide resolved
errorMaxLines: 1,
errorStyle: TextStyle(
height: 0.0,
),
suffixIcon: IconButton(
icon: Icon(
_obscureText ? Icons.visibility_off : Icons.visibility,
),
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
),
),
// TODO: Implement email validation and password validation (backend), uncomment when ready
// validator: (value) {
// if (value == null || value.isEmpty) {
// return 'Password or email is incorrect';
// }
// return null;
// },
),
SizedBox(height: 16.0),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
// TODO: Implement login functionality (backend)
// If this condition passed, a redirect call to the home
// page should be made
context.go('/temp_view');
}
},
style: ElevatedButton.styleFrom(
minimumSize: Size(double.infinity, 50),
),
child: Text('Log In'),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Don\'t have an account?'),
TextButton(
onPressed: () {
// TODO: Implement redirect to sign up page
},
child: Text('Sign Up'),
),
],
),
TextButton(
onPressed: () {
// TODO: Implement forgot password functionality
},
child: Text('Forgot Password?'),
),
Spacer(flex: 1),
],
),
),
),
);
}
}