Skip to content

Commit

Permalink
style: add login screen
Browse files Browse the repository at this point in the history
  • Loading branch information
Qubits-01 committed Feb 15, 2024
1 parent cbb8640 commit 7dfe0ec
Show file tree
Hide file tree
Showing 13 changed files with 312 additions and 28 deletions.
1 change: 1 addition & 0 deletions assets/icons/icons8-facebook.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/icons/icons8-google.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/icons8-incognito-48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions lib/constants/no_params.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import '../core/interfaces/params_intf.dart';
import '../core/base_classes/params_base.dart';

// TODO: [P3] Write a test for the NoParams class.

/// When a use case does not require any parameters, this class should be used.
class NoParams implements ParamsIntf {
class NoParams implements ParamsBase {
const NoParams();

@override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
import 'package:go_router/go_router.dart';

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

@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
// Welcome message.
Text(
'Forgot Password',
style: Theme.of(context).textTheme.titleLarge,
),
const Text('Enter your email to reset your password'),
const SizedBox(height: 32.0),

// Email field.
FormBuilderTextField(
name: 'email',
decoration: const InputDecoration(
labelText: 'Email',
hintText: 'Enter your email',
),
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(),
FormBuilderValidators.email(),
]),
),
const SizedBox(height: 16.0),

// Reset password button.
FilledButton(
onPressed: () {
GoRouter.of(context).go('/login');
},
child: const Text('Reset Password'),
),
],
),
),
),
),
);
}
}
196 changes: 196 additions & 0 deletions lib/core/features/auth/presentation/screens/login_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:flutter_svg/svg.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
import 'package:go_router/go_router.dart';

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

final String googleIconPath = 'assets/icons/icons8-google.svg';
// final String facebookIconPath = 'assets/icons/icons8-facebook.svg';
final String anonymousIconPath = 'assets/icons/icons8-incognito-48.png';

@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
// Welcome message.
Text(
'Welcome Scholar',
style: Theme.of(context).textTheme.titleLarge,
),
const Text('Log in to continue your learning journey'),
const SizedBox(height: 32.0),

// Log in with Google.
FilledButton.tonal(
onPressed: () {},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SvgPicture.asset(
googleIconPath,
width: 24.0,
height: 24.0,
semanticsLabel: 'Google Icon Logo',
),
const SizedBox(width: 8.0),
const Text('Log in with Google'),
],
),
),
const SizedBox(height: 8.0),

// Log in Anonymously.
FilledButton.tonal(
onPressed: () {},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(
anonymousIconPath,
width: 24.0,
height: 24.0,
semanticLabel: 'Anonymous Icon',
),
const SizedBox(width: 8.0),
const Text('Log in Anonymously'),
],
),
),
const SizedBox(height: 32.0),

// Horizontal divider.
const Row(
children: <Widget>[
Expanded(child: Divider(endIndent: 8.0)),
Text('OR'),
Expanded(child: Divider(indent: 8.0)),
],
),
const SizedBox(height: 32.0),

// Log in with Email form.
const LoginForm(),
],
),
),
),
),
);
}
}

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

@override
State<LoginForm> createState() => _LoginFormState();
}

class _LoginFormState extends State<LoginForm> {
final _formKey = GlobalKey<FormBuilderState>();
final _emailFieldKey = GlobalKey<FormFieldState<String>>();
bool _isPasswordVisible = false;

@override
Widget build(BuildContext context) {
return FormBuilder(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
// Email text field.
FormBuilderTextField(
key: _emailFieldKey,
name: 'email',
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.next,
decoration: const InputDecoration(
labelText: 'Email',
hintText: 'Enter your email',
border: OutlineInputBorder(),
),
validator: FormBuilderValidators.compose(
[
FormBuilderValidators.required(),
FormBuilderValidators.email(),
],
),
),
const SizedBox(height: 32.0),

// Password text field.
FormBuilderTextField(
name: 'password',
keyboardType: TextInputType.visiblePassword,
textInputAction: TextInputAction.done,
obscureText: !_isPasswordVisible,
decoration: InputDecoration(
labelText: 'Password',
hintText: 'Enter your password',
border: const OutlineInputBorder(),
suffixIcon: IconButton(
onPressed: () {
setState(() {
_isPasswordVisible = !_isPasswordVisible;
});
},
icon: _isPasswordVisible
? const Icon(Icons.visibility)
: const Icon(Icons.visibility_off),
),
),
validator: FormBuilderValidators.compose(
[
FormBuilderValidators.required(),
FormBuilderValidators.minLength(16),
],
),
),

// Forgot password?
Align(
alignment: Alignment.centerRight,
child: TextButton(
onPressed: () {
GoRouter.of(context).go('/login/forgot-password');
},
child: const Text('Forgot password?'),
),
),
const SizedBox(height: 32.0),

// Log in button.
FilledButton(
onPressed: () {
if (_formKey.currentState?.saveAndValidate() ?? false) {
GoRouter.of(context).go('/home');
}
},
child: const Text('Log in'),
),
const SizedBox(height: 16.0),

// Don't have an account? Sign up.
Row(
children: <Widget>[
const Text('Don\'t have an account?'),
TextButton(
onPressed: () {},
child: const Text('Sign up'),
),
],
),
],
),
);
}
}
10 changes: 0 additions & 10 deletions lib/core/interfaces/entity_intf.dart

This file was deleted.

9 changes: 0 additions & 9 deletions lib/core/interfaces/params_intf.dart

This file was deleted.

7 changes: 5 additions & 2 deletions lib/core/interfaces/use_case_intf.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import 'params_intf.dart';
import '../base_classes/params_base.dart';

// TODO: [P3] Write a test for the UseCaseIntf abstract class.

/// All use cases should implement this interface.
abstract class UseCaseIntf<T, P extends ParamsIntf> {
///
/// [T] is the return type of the use case.
/// [P] is the parameter type of the use case, which should be of type [ParamsBase].
abstract class UseCaseIntf<T, P extends ParamsBase> {
const UseCaseIntf();

/// Calls the use case (i.e., executes the use case).
Expand Down
17 changes: 16 additions & 1 deletion lib/router/app_router.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

import '../core/features/auth/presentation/screens/login_screen.dart';
import 'scaffold_with_nav_bar.dart';
import 'temp/details_placeholder_screen.dart';
import 'temp/root_placeholder_screen.dart';
Expand All @@ -20,9 +22,22 @@ class AppRouter {
GlobalKey<NavigatorState>(debugLabel: 'sectionProfileNav');

static final GoRouter router = GoRouter(
initialLocation: '/home',
initialLocation: '/login',
navigatorKey: _rootNavigatorKey,
routes: <RouteBase>[
// AUTH.
GoRoute(
path: '/login',
builder: (context, state) => const LoginScreen(),
routes: <RouteBase>[
GoRoute(
path: 'forgot-password',
builder: (context, state) => const ForgotPasswordScreen(),
),
],
),

// BOTTOM NAVIGATION.
// Stateful nested navigator.
StatefulShellRoute.indexedStack(
builder: (
Expand Down
22 changes: 19 additions & 3 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "8.1.4"
flutter_form_builder:
dependency: "direct main"
description:
name: flutter_form_builder
sha256: "560eb5e367d81170c6ade1e7ae63ecc5167936ae2cdfaae8a345e91bce19d2f2"
url: "https://pub.dev"
source: hosted
version: "9.2.1"
flutter_lints:
dependency: "direct dev"
description:
Expand All @@ -428,7 +436,7 @@ packages:
source: hosted
version: "2.3.10"
flutter_svg:
dependency: transitive
dependency: "direct main"
description:
name: flutter_svg
sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c
Expand All @@ -445,6 +453,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
form_builder_validators:
dependency: "direct main"
description:
name: form_builder_validators
sha256: "19aa5282b7cede82d0025ab031a98d0554b84aa2ba40f12013471a3b3e22bf02"
url: "https://pub.dev"
source: hosted
version: "9.1.0"
frontend_server_client:
dependency: transitive
description:
Expand Down Expand Up @@ -907,7 +923,7 @@ packages:
source: hosted
version: "1.1.10+1"
vector_graphics_compiler:
dependency: transitive
dependency: "direct dev"
description:
name: vector_graphics_compiler
sha256: "18489bdd8850de3dd7ca8a34e0c446f719ec63e2bab2e7a8cc66a9028dd76c5a"
Expand Down Expand Up @@ -996,4 +1012,4 @@ packages:
version: "3.1.2"
sdks:
dart: ">=3.2.6 <4.0.0"
flutter: ">=3.13.0"
flutter: ">=3.16.0"
Loading

0 comments on commit 7dfe0ec

Please sign in to comment.