feat: Implement home screen with navigation and update login flow

This commit is contained in:
2025-08-20 23:08:08 +02:00
parent 592e79a375
commit 7792023b22
7 changed files with 71 additions and 19 deletions

View File

@@ -105,7 +105,6 @@
45421BF3EE052CE5236E185A /* Pods-RunnerTests.release.xcconfig */, 45421BF3EE052CE5236E185A /* Pods-RunnerTests.release.xcconfig */,
79C02E3489ECFAEE3BBD5226 /* Pods-RunnerTests.profile.xcconfig */, 79C02E3489ECFAEE3BBD5226 /* Pods-RunnerTests.profile.xcconfig */,
); );
name = Pods;
path = Pods; path = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@@ -474,11 +473,14 @@
DEVELOPMENT_TEAM = R9PZGUCNJ3; DEVELOPMENT_TEAM = R9PZGUCNJ3;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.example.mcbeno; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = lol.devbeni.mcbeno;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -657,11 +659,14 @@
DEVELOPMENT_TEAM = R9PZGUCNJ3; DEVELOPMENT_TEAM = R9PZGUCNJ3;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.example.mcbeno; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = lol.devbeni.mcbeno;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -680,11 +685,14 @@
DEVELOPMENT_TEAM = R9PZGUCNJ3; DEVELOPMENT_TEAM = R9PZGUCNJ3;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.example.mcbeno; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = lol.devbeni.mcbeno;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;

View File

@@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
@@ -24,6 +26,8 @@
<string>$(FLUTTER_BUILD_NUMBER)</string> <string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
@@ -41,9 +45,5 @@
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict> </dict>
</plist> </plist>

View File

@@ -5,6 +5,7 @@ import 'screens/login_screen.dart';
import 'screens/profile_screen.dart'; import 'screens/profile_screen.dart';
import 'screens/schedule_screen.dart'; import 'screens/schedule_screen.dart';
import 'screens/splash_screen.dart'; import 'screens/splash_screen.dart';
import 'screens/home_screen.dart';
import 'services/auth_service.dart'; import 'services/auth_service.dart';
void main() { void main() {
@@ -26,6 +27,7 @@ class MyApp extends StatelessWidget {
routes: { routes: {
'/splash': (context) => SplashScreen(), '/splash': (context) => SplashScreen(),
'/': (context) => LoginScreen(), '/': (context) => LoginScreen(),
'/home': (context) => HomeScreen(),
'/profile': (context) => ProfileScreen(), '/profile': (context) => ProfileScreen(),
'/schedule': (context) => ScheduleScreen(), '/schedule': (context) => ScheduleScreen(),
}, },

View File

@@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import 'schedule_screen.dart';
import 'profile_screen.dart';
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int _idx = 0;
final _pages = [ScheduleScreen(), ProfileScreen()];
@override
Widget build(BuildContext context) {
return Scaffold(
body: _pages[_idx],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _idx,
backgroundColor: Color(0xFF0c0a0a),
selectedItemColor: Color(0xFFA24BFA),
unselectedItemColor: Colors.white70,
items: [
BottomNavigationBarItem(icon: Icon(Icons.calendar_today), label: 'Beosztás'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profil'),
],
onTap: (i) => setState(() { _idx = i; }),
),
);
}
}

View File

@@ -44,7 +44,7 @@ class _LoginScreenState extends State<LoginScreen> {
setState(() { _loading = true; }); setState(() { _loading = true; });
final ok = await auth.login(username, password); final ok = await auth.login(username, password);
setState(() { _loading = false; }); setState(() { _loading = false; });
if (ok) Navigator.pushReplacementNamed(context, '/profile'); if (ok) Navigator.pushReplacementNamed(context, '/home');
else ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Bejelentkezés sikertelen'))); else ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Bejelentkezés sikertelen')));
}, },
child: _loading ? SizedBox(height: 16, width: 16, child: CircularProgressIndicator(strokeWidth: 2)) : Text('Bejelentkezés'), child: _loading ? SizedBox(height: 16, width: 16, child: CircularProgressIndicator(strokeWidth: 2)) : Text('Bejelentkezés'),

View File

@@ -13,9 +13,9 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
void initState() { void initState() {
super.initState(); super.initState();
_ctrl = AnimationController(vsync: this, duration: Duration(seconds: 3))..repeat(reverse: true); _ctrl = AnimationController(vsync: this, duration: Duration(seconds: 3))..repeat(reverse: true);
// simulate load then navigate // simulate load then navigate depending on token presence
Timer(Duration(seconds: 2), () { Timer(Duration(seconds: 1), () {
Navigator.pushReplacementNamed(context, '/'); Navigator.pushReplacementNamed(context, '/home');
}); });
} }

View File

@@ -30,13 +30,24 @@ class AuthService extends ChangeNotifier {
Future<void> loadProfile() async { Future<void> loadProfile() async {
if (token == null) return; if (token == null) return;
final res = await http.get( try {
Uri.parse('https://menuapi.devbeni.lol/api/me'), final res = await http.get(
headers: {'Authorization': 'Bearer $token'}, Uri.parse('https://menuapi.devbeni.lol/api/me'),
); headers: {'Authorization': 'Bearer $token'},
if (res.statusCode == 200) { );
profile = jsonDecode(res.body)['data']; if (res.statusCode == 200) {
notifyListeners(); final body = jsonDecode(res.body);
// Try common shapes: { data: {...} } or { user: {...} } or the whole body
if (body is Map && body['data'] is Map) profile = Map<String, dynamic>.from(body['data']);
else if (body is Map && body['user'] is Map) profile = Map<String, dynamic>.from(body['user']);
else if (body is Map && body['FullName'] != null) profile = Map<String, dynamic>.from(body);
else profile = {'raw': body};
notifyListeners();
} else {
if (kDebugMode) print('[AuthService] loadProfile failed ${res.statusCode}: ${res.body}');
}
} catch (e) {
if (kDebugMode) print('[AuthService] loadProfile error: $e');
} }
} }