added splash screen

This commit is contained in:
amirrezaghabeli
2025-09-22 16:04:49 +03:30
parent 197ad18618
commit f1214d8a28
14 changed files with 253 additions and 12 deletions
+1
View File
@@ -4,6 +4,7 @@ class AssetsManager {
//logo
static const logoSmall = 'assets/icons/logo_small.svg';
static const logoBig = 'assets/icons/logo_big.svg';
static const logoBigPng = 'assets/pngs/logo-big.png';
//bottom navigation
static const homeActive = 'assets/icons/home_active.svg';
+13 -1
View File
@@ -12,6 +12,7 @@ import '../../views/final_completion_of_documents/pages/final_completion_of_docu
import '../../views/forgot_password/pages/forgot_password_screen.dart';
import '../../views/notification/pages/notification_screen.dart';
import '../../views/profile/pages/profile_screen.dart';
import '../../views/splash/pages/splash_screen.dart';
import '../../views/tenders/pages/tenders_screen.dart';
import '../../views/your_tenders/pages/your_tenders_screen.dart';
import 'app_shell/app_shell_screen.dart';
@@ -31,7 +32,7 @@ final GlobalKey<NavigatorState> notificationNavigatorKey =
// Main app router
final GoRouter appRouter = GoRouter(
routes: $appRoutes,
initialLocation: '/login',
initialLocation: '/splash',
navigatorKey: rootNavigatorKey,
);
@@ -46,6 +47,17 @@ class LoginScreenRoute extends GoRouteData with _$LoginScreenRoute {
}
}
// splash route - outside the shell
@TypedGoRoute<SplashScreenRoute>(path: '/splash')
class SplashScreenRoute extends GoRouteData with _$SplashScreenRoute {
const SplashScreenRoute();
@override
Widget build(BuildContext context, GoRouterState state) {
return const SplashScreen();
}
}
// Shell route with bottom navigation using StatefulShellRoute.indexedStack
@TypedStatefulShellRoute<AppShellRouteData>(
branches: <TypedStatefulShellBranch<StatefulShellBranchData>>[
+28
View File
@@ -8,6 +8,7 @@ part of 'app_routes.dart';
List<RouteBase> get $appRoutes => [
$loginScreenRoute,
$splashScreenRoute,
$appShellRouteData,
$yourTendersRouteData,
$finalCompletionOfDocumentsRouteData,
@@ -43,6 +44,33 @@ mixin _$LoginScreenRoute on GoRouteData {
void replace(BuildContext context) => context.replace(location);
}
RouteBase get $splashScreenRoute => GoRouteData.$route(
path: '/splash',
factory: _$SplashScreenRoute._fromState,
);
mixin _$SplashScreenRoute on GoRouteData {
static SplashScreenRoute _fromState(GoRouterState state) =>
const SplashScreenRoute();
@override
String get location => GoRouteData.$location('/splash');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
RouteBase get $appShellRouteData => StatefulShellRouteData.$route(
parentNavigatorKey: AppShellRouteData.$parentNavigatorKey,
factory: $AppShellRouteDataExtension._fromState,
@@ -47,4 +47,8 @@ class AuthRepository {
}) async {
return _authService.verifyOtp(code: code, email: email);
}
Future<Result<bool>> checkIsLoggedIn() async {
return _authService.checkIsLoggedIn();
}
}
+9
View File
@@ -113,4 +113,13 @@ class AuthService {
return result;
}
Future<Result<bool>> checkIsLoggedIn() async {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('bearer');
if (token == null) {
return const Result.ok(false);
}
return const Result.ok(true);
}
}
+3 -3
View File
@@ -230,8 +230,8 @@ class $AssetsPngsGen {
AssetGenImage get forgotPassword =>
const AssetGenImage('assets/pngs/forgot_password.png');
/// File path: assets/pngs/logo.png
AssetGenImage get logo => const AssetGenImage('assets/pngs/logo.png');
/// File path: assets/pngs/logo-big.png
AssetGenImage get logoBig => const AssetGenImage('assets/pngs/logo-big.png');
/// File path: assets/pngs/web_login_image.png
AssetGenImage get webLoginImage =>
@@ -241,7 +241,7 @@ class $AssetsPngsGen {
List<AssetGenImage> get values => [
appLogo,
forgotPassword,
logo,
logoBig,
webLoginImage,
];
}
+17
View File
@@ -44,6 +44,7 @@ class AuthViewModel with ChangeNotifier {
// Auth state
bool _isLoading = false;
bool _isLoggedIn = false;
bool _isResetPasswordLoading = false;
String? _errorMessage;
String? _resetPasswordErrorMessage;
@@ -59,6 +60,7 @@ class AuthViewModel with ChangeNotifier {
bool _verifyOtpSuccess = false;
bool get isLoading => _isLoading;
bool get isLoggedIn => _isLoggedIn;
bool get isLoadingForgot => _isLoadingForgot;
bool get isLoadingOtp => _isLoadingOtp;
bool get isResetPasswordLoading => _isResetPasswordLoading;
@@ -282,6 +284,21 @@ class AuthViewModel with ChangeNotifier {
}
}
Future<void> checkIsLoggedIn() async {
final result = await _authRepository.checkIsLoggedIn();
if (result is Ok<bool>) {
if (result.value) {
_isLoggedIn = result.value;
} else {
_isLoggedIn = false;
}
} else {
_isLoggedIn = false;
}
notifyListeners();
}
void clearResetPasswordSuccess() {
_resetPasswordSuccess = false;
+49
View File
@@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tm_app/core/routes/app_routes.dart';
import 'package:tm_app/view_models/auth_view_model.dart';
import '../../../core/constants/assets.dart';
class DesktopSplashPage extends StatefulWidget {
const DesktopSplashPage({super.key});
@override
State<DesktopSplashPage> createState() => _DesktopSplashPageState();
}
class _DesktopSplashPageState extends State<DesktopSplashPage> {
late final AuthViewModel viewModel;
@override
void initState() {
super.initState();
viewModel = context.read<AuthViewModel>();
Future.delayed(const Duration(seconds: 1), () async {
await viewModel.checkIsLoggedIn();
});
viewModel.addListener(_viewModelListener);
}
@override
void dispose() {
viewModel.removeListener(_viewModelListener);
super.dispose();
}
void _viewModelListener() {
if (viewModel.isLoggedIn) {
Router.neglect(context, () => const HomeRouteData().go(context));
} else {
Router.neglect(context, () => const LoginScreenRoute().go(context));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Image.asset(AssetsManager.logoBigPng, fit: BoxFit.cover),
),
);
}
}
+52
View File
@@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tm_app/core/constants/assets.dart';
import 'package:tm_app/core/routes/app_routes.dart';
import 'package:tm_app/view_models/auth_view_model.dart';
class MobileSplashPage extends StatefulWidget {
const MobileSplashPage({super.key});
@override
State<MobileSplashPage> createState() => _MobileSplashPageState();
}
class _MobileSplashPageState extends State<MobileSplashPage> {
late final AuthViewModel viewModel;
@override
void initState() {
viewModel = context.read<AuthViewModel>();
Future.delayed(const Duration(seconds: 1), () async {
await viewModel.checkIsLoggedIn();
});
viewModel.addListener(_viewModelListener);
super.initState();
}
void _viewModelListener() {
if (viewModel.isLoggedIn) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Router.neglect(context, () => const HomeRouteData().go(context));
});
} else {
WidgetsBinding.instance.addPostFrameCallback((_) {
Router.neglect(context, () => const LoginScreenRoute().go(context));
});
}
}
@override
void dispose() {
viewModel.removeListener(_viewModelListener);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Image.asset(AssetsManager.logoBigPng, fit: BoxFit.cover),
),
);
}
}
+20
View File
@@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
import 'package:tm_app/core/utils/size_config.dart';
import 'package:tm_app/views/shared/responsive_builder.dart';
import 'package:tm_app/views/splash/pages/d_splash_page.dart';
import 'package:tm_app/views/splash/pages/m_splash_page.dart';
import 'package:tm_app/views/splash/pages/t_splash_page.dart';
class SplashScreen extends StatelessWidget {
const SplashScreen({super.key});
@override
Widget build(BuildContext context) {
SizeConfig.init(context);
return const ResponsiveBuilder(
mobile: MobileSplashPage(),
tablet: TabletSplashPage(),
desktop: DesktopSplashPage(),
);
}
}
+49
View File
@@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tm_app/view_models/auth_view_model.dart';
import '../../../core/constants/assets.dart';
import '../../../core/routes/app_routes.dart';
class TabletSplashPage extends StatefulWidget {
const TabletSplashPage({super.key});
@override
State<TabletSplashPage> createState() => _TabletSplashPageState();
}
class _TabletSplashPageState extends State<TabletSplashPage> {
late final AuthViewModel viewModel;
@override
void initState() {
viewModel = context.read<AuthViewModel>();
Future.delayed(const Duration(seconds: 1), () async {
await viewModel.checkIsLoggedIn();
});
viewModel.addListener(_viewModelListener);
super.initState();
}
void _viewModelListener() {
if (viewModel.isLoggedIn) {
Router.neglect(context, () => const HomeRouteData().go(context));
} else {
Router.neglect(context, () => const LoginScreenRoute().go(context));
}
}
@override
void dispose() {
viewModel.removeListener(_viewModelListener);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Image.asset(AssetsManager.logoBigPng, fit: BoxFit.cover),
),
);
}
}