updated assets and improved authentication flow

This commit is contained in:
Vaibhav Surve 2025-04-03 17:16:16 +05:30
parent 4b6f2bd86c
commit 3d28cf9e09
25 changed files with 146 additions and 161 deletions

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- <item android:drawable="?android:colorBackground" /> -->
<!-- You can insert your own image assets here -->
<!-- <item>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
assets/logo/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

View File

@ -1,54 +1,24 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:maxdash/controller/my_controller.dart';
import 'package:maxdash/helpers/services/auth_service.dart';
import 'package:maxdash/helpers/widgets/my_form_validator.dart';
import 'package:maxdash/helpers/widgets/my_validators.dart';
class LoginController extends MyController {
class LoginController extends GetxController {
MyFormValidator basicValidator = MyFormValidator();
bool showPassword = false, isChecked = false;
final String _dummyEmail = "demo@example.com";
final String _dummyPassword = "1234567";
bool showPassword = false;
@override
void onInit() {
basicValidator.addField('email', required: true, label: "Email", validators: [MyEmailValidator()], controller: TextEditingController(text: _dummyEmail));
basicValidator.addField('password',
required: true, label: "Password", validators: [MyLengthValidator(min: 6, max: 10)], controller: TextEditingController(text: _dummyPassword));
basicValidator.addField('username', required: true, label: "Username", controller: TextEditingController());
basicValidator.addField('password', required: true, label: "Password", controller: TextEditingController());
super.onInit();
}
void onChangeCheckBox(bool? value) {
isChecked = value ?? isChecked;
update();
}
void onChangeShowPassword() {
showPassword = !showPassword;
update();
}
Future<void> onLogin() async {
if (basicValidator.validateForm()) {
update();
var errors = await AuthService.loginUser(basicValidator.getData());
if (errors != null) {
basicValidator.addErrors(errors);
basicValidator.validateForm();
basicValidator.clearErrors();
} else {
String nextUrl = Uri.parse(ModalRoute.of(Get.context!)?.settings.name ?? "").queryParameters['next'] ?? "/home";
Get.toNamed(nextUrl);
}
update();
}
}
void goToForgotPassword() {
Get.toNamed('/auth/forgot_password');
}
@ -56,4 +26,47 @@ class LoginController extends MyController {
void gotoRegister() {
Get.offAndToNamed('/auth/register_account');
}
Future<void> onLogin() async {
if (basicValidator.validateForm()) {
final data = basicValidator.getData();
// Log the data being sent
print('Sending login request with data: $data');
// Show a loading dialog
Get.dialog(
Center(child: CircularProgressIndicator()),
barrierDismissible: false,
);
try {
// Call the AuthService to perform the login
final errors = await AuthService.loginUser(data);
print('Login errors: $errors');
// Close the loading dialog
Get.back();
if (errors != null) {
// Display errors if login fails
print('Login failed with errors: $errors');
basicValidator.addErrors(errors);
basicValidator.validateForm();
basicValidator.clearErrors();
} else {
// Navigate to the home screen on successful login
print('Login successful. Navigating to home...');
Get.offAllNamed('/home');
}
} catch (e) {
// Close the loading dialog in case of an exception
Get.back();
// Show a generic error message
print('Unexpected error: $e');
Get.snackbar('Error', 'An unexpected error occurred. Please try again.');
}
}
}
}

View File

@ -1,22 +1,46 @@
import 'package:maxdash/helpers/services/storage/local_storage.dart';
import 'package:maxdash/model/user.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
class AuthService {
static const String baseUrl = 'http://localhost:5032/api';
static bool isLoggedIn = false;
static User get dummyUser => User(-1, "demo@example.com", "Denish", "Navadiya");
static Future<Map<String, String>?> loginUser(Map<String, dynamic> data) async {
try {
print('Sending login request with data: $data');
static Future<Map<String, String>?> loginUser(
Map<String, dynamic> data) async {
await Future.delayed(Duration(seconds: 1));
if (data['email'] != dummyUser.email) {
return {"email": "This email is not registered"};
} else if (data['password'] != "1234567") {
return {"password": "Password is incorrect"};
// Make the API call
final response = await http.post(
Uri.parse('$baseUrl/Auth/login'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(data),
);
print('Response received: ${response.body}');
if (response.statusCode == 200) {
final responseData = jsonDecode(response.body);
// Extract the token from the "data" field
final token = responseData['data']?['token']; // Adjust based on your API response
if (token != null) {
// Save the token locally
final prefs = await SharedPreferences.getInstance();
await prefs.setString('auth_token', token);
isLoggedIn = true;
return null; // No errors
} else {
return {'error': 'Token not found in response'};
}
} else {
final responseData = jsonDecode(response.body);
return {'error': responseData['message'] ?? 'Invalid response from server'};
}
} catch (e) {
print('Unexpected error: $e');
return {'error': 'Unexpected error: $e'};
}
isLoggedIn = true;
await LocalStorage.setLoggedInUser(true);
return null;
}
}

View File

@ -5,10 +5,10 @@ class Images {
static List<String> dummy = List.generate(5, (index) => 'assets/dummy/dummy_${index + 1}.jpg');
static List<String> product = List.generate(10, (index) => 'assets/dummy/ecommerce/product_${index + 1}.jpg');
static String logoLight = 'assets/logo/logo_light.png';
static String logoLightSmall = 'assets/logo/logo_light_small.png';
static String logoDark = 'assets/logo/logo_dark.png';
static String logoDarkSmall = 'assets/logo/logo_dark_small.png';
static String logoLight = 'assets/logo/logo.png';
static String logoLightSmall = 'assets/logo/logo.png';
static String logoDark = 'assets/logo/logo.png';
static String logoDarkSmall = 'assets/logo/logo.png';
static String authBackground = 'assets/auth_background.jpg';
static String randomImage(List<String> images) {

View File

@ -1,14 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:get/get.dart';
import 'package:maxdash/controller/auth/login_controller.dart';
import 'package:maxdash/helpers/theme/app_theme.dart';
import 'package:maxdash/helpers/utils/mixins/ui_mixin.dart';
import 'package:maxdash/helpers/widgets/my_button.dart';
import 'package:maxdash/helpers/widgets/my_spacing.dart';
import 'package:maxdash/helpers/widgets/my_text.dart';
import 'package:maxdash/helpers/widgets/my_text_style.dart';
import 'package:maxdash/view/layouts/auth_layout.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@ -17,7 +9,7 @@ class LoginScreen extends StatefulWidget {
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> with UIMixin{
class _LoginScreenState extends State<LoginScreen> {
late LoginController controller;
@override
@ -28,115 +20,71 @@ class _LoginScreenState extends State<LoginScreen> with UIMixin{
@override
Widget build(BuildContext context) {
return AuthLayout(
child: GetBuilder(
init: controller,
tag: 'login_controller',
builder: (controller) {
return Form(
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: controller.basicValidator.formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
MyText.titleLarge("Sign in with email", fontWeight: 600),
MySpacing.height(12),
MyText.bodyMedium("Make a new doc to bring your words, data and terms together. For free", fontWeight: 600, xMuted: true),
MySpacing.height(12),
TextFormField(
validator: controller.basicValidator.getValidation('email'),
controller: controller.basicValidator.getController('email'),
keyboardType: TextInputType.emailAddress,
style: MyTextStyle.labelMedium(),
decoration: InputDecoration(
labelText: "Email Address",
labelStyle: MyTextStyle.bodySmall(xMuted: true),
border: OutlineInputBorder(borderSide: BorderSide.none),
filled: true,
fillColor: contentTheme.secondary.withAlpha(36),
prefixIcon: const Icon(LucideIcons.mail, size: 16),
contentPadding: MySpacing.all(14),
isDense: true,
isCollapsed: true,
floatingLabelBehavior: FloatingLabelBehavior.never),
Text(
'Welcome Back!',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
MySpacing.height(20),
SizedBox(height: 10),
Text(
'Please login to your account.',
style: TextStyle(fontSize: 16, color: Colors.grey),
),
SizedBox(height: 20),
TextFormField(
controller: controller.basicValidator.getController('username'),
decoration: InputDecoration(
labelText: 'Username',
border: OutlineInputBorder(),
),
),
SizedBox(height: 10),
TextFormField(
validator: controller.basicValidator.getValidation('password'),
controller: controller.basicValidator.getController('password'),
keyboardType: TextInputType.visiblePassword,
obscureText: !controller.showPassword,
style: MyTextStyle.labelMedium(),
decoration: InputDecoration(
labelText: "Password",
labelStyle: MyTextStyle.bodySmall(xMuted: true),
border: OutlineInputBorder(borderSide: BorderSide.none),
filled: true,
fillColor: contentTheme.secondary.withAlpha(36),
prefixIcon: const Icon(LucideIcons.mail, size: 16),
contentPadding: MySpacing.all(16),
isCollapsed: true,
isDense: true,
floatingLabelBehavior: FloatingLabelBehavior.never,
suffixIcon: InkWell(
onTap: controller.onChangeShowPassword,
child: Icon(controller.showPassword ? LucideIcons.eye : LucideIcons.eye_off, size: 16),
)),
labelText: 'Password',
border: OutlineInputBorder(),
),
obscureText: true,
),
SizedBox(height: 20),
ElevatedButton(
onPressed: controller.onLogin,
child: Text('Login'),
style: ElevatedButton.styleFrom(
minimumSize: Size(double.infinity, 50),
),
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.center,
children: [
InkWell(
onTap: () => controller.onChangeCheckBox(!controller.isChecked),
child: Row(
children: [
Checkbox(
onChanged: controller.onChangeCheckBox,
value: controller.isChecked,
fillColor: WidgetStatePropertyAll(Colors.white),
activeColor: theme.colorScheme.primary,
checkColor: contentTheme.primary,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: getCompactDensity,
),
MySpacing.width(8),
MyText.bodySmall("Remember Me"),
],
),
),
MyButton.text(
onPressed: controller.goToForgotPassword,
elevation: 0,
padding: MySpacing.xy(8, 0),
splashColor: contentTheme.secondary.withAlpha(36),
child: MyText.bodySmall('Forgot password?', color: contentTheme.secondary),
Text('Dont have an account?'),
TextButton(
onPressed: () {
Get.toNamed('/auth/register_account'); // Navigate to the register screen
},
child: Text('Sign Up'),
),
],
),
MySpacing.height(28),
Center(
child: MyButton.rounded(
onPressed: controller.onLogin,
elevation: 0,
padding: MySpacing.xy(20, 16),
backgroundColor: contentTheme.primary,
child: MyText.labelMedium('Login', color: contentTheme.onPrimary),
),
),
Center(
child: MyButton.text(
onPressed: controller.gotoRegister,
elevation: 0,
padding: MySpacing.x(16),
splashColor: contentTheme.secondary.withValues(alpha:0.1),
child: MyText.bodySmall('I haven\'t account'),
),
TextButton(
onPressed: () {
Get.toNamed('/auth/forgot_password'); // Navigate to the forgot password screen
},
child: Text('Forgot Password?'),
),
],
),
);
},),
),
),
);
}
}

View File

@ -366,7 +366,7 @@ packages:
source: hosted
version: "0.15.5"
http:
dependency: transitive
dependency: "direct main"
description:
name: http
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010

View File

@ -58,7 +58,7 @@ dependencies:
appflowy_board: ^0.1.2
syncfusion_flutter_calendar: ^28.2.6
syncfusion_flutter_maps: ^28.1.33
http: ^1.2.2
dev_dependencies:
flutter_test:
sdk: flutter

Binary file not shown.

Before

Width:  |  Height:  |  Size: 917 B

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -29,7 +29,7 @@
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>maxdash</title>
<title>Marco PMS</title>
<link rel="manifest" href="manifest.json">
</head>
<head>

View File

@ -1,6 +1,6 @@
{
"name": "maxdash",
"short_name": "maxdash",
"name": "Marco PMS",
"short_name": "Marco",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",