feat(app): Вынести инициализацию приложения за splash (#4)

Co-authored-by: PetrovY <y.petrov@friflex.com>
This commit is contained in:
Yuri Petrov
2025-02-12 10:53:38 +03:00
committed by GitHub
parent 0a7452e1eb
commit af3b941711
18 changed files with 503 additions and 155 deletions

View File

@@ -0,0 +1,19 @@
{
"imports": [
"import 'package:flutter/widgets.dart';"
],
"classStructure": [
"/// {@template {className}}",
"/// ",
"/// {@endtemplate}",
"class {className} extends StatelessWidget {",
" /// {@macro {className}}",
" const {className}({super.key});",
" ",
" @override",
" Widget build(BuildContext context) {",
" return const Placeholder();",
" }",
"}"
]
}

File diff suppressed because one or more lines are too long

View File

@@ -1,46 +1,96 @@
import 'package:flutter/material.dart';
import 'package:friflex_starter/app/app_context_ext.dart';
import 'package:friflex_starter/app/app_providers.dart';
import 'package:friflex_starter/app/depends_providers.dart';
import 'package:friflex_starter/app/theme/app_theme.dart';
import 'package:friflex_starter/app/theme/theme_notifier.dart';
import 'package:friflex_starter/di/di_container.dart';
import 'package:friflex_starter/features/error/error_screen.dart';
import 'package:friflex_starter/features/splash/splash_screen.dart';
import 'package:friflex_starter/l10n/gen/app_localizations.dart';
import 'package:friflex_starter/l10n/localization_notifier.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
/// Класс для реализации объекта приложения
class App extends StatelessWidget {
/// Создает экземпляр приложения
///
/// Принимает:
/// - [diContainer] - набор зависимостей приложения
/// - [router] - экземпляр роутера приложения
/// Класс приложения
class App extends StatefulWidget {
const App({
super.key,
required this.diContainer,
required this.router,
required this.initDependencies,
});
/// Набор зависимостей приложения
final DiContainer diContainer;
/// Экземпляр роутера приложения
/// Роутер приложения
final GoRouter router;
/// Функция для инициализации зависимостей
final Future<DiContainer> Function() initDependencies;
@override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
/// Мутабельная Future для инициализации зависимостей
late Future<DiContainer> _initFuture;
@override
void initState() {
super.initState();
_initFuture = widget.initDependencies();
}
@override
Widget build(BuildContext context) {
return AppProviders(
diContainer: diContainer,
// Consumer для локализации добавляем выше чем DependsProviders
// чтобы при изменении локализации перестраивался весь виджет
// Но, это не обязательно, можно добавить в DependsProviders
child: LocalizationConsumer(
builder: () => ThemeConsumer(
builder: () => _App(router: router),
builder: () => FutureBuilder<DiContainer>(
future: _initFuture,
builder: (_, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
case ConnectionState.active:
// Пока инициализация показываем Splash
return const SplashScreen();
case ConnectionState.done:
if (snapshot.hasError) {
return ErrorScreen(
error: snapshot.error,
stackTrace: snapshot.stackTrace,
onRetry: _retryInit,
);
}
final diContainer = snapshot.data;
if (diContainer == null) {
return ErrorScreen(
error:
'Ошибка инициализации зависимостей, diContainer = null',
stackTrace: null,
onRetry: _retryInit,
);
}
return DependsProviders(
diContainer: diContainer,
child: ThemeConsumer(
builder: () => _App(router: widget.router),
),
);
}
},
),
),
);
}
void _retryInit() {
setState(() {
_initFuture = widget.initDependencies();
});
}
}
/// Класс для реализации объекта приложения
class _App extends StatelessWidget {
const _App({required this.router});
@@ -59,31 +109,3 @@ class _App extends StatelessWidget {
);
}
}
/// Класс для реализации провайдеров приложения
final class AppProviders extends StatelessWidget {
const AppProviders({
super.key,
required this.child,
required this.diContainer,
});
final Widget child;
final DiContainer diContainer;
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
Provider.value(value: diContainer), // Передаем контейнер зависимостей
ChangeNotifierProvider(
create: (_) => ThemeNotifier(),
), // Провайдер для темы
ChangeNotifierProvider(
create: (_) => LocalizationNotifier(),
), // Провайдер для локализации
],
child: child,
);
}
}

View File

@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import 'package:friflex_starter/app/theme/theme_notifier.dart';
import 'package:friflex_starter/l10n/localization_notifier.dart';
import 'package:provider/provider.dart';
/// Класс для добавления провайдеров темы и локализации
final class AppProviders extends StatelessWidget {
const AppProviders({
super.key,
required this.child,
});
final Widget child;
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => ThemeNotifier(),
), // Провайдер для темы
ChangeNotifierProvider(
create: (_) => LocalizationNotifier(),
), // Провайдер для локализации
],
child: child,
);
}
}

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import 'package:friflex_starter/di/di_container.dart';
import 'package:provider/provider.dart';
/// Класс для внедрения глобальных зависимостей
final class DependsProviders extends StatelessWidget {
const DependsProviders({
super.key,
required this.child,
required this.diContainer,
});
final Widget child;
final DiContainer diContainer;
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
// Сюда добавляем глобальные блоки, inherited и т.д.
Provider.value(value: diContainer), // Передаем контейнер зависимостей
],
child: child,
);
}
}

View File

@@ -1,13 +1,12 @@
import 'package:app_services/app_services.dart';
import 'package:friflex_starter/app/app_config/app_config.dart';
import 'package:friflex_starter/app/app_config/i_app_config.dart';
import 'package:friflex_starter/app/app_env.dart';
import 'package:friflex_starter/app/http/app_http_client.dart';
import 'package:friflex_starter/app/http/i_http_client.dart';
import 'package:friflex_starter/di/di_repositories.dart';
import 'package:friflex_starter/di/di_services.dart';
import 'package:friflex_starter/di/di_typedefs.dart';
import 'package:friflex_starter/features/debug/i_debug_service.dart';
import 'package:i_app_services/i_app_services.dart';
/// {@template dependencies_container}
/// Контейнер для зависимостей
@@ -22,82 +21,53 @@ final class DiContainer {
/// Сервис для отладки, получаем из конструктора
late final IDebugService debugService;
/// Сервис для работы с путями
late final IPathProvider pathProvider;
/// Конфигурация приложения
late final IAppConfig appConfig;
/// Сервис для работы с локальным хранилищем
late final ISecureStorage secureStorage;
/// Сервис для работы с HTTP запросами
late final IHttpClient httpClient;
late final IHttpClient Function(IDebugService, IAppConfig) httpClientFactory;
/// Репозитории приложения
late final DiRepositories repositories;
/// Сервисы приложения
late final DiServices services;
/// Метод для инициализации зависимостей
Future<void> init({
required OnProgress onProgress,
required OnComplete onComplete,
required OnError onError,
}) async {
// Инициализация сервисов
await _initServices(
onComplete: onComplete,
onError: onError,
onProgress: onProgress,
);
// Инициализация репозиториев
repositories = DiRepositories();
repositories.init(
onProgress: onProgress,
onComplete: onComplete,
onError: onError,
diContainer: this,
);
onComplete('Инициализация зависимостей завершена!');
}
/// Метод для инициализации сервисов
Future<void> _initServices({
required OnComplete onComplete,
required OnError onError,
required OnProgress onProgress,
}) async {
// Инициализация конфигурации приложения
appConfig = switch (env) {
AppEnv.dev => AppConfigDev(),
AppEnv.prod => AppConfigProd(),
AppEnv.stage => AppConfigStage()
};
httpClient = AppHttpClient(
debugService: debugService,
appConfig: appConfig,
);
// Инициализация HTTP клиента
httpClientFactory = (debugService, appConfig) => AppHttpClient(
debugService: debugService,
appConfig: appConfig,
);
try {
pathProvider = AppPathProvider();
onProgress(AppPathProvider.name);
} on Object catch (error, stackTrace) {
onError(
'Ошибка инициализации ${IPathProvider.name}',
error,
stackTrace,
// Инициализация сервисов
services = DiServices()
..init(
onProgress: onProgress,
onError: onError,
diContainer: this,
);
// throw Exception('Тестовая - ошибка инициализации зависимостей');
// Инициализация репозиториев
repositories = DiRepositories()
..init(
onProgress: onProgress,
onError: onError,
diContainer: this,
);
}
try {
secureStorage = AppSecureStorage(secretKey: appConfig.secretKey);
onProgress(AppSecureStorage.name);
} on Object catch (error, stackTrace) {
onError(
'Ошибка инициализации ${ISecureStorage.name}',
error,
stackTrace,
);
}
onComplete('Инициализация зависимостей завершена!');
}
}

View File

@@ -38,20 +38,21 @@ final class DiRepositories {
///
/// Принимает:
/// - [onProgress] - обратный вызов при прогрессе
/// - [onComplete] - обратный вызов при успешной инициализации
/// - [diContainer] - контейнер зависимостей
void init({
required OnProgress onProgress,
required OnComplete onComplete,
required OnError onError,
required DiContainer diContainer,
}) {
try {
//Инициализация репозитория авторизации
authRepository = lazyInitRepo<IAuthRepository>(
authRepository = _lazyInitRepo<IAuthRepository>(
mockFactory: AuthMockRepository.new,
mainFactory: () => AuthRepository(
httpClient: diContainer.httpClient,
httpClient: diContainer.httpClientFactory(
diContainer.debugService,
diContainer.appConfig,
),
),
onProgress: onProgress,
environment: diContainer.env,
@@ -67,10 +68,13 @@ final class DiRepositories {
try {
// Инициализация репозитория сервиса управления токеном доступа
mainRepository = lazyInitRepo<IMainRepository>(
mainRepository = _lazyInitRepo<IMainRepository>(
mockFactory: MainMockRepository.new,
mainFactory: () => MainRepository(
httpClient: diContainer.httpClient,
httpClient: diContainer.httpClientFactory(
diContainer.debugService,
diContainer.appConfig,
),
),
onProgress: onProgress,
environment: diContainer.env,
@@ -84,7 +88,7 @@ final class DiRepositories {
);
}
onComplete(
onProgress(
'Инициализация репозиториев завершена! Было подменено репозиториев - ${_mockReposToSwitch.length} (${_mockReposToSwitch.join(', ')})',
);
}
@@ -96,18 +100,22 @@ final class DiRepositories {
/// - [mockFactory] - функция - фабрика для инициализации репозитория для управления моковыми запросами
/// - [mainFactory] - функция - фабрика для инициализации основного репозиторий
/// - [onProgress] - обратный вызов при прогрессе
T lazyInitRepo<T extends DiBaseRepo>({
T _lazyInitRepo<T extends DiBaseRepo>({
required AppEnv environment,
required T Function() mainFactory,
required T Function() mockFactory,
required OnProgress onProgress,
}) {
final mockRepo = mockFactory();
final mainRepo = mainFactory();
final repo = switch (environment) {
AppEnv.dev => mockFactory(),
AppEnv.prod => mainFactory(),
AppEnv.dev => mockRepo,
AppEnv.prod => mainRepo,
AppEnv.stage =>
_mockReposToSwitch.contains(mockFactory().name) ? mockFactory() : mainFactory(),
_mockReposToSwitch.contains(mockRepo.name) ? mockRepo : mainRepo,
};
onProgress(repo.name);
return repo;
}

50
lib/di/di_services.dart Normal file
View File

@@ -0,0 +1,50 @@
import 'package:app_services/app_services.dart';
import 'package:friflex_starter/di/di_container.dart';
import 'package:friflex_starter/di/di_typedefs.dart';
import 'package:i_app_services/i_app_services.dart';
/// Класс для инициализации сервисов
final class DiServices {
/// Сервис для работы с путями
late final IPathProvider pathProvider;
/// Сервис для работы с локальным хранилищем
late final ISecureStorage secureStorage;
/// Метод для инициализации репозиториев в приложении
///
/// Принимает:
/// - [onProgress] - обратный вызов при прогрессе
/// - [diContainer] - контейнер зависимостей
/// - [onError] - обратный вызов при ошибке
void init({
required OnProgress onProgress,
required OnError onError,
required DiContainer diContainer,
}) {
try {
pathProvider = AppPathProvider();
onProgress(AppPathProvider.name);
} on Object catch (error, stackTrace) {
onError(
'Ошибка инициализации ${IPathProvider.name}',
error,
stackTrace,
);
}
try {
secureStorage = AppSecureStorage(
secretKey: diContainer.appConfig.secretKey,
);
onProgress(AppSecureStorage.name);
} on Object catch (error, stackTrace) {
onError(
'Ошибка инициализации ${ISecureStorage.name}',
error,
stackTrace,
);
}
onProgress('Инициализация сервисов завершена!');
}
}

View File

@@ -16,7 +16,7 @@ class DebugScreen extends StatelessWidget {
padding: const EdgeInsets.all(16),
children: [
Text(
'Реализация SecureStorage: ${context.di.secureStorage.nameImpl}',
'Реализация SecureStorage: ${context.di.services.secureStorage.nameImpl}',
),
const SizedBox(height: 16),
Text(
@@ -82,14 +82,37 @@ class DebugScreen extends StatelessWidget {
),
const SizedBox(height: 16),
const Text('Тестовая иконка из assets'),
const SizedBox(height: 16),
Assets.icons.home.svg(),
ElevatedButton(
onPressed: () {
throw Exception(
'Тестовая ошибка Exception для отладки FlutterError',
);
},
child: const Text('Вызывать ошибку FlutterError'),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () async {
await _callError();
},
child: const Text('Вызывать ошибку PlatformDispatcher'),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () async {
await context.di.debugService.openDebugScreen(context);
},
child: const Text('Вызывать Экран отладки'),
),
],
),
),
);
}
Future<void> callError() async {
Future<void> _callError() async {
throw Exception('Тестовая ошибка Exception для отладки PlatformDispatcher');
}
}

View File

@@ -5,16 +5,42 @@ import 'package:flutter/material.dart';
/// {@endtemplate}
class ErrorScreen extends StatelessWidget {
/// {@macro ErrorScreen}
const ErrorScreen({super.key});
const ErrorScreen({
super.key,
required this.error,
required this.stackTrace,
this.onRetry,
});
final Object? error;
final StackTrace? stackTrace;
final VoidCallback? onRetry;
@override
Widget build(BuildContext context) {
return const MaterialApp(
return MaterialApp(
home: Scaffold(
body: Center(
child: Text(
'Что-то пошло не так, попробуйте перезагрузить приложение',
textAlign: TextAlign.center,
body: SafeArea(
child: Center(
child: ListView(
padding: const EdgeInsets.all(16),
children: [
const SizedBox(height: 16),
ElevatedButton(
onPressed: onRetry,
child: const Text('Перезагрузить приложение'),
),
const SizedBox(height: 16),
Text(
'''
Что-то пошло не так, попробуйте перезагрузить приложение
error: $error
stackTrace: $stackTrace
''',
textAlign: TextAlign.center,
),
],
),
),
),
),

View File

@@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:friflex_starter/gen/assets.gen.dart';
/// {@template SplashScreen}
/// Экран загрузки приложения.
/// {@endtemplate}
class SplashScreen extends StatelessWidget {
/// {@macro SplashScreen}
const SplashScreen({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: Assets.lottie.splash.lottie(),
);
}
}

View File

@@ -10,6 +10,7 @@
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart' as _svg;
import 'package:lottie/lottie.dart' as _lottie;
import 'package:vector_graphics/vector_graphics.dart' as _vg;
class $AssetsFontsGen {
@@ -50,11 +51,23 @@ class $AssetsIconsGen {
List<SvgGenImage> get values => [home];
}
class $AssetsLottieGen {
const $AssetsLottieGen();
/// File path: assets/lottie/splash.json
LottieGenImage get splash =>
const LottieGenImage('assets/lottie/splash.json');
/// List of all assets
List<LottieGenImage> get values => [splash];
}
class Assets {
Assets._();
static const $AssetsFontsGen fonts = $AssetsFontsGen();
static const $AssetsIconsGen icons = $AssetsIconsGen();
static const $AssetsLottieGen lottie = $AssetsLottieGen();
}
class SvgGenImage {
@@ -133,3 +146,70 @@ class SvgGenImage {
String get keyName => _assetName;
}
class LottieGenImage {
const LottieGenImage(
this._assetName, {
this.flavors = const {},
});
final String _assetName;
final Set<String> flavors;
_lottie.LottieBuilder lottie({
Animation<double>? controller,
bool? animate,
_lottie.FrameRate? frameRate,
bool? repeat,
bool? reverse,
_lottie.LottieDelegates? delegates,
_lottie.LottieOptions? options,
void Function(_lottie.LottieComposition)? onLoaded,
_lottie.LottieImageProviderFactory? imageProviderFactory,
Key? key,
AssetBundle? bundle,
Widget Function(
BuildContext,
Widget,
_lottie.LottieComposition?,
)? frameBuilder,
ImageErrorWidgetBuilder? errorBuilder,
double? width,
double? height,
BoxFit? fit,
AlignmentGeometry? alignment,
String? package,
bool? addRepaintBoundary,
FilterQuality? filterQuality,
void Function(String)? onWarning,
}) {
return _lottie.Lottie.asset(
_assetName,
controller: controller,
animate: animate,
frameRate: frameRate,
repeat: repeat,
reverse: reverse,
delegates: delegates,
options: options,
onLoaded: onLoaded,
imageProviderFactory: imageProviderFactory,
key: key,
bundle: bundle,
frameBuilder: frameBuilder,
errorBuilder: errorBuilder,
width: width,
height: height,
fit: fit,
alignment: alignment,
package: package,
addRepaintBoundary: addRepaintBoundary,
filterQuality: filterQuality,
onWarning: onWarning,
);
}
String get path => _assetName;
String get keyName => _assetName;
}

View File

@@ -3,6 +3,7 @@ import 'package:friflex_starter/features/debug/debug_routes.dart';
import 'package:friflex_starter/features/debug/i_debug_service.dart';
import 'package:friflex_starter/features/main/presentation/main_routes.dart';
import 'package:friflex_starter/features/root/root_screen.dart';
import 'package:friflex_starter/features/splash/splash_screen.dart';
import 'package:go_router/go_router.dart';
/// Класс, реализующий роутер приложения и все поля классов
@@ -14,7 +15,7 @@ class AppRouter {
static final rootNavigatorKey = GlobalKey<NavigatorState>();
/// Начальный роут приложения
static String get initialLocation => '/debug';
static String get initialLocation => '/debug';
/// Метод для создания экземпляра GoRouter
static GoRouter createRouter(IDebugService debugService) {
@@ -32,6 +33,10 @@ class AppRouter {
DebugRoutes.buildShellBranch(),
],
),
GoRoute(
path: '/splash',
builder: (context, state) => const SplashScreen(),
),
],
);
}

View File

@@ -15,6 +15,11 @@ import 'package:go_router/go_router.dart';
part 'errors_handlers.dart';
/// Время ожидания инициализации зависимостей
/// Если время превышено, то будет показан экран ошибки
/// В дальнейшем нужно убрать в env
const _initTimeout = Duration(seconds: 7);
/// Класс, реализующий раннер для конфигурирования приложения при запуске
///
/// Порядок инициализации:
@@ -33,38 +38,63 @@ class AppRunner {
final AppEnv env;
/// Контейнер зависимостей приложения
late final IDebugService _debugService;
late IDebugService _debugService;
/// Роутер приложения
late final GoRouter router;
late GoRouter router;
/// Таймер для отслеживания времени инициализации приложения
late final TimerRunner _timerRunner;
late TimerRunner _timerRunner;
/// Метод для запуска приложения
Future<void> run() async {
WidgetsFlutterBinding.ensureInitialized();
// Инициализация сервиса отладки
_debugService = DebugService();
try {
WidgetsFlutterBinding.ensureInitialized();
// Инициализация сервиса отладки
_debugService = DebugService();
_timerRunner = TimerRunner(_debugService);
_timerRunner = TimerRunner(_debugService);
// Инициализация приложения
await _initApp();
// Инициализация приложения
await _initApp();
// Инициализация метода обработки ошибок
_initErrorHandlers(_debugService);
// Инициализация метода обработки ошибок
_initErrorHandlers(_debugService);
// Инициализация репозиториев и сервисов
final diContainer = await _initDependencies(_debugService);
// Инициализация роутера
router = AppRouter.createRouter(_debugService);
// Инициализация роутера
router = AppRouter.createRouter(_debugService);
// throw Exception('Test error');
runApp(
App(diContainer: diContainer, router: router),
);
await _onAppLoaded();
runApp(
App(
router: router,
initDependencies: () {
return _initDependencies(
debugService: _debugService,
env: env,
timerRunner: _timerRunner,
).timeout(
_initTimeout,
onTimeout: () {
return Future.error(
TimeoutException(
'Превышено время ожидания инициализации зависимостей',
),
);
},
);
},
),
);
await _onAppLoaded();
} on Object catch (e, stackTrace) {
await _onAppLoaded();
/// Если произошла ошибка при инициализации приложения,
/// то запускаем экран ошибки
runApp(ErrorScreen(error: e, stackTrace: stackTrace, onRetry: run));
}
}
/// Метод инициализации приложения,
@@ -85,23 +115,36 @@ class AppRunner {
WidgetsBinding.instance.addPostFrameCallback((_) {
WidgetsBinding.instance.allowFirstFrame();
});
_timerRunner.stop();
}
/// Метод для инициализации зависимостей приложения
Future<DiContainer> _initDependencies(IDebugService debugService) async {
// Метод для инициализации зависимостей приложения
Future<DiContainer> _initDependencies({
required IDebugService debugService,
required AppEnv env,
required TimerRunner timerRunner,
}) async {
// Имитация задержки инициализации
// TODO(yura): Удалить после проверки
await Future.delayed(const Duration(seconds: 3));
debugService.log(() => 'Тип сборки: ${env.name}');
final diContainer = DiContainer(
env: env,
dService: debugService,
);
await diContainer.init(
onProgress: _timerRunner.logOnProgress,
onComplete: _timerRunner.logOnComplete,
onError: _timerRunner.logOnError,
onProgress: (name) => timerRunner.logOnProgress(name),
onComplete: (name) {
timerRunner
..logOnComplete(name)
..stop();
},
onError: (message, error, [stackTrace]) => debugService.logError(
message,
error: error,
stackTrace: stackTrace,
),
);
//throw Exception('Test error');
return diContainer;
}
}

View File

@@ -4,7 +4,7 @@ part of 'app_runner.dart';
void _initErrorHandlers(IDebugService debugService) {
// Обработка ошибок в приложении
FlutterError.onError = (details) {
_showErrorScreen();
_showErrorScreen(details.exception, details.stack);
debugService.logError(
() => 'FlutterError.onError: ${details.exceptionAsString()}',
error: details.exception,
@@ -13,7 +13,7 @@ void _initErrorHandlers(IDebugService debugService) {
};
// Обработка асинхронных ошибок в приложении
PlatformDispatcher.instance.onError = (error, stack) {
_showErrorScreen();
_showErrorScreen(error, stack);
debugService.logError(
() => 'PlatformDispatcher.instance.onError',
error: error,
@@ -24,11 +24,11 @@ void _initErrorHandlers(IDebugService debugService) {
}
/// Метод для показа экрана ошибки
void _showErrorScreen() {
void _showErrorScreen(Object error, StackTrace? stackTrace) {
WidgetsBinding.instance.addPostFrameCallback((_) {
AppRouter.rootNavigatorKey.currentState?.push(
MaterialPageRoute(
builder: (_) => const ErrorScreen(),
builder: (_) => ErrorScreen(error: error, stackTrace: stackTrace),
),
);
});

View File

@@ -36,6 +36,7 @@ class TimerRunner {
_debugService.log(
'$message, прогресс: ${_stopwatch.elapsedMilliseconds} мс',
);
}
/// Метод для обработки прогресса инициализации зависимостей

View File

@@ -29,6 +29,14 @@ packages:
relative: true
source: path
version: "0.0.1"
archive:
dependency: transitive
description:
name: archive
sha256: "6199c74e3db4fbfbd04f66d739e72fe11c8a8957d5f219f1f4482dbde6420b5a"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
args:
dependency: transitive
description:
@@ -585,6 +593,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
lottie:
dependency: "direct main"
description:
name: lottie
sha256: c5fa04a80a620066c15cf19cc44773e19e9b38e989ff23ea32e5903ef1015950
url: "https://pub.dev"
source: hosted
version: "3.3.1"
macros:
dependency: transitive
description:
@@ -754,6 +770,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.1"
posix:
dependency: transitive
description:
name: posix
sha256: a0117dc2167805aa9125b82eee515cc891819bac2f538c83646d355b16f58b9a
url: "https://pub.dev"
source: hosted
version: "6.0.1"
provider:
dependency: "direct main"
description:
@@ -993,4 +1017,4 @@ packages:
version: "3.1.3"
sdks:
dart: ">=3.6.0 <4.0.0"
flutter: ">=3.24.0"
flutter: ">=3.27.0"

View File

@@ -21,6 +21,7 @@ dependencies:
flutter_svg: 2.0.17
flutter_localizations:
sdk: flutter
lottie: 3.3.1
### основной сервис с интерфейсами
i_app_services:
@@ -51,6 +52,7 @@ flutter:
- assets/icons/
- assets/fonts/
- assets/images/
- assets/lottie/
fonts:
- family: Montserrat
@@ -72,4 +74,5 @@ flutter_gen:
line_length: 100
integrations:
flutter_svg: true
lottie: true