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
Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

+1
View File
@@ -4,6 +4,7 @@ class AssetsManager {
//logo //logo
static const logoSmall = 'assets/icons/logo_small.svg'; static const logoSmall = 'assets/icons/logo_small.svg';
static const logoBig = 'assets/icons/logo_big.svg'; static const logoBig = 'assets/icons/logo_big.svg';
static const logoBigPng = 'assets/pngs/logo-big.png';
//bottom navigation //bottom navigation
static const homeActive = 'assets/icons/home_active.svg'; 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/forgot_password/pages/forgot_password_screen.dart';
import '../../views/notification/pages/notification_screen.dart'; import '../../views/notification/pages/notification_screen.dart';
import '../../views/profile/pages/profile_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/tenders/pages/tenders_screen.dart';
import '../../views/your_tenders/pages/your_tenders_screen.dart'; import '../../views/your_tenders/pages/your_tenders_screen.dart';
import 'app_shell/app_shell_screen.dart'; import 'app_shell/app_shell_screen.dart';
@@ -31,7 +32,7 @@ final GlobalKey<NavigatorState> notificationNavigatorKey =
// Main app router // Main app router
final GoRouter appRouter = GoRouter( final GoRouter appRouter = GoRouter(
routes: $appRoutes, routes: $appRoutes,
initialLocation: '/login', initialLocation: '/splash',
navigatorKey: rootNavigatorKey, 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 // Shell route with bottom navigation using StatefulShellRoute.indexedStack
@TypedStatefulShellRoute<AppShellRouteData>( @TypedStatefulShellRoute<AppShellRouteData>(
branches: <TypedStatefulShellBranch<StatefulShellBranchData>>[ branches: <TypedStatefulShellBranch<StatefulShellBranchData>>[
+28
View File
@@ -8,6 +8,7 @@ part of 'app_routes.dart';
List<RouteBase> get $appRoutes => [ List<RouteBase> get $appRoutes => [
$loginScreenRoute, $loginScreenRoute,
$splashScreenRoute,
$appShellRouteData, $appShellRouteData,
$yourTendersRouteData, $yourTendersRouteData,
$finalCompletionOfDocumentsRouteData, $finalCompletionOfDocumentsRouteData,
@@ -43,6 +44,33 @@ mixin _$LoginScreenRoute on GoRouteData {
void replace(BuildContext context) => context.replace(location); 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( RouteBase get $appShellRouteData => StatefulShellRouteData.$route(
parentNavigatorKey: AppShellRouteData.$parentNavigatorKey, parentNavigatorKey: AppShellRouteData.$parentNavigatorKey,
factory: $AppShellRouteDataExtension._fromState, factory: $AppShellRouteDataExtension._fromState,
@@ -47,4 +47,8 @@ class AuthRepository {
}) async { }) async {
return _authService.verifyOtp(code: code, email: email); 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; 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 => AssetGenImage get forgotPassword =>
const AssetGenImage('assets/pngs/forgot_password.png'); const AssetGenImage('assets/pngs/forgot_password.png');
/// File path: assets/pngs/logo.png /// File path: assets/pngs/logo-big.png
AssetGenImage get logo => const AssetGenImage('assets/pngs/logo.png'); AssetGenImage get logoBig => const AssetGenImage('assets/pngs/logo-big.png');
/// File path: assets/pngs/web_login_image.png /// File path: assets/pngs/web_login_image.png
AssetGenImage get webLoginImage => AssetGenImage get webLoginImage =>
@@ -241,7 +241,7 @@ class $AssetsPngsGen {
List<AssetGenImage> get values => [ List<AssetGenImage> get values => [
appLogo, appLogo,
forgotPassword, forgotPassword,
logo, logoBig,
webLoginImage, webLoginImage,
]; ];
} }
+17
View File
@@ -44,6 +44,7 @@ class AuthViewModel with ChangeNotifier {
// Auth state // Auth state
bool _isLoading = false; bool _isLoading = false;
bool _isLoggedIn = false;
bool _isResetPasswordLoading = false; bool _isResetPasswordLoading = false;
String? _errorMessage; String? _errorMessage;
String? _resetPasswordErrorMessage; String? _resetPasswordErrorMessage;
@@ -59,6 +60,7 @@ class AuthViewModel with ChangeNotifier {
bool _verifyOtpSuccess = false; bool _verifyOtpSuccess = false;
bool get isLoading => _isLoading; bool get isLoading => _isLoading;
bool get isLoggedIn => _isLoggedIn;
bool get isLoadingForgot => _isLoadingForgot; bool get isLoadingForgot => _isLoadingForgot;
bool get isLoadingOtp => _isLoadingOtp; bool get isLoadingOtp => _isLoadingOtp;
bool get isResetPasswordLoading => _isResetPasswordLoading; 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() { void clearResetPasswordSuccess() {
_resetPasswordSuccess = false; _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),
),
);
}
}
+8 -8
View File
@@ -45,10 +45,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: async name: async
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.12.0" version: "2.13.0"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@@ -269,10 +269,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: fake_async name: fake_async
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.2" version: "1.3.3"
ffi: ffi:
dependency: transitive dependency: transitive
description: description:
@@ -572,10 +572,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker name: leak_tracker
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.8" version: "10.0.9"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
@@ -1049,10 +1049,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.3.1" version: "15.0.0"
watcher: watcher:
dependency: transitive dependency: transitive
description: description: