diff --git a/lib/application/application.dart b/lib/application/application.dart index 654cb38..5c38554 100644 --- a/lib/application/application.dart +++ b/lib/application/application.dart @@ -1,3 +1,4 @@ export 'bottom_navbar_cubit/bottom_navbar_cubit.dart'; -export 'user_bloc/user_bloc.dart'; export 'language_bloc/language_bloc.dart'; +export 'splash_cubit/splash_cubit.dart'; +export 'user_bloc/user_bloc.dart'; diff --git a/lib/application/splash_cubit/splash_cubit.dart b/lib/application/splash_cubit/splash_cubit.dart new file mode 100644 index 0000000..452f3cb --- /dev/null +++ b/lib/application/splash_cubit/splash_cubit.dart @@ -0,0 +1,31 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../core/usecases/usecases.dart'; +import '../../domain/usecases/splash/splash_usecase.dart'; + +// Define the states +abstract class SplashState {} + +class SplashInitial extends SplashState {} + +class NavigateToSplash2 extends SplashState {} + +class NavigateToRoot extends SplashState {} + +class SplashCubit extends Cubit { + final SplashUseCase _splashUseCase; + + SplashCubit(this._splashUseCase) : super(SplashInitial()); + + Future checkToken() async { + await Future.delayed(const Duration(seconds: 1)); // Simulating some loading time + try { + final result = await _splashUseCase(NoParams()); + result.fold((failure) => emit(NavigateToSplash2()), (result) { + result ? emit(NavigateToRoot()) : emit(NavigateToSplash2()); + }); + } catch (e) { + emit(NavigateToSplash2()); + } + } +} diff --git a/lib/core/app/app.dart b/lib/core/app/app.dart index dfea71e..6d4f840 100644 --- a/lib/core/app/app.dart +++ b/lib/core/app/app.dart @@ -14,6 +14,9 @@ class MyApp extends StatelessWidget { return MultiBlocProvider( providers: [ BlocProvider(create: (context) => di.sl()), + BlocProvider( + create: (context) => di.sl()..checkToken(), + ), BlocProvider( create: (context) => di.sl()..add(LanguageInitial()), ), @@ -29,7 +32,7 @@ class MyApp extends StatelessWidget { localizationsDelegates: context.localizationDelegates, supportedLocales: context.supportedLocales, locale: context.locale, - initialRoute: AppRouter.root, + initialRoute: AppRouter.splash, ), ); } diff --git a/lib/core/constants/api.dart b/lib/core/constants/api.dart index 872ed5c..29fc01b 100644 --- a/lib/core/constants/api.dart +++ b/lib/core/constants/api.dart @@ -1,3 +1,3 @@ -const String baseUrl = 'https://rich-jade-mackerel-kit.cyclic.app'; +const String baseUrl = 'https://192.168.99.64:5001/api'; const String defaultApiKey = ''; const String defaultSources = ''; diff --git a/lib/core/constants/strings.dart b/lib/core/constants/strings.dart index 10c7901..64c90a8 100644 --- a/lib/core/constants/strings.dart +++ b/lib/core/constants/strings.dart @@ -1,9 +1,10 @@ // App -const String appTitle = 'Cargo App'; +const String appTitle = 'Durnukly ýol'; // Storage and Databases const String articlesTableName = ''; const String databaseName = ''; -// SharedPreferences +// locale storage const String activeLang = 'activeLang'; +// const String token = 'token'; diff --git a/lib/core/router/app_router.dart b/lib/core/router/app_router.dart index 6157207..ca500bd 100644 --- a/lib/core/router/app_router.dart +++ b/lib/core/router/app_router.dart @@ -9,6 +9,38 @@ sealed class AppRouter { static const String login = '/login'; static const String root = '/root'; static const String orderDetails = '/order-details'; + + static Route onGenerateRoute(RouteSettings routeSettings) { + debugPrint('onGenerateRoute ${routeSettings.name}'); + switch (routeSettings.name) { + case splash: + return MaterialPageRoute(builder: (_) => const SplashScreen()); + case splash2: + return MaterialPageRoute(builder: (_) => const Splash2Screen()); + case login: + return MaterialPageRoute(builder: (_) => const LoginScreen()); + case root: + return MaterialPageRoute(builder: (_) => const RootScreen()); + case orderDetails: + return MaterialPageRoute(builder: (_) => const OrderDetailsScreen()); + + default: + throw const RouteException('Route not found!'); + } + } + + static List> generateInitialRoutes(String initialRoute) { + debugPrint('generateInitialRoutes $initialRoute'); + + return [ + MaterialPageRoute( + builder: (context) => initialRoute == AppRouter.root ? const RootScreen() : const SplashScreen(), + ), + ]; + } +} + + // static const String search = '/search'; // static const String filter = '/filter'; // static const String signup = '/signup'; @@ -25,21 +57,8 @@ sealed class AppRouter { // static const String orders = '/orders'; // static const String notifications = '/notifications'; - static Route onGenerateRoute(RouteSettings routeSettings) { - debugPrint('onGenerateRoute ${routeSettings.name}'); - switch (routeSettings.name) { - case splash: - return MaterialPageRoute(builder: (_) => const SplashScreen()); - case splash2: - return MaterialPageRoute(builder: (_) => const Splash2Screen()); - case login: - return MaterialPageRoute(builder: (_) => const LoginScreen()); - case root: - return MaterialPageRoute(builder: (_) => const RootScreen()); - case orderDetails: - // ProductEntity product = routeSettings.arguments as ProductEntity; - return MaterialPageRoute(builder: (_) => const OrderDetailsScreen()); - // case search: + + // case search: // return MaterialPageRoute(builder: (_) => const SearchScreen()); // case filter: // return MaterialPageRoute(builder: (_) => const FilterScreen()); @@ -82,21 +101,4 @@ sealed class AppRouter { // case orders: // return MaterialPageRoute(builder: (_) => const OrdersScreen()); // case notifications: - // return MaterialPageRoute(builder: (_) => const NotificationsScreen()); - default: - throw const RouteException('Route not found!'); - } - } - - static List> generateInitialRoutes(String initialRoute) { - debugPrint('generateInitialRoutes $initialRoute'); - - return [ - MaterialPageRoute(builder: (context) => const RootScreen()), - if (initialRoute != AppRouter.root) - MaterialPageRoute( - builder: (context) => const SplashScreen(), - ), - ]; - } -} + // return MaterialPageRoute(builder: (_) => const NotificationsScreen()); \ No newline at end of file diff --git a/lib/core/utils/form_validator.dart b/lib/core/utils/form_validator.dart index b9f78bb..1b645fa 100644 --- a/lib/core/utils/form_validator.dart +++ b/lib/core/utils/form_validator.dart @@ -1 +1,8 @@ -// TODO Implement this library. \ No newline at end of file +class FormValidator { + static String? validateField(String? val) { + if (val == null || val.isEmpty) { + return 'This field can\'t be empty'; + } + return null; + } +} diff --git a/lib/core/utils/keyboard.dart b/lib/core/utils/keyboard.dart new file mode 100644 index 0000000..f8c437d --- /dev/null +++ b/lib/core/utils/keyboard.dart @@ -0,0 +1,7 @@ +import 'package:flutter/material.dart'; + +class Keyboard { + static hide(BuildContext context) { + FocusScope.of(context).requestFocus(FocusNode()); + } +} diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index e8835a0..bea8a69 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -1,10 +1,2 @@ export 'form_validator.dart'; - -class FormValidator { - static String? validateField(String? val) { - if (val == null || val.isEmpty) { - return 'This field can\'t be empty'; - } - return null; - } -} +export 'keyboard.dart'; diff --git a/lib/data/data_sources/remote/user_remote_data_source.dart b/lib/data/data_sources/remote/user_remote_data_source.dart index a3245b4..2d1ae4b 100644 --- a/lib/data/data_sources/remote/user_remote_data_source.dart +++ b/lib/data/data_sources/remote/user_remote_data_source.dart @@ -20,9 +20,20 @@ class UserRemoteDataSourceImpl implements UserRemoteDataSource { Future signIn(SignInParams params) async { debugPrint('signIn'); - const data = ''' + try { + final response = await client.post(Uri.parse('$baseUrl/Authentication/Authenticate'), + headers: { + 'Content-Type': 'application/json', + 'accept': '*/*', + }, + body: json.encode({ + 'UserName': params.username, + 'Password': '', // params.password, + })); + if (response.statusCode == 200) { + const userData = ''' { - "token": "exampleToken123", + "token": "", "user": { "_id": "user123", "firstName": "John", @@ -31,23 +42,21 @@ class UserRemoteDataSourceImpl implements UserRemoteDataSource { } } '''; + AuthenticationResponseModel user = authenticationResponseModelFromJson(userData); - return authenticationResponseModelFromJson(data); - /* final response = await client.post(Uri.parse('$baseUrl/authentication/local/sign-in'), - headers: { - 'Content-Type': 'application/json', - }, - body: json.encode({ - 'identifier': params.username, - 'password': params.password, - })); - if (response.statusCode == 200) { - return authenticationResponseModelFromJson(response.body); - } else if (response.statusCode == 400 || response.statusCode == 401) { - throw CredentialFailure(); - } else { + // Update the token value + AuthenticationResponseModel updatedUser = user.copyWith(token: response.body); + + return updatedUser; + } else if (response.statusCode == 400 || response.statusCode == 401) { + throw CredentialFailure(); + } else { + throw ServerException(); + } + } catch (e) { + debugPrint('e $e'); throw ServerException(); - } */ + } } @override diff --git a/lib/data/models/user/authentication_response_model.dart b/lib/data/models/user/authentication_response_model.dart index 587960a..ccc984c 100644 --- a/lib/data/models/user/authentication_response_model.dart +++ b/lib/data/models/user/authentication_response_model.dart @@ -25,4 +25,14 @@ class AuthenticationResponseModel { 'token': token, 'user': user.toJson(), }; + + AuthenticationResponseModel copyWith({ + String? token, + UserModel? user, + }) { + return AuthenticationResponseModel( + token: token ?? this.token, + user: user ?? this.user, + ); + } } diff --git a/lib/data/repositories/repositories.dart b/lib/data/repositories/repositories.dart index 9099d07..f31906f 100644 --- a/lib/data/repositories/repositories.dart +++ b/lib/data/repositories/repositories.dart @@ -1 +1,2 @@ export 'user_repository_impl.dart'; +export 'splash_repository_impl.dart'; diff --git a/lib/data/repositories/splash_repository_impl.dart b/lib/data/repositories/splash_repository_impl.dart new file mode 100644 index 0000000..14f1538 --- /dev/null +++ b/lib/data/repositories/splash_repository_impl.dart @@ -0,0 +1,21 @@ +import 'package:dartz/dartz.dart'; + +import '../../core/core.dart'; +import '../../domain/domain.dart'; +import '../data.dart'; + +class SplashRepositoryImpl implements SplashRepository { + final UserLocalDataSource localDataSource; + + SplashRepositoryImpl({required this.localDataSource}); + + @override + Future> isTokenAvailable() async { + try { + final isAvl = await localDataSource.isTokenAvailable(); + return Right(isAvl); + } on CacheFailure { + return Left(CacheFailure()); + } + } +} diff --git a/lib/di/di.dart b/lib/di/di.dart index 7ed1d4e..d88352a 100644 --- a/lib/di/di.dart +++ b/lib/di/di.dart @@ -3,6 +3,7 @@ import 'package:get_it/get_it.dart'; import 'common.dart'; import 'cubits.dart'; import 'language.dart'; +import 'splash.dart'; import 'user.dart'; final sl = GetIt.instance; @@ -10,8 +11,11 @@ final sl = GetIt.instance; // Main Initialization Future init() async { // Register features + registerUserFeature(); registerLanguageFeature(); + registerSplashFeature(); + // registerCategoryFeature(); // registerProductFeature(); // registerDeliveryInfoFeature(); diff --git a/lib/di/splash.dart b/lib/di/splash.dart new file mode 100644 index 0000000..37ef4a7 --- /dev/null +++ b/lib/di/splash.dart @@ -0,0 +1,18 @@ +import 'package:cargo/data/data.dart'; +import 'package:cargo/domain/domain.dart'; + +import '../application/splash_cubit/splash_cubit.dart'; +import 'di.dart'; + +void registerSplashFeature() { + // Splash cubit and Use Cases + sl.registerFactory(() => SplashCubit(sl())); + sl.registerLazySingleton(() => SplashUseCase(sl())); + + // Splash Repository and Data Sources + sl.registerLazySingleton( + () => SplashRepositoryImpl( + localDataSource: sl(), + ), + ); +} diff --git a/lib/domain/repositories/repositories.dart b/lib/domain/repositories/repositories.dart index 719ec60..d45f25c 100644 --- a/lib/domain/repositories/repositories.dart +++ b/lib/domain/repositories/repositories.dart @@ -1 +1,2 @@ export 'user_repository.dart'; +export 'splash_repository.dart'; diff --git a/lib/domain/repositories/splash_repository.dart b/lib/domain/repositories/splash_repository.dart new file mode 100644 index 0000000..988be1e --- /dev/null +++ b/lib/domain/repositories/splash_repository.dart @@ -0,0 +1,7 @@ +import 'package:dartz/dartz.dart'; + +import '../../core/core.dart'; + +abstract class SplashRepository { + Future> isTokenAvailable(); +} diff --git a/lib/domain/usecases/splash/splash_usecase.dart b/lib/domain/usecases/splash/splash_usecase.dart new file mode 100644 index 0000000..244f433 --- /dev/null +++ b/lib/domain/usecases/splash/splash_usecase.dart @@ -0,0 +1,14 @@ +import 'package:dartz/dartz.dart'; + +import '../../../core/core.dart'; +import '../../domain.dart'; + +class SplashUseCase implements UseCase { + final SplashRepository repository; + SplashUseCase(this.repository); + + @override + Future> call(NoParams params) async { + return await repository.isTokenAvailable(); + } +} diff --git a/lib/domain/usecases/usecases.dart b/lib/domain/usecases/usecases.dart index 3c543ce..d641191 100644 --- a/lib/domain/usecases/usecases.dart +++ b/lib/domain/usecases/usecases.dart @@ -1 +1,2 @@ export 'user/user.dart'; +export 'splash/splash_usecase.dart'; diff --git a/lib/main.dart b/lib/main.dart index f2e08c3..4986ce1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -27,6 +29,8 @@ Future main() async { statusBarBrightness: Brightness.dark, )); + HttpOverrides.global = MyHttpOverrides(); + runApp( EasyLocalization( supportedLocales: const [ @@ -48,3 +52,11 @@ Future main() async { // 4 create repository implementation // 5 create data source // 6 add di register + +class MyHttpOverrides extends HttpOverrides { + @override + HttpClient createHttpClient(SecurityContext? context) { + return super.createHttpClient(context) + ..badCertificateCallback = (X509Certificate cert, String host, int port) => true; + } +} diff --git a/lib/presentation/screens/splash.dart b/lib/presentation/screens/splash.dart index e07f2f5..0aba557 100644 --- a/lib/presentation/screens/splash.dart +++ b/lib/presentation/screens/splash.dart @@ -1,77 +1,146 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import '../../application/application.dart'; import '../../configs/configs.dart'; import '../../core/core.dart'; -class SplashScreen extends StatefulWidget { +import 'package:flutter_bloc/flutter_bloc.dart'; + +class SplashScreen extends StatelessWidget { const SplashScreen({super.key}); - @override - State createState() => _SplashScreenState(); -} - -class _SplashScreenState extends State { - void _nextScreen() { - Future.delayed(const Duration(seconds: 1), () { - Navigator.of(context).pushNamedAndRemoveUntil( - AppRouter.splash2, - (route) => false, - ); - }); - } - - @override - void initState() { - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - _nextScreen(); - }); - super.initState(); - } - @override Widget build(BuildContext context) { App.init(context); - return Scaffold( - body: Container( - decoration: const BoxDecoration( - gradient: RadialGradient( - center: Alignment.center, - radius: 1.0, - colors: [ - Color(0xFF5468FF), // Lighter blue in the center - AppColors.primary, // Darker blue at the edges - ], - ), - ), - child: SafeArea( - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SvgPicture.asset( - AppAssets.logo, - height: AppDimensions.normalize(30), - ), - Space.yf(0.80), - Text( - appTitle, - style: AppText.h1b?.copyWith( - color: Colors.white, - ), - ), - Space.yf(0.30), - Text( - 'Довезём всё!', - style: AppText.b1?.copyWith( - color: AppColors.yellow, - ), - ), + return BlocListener( + listener: (context, state) { + Keyboard.hide(context); + if (state is NavigateToSplash2) { + Navigator.of(context).pushNamedAndRemoveUntil( + AppRouter.splash2, + (route) => false, + ); + } else if (state is NavigateToRoot) { + Navigator.of(context).pushNamedAndRemoveUntil( + AppRouter.root, + (route) => false, + ); + } + }, + child: Scaffold( + body: Container( + decoration: const BoxDecoration( + gradient: RadialGradient( + center: Alignment.center, + radius: 1.0, + colors: [ + Color(0xFF5468FF), // Lighter blue in the center + AppColors.primary, // Darker blue at the edges ], ), ), + child: SafeArea( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + AppAssets.logo, + height: AppDimensions.normalize(30), + ), + Space.yf(0.80), + Text( + appTitle, + style: AppText.h1b?.copyWith( + color: Colors.white, + ), + ), + Space.yf(0.30), + Text( + 'Довезём всё!', + style: AppText.b1?.copyWith( + color: AppColors.yellow, + ), + ), + ], + ), + ), + ), ), ), ); } } + +// class SplashScreen extends StatefulWidget { +// const SplashScreen({super.key}); + +// @override +// State createState() => _SplashScreenState(); +// } + +// class _SplashScreenState extends State { +// void _nextScreen() { +// Future.delayed(const Duration(seconds: 1), () { +// Navigator.of(context).pushNamedAndRemoveUntil( +// AppRouter.splash2, +// (route) => false, +// ); +// }); +// } + +// @override +// void initState() { +// WidgetsBinding.instance.addPostFrameCallback((timeStamp) { +// _nextScreen(); +// }); +// super.initState(); +// } + +// @override +// Widget build(BuildContext context) { +// App.init(context); +// return Scaffold( +// body: Container( +// decoration: const BoxDecoration( +// gradient: RadialGradient( +// center: Alignment.center, +// radius: 1.0, +// colors: [ +// Color(0xFF5468FF), // Lighter blue in the center +// AppColors.primary, // Darker blue at the edges +// ], +// ), +// ), +// child: SafeArea( +// child: Center( +// child: Column( +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// SvgPicture.asset( +// AppAssets.logo, +// height: AppDimensions.normalize(30), +// ), +// Space.yf(0.80), +// Text( +// appTitle, +// style: AppText.h1b?.copyWith( +// color: Colors.white, +// ), +// ), +// Space.yf(0.30), +// Text( +// 'Довезём всё!', +// style: AppText.b1?.copyWith( +// color: AppColors.yellow, +// ), +// ), +// ], +// ), +// ), +// ), +// ), +// ); +// } +// }