From 8e42e54c98d05539cfb1932e8f758231b8372ada Mon Sep 17 00:00:00 2001 From: Reimar Date: Wed, 21 Aug 2024 12:49:38 +0200 Subject: [PATCH] Add logout button --- Mobile/lib/api.dart | 30 ++++++++++- Mobile/lib/login.dart | 6 +-- Mobile/lib/main.dart | 113 ++++++++++++++++++++++++++------------- Mobile/lib/register.dart | 8 +-- 4 files changed, 112 insertions(+), 45 deletions(-) diff --git a/Mobile/lib/api.dart b/Mobile/lib/api.dart index c2e8cbd..4a4276c 100644 --- a/Mobile/lib/api.dart +++ b/Mobile/lib/api.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; @@ -50,4 +51,31 @@ Future request(BuildContext context, ApiService service, String method, } return response.body; -} \ No newline at end of file +} + +Future isLoggedIn(BuildContext context) async { + final messenger = ScaffoldMessenger.of(context); + final prefs = await SharedPreferences.getInstance(); + + final token = prefs.getString('token'); + if (token == null) return false; + + try { + String base64 = token.split('.')[1]; + base64 += List.filled(4 - base64.length % 4, '=').join(); + + final payload = jsonDecode(String.fromCharCodes(base64Decode(base64))); + + if (payload['exp'] < DateTime.now().millisecondsSinceEpoch / 1000) { + messenger.showSnackBar(const SnackBar(content: Text('Token expired, please sign in again'))); + prefs.remove('token'); + return false; + } + } catch (e) { + messenger.showSnackBar(const SnackBar(content: Text('Invalid token, please sign in again'))); + prefs.remove('token'); + return false; + } + + return true; +} diff --git a/Mobile/lib/login.dart b/Mobile/lib/login.dart index c5181bb..2f89d84 100644 --- a/Mobile/lib/login.dart +++ b/Mobile/lib/login.dart @@ -52,11 +52,11 @@ class _LoginPageState extends State { const Text('Password'), TextField(controller: passwordInput, obscureText: true, enableSuggestions: false, autocorrect: false), const SizedBox(height: 30), - ElevatedButton(onPressed: _login, child: const Text('Log ind')), + ElevatedButton(onPressed: _login, child: const Text('Login')), const SizedBox(height: 10), TextButton( - child: const Text('Registrer konto'), - onPressed: () => Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const RegisterPage(title: 'Registrer'))), + child: const Text('Register account'), + onPressed: () => Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const RegisterPage(title: 'Register'))), ) ] ) diff --git a/Mobile/lib/main.dart b/Mobile/lib/main.dart index c7524f0..0956c25 100644 --- a/Mobile/lib/main.dart +++ b/Mobile/lib/main.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; -import "package:latlong2/latlong.dart"; +import 'package:latlong2/latlong.dart'; import 'package:mobile/register.dart'; -import "login.dart"; +import 'package:shared_preferences/shared_preferences.dart'; +import 'login.dart'; +import 'api.dart' as api; void main() { runApp(const MyApp()); @@ -33,7 +35,9 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { + final GlobalKey _scaffoldKey = GlobalKey(); int _selectedIndex = 0; + bool _isLoggedIn = false; void _onItemTapped(int index) { setState(() { @@ -41,9 +45,40 @@ class _MyHomePageState extends State { }); } + void _logout() async { + final prefs = await SharedPreferences.getInstance(); + + prefs.remove('token'); + setState(() => _isLoggedIn = false); + + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Successfully logged out'))); + Navigator.pop(context); + } + } + + Future _postNavigationCallback(dynamic _) async { + final isLoggedIn = await api.isLoggedIn(context); + setState(() => _isLoggedIn = isLoggedIn); + + // Close sidebar + if (mounted && _scaffoldKey.currentState?.isDrawerOpen == true) { + Navigator.pop(context); + } + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + api.isLoggedIn(context) + .then((value) => setState(() => _isLoggedIn = value)); + } + @override Widget build(BuildContext context) { return Scaffold( + key: _scaffoldKey, appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), @@ -61,13 +96,11 @@ class _MyHomePageState extends State { ), floatingActionButton: Row( mainAxisAlignment: MainAxisAlignment.end, - children: [ + children: _isLoggedIn ? [] : [ FloatingActionButton( onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const LoginPage(title: "Login"))); + Navigator.push(context, MaterialPageRoute(builder: (context) => const LoginPage(title: "Login"))) + .then(_postNavigationCallback); }, tooltip: 'Login', child: const Icon(Icons.login), @@ -125,36 +158,42 @@ class _MyHomePageState extends State { thickness: 2, indent: 40, ), - ListTile( - title: const Text('Register'), - leading: const Icon(Icons.add_box_outlined), - selected: _selectedIndex == 3, - onTap: () { - // Update the state of the app - _onItemTapped(3); - // Then close the drawer - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - const RegisterPage(title: "Register"))); - }, - ), - ListTile( - title: const Text('Login'), - leading: const Icon(Icons.login), - selected: _selectedIndex == 4, - onTap: () { - // Update the state of the app - _onItemTapped(4); - // Then close the drawer - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const LoginPage(title: "Login"))); - }, - ), - ], + ...( + _isLoggedIn ? [ + ListTile( + title: const Text('Log out'), + leading: const Icon(Icons.logout), + selected: false, + onTap: _logout, + ) + ] : [ + ListTile( + title: const Text('Register'), + leading: const Icon(Icons.add_box_outlined), + selected: _selectedIndex == 3, + onTap: () { + // Update the state of the app + _onItemTapped(3); + // Then close the drawer + Navigator.push(context, MaterialPageRoute(builder: (context) => const RegisterPage(title: 'Register'))) + .then(_postNavigationCallback); + }, + ), + ListTile( + title: const Text('Login'), + leading: const Icon(Icons.login), + selected: _selectedIndex == 4, + onTap: () { + // Update the state of the app + _onItemTapped(4); + // Then close the drawer + Navigator.push(context, MaterialPageRoute(builder: (context) => const LoginPage(title: 'Login'))) + .then(_postNavigationCallback); + }, + ) + ] + ) + ] ), ); } diff --git a/Mobile/lib/register.dart b/Mobile/lib/register.dart index 6c72e4b..4caee5b 100644 --- a/Mobile/lib/register.dart +++ b/Mobile/lib/register.dart @@ -44,7 +44,7 @@ class _RegisterPageState extends State { child: Column( children: [ const SizedBox(height: 80), - const Text('Brugernavn'), + const Text('Username'), TextField(controller: usernameInput), const SizedBox(height: 30), const Text('Email'), @@ -53,11 +53,11 @@ class _RegisterPageState extends State { const Text('Password'), TextField(controller: passwordInput, obscureText: true, enableSuggestions: false, autocorrect: false), const SizedBox(height: 30), - ElevatedButton(onPressed: _register, child: const Text('Registrer')), + ElevatedButton(onPressed: _register, child: const Text('Register')), const SizedBox(height: 10), TextButton( - child: const Text('Log ind'), - onPressed: () => Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const LoginPage(title: 'Log ind'))) + child: const Text('Login'), + onPressed: () => Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const LoginPage(title: 'Login'))) ), ] )