From e073bd3f970e52835f1057fe1004a9946b7e3b68 Mon Sep 17 00:00:00 2001 From: misha Date: Wed, 15 Oct 2025 14:21:43 +0300 Subject: [PATCH] feat(socail): finished registration and ligin via social media --- lib/core/app_config.dart | 6 +- .../auth/entities/auth_result_entity.dart | 1 + .../auth/models/oauth_login_request.dart | 3 +- lib/features/auth/models/oauth_model.dart | 7 +- .../bloc/auth_cubit/auth_cubit.dart | 15 - .../bloc/oauth_cubit/oauth_cubit.dart | 23 +- .../auth/presentation/pages/auth_screen.dart | 275 +++++++++--------- .../pages/register_via_social_screen.dart | 5 +- .../auth/repositories/auth_repository.dart | 29 +- lib/features/auth/sdk/social_auth_facade.dart | 14 +- lib/features/auth/services/auth_service.dart | 8 +- lib/main.dart | 2 +- 12 files changed, 187 insertions(+), 201 deletions(-) diff --git a/lib/core/app_config.dart b/lib/core/app_config.dart index 03513645..45ad250b 100644 --- a/lib/core/app_config.dart +++ b/lib/core/app_config.dart @@ -15,10 +15,10 @@ import 'package:flutter_localizations/flutter_localizations.dart'; /// Конфигурация приложения class AppConfig { - AppConfig({required this.buildType}){ + AppConfig({required this.buildType}) { _baseUrl = switch (buildType) { AppBuildType.prod => EnvConfig.baseUrl, - AppBuildType.tests => EnvConfig.devBaseUrl, + AppBuildType.tests => EnvConfig.devBaseUrl, AppBuildType.dev => EnvConfig.devBaseUrl, }; } @@ -30,7 +30,7 @@ class AppConfig { void setBaseUrl(String baseUrls) { _baseUrl = baseUrls; } - + /// Список глобальных BlocProvider'ов List get globalBlocProviders => [ BlocProvider(create: (context) => sl()), diff --git a/lib/features/auth/entities/auth_result_entity.dart b/lib/features/auth/entities/auth_result_entity.dart index 2f296e5b..a8d08ff4 100644 --- a/lib/features/auth/entities/auth_result_entity.dart +++ b/lib/features/auth/entities/auth_result_entity.dart @@ -8,6 +8,7 @@ abstract class AuthResultEntity with _$AuthResultEntity { const factory AuthResultEntity({ required String accessToken, required String userId, + required String clientId, String? refreshToken, String? firstName, String? lastName, diff --git a/lib/features/auth/models/oauth_login_request.dart b/lib/features/auth/models/oauth_login_request.dart index 43da454b..97145a4d 100644 --- a/lib/features/auth/models/oauth_login_request.dart +++ b/lib/features/auth/models/oauth_login_request.dart @@ -6,9 +6,10 @@ part 'oauth_login_request.g.dart'; @freezed abstract class OAuthLoginRequest with _$OAuthLoginRequest { const factory OAuthLoginRequest({ - required String provider, + @JsonKey(name: 'client_id') required String clientId, @JsonKey(name: 'access_token') required String accessToken, @JsonKey(name: 'refresh_token') required String refreshToken, + required String provider, }) = _OAuthLoginRequest; factory OAuthLoginRequest.fromJson(Map json) => _$OAuthLoginRequestFromJson(json); diff --git a/lib/features/auth/models/oauth_model.dart b/lib/features/auth/models/oauth_model.dart index aff79b5f..0156e7aa 100644 --- a/lib/features/auth/models/oauth_model.dart +++ b/lib/features/auth/models/oauth_model.dart @@ -6,15 +6,16 @@ part 'oauth_model.g.dart'; @freezed abstract class OAuthModel with _$OAuthModel { const factory OAuthModel({ - required String provider, - required String id, @JsonKey(name: 'access_token') required String accessToken, @JsonKey(name: 'refresh_token') required String refreshToken, @JsonKey(name: 'first_name') required String firstName, @JsonKey(name: 'last_name') required String lastName, + @JsonKey(name: 'date_of_birth') required String dateOfBirth, + @JsonKey(name: 'client_id') required String clientId, + required String provider, + required String id, required String email, required String phone, - @JsonKey(name: 'date_of_birth') required String dateOfBirth, required String gender, required int weight, required int height, diff --git a/lib/features/auth/presentation/bloc/auth_cubit/auth_cubit.dart b/lib/features/auth/presentation/bloc/auth_cubit/auth_cubit.dart index 7b58a4b0..6f1a59f5 100644 --- a/lib/features/auth/presentation/bloc/auth_cubit/auth_cubit.dart +++ b/lib/features/auth/presentation/bloc/auth_cubit/auth_cubit.dart @@ -25,19 +25,4 @@ class AuthCubit extends Cubit { return false; } } - - Future getTokenBySocial({required String provider, required String accessToken}) async { - emit(const AuthState.loading()); - try { - final token = (await authRepository.getTokenBySocial(provider: provider, accessToken: accessToken)).token; - emit(AuthState.loaded(token: token)); - await TokenStorage.saveTokenInStorage(token); - return true; - } catch (e) { - log(e.toString()); - showErrorToast('Не удалось войти через $provider'); - emit(AuthState.error(errorMessage: '$e')); - return false; - } - } } diff --git a/lib/features/auth/presentation/bloc/oauth_cubit/oauth_cubit.dart b/lib/features/auth/presentation/bloc/oauth_cubit/oauth_cubit.dart index 7d45b4b1..394abc4d 100644 --- a/lib/features/auth/presentation/bloc/oauth_cubit/oauth_cubit.dart +++ b/lib/features/auth/presentation/bloc/oauth_cubit/oauth_cubit.dart @@ -1,4 +1,5 @@ import 'dart:developer'; +import 'package:eatvio_app/core/token_storage.dart'; import 'package:eatvio_app/features/auth/presentation/bloc/oauth_cubit/oauth_state.dart'; import 'package:eatvio_app/features/auth/repositories/auth_repository.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -8,24 +9,27 @@ class OAuthCubit extends Cubit { OAuthCubit({required this.authRepository}) : super(const OAuthState()); - Future getOAuthLogin({required String provider, required String accessToken, required String refreshToken}) async { + Future signInViaSocialMedia({required String provider, required String accessToken, required String refreshToken, required String clientId}) async { try { - log('OAuthCubit: Starting OAuth login for provider: $provider'); emit(const OAuthState.loading()); - log('OAuthCubit: Emitted loading state'); - final tokenModel = await authRepository.getOAuthLogin(provider: provider, accessToken: accessToken, refreshToken: refreshToken); + final tokenModel = await authRepository.signInViaSocialMedia( + provider: provider, + accessToken: accessToken, + refreshToken: refreshToken, + clientId: clientId, + ); + log('OAuthCubit: Got token model: ${tokenModel.token}'); + TokenStorage.saveTokenInStorage(tokenModel.token); emit(OAuthState.loaded(token: tokenModel.token)); - log('OAuthCubit: Emitted loaded state'); } catch (e) { log('OAuthCubit: Error occurred: $e'); emit(OAuthState.error(errorMessage: e.toString())); - log('OAuthCubit: Emitted error state'); } } - Future getOAuthToken({ + Future signUpViaSocialMedia({ required String provider, required String accessToken, required String id, @@ -38,10 +42,11 @@ class OAuthCubit extends Cubit { required String gender, required int weight, required int height, + required String clientId, }) async { try { emit(const OAuthState.loading()); - final token = await authRepository.getOAuthToken( + final token = await authRepository.signUpViaSocialMedia( provider: provider, accessToken: accessToken, id: id, @@ -54,7 +59,9 @@ class OAuthCubit extends Cubit { gender: gender, weight: weight, height: height, + clientId: clientId, ); + TokenStorage.saveTokenInStorage(token); emit(OAuthState.loaded(token: token)); } catch (e) { emit(OAuthState.error(errorMessage: e.toString())); diff --git a/lib/features/auth/presentation/pages/auth_screen.dart b/lib/features/auth/presentation/pages/auth_screen.dart index c21e77ea..f7484a87 100644 --- a/lib/features/auth/presentation/pages/auth_screen.dart +++ b/lib/features/auth/presentation/pages/auth_screen.dart @@ -1,5 +1,3 @@ -// ignore_for_file: unused_element, duplicate_ignore - import 'dart:async'; import 'dart:developer'; import 'package:eatvio_app/app_keys.dart'; @@ -95,6 +93,138 @@ class _AuthScreenState extends State { AppRouter.navigatorKey.currentContext?.go('/feed-screen', extra: {'needToInitializeWebSocket': false}); } + void _onVkPressed() async { + try { + log('VK button pressed'); + final context = AppRouter.navigatorKey.currentContext; + if (context == null) { + log('Context is null'); + return; + } + + log('Getting VK auth result'); + final authResult = await SocialAuthFacade.signInWithVkAndFetchProfile(context: context); + if (authResult == null) { + log('VK auth result is null'); + return; + } + + log('VK auth result received, trying OAuth login'); + if (context.mounted) { + await _tryOAuthLogin(context, authResult, 'vk'); + } else { + log('Context is not mounted'); + } + } catch (e) { + log('VK button error: $e'); + } + } + + void _onOkPressed() async { + try { + final context = AppRouter.navigatorKey.currentContext; + if (context == null) return; + + final authResult = await SocialAuthFacade.signInWithOkAndFetchProfile(); + if (authResult == null) { + return; + } + + if (context.mounted) { + await _tryOAuthLogin(context, authResult, 'vk'); + } + } catch (e) { + log(e.toString()); + } + } + + void _onMailRuPressed() async { + try { + final context = AppRouter.navigatorKey.currentContext; + if (context == null) return; + + final authResult = await SocialAuthFacade.signInWithMailRuAndFetchProfile(); + if (authResult == null) { + return; + } + + if (context.mounted) { + await _tryOAuthLogin(context, authResult, 'vk'); + } + } catch (e) { + log(e.toString()); + } + } + + void _onYandexPressed() async { + try { + final context = AppRouter.navigatorKey.currentContext; + if (context == null) return; + + final authResult = await SocialAuthFacade.signInWithYandex(); + if (authResult == null) return; + + if (context.mounted) { + await _tryOAuthLogin(context, authResult, 'yandex'); + } + } catch (e) { + log(e.toString()); + } + } + + Future _tryOAuthLogin(BuildContext context, AuthResultEntity authResult, String provider) async { + log('Starting OAuth login for provider: $provider'); + + try { + final oauthCubit = CubitRegistrations.sl(); + StreamSubscription? subscription; + + subscription = oauthCubit.stream.listen((state) { + log('OAuth state changed: ${state.runtimeType}'); + + if (state is OAuthStateLoaded) { + log('OAuth login successful, navigating to main screen'); + subscription?.cancel(); + _reconnectAfterAuth(); + } else if (state is OAuthStateError) { + log('OAuth login failed: ${state.errorMessage}'); + subscription?.cancel(); + AppRouter.navigateToRegisterViaSocial(authResult: authResult, provider: provider); + } + }); + + log('Calling getOAuthLogin with provider: $provider'); + await oauthCubit.signInViaSocialMedia( + provider: provider, + accessToken: authResult.accessToken, + refreshToken: authResult.refreshToken ?? '', + clientId: authResult.clientId, + ); + } catch (e) { + log('OAuth login error in catch block: $e'); + AppRouter.navigateToRegisterViaSocial(authResult: authResult, provider: provider); + } + } + + Future _reconnectAfterAuth() async { + final context = AppRouter.navigatorKey.currentContext; + if (context == null) return; + try { + await sl().clear(); + await sl().initialize(); + await sl().getMyProfile(); + await sl().getChatRoomsWithLoading(); + await sl().connectToWebSocket(); + await sl().getNotifications(); + await sl().connectToWebSocket(); + await sl().registerPushToken(); + showSuccessToast('Успешный вход'); + } catch (e) { + log(e.toString()); + } + AppRouter.navigatorKey.currentContext?.go('/feed-screen', extra: {'needToInitializeWebSocket': false}); + } + @override Widget build(BuildContext context) { var shortestSide = MediaQuery.sizeOf(context).shortestSide; @@ -129,7 +259,6 @@ class _AuthScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Social auth buttons at the top (outlined) const Center(child: Text('Авторизация через')), const SizedBox(height: 12), Row( @@ -405,143 +534,3 @@ class _SocialButton extends StatelessWidget { ); } } - -void _onVkPressed() async { - try { - log('VK button pressed'); - final context = AppRouter.navigatorKey.currentContext; - if (context == null) { - log('Context is null'); - return; - } - - log('Getting VK auth result'); - final authResult = await SocialAuthFacade.signInWithVkAndFetchProfile(context: context); - if (authResult == null) { - log('VK auth result is null'); - return; - } - - log('VK auth result received, trying OAuth login'); - // Пробуем сначала авторизоваться через новый API - if (context.mounted) { - await _tryOAuthLogin(context, authResult, 'vk'); - } else { - log('Context is not mounted'); - } - } catch (e) { - log('VK button error: $e'); - } -} - -void _onOkPressed() async { - try { - final context = AppRouter.navigatorKey.currentContext; - if (context == null) return; - - final authResult = await SocialAuthFacade.signInWithOkAndFetchProfile(); - if (authResult == null) { - return; - } - - // Пробуем сначала авторизоваться через новый API - if (context.mounted) { - await _tryOAuthLogin(context, authResult, 'ok'); - } - } catch (e) { - log(e.toString()); - } -} - -void _onMailRuPressed() async { - try { - final context = AppRouter.navigatorKey.currentContext; - if (context == null) return; - - final authResult = await SocialAuthFacade.signInWithMailRuAndFetchProfile(); - if (authResult == null) { - return; - } - - // Пробуем сначала авторизоваться через новый API - if (context.mounted) { - await _tryOAuthLogin(context, authResult, 'mailru'); - } - } catch (e) { - log(e.toString()); - } -} - -void _onYandexPressed() async { - try { - final context = AppRouter.navigatorKey.currentContext; - if (context == null) return; - - final authResult = await SocialAuthFacade.signInWithYandex(); - if (authResult == null) return; - - // Пробуем сначала авторизоваться через новый API - if (context.mounted) { - await _tryOAuthLogin(context, authResult, 'yandex'); - } - } catch (e) { - log(e.toString()); - } -} - -/// Пробует авторизоваться через новый OAuth login API -/// Если получает ошибку (включая 422 - пользователь не найден), переходит к регистрации -Future _tryOAuthLogin(BuildContext context, AuthResultEntity authResult, String provider) async { - log('Starting OAuth login for provider: $provider'); - - try { - // Создаем OAuth cubit - final oauthCubit = CubitRegistrations.sl(); - StreamSubscription? subscription; - - // Слушаем изменения состояния - subscription = oauthCubit.stream.listen((state) { - log('OAuth state changed: ${state.runtimeType}'); - - if (state is OAuthStateLoaded) { - // Успешная авторизация - переходим на главный экран - log('OAuth login successful, navigating to main screen'); - subscription?.cancel(); - _reconnectAfterAuth(); - } else if (state is OAuthStateError) { - // Ошибка авторизации (включая 422 - пользователь не найден) - переходим к регистрации - log('OAuth login failed: ${state.errorMessage}'); - subscription?.cancel(); - AppRouter.navigateToRegisterViaSocial(authResult: authResult, provider: provider); - } - }); - - // Пробуем авторизоваться - log('Calling getOAuthLogin with provider: $provider'); - await oauthCubit.getOAuthLogin(provider: provider, accessToken: authResult.accessToken, refreshToken: authResult.refreshToken ?? ''); - - // Ждем немного, чтобы состояние успело обновиться - await Future.delayed(const Duration(milliseconds: 100)); - } catch (e) { - // В случае любой ошибки (включая 422 - пользователь не найден) переходим к регистрации - log('OAuth login error in catch block: $e'); - AppRouter.navigateToRegisterViaSocial(authResult: authResult, provider: provider); - } -} - -Future _reconnectAfterAuth() async { - final context = AppRouter.navigatorKey.currentContext; - if (context == null) return; - try { - await sl().clear(); - await sl().initialize(); - await sl().getMyProfile(); - await sl().getChatRoomsWithLoading(); - await sl().connectToWebSocket(); - await sl().getNotifications(); - await sl().connectToWebSocket(); - await sl().registerPushToken(); - showSuccessToast('Успешный вход'); - } catch (_) {} - AppRouter.navigatorKey.currentContext?.go('/feed-screen', extra: {'needToInitializeWebSocket': false}); -} diff --git a/lib/features/auth/presentation/pages/register_via_social_screen.dart b/lib/features/auth/presentation/pages/register_via_social_screen.dart index c803f687..84a6f2ae 100644 --- a/lib/features/auth/presentation/pages/register_via_social_screen.dart +++ b/lib/features/auth/presentation/pages/register_via_social_screen.dart @@ -439,6 +439,7 @@ class _RegisterViaSocialScreenState extends State { } void _handleRegistration(BuildContext context) { + final clientID = widget.authResult?.clientId; // Валидация полей if (firstNameController.text.isEmpty || lastNameController.text.isEmpty || @@ -465,8 +466,9 @@ class _RegisterViaSocialScreenState extends State { // Очищаем номер телефона от маски для отправки final cleanPhone = phoneController.text.replaceAll(RegExp(r'[^\d+]'), ''); + if (clientID == null) return; // Вызываем OAuth регистрацию - context.read().getOAuthToken( + context.read().signUpViaSocialMedia( provider: widget.provider ?? 'social', accessToken: widget.authResult?.accessToken ?? 'dummy_token', id: widget.authResult?.userId ?? 'dummy_id', @@ -479,6 +481,7 @@ class _RegisterViaSocialScreenState extends State { gender: isMaleSelected ? 'М' : 'Ж', weight: weight, height: height, + clientId: clientID, ); } } diff --git a/lib/features/auth/repositories/auth_repository.dart b/lib/features/auth/repositories/auth_repository.dart index 1f66c039..a65f309c 100644 --- a/lib/features/auth/repositories/auth_repository.dart +++ b/lib/features/auth/repositories/auth_repository.dart @@ -3,7 +3,6 @@ import 'package:eatvio_app/features/auth/models/oauth_model.dart'; import 'package:eatvio_app/features/auth/models/oauth_login_request.dart'; import 'package:eatvio_app/features/auth/models/token_model.dart'; import 'package:eatvio_app/features/auth/services/auth_service.dart'; -import 'package:eatvio_app/features/auth/models/social_exchange_request.dart'; class AuthRepository { final AuthService authService; @@ -20,27 +19,23 @@ class AuthRepository { } } - Future getTokenBySocial({required String provider, required String accessToken}) async { + + Future signInViaSocialMedia({ + required String provider, + required String accessToken, + required String refreshToken, + required String clientId, + }) async { try { - return await authService.getTokenBySocial( - body: SocialExchangeRequest(provider: provider, accessToken: accessToken), + return await authService.signInViaSocialMedia( + body: OAuthLoginRequest(provider: provider, accessToken: accessToken, refreshToken: refreshToken, clientId: clientId), ); } catch (e) { rethrow; } } - Future getOAuthLogin({required String provider, required String accessToken, required String refreshToken}) async { - try { - return await authService.getOAuthLogin( - body: OAuthLoginRequest(provider: provider, accessToken: accessToken, refreshToken: refreshToken), - ); - } catch (e) { - rethrow; - } - } - - Future getOAuthToken({ + Future signUpViaSocialMedia({ required String provider, required String accessToken, required String id, @@ -53,9 +48,10 @@ class AuthRepository { required String gender, required int weight, required int height, + required String clientId, }) async { try { - return (await authService.getOAuthToken( + return (await authService.signUpViaSocialMedia( body: OAuthModel( provider: provider, accessToken: accessToken, @@ -69,6 +65,7 @@ class AuthRepository { gender: gender == 'М' ? 'male' : 'female', weight: weight, height: height, + clientId: clientId, ), ))['token']! as String; diff --git a/lib/features/auth/sdk/social_auth_facade.dart b/lib/features/auth/sdk/social_auth_facade.dart index b3543700..4d2e9745 100644 --- a/lib/features/auth/sdk/social_auth_facade.dart +++ b/lib/features/auth/sdk/social_auth_facade.dart @@ -8,6 +8,7 @@ import 'package:eatvio_app/features/auth/models/yandex_profile_model.dart'; import 'package:flutter/material.dart'; import 'package:eatvio_app/core/app_router.dart'; import 'package:eatvio_app/core/env_config.dart'; +import 'package:go_router/go_router.dart'; import 'package:http/http.dart' as http; import 'package:vk_id/vk_id.dart'; import 'package:webview_flutter/webview_flutter.dart'; @@ -38,7 +39,7 @@ class SocialAuthFacade { if (redirectParams == null) { return null; } - return await _parseVKAuthResult(redirectParams: redirectParams, controller: controller, codeVerifier: codeVerifier); + return await _parseVKAuthResult(redirectParams: redirectParams, controller: controller, codeVerifier: codeVerifier, clientId: clientIdStr); } catch (e) { return null; } @@ -48,6 +49,7 @@ class SocialAuthFacade { required Map redirectParams, required VkIDController controller, required String codeVerifier, + required String clientId, }) async { final code = redirectParams['code']; final deviceId = redirectParams['device_id']; @@ -88,6 +90,7 @@ class SocialAuthFacade { phone: profile.phone, gender: GenderEnum.fromVkGender(profile.parsedSex), birthDate: profile.bDayDt?.toString(), + clientId: clientId, ); } @@ -151,6 +154,7 @@ class SocialAuthFacade { phone: yandexProfileData.defaultPhone.number, gender: GenderEnum.fromString(yandexProfileData.sex), birthDate: yandexProfileData.birthday, + clientId: clientId, ); } catch (e) { log(e.toString()); @@ -188,7 +192,7 @@ class SocialAuthFacade { try { final controller = VkIDController(clID: int.parse(clientIdStr)); - final authUri = controller.generateAuthorizeLinkWithCodeVerifier(provider: provider, scopes: [VkScope.phone, VkScope.email,]); + final authUri = controller.generateAuthorizeLinkWithCodeVerifier(provider: provider, scopes: [VkScope.phone, VkScope.email]); final codeVerifier = authUri.key; final authUrl = authUri.value.toString(); final redirectScheme = 'vk$clientIdStr://'; @@ -199,7 +203,7 @@ class SocialAuthFacade { final deviceId = redirectParams['device_id']; if (code == null || deviceId == null) return null; - return await _parseVKAuthResult(redirectParams: redirectParams, controller: controller, codeVerifier: codeVerifier); + return await _parseVKAuthResult(redirectParams: redirectParams, controller: controller, codeVerifier: codeVerifier, clientId: clientIdStr); } catch (e) { return null; } @@ -230,7 +234,9 @@ class SocialAuthFacade { } } } - Navigator.of(context, rootNavigator: true).pop(params); + if (GoRouter.of(context).canPop()) { + GoRouter.of(context).pop(params); + } return NavigationDecision.prevent; } return NavigationDecision.navigate; diff --git a/lib/features/auth/services/auth_service.dart b/lib/features/auth/services/auth_service.dart index 02f8722a..41d35da7 100644 --- a/lib/features/auth/services/auth_service.dart +++ b/lib/features/auth/services/auth_service.dart @@ -3,7 +3,6 @@ import 'package:eatvio_app/features/auth/models/credentials_model.dart'; import 'package:eatvio_app/features/auth/models/oauth_model.dart'; import 'package:eatvio_app/features/auth/models/oauth_login_request.dart'; import 'package:eatvio_app/features/auth/models/token_model.dart'; -import 'package:eatvio_app/features/auth/models/social_exchange_request.dart'; import 'package:retrofit/retrofit.dart'; part 'auth_service.g.dart'; @@ -15,12 +14,9 @@ abstract class AuthService { @POST('/api/login') Future getTokenByCredentials({@Body() required CredentialsModel credentials}); - @POST('/api/social/login') - Future getTokenBySocial({@Body() required SocialExchangeRequest body}); - @POST('/api/oauth/callback') - Future> getOAuthToken({@Body() required OAuthModel body}); + Future> signUpViaSocialMedia({@Body() required OAuthModel body}); @POST('/api/oauth/login') - Future getOAuthLogin({@Body() required OAuthLoginRequest body}); + Future signInViaSocialMedia({@Body() required OAuthLoginRequest body}); } diff --git a/lib/main.dart b/lib/main.dart index af6db638..b6171864 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,7 +9,7 @@ import 'package:flutter_portal/flutter_portal.dart'; import 'package:keyboard_dismisser/keyboard_dismisser.dart'; Future main() async { - await AppInitializer.initialize(buildType: AppBuildType.prod); + await AppInitializer.initialize(buildType: AppBuildType.dev); await AppInitializer.runAppWithSentry(() => runApp(const MyApp())); }