diff --git a/Mobile/lib/base/sidemenu.dart b/Mobile/lib/base/sidemenu.dart new file mode 100644 index 0000000..3683d8f --- /dev/null +++ b/Mobile/lib/base/sidemenu.dart @@ -0,0 +1,104 @@ +import 'package:flutter/material.dart'; + +class SideMenu extends StatefulWidget { + final Widget body; + + const SideMenu({Key? key, required this.body}) : super(key: key); + + @override + State createState() => _SideMenuState(); +} + +class _SideMenuState extends State { + int _selectedIndex = 0; + + void _onItemTapped(int index) { + setState(() { + _selectedIndex = index; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + title: const Text('SkanTavels'), + ), + drawer: Drawer( + child: ListView( + padding: EdgeInsets.zero, + children: [ + const DrawerHeader( + decoration: BoxDecoration( + color: Colors.blue, + ), + child: Text('Drawer Header'), + ), + ListTile( + title: const Text('Home'), + leading: const Icon(Icons.home), + selected: _selectedIndex == 0, + onTap: () { + // Update the state of the app + _onItemTapped(0); + // Then close the drawer + Navigator.pushReplacementNamed(context, '/home'); + }, + ), + ListTile( + title: const Text('Favourites'), + leading: const Icon(Icons.star), + selected: _selectedIndex == 1, + onTap: () { + // Update the state of the app + _onItemTapped(1); + // Then close the drawer + Navigator.pop(context); + }, + ), + ListTile( + title: const Text('Profile'), + leading: const Icon(Icons.person), + selected: _selectedIndex == 2, + onTap: () { + // Update the state of the app + _onItemTapped(2); + // Then close the drawer + Navigator.pushReplacementNamed(context, '/profile'); + }, + ), + const Divider( + color: Colors.grey, + 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.pushReplacementNamed(context, '/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.pushReplacementNamed(context, '/login'); + }, + ), + ], + ), + ), + body: widget.body, + ); + } +} diff --git a/Mobile/lib/login.dart b/Mobile/lib/login.dart index 2f89d84..fa40de9 100644 --- a/Mobile/lib/login.dart +++ b/Mobile/lib/login.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:mobile/base/sidemenu.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'register.dart'; import 'api.dart' as api; @@ -17,7 +18,8 @@ class _LoginPageState extends State { final passwordInput = TextEditingController(); Future _login() async { - final token = await api.request(context, api.ApiService.auth, 'POST', '/api/Users/login', { + final token = await api + .request(context, api.ApiService.auth, 'POST', '/api/Users/login', { 'email': emailInput.text, 'password': passwordInput.text, }); @@ -28,40 +30,47 @@ class _LoginPageState extends State { prefs.setString('token', token); if (mounted) { - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Successfully logged in'))); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Successfully logged in'))); Navigator.pop(context); } } @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - title: Text(widget.title), - ), - body: Center( - child: Container( - constraints: const BoxConstraints(minWidth: 100, maxWidth: 400), - child: Column( - children: [ + return SideMenu( + body: Scaffold( + body: Center( + child: Container( + constraints: + const BoxConstraints(minWidth: 100, maxWidth: 400), + child: Column(children: [ const SizedBox(height: 80), const Text('Email'), TextField(controller: emailInput), const SizedBox(height: 30), const Text('Password'), - TextField(controller: passwordInput, obscureText: true, enableSuggestions: false, autocorrect: false), + TextField( + controller: passwordInput, + obscureText: true, + enableSuggestions: false, + autocorrect: false), const SizedBox(height: 30), ElevatedButton(onPressed: _login, child: const Text('Login')), const SizedBox(height: 10), TextButton( child: const Text('Register account'), - onPressed: () => Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const RegisterPage(title: 'Register'))), - ) - ] - ) - ) - ) + onPressed: () => Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const RegisterPage(title: 'Register')))), + ElevatedButton(onPressed: _login, child: const Text('Log ind')), + const SizedBox(height: 10), + TextButton( + child: const Text('Registrer konto'), + onPressed: () => Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const RegisterPage(title: 'Registrer'))), + ), + ]), + ), + ), + ), ); } diff --git a/Mobile/lib/main.dart b/Mobile/lib/main.dart index 0956c25..bc8ef5c 100644 --- a/Mobile/lib/main.dart +++ b/Mobile/lib/main.dart @@ -5,6 +5,9 @@ import 'package:mobile/register.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'login.dart'; import 'api.dart' as api; +import 'base/sidemenu.dart'; +import "login.dart"; +import 'profile.dart'; void main() { runApp(const MyApp()); @@ -21,6 +24,15 @@ class MyApp extends StatelessWidget { useMaterial3: true, ), home: const MyHomePage(title: 'SkanTravels'), + initialRoute: '/', + routes: { + '/home': (context) => const MyHomePage( + title: 'SkasdanTravels', + ), + '/profile': (context) => const ProfilePage(), + '/login': (context) => const LoginPage(title: 'SkanTravels'), + '/register': (context) => const RegisterPage(title: 'SkanTravels'), + }, ); } } @@ -77,21 +89,32 @@ class _MyHomePageState extends State { @override Widget build(BuildContext context) { - return Scaffold( - key: _scaffoldKey, - appBar: AppBar( - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - title: Text(widget.title), - ), - drawer: navigationMenu, + return SideMenu( + body: Scaffold( + key: _scaffoldKey, + appBar: AppBar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + title: Text(widget.title), + ), + drawer: navigationMenu, body: FlutterMap( options: const MapOptions( initialCenter: LatLng(55.9397, 9.5156), initialZoom: 7.0), children: [ - TileLayer( - urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', - userAgentPackageName: 'dev.fleaflet.flutter_map.example', - ) + openStreetMapTileLayer, + const MarkerLayer(markers: [ + Marker( + point: LatLng(56.465511, 9.411366), + width: 60, + height: 100, + alignment: Alignment.center, + child: Icon( + Icons.location_pin, + size: 60, + color: Colors.purple, + ), + ), + ]), ], ), floatingActionButton: Row( @@ -107,93 +130,99 @@ class _MyHomePageState extends State { ), ], ), + ), ); } Drawer get navigationMenu => Drawer( - child: ListView( - padding: EdgeInsets.zero, - children: [ - const DrawerHeader( - decoration: BoxDecoration( - color: Colors.blue, - ), - child: Text('Drawer Header'), - ), + child: ListView( + padding: EdgeInsets.zero, + children: [ + const DrawerHeader( + decoration: BoxDecoration( + color: Colors.blue, + ), + child: Text('Drawer Header'), + ), + ListTile( + title: const Text('Home'), + leading: const Icon(Icons.home), + selected: _selectedIndex == 0, + onTap: () { + // Update the state of the app + _onItemTapped(0); + // Then close the drawer + Navigator.pop(context); + }, + ), + ListTile( + title: const Text('Favourites'), + leading: const Icon(Icons.star), + selected: _selectedIndex == 1, + onTap: () { + // Update the state of the app + _onItemTapped(1); + // Then close the drawer + Navigator.pop(context); + }, + ), + ListTile( + title: const Text('Profile'), + leading: const Icon(Icons.person), + selected: _selectedIndex == 2, + onTap: () { + // Update the state of the app + _onItemTapped(2); + // Then close the drawer + Navigator.pop(context); + }, + ), + const Divider( + color: Colors.grey, + thickness: 2, + indent: 40, + ), + ...( + _isLoggedIn ? [ ListTile( - title: const Text('Home'), - leading: const Icon(Icons.home), - selected: _selectedIndex == 0, + 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(0); + _onItemTapped(3); // Then close the drawer - Navigator.pop(context); + Navigator.push(context, MaterialPageRoute(builder: (context) => const RegisterPage(title: 'Register'))) + .then(_postNavigationCallback); }, ), ListTile( - title: const Text('Favourites'), - leading: const Icon(Icons.star), - selected: _selectedIndex == 1, + title: const Text('Login'), + leading: const Icon(Icons.login), + selected: _selectedIndex == 4, onTap: () { // Update the state of the app - _onItemTapped(1); + _onItemTapped(4); // Then close the drawer - Navigator.pop(context); + Navigator.push(context, MaterialPageRoute(builder: (context) => const LoginPage(title: 'Login'))) + .then(_postNavigationCallback); }, - ), - ListTile( - title: const Text('Profile'), - leading: const Icon(Icons.person), - selected: _selectedIndex == 2, - onTap: () { - // Update the state of the app - _onItemTapped(2); - // Then close the drawer - Navigator.pop(context); - }, - ), - const Divider( - color: Colors.grey, - thickness: 2, - indent: 40, - ), - ...( - _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); - }, - ) - ] ) ] - ), - ); + ) + ], + ), + ); + + TileLayer get openStreetMapTileLayer => TileLayer( + urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', + userAgentPackageName: 'dev.fleaflet.flutter_map.example', + ); } diff --git a/Mobile/lib/profile.dart b/Mobile/lib/profile.dart index 8b13789..7b8fcae 100644 --- a/Mobile/lib/profile.dart +++ b/Mobile/lib/profile.dart @@ -1 +1,15 @@ +import 'package:flutter/material.dart'; +import 'base/sidemenu.dart'; // Import the base layout widget +class ProfilePage extends StatelessWidget { + const ProfilePage({super.key}); + + @override + Widget build(BuildContext context) { + return const SideMenu( + body: Center( + child: Text('This is Page 1'), + ), + ); + } +} diff --git a/Mobile/lib/register.dart b/Mobile/lib/register.dart index 4caee5b..9b04366 100644 --- a/Mobile/lib/register.dart +++ b/Mobile/lib/register.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:mobile/base/sidemenu.dart'; import 'login.dart'; import 'api.dart' as api; @@ -17,7 +18,8 @@ class _RegisterPageState extends State { final passwordInput = TextEditingController(); Future _register() async { - final result = await api.request(context, api.ApiService.auth, 'POST', '/api/Users', { + final result = + await api.request(context, api.ApiService.auth, 'POST', '/api/Users', { 'username': usernameInput.text, 'email': emailInput.text, 'password': passwordInput.text, @@ -26,23 +28,24 @@ class _RegisterPageState extends State { if (result == null) return; if (mounted) { - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Successfully registered, please login'))); - Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const LoginPage(title: 'Log ind'))); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text('Successfully registered, please login'))); + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => const LoginPage(title: 'Log ind'))); } } @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - title: Text(widget.title), - ), - body: Center( - child: Container( - constraints: const BoxConstraints(minWidth: 100, maxWidth: 400), - child: Column( - children: [ + return SideMenu( + body: Scaffold( + body: Center( + child: Container( + constraints: + const BoxConstraints(minWidth: 100, maxWidth: 400), + child: Column(children: [ const SizedBox(height: 80), const Text('Username'), TextField(controller: usernameInput), @@ -51,7 +54,11 @@ class _RegisterPageState extends State { TextField(controller: emailInput), const SizedBox(height: 30), const Text('Password'), - TextField(controller: passwordInput, obscureText: true, enableSuggestions: false, autocorrect: false), + TextField( + controller: passwordInput, + obscureText: true, + enableSuggestions: false, + autocorrect: false), const SizedBox(height: 30), ElevatedButton(onPressed: _register, child: const Text('Register')), const SizedBox(height: 10), @@ -59,10 +66,10 @@ class _RegisterPageState extends State { child: const Text('Login'), onPressed: () => Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const LoginPage(title: 'Login'))) ), - ] - ) - ) - ) + ]), + ), + ), + ), ); }