From debbddd05b802fc9e45e7cc8ff68c60d638895a9 Mon Sep 17 00:00:00 2001 From: Reimar Date: Wed, 21 Aug 2024 10:03:03 +0200 Subject: [PATCH] Implement login and save token --- .../android/app/src/main/AndroidManifest.xml | 2 + Mobile/lib/api.dart | 53 +++++++ Mobile/lib/login.dart | 39 +++++- Mobile/lib/{Profile.dart => profile.dart} | 0 Mobile/lib/register.dart | 2 +- .../Flutter/GeneratedPluginRegistrant.swift | 2 + Mobile/macos/Runner/DebugProfile.entitlements | 2 + Mobile/macos/Runner/Release.entitlements | 2 + Mobile/pubspec.lock | 129 +++++++++++++++++- Mobile/pubspec.yaml | 1 + 10 files changed, 225 insertions(+), 7 deletions(-) create mode 100644 Mobile/lib/api.dart rename Mobile/lib/{Profile.dart => profile.dart} (100%) diff --git a/Mobile/android/app/src/main/AndroidManifest.xml b/Mobile/android/app/src/main/AndroidManifest.xml index de26880..df83995 100644 --- a/Mobile/android/app/src/main/AndroidManifest.xml +++ b/Mobile/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,6 @@ + + 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; +} \ No newline at end of file diff --git a/Mobile/lib/login.dart b/Mobile/lib/login.dart index b3800c2..ac48bbd 100644 --- a/Mobile/lib/login.dart +++ b/Mobile/lib/login.dart @@ -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 { + final emailInput = TextEditingController(); + final passwordInput = TextEditingController(); + + Future _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 { 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 { ) ); } + + @override + void dispose() { + emailInput.dispose(); + passwordInput.dispose(); + super.dispose(); + } } diff --git a/Mobile/lib/Profile.dart b/Mobile/lib/profile.dart similarity index 100% rename from Mobile/lib/Profile.dart rename to Mobile/lib/profile.dart diff --git a/Mobile/lib/register.dart b/Mobile/lib/register.dart index c3cba7e..2432a14 100644 --- a/Mobile/lib/register.dart +++ b/Mobile/lib/register.dart @@ -37,7 +37,7 @@ class _RegisterPageState extends State { 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'))) ), ] ) diff --git a/Mobile/macos/Flutter/GeneratedPluginRegistrant.swift b/Mobile/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..724bb2a 100644 --- a/Mobile/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/Mobile/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,8 @@ import FlutterMacOS import Foundation +import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/Mobile/macos/Runner/DebugProfile.entitlements b/Mobile/macos/Runner/DebugProfile.entitlements index dddb8a3..08c3ab1 100644 --- a/Mobile/macos/Runner/DebugProfile.entitlements +++ b/Mobile/macos/Runner/DebugProfile.entitlements @@ -8,5 +8,7 @@ com.apple.security.network.server + com.apple.security.network.client + diff --git a/Mobile/macos/Runner/Release.entitlements b/Mobile/macos/Runner/Release.entitlements index 852fa1a..ee95ab7 100644 --- a/Mobile/macos/Runner/Release.entitlements +++ b/Mobile/macos/Runner/Release.entitlements @@ -4,5 +4,7 @@ com.apple.security.app-sandbox + com.apple.security.network.client + diff --git a/Mobile/pubspec.lock b/Mobile/pubspec.lock index 4a42829..74b0042 100644 --- a/Mobile/pubspec.lock +++ b/Mobile/pubspec.lock @@ -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" diff --git a/Mobile/pubspec.yaml b/Mobile/pubspec.yaml index 78cd7d6..ab442b3 100644 --- a/Mobile/pubspec.yaml +++ b/Mobile/pubspec.yaml @@ -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: