Implement login and save token

This commit is contained in:
Reimar 2024-08-21 10:03:03 +02:00
parent c9769fcada
commit debbddd05b
Signed by: Reimar
GPG Key ID: 93549FA07F0AE268
10 changed files with 225 additions and 7 deletions

View File

@ -1,4 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:label="mobile"
android:name="${applicationName}"

53
Mobile/lib/api.dart Normal file
View File

@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
enum ApiService {
auth,
app,
}
Future<String?> request(BuildContext context, ApiService service, String method, String path, Object? body) async {
final messenger = ScaffoldMessenger.of(context);
final host = switch (service) {
ApiService.auth => const String.fromEnvironment('AUTH_SERVICE_HOST'),
ApiService.app => const String.fromEnvironment('APP_SERVICE_HOST'),
};
final http.Response response;
try {
if (method == 'GET') {
response = await http.get(Uri.parse(host + path));
} else {
final function = switch (method) {
'POST' => http.post,
'PUT' => http.put,
'DELETE' => http.delete,
_ => throw const FormatException('Invalid method'),
};
response = await function(
Uri.parse(host + path),
headers: {'Content-Type': 'application/json'},
body: body != null ? jsonEncode(body) : null,
);
}
} catch (_) {
messenger.showSnackBar(const SnackBar(content: Text('Unable to connect to server')));
return null;
}
if (response.statusCode < 200 || response.statusCode >= 300) {
try {
final json = jsonDecode(response.body);
messenger.showSnackBar(SnackBar(content: Text(json['message'])));
} catch (_) {
messenger.showSnackBar(SnackBar(content: Text('Something went wrong (HTTP ${response.statusCode})')));
}
return null;
}
return response.body;
}

View File

@ -1,5 +1,9 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'register.dart';
import 'api.dart' as api;
class LoginPage extends StatefulWidget {
const LoginPage({super.key, required this.title});
@ -11,6 +15,26 @@ class LoginPage extends StatefulWidget {
}
class _LoginPageState extends State<LoginPage> {
final emailInput = TextEditingController();
final passwordInput = TextEditingController();
Future<void> _login() async {
final token = await api.request(context, api.ApiService.auth, 'POST', '/api/Users/login', {
'email': emailInput.text,
'password': passwordInput.text,
});
if (token == null) return;
final prefs = await SharedPreferences.getInstance();
prefs.setString('token', token);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Successfully logged in')));
Navigator.pop(context);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
@ -25,16 +49,16 @@ class _LoginPageState extends State<LoginPage> {
children: [
const SizedBox(height: 80),
const Text('Email'),
const TextField(),
TextField(controller: emailInput),
const SizedBox(height: 30),
const Text('Password'),
const TextField(obscureText: true, enableSuggestions: false, autocorrect: false),
TextField(controller: passwordInput, obscureText: true, enableSuggestions: false, autocorrect: false),
const SizedBox(height: 30),
ElevatedButton(child: const Text('Log ind'), onPressed: () => Navigator.pop(context)),
ElevatedButton(onPressed: _login, child: const Text('Log ind')),
const SizedBox(height: 10),
TextButton(
child: const Text('Registrer konto'),
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) => const RegisterPage(title: 'Registrer'))),
onPressed: () => Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const RegisterPage(title: 'Registrer'))),
)
]
)
@ -42,4 +66,11 @@ class _LoginPageState extends State<LoginPage> {
)
);
}
@override
void dispose() {
emailInput.dispose();
passwordInput.dispose();
super.dispose();
}
}

View File

@ -37,7 +37,7 @@ class _RegisterPageState extends State<RegisterPage> {
const SizedBox(height: 10),
TextButton(
child: const Text('Log ind'),
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) => const LoginPage(title: 'Log ind')))
onPressed: () => Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const LoginPage(title: 'Log ind')))
),
]
)

View File

@ -5,6 +5,8 @@
import FlutterMacOS
import Foundation
import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
}

View File

@ -8,5 +8,7 @@
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>

View File

@ -4,5 +4,7 @@
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>

View File

@ -65,6 +65,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.1"
ffi:
dependency: transitive
description:
name: ffi
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
url: "https://pub.dev"
source: hosted
version: "2.1.3"
file:
dependency: transitive
description:
name: file
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
flutter:
dependency: "direct main"
description: flutter
@ -91,6 +107,11 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
http:
dependency: "direct main"
description:
@ -211,6 +232,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.0"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
url: "https://pub.dev"
source: hosted
version: "2.3.0"
platform:
dependency: transitive
description:
name: platform
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
url: "https://pub.dev"
source: hosted
version: "3.1.5"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
version: "2.1.8"
polylabel:
dependency: transitive
description:
@ -227,6 +288,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.0"
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: a7e8467e9181cef109f601e3f65765685786c1a738a83d7fbbde377589c0d974
url: "https://pub.dev"
source: hosted
version: "2.3.1"
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
@ -328,6 +445,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.0"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
url: "https://pub.dev"
source: hosted
version: "1.0.4"
sdks:
dart: ">=3.3.4 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
dart: ">=3.4.0 <4.0.0"
flutter: ">=3.22.0"

View File

@ -37,6 +37,7 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.6
shared_preferences: ^2.3.2
dev_dependencies:
flutter_test: