Skip to content

Commit

Permalink
Merge pull request #27 from Hypertext-Assassin-RSS/android
Browse files Browse the repository at this point in the history
update login screen
  • Loading branch information
Hypertext-Assassin-RSS authored Sep 9, 2024
2 parents 73fb53b + 02e51c9 commit 91789b9
Show file tree
Hide file tree
Showing 3 changed files with 232 additions and 31 deletions.
198 changes: 167 additions & 31 deletions lib/src/screens/login.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// ignore_for_file: library_private_types_in_public_api

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:mindful_reader/src/widgets/bottomnav.dart';

// Import the HomeScreen
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';

class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
Expand All @@ -16,18 +17,142 @@ class LoginScreen extends StatefulWidget {

class _LoginScreenState extends State<LoginScreen> {
bool isLogin = true;
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final TextEditingController _confirmPasswordController = TextEditingController();

void toggleForm() {
setState(() {
isLogin = !isLogin;
});
}

void _login() {
// Navigate to the HomeScreen when the login button is pressed
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => const BottomNavBar()),
);
// Perform Login
Future<void> _login() async {
final username = _usernameController.text;
final password = _passwordController.text;
await dotenv.load(fileName: "assets/config/.env");

try {
final response = await Dio().post('${dotenv.env['API_BASE_URL']}/login',
data: {
'username': username,
'password': password,
},
);

if (response.statusCode == 200) {
final data = response.data;
final token = data['token'];

// Save the JWT token in shared preferences
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('token', token);

ScaffoldMessenger.of(context).showMaterialBanner(
MaterialBanner(
padding: const EdgeInsets.all(20),
content: const Text('Login successful! Redirecting...'),
leading: const Icon(Icons.check_circle_outline, color: Colors.green),
backgroundColor: Colors.green[300],
actions: <Widget>[
TextButton(
onPressed: () => ScaffoldMessenger.of(context).hideCurrentMaterialBanner(),
child: const Text('DISMISS'),
),
],
),
);

Future.delayed(const Duration(seconds: 2), () {
ScaffoldMessenger.of(context).hideCurrentMaterialBanner();
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => const BottomNavBar()),
);
});
} else {
// Handle errors
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Invalid login credentials')),
);
}
} catch (error) {
debugPrint('error: $error');
// Handle server errors
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Error connecting to the server')),
);
}
}

// Perform Signup
Future<void> _signup() async {
final username = _usernameController.text;
final email = _emailController.text;
final password = _passwordController.text;
final confirmPassword = _confirmPasswordController.text;
await dotenv.load(fileName: "assets/config/.env");

if (password != confirmPassword) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Passwords do not match')),
);
return;
}

try {
final response = await http.post(
Uri.parse('${dotenv.env['API_BASE_URL']}/signup'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'username': username,
'email': email,
'password': password,
}),
);

if (response.statusCode == 201) {
final data = jsonDecode(response.body);
final token = data['token'];

// Save the JWT token in shared preferences
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('token', token);

ScaffoldMessenger.of(context).showMaterialBanner(
MaterialBanner(
padding: const EdgeInsets.all(20),
content: const Text('Signup successful! Redirecting...'),
leading: const Icon(Icons.check_circle_outline, color: Colors.green),
backgroundColor: Colors.green[300],
actions: <Widget>[
TextButton(
onPressed: () => ScaffoldMessenger.of(context).hideCurrentMaterialBanner(),
child: const Text('DISMISS'),
),
],
),
);

Future.delayed(const Duration(seconds: 2), () {
ScaffoldMessenger.of(context).hideCurrentMaterialBanner();
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => const BottomNavBar()),
);
});
} else {
// Handle errors
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Signup failed')),
);
}
} catch (error) {
// Handle server errors
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Error connecting to the server')),
);
}
}

@override
Expand All @@ -49,43 +174,53 @@ class _LoginScreenState extends State<LoginScreen> {
),
),
)
.animate()
.slideY(begin: -0.3, duration: 600.ms)
.fadeIn(duration: 600.ms)
.then(delay: 200.ms),
.animate()
.slideY(begin: -0.3, duration: 600.ms)
.fadeIn(duration: 600.ms)
.then(delay: 200.ms),

const SizedBox(height: 40),

Padding(
padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: _buildTextField('Email', false)
.animate()
.fadeIn(duration: 800.ms),
child: _buildTextField('Username', false, _usernameController)
.animate()
.fadeIn(duration: 700.ms),
),

if (!isLogin) ...[
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: _buildTextField('Email', false, _emailController)
.animate()
.fadeIn(duration: 800.ms),
),
],

const SizedBox(height: 20),

Padding(
padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: _buildTextField('Password', true)
.animate()
.fadeIn(duration: 900.ms),
child: _buildTextField('Password', true, _passwordController)
.animate()
.fadeIn(duration: 900.ms),
),

if (!isLogin) ...[
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: _buildTextField('Confirm Password', true)
.animate()
.fadeIn(duration: 1000.ms),
child: _buildTextField('Confirm Password', true, _confirmPasswordController)
.animate()
.fadeIn(duration: 1000.ms),
),
],

const SizedBox(height: 40),

ElevatedButton(
onPressed: _login, // Call _login when pressed
onPressed: isLogin ? _login : _signup,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 60, vertical: 16),
backgroundColor: Colors.white,
Expand All @@ -102,9 +237,9 @@ class _LoginScreenState extends State<LoginScreen> {
),
),
)
.animate()
.scale(duration: 500.ms, curve: Curves.easeInOut)
.fadeIn(duration: 500.ms),
.animate()
.scale(duration: 500.ms, curve: Curves.easeInOut)
.fadeIn(duration: 500.ms),

const SizedBox(height: 20),

Expand All @@ -121,18 +256,19 @@ class _LoginScreenState extends State<LoginScreen> {
),
),
)
.animate()
.slideY(begin: 0.3, duration: 600.ms)
.fadeIn(duration: 600.ms),
.animate()
.slideY(begin: 0.3, duration: 600.ms)
.fadeIn(duration: 600.ms),
],
),
),
),
);
}

Widget _buildTextField(String hint, bool obscureText) {
Widget _buildTextField(String hint, bool obscureText, TextEditingController controller) {
return TextField(
controller: controller,
obscureText: obscureText,
style: const TextStyle(color: Colors.white),
decoration: InputDecoration(
Expand Down
64 changes: 64 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
file:
dependency: transitive
description:
name: file
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
flutter:
dependency: "direct main"
description: flutter
Expand Down Expand Up @@ -381,6 +389,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.8"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f
url: "https://pub.dev"
source: hosted
version: "2.5.2"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e
url: "https://pub.dev"
source: hosted
version: "2.4.2"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sky_engine:
dependency: transitive
description: flutter
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dependencies:
flutter_pdfview: ^1.3.2
path_provider: ^2.1.4
flutter_dotenv: ^5.1.0
shared_preferences: ^2.3.2

dev_dependencies:
flutter_launcher_icons: "^0.13.1"
Expand Down

0 comments on commit 91789b9

Please sign in to comment.