mirror of
https://github.com/smmarty/friflex_flutter_starter.git
synced 2026-02-05 03:32:18 +00:00
refactor(app): обновить структуру приложения и удалить устаревшие провайдеры (#44)
* refactor(app): обновить структуру приложения и удалить устаревшие провайдеры * refactor(runner): упростить обработку ошибок и улучшить логирование времени инициализации * refactor(runner): улучшить порядок инициализации приложения и обработку ошибок * refactor(app): исправить контекст MediaQuery для предотвращения перерисовки * refactor(pp): удалить главный виджет приложения и заменить его на AppRoot * docs(copilot-instructions): уточнить правила проведения Code Review на русском языке * refactor(linter): добавить правило avoid_catches_without_on_clauses для улучшения обработки исключений --------- Co-authored-by: petrovyuri <petrovyuri@example.com>
This commit is contained in:
157
lib/app/app.dart
157
lib/app/app.dart
@@ -1,157 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.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/features/update/domain/state/cubit/update_cubit.dart';
|
||||
import 'package:friflex_starter/features/update/update_routes.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';
|
||||
|
||||
/// {@template app}
|
||||
/// Главный виджет приложения, управляющий инициализацией зависимостей
|
||||
/// и отображением основного интерфейса приложения.
|
||||
///
|
||||
/// Отвечает за:
|
||||
/// - Инициализацию зависимостей приложения
|
||||
/// - Отображение экрана загрузки во время инициализации
|
||||
/// - Обработку ошибок инициализации
|
||||
/// - Настройку провайдеров для темы и локализации
|
||||
/// {@endtemplate}
|
||||
class App extends StatefulWidget {
|
||||
/// {@macro app}
|
||||
const App({required this.router, required this.initDependencies, super.key});
|
||||
|
||||
/// Роутер приложения для навигации между экранами
|
||||
final GoRouter router;
|
||||
|
||||
/// Функция для инициализации зависимостей приложения
|
||||
/// Возвращает Future с контейнером зависимостей
|
||||
final Future<DiContainer> Function() initDependencies;
|
||||
|
||||
@override
|
||||
State<App> createState() => _AppState();
|
||||
}
|
||||
|
||||
/// {@template app_state}
|
||||
/// Состояние главного виджета приложения.
|
||||
///
|
||||
/// Управляет процессом инициализации зависимостей и отображением
|
||||
/// соответствующих экранов в зависимости от состояния инициализации.
|
||||
/// {@endtemplate}
|
||||
class _AppState extends State<App> {
|
||||
/// {@macro app_state}
|
||||
_AppState();
|
||||
|
||||
/// Мутабельная Future для инициализации зависимостей
|
||||
/// Позволяет перезапускать инициализацию при ошибках
|
||||
late Future<DiContainer> _initFuture;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initFuture = widget.initDependencies();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppProviders(
|
||||
// Consumer для локализации добавляем выше чем DependsProviders
|
||||
// чтобы при изменении локализации перестраивался весь виджет
|
||||
// Но, это не обязательно, можно добавить в DependsProviders
|
||||
child: LocalizationConsumer(
|
||||
builder: () => FutureBuilder<DiContainer>(
|
||||
future: _initFuture,
|
||||
builder: (_, snapshot) {
|
||||
return switch (snapshot.connectionState) {
|
||||
// Если состояние не определено, ожидается или активно, то отображаем экран загрузки
|
||||
ConnectionState.none ||
|
||||
ConnectionState.waiting ||
|
||||
ConnectionState.active => const SplashScreen(),
|
||||
ConnectionState.done =>
|
||||
// Если данные получены и не равны null, то отображаем внутренний виджет приложения
|
||||
// Иначе отображаем экран ошибки
|
||||
(snapshot.hasData && snapshot.data != null)
|
||||
? _App(router: widget.router, diContainer: snapshot.data!)
|
||||
: ErrorScreen(
|
||||
error: snapshot.error,
|
||||
stackTrace: snapshot.stackTrace,
|
||||
onRetry: _retryInit,
|
||||
),
|
||||
};
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Метод для перезапуска инициализации зависимостей
|
||||
/// Вызывается при ошибках инициализации для повторной попытки
|
||||
void _retryInit() {
|
||||
setState(() {
|
||||
_initFuture = widget.initDependencies();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// {@template app_internal}
|
||||
/// Внутренний виджет приложения, отображающий основной интерфейс
|
||||
/// после успешной инициализации зависимостей.
|
||||
///
|
||||
/// Настраивает MaterialApp с роутером, темами и локализацией.
|
||||
/// {@endtemplate}
|
||||
class _App extends StatelessWidget {
|
||||
/// {@macro app_internal}
|
||||
const _App({required this.router, required this.diContainer});
|
||||
|
||||
/// Роутер приложения для навигации
|
||||
final GoRouter router;
|
||||
|
||||
/// Контейнер зависимостей
|
||||
final DiContainer diContainer;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DependsProviders(
|
||||
diContainer: diContainer,
|
||||
child: BlocConsumer<UpdateCubit, UpdateState>(
|
||||
listener: (context, state) {
|
||||
if (state is UpdateSuccessState &&
|
||||
state.updateInfo.updateType == .hard &&
|
||||
context.mounted) {
|
||||
router.goNamed(UpdateRoutes.hardUpdateScreenName);
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
// Если состояние загрузки, то отображаем экран загрузки
|
||||
if (state is UpdateLoadingState) {
|
||||
return const SplashScreen();
|
||||
}
|
||||
return ThemeConsumer(
|
||||
builder: () => MediaQuery(
|
||||
key: const ValueKey('prevent_rebuild'),
|
||||
data: MediaQuery.of(
|
||||
context,
|
||||
).copyWith(textScaler: TextScaler.noScaling, boldText: false),
|
||||
child: MaterialApp.router(
|
||||
routerConfig: router,
|
||||
darkTheme: AppTheme.dark,
|
||||
theme: AppTheme.light,
|
||||
themeMode: context.theme.themeMode,
|
||||
locale: context.localization.locale,
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,28 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:friflex_starter/app/theme/theme_notifier.dart';
|
||||
import 'package:friflex_starter/di/di_container.dart';
|
||||
import 'package:friflex_starter/features/update/domain/state/cubit/update_cubit.dart';
|
||||
import 'package:friflex_starter/l10n/localization_notifier.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
/// Класс для добавления провайдеров темы и локализации
|
||||
/// {@template app_providers}
|
||||
/// Класс для добавления зависимостей приложения
|
||||
/// {@endtemplate}
|
||||
final class AppProviders extends StatelessWidget {
|
||||
const AppProviders({required this.child, super.key});
|
||||
/// {@macro app_providers}
|
||||
const AppProviders({
|
||||
required this.child,
|
||||
required this.diContainer,
|
||||
super.key,
|
||||
});
|
||||
|
||||
/// Виджет, который будет отображаться внутри провайдеров
|
||||
final Widget child;
|
||||
|
||||
/// Контейнер зависимостей
|
||||
final DiContainer diContainer;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MultiProvider(
|
||||
@@ -19,6 +33,10 @@ final class AppProviders extends StatelessWidget {
|
||||
ChangeNotifierProvider(
|
||||
create: (_) => LocalizationNotifier(),
|
||||
), // Провайдер для локализации
|
||||
Provider.value(value: diContainer), // Передаем контейнер зависимостей
|
||||
BlocProvider(
|
||||
create: (_) => UpdateCubit(diContainer.repositories.updateRepository),
|
||||
),
|
||||
],
|
||||
child: child,
|
||||
);
|
||||
|
||||
54
lib/app/app_root.dart
Normal file
54
lib/app/app_root.dart
Normal file
@@ -0,0 +1,54 @@
|
||||
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/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/l10n/gen/app_localizations.dart';
|
||||
import 'package:friflex_starter/l10n/localization_notifier.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
/// {@template app}
|
||||
/// Главный виджет приложения, отображающий основной интерфейс приложения
|
||||
///
|
||||
/// Отвечает за:
|
||||
/// - Настройку провайдеров для темы и локализации
|
||||
/// {@endtemplate}
|
||||
class AppRoot extends StatelessWidget {
|
||||
/// {@macro app_root}
|
||||
const AppRoot({required this.diContainer, required this.router, super.key});
|
||||
|
||||
/// Контейнер зависимостей
|
||||
final DiContainer diContainer;
|
||||
|
||||
/// Роутер приложения
|
||||
final GoRouter router;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppProviders(
|
||||
diContainer: diContainer,
|
||||
child: LocalizationConsumer(
|
||||
builder: (localizationContext) {
|
||||
return ThemeConsumer(
|
||||
builder: (themeContext) => MediaQuery(
|
||||
key: const ValueKey('prevent_rebuild'),
|
||||
data: MediaQuery.of(
|
||||
themeContext,
|
||||
).copyWith(textScaler: TextScaler.noScaling, boldText: false),
|
||||
child: MaterialApp.router(
|
||||
darkTheme: AppTheme.dark,
|
||||
theme: AppTheme.light,
|
||||
themeMode: themeContext.theme.themeMode,
|
||||
locale: localizationContext.localization.locale,
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
routerConfig: router,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:friflex_starter/di/di_container.dart';
|
||||
import 'package:friflex_starter/features/update/domain/state/cubit/update_cubit.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
/// Класс для внедрения глобальных зависимостей
|
||||
final class DependsProviders extends StatelessWidget {
|
||||
const DependsProviders({
|
||||
required this.child,
|
||||
required this.diContainer,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
final DiContainer diContainer;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
// Сюда добавляем глобальные блоки, inherited и т.д.
|
||||
Provider.value(value: diContainer), // Передаем контейнер зависимостей
|
||||
BlocProvider(
|
||||
create: (_) {
|
||||
final updateCubit = UpdateCubit(
|
||||
diContainer.repositories.updatesRepository,
|
||||
);
|
||||
unawaited(
|
||||
updateCubit.checkForUpdates(
|
||||
versionCode:
|
||||
'1.0.0', // TODO(yura): заменить на получение из diContainer
|
||||
platform: 'android',
|
||||
),
|
||||
);
|
||||
return updateCubit;
|
||||
},
|
||||
),
|
||||
],
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
/// Тип функции для построения виджета с учетом темы
|
||||
typedef ThemeBuilder = Widget Function();
|
||||
typedef ThemeBuilder = Widget Function(BuildContext context);
|
||||
|
||||
/// {@template theme_consumer}
|
||||
/// Виджет для подписки на изменения темы приложения.
|
||||
@@ -20,8 +20,8 @@ class ThemeConsumer extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<ThemeNotifier>(
|
||||
builder: (_, _, _) {
|
||||
return builder();
|
||||
builder: (context, _, _) {
|
||||
return builder(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ final class DiRepositories {
|
||||
late final IProfileRepository profileRepository;
|
||||
|
||||
/// Интерфейс для работы с репозиторием обновлений
|
||||
late final IUpdateRepository updatesRepository;
|
||||
late final IUpdateRepository updateRepository;
|
||||
|
||||
/// Метод для инициализации репозиториев в приложении.
|
||||
///
|
||||
@@ -70,7 +70,7 @@ final class DiRepositories {
|
||||
onProgress('Начинаем инициализацию репозиториев...');
|
||||
|
||||
// Инициализация репозитория обновлений
|
||||
updatesRepository = _lazyInitRepo<IUpdateRepository>(
|
||||
updateRepository = _lazyInitRepo<IUpdateRepository>(
|
||||
mockFactory: () => const UpdateMockRepository(),
|
||||
mainFactory: () => UpdateRepository(httpClient: diContainer.httpClient),
|
||||
onProgress: onProgress,
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
/// Тип функции для построения виджета с учетом локализации
|
||||
typedef LocalizationBuilder = Widget Function();
|
||||
typedef LocalizationBuilder = Widget Function(BuildContext context);
|
||||
|
||||
/// {@template localization_consumer}
|
||||
/// Виджет для подписки на изменения локализации приложения.
|
||||
@@ -20,8 +20,8 @@ class LocalizationConsumer extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<LocalizationNotifier>(
|
||||
builder: (_, _, _) {
|
||||
return builder();
|
||||
builder: (context, _, _) {
|
||||
return builder(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import 'package:friflex_starter/runner/app_runner.dart';
|
||||
import 'package:friflex_starter/targets/prod.dart' as prod;
|
||||
|
||||
void main() => AppRunner(.prod).run();
|
||||
void main(List<String> arguments) => prod.main(arguments);
|
||||
|
||||
@@ -4,7 +4,6 @@ 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/profile/presentation/profile_routes.dart';
|
||||
import 'package:friflex_starter/features/root/root_screen.dart';
|
||||
import 'package:friflex_starter/features/splash/splash_screen.dart';
|
||||
import 'package:friflex_starter/features/update/update_routes.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
@@ -39,10 +38,6 @@ class AppRouter {
|
||||
],
|
||||
),
|
||||
DebugRoutes.buildRoutes(),
|
||||
GoRoute(
|
||||
path: '/splash',
|
||||
builder: (context, state) => const SplashScreen(),
|
||||
),
|
||||
UpdateRoutes.buildRoutes(),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -4,8 +4,8 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:friflex_starter/app/app.dart';
|
||||
import 'package:friflex_starter/app/app_env.dart';
|
||||
import 'package:friflex_starter/app/app_root.dart';
|
||||
import 'package:friflex_starter/di/di_container.dart';
|
||||
import 'package:friflex_starter/features/debug/debug_service.dart';
|
||||
import 'package:friflex_starter/features/debug/i_debug_service.dart';
|
||||
@@ -16,11 +16,6 @@ import 'package:go_router/go_router.dart';
|
||||
|
||||
part 'errors_handlers.dart';
|
||||
|
||||
/// Время ожидания инициализации зависимостей
|
||||
/// Если время превышено, то будет показан экран ошибки
|
||||
/// В дальнейшем нужно убрать в env
|
||||
const _initTimeout = Duration(seconds: 7);
|
||||
|
||||
/// Класс, реализующий раннер для конфигурирования приложения при запуске
|
||||
///
|
||||
/// Порядок инициализации:
|
||||
@@ -48,7 +43,7 @@ class AppRunner {
|
||||
late TimerRunner _timerRunner;
|
||||
|
||||
/// Метод для запуска приложения
|
||||
Future<void> run() async {
|
||||
Future<void> run(List<String> arguments) async {
|
||||
try {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
// Инициализация сервиса отладки
|
||||
@@ -62,42 +57,31 @@ class AppRunner {
|
||||
// Инициализация приложения
|
||||
await _initApp();
|
||||
|
||||
// Инициализация метода обработки ошибок
|
||||
_initErrorHandlers(_debugService);
|
||||
|
||||
// Инициализация роутера
|
||||
router = AppRouter.createRouter(_debugService);
|
||||
|
||||
// throw Exception('Test error');
|
||||
|
||||
runApp(
|
||||
App(
|
||||
router: router,
|
||||
initDependencies: () {
|
||||
return _initDependencies(
|
||||
debugService: _debugService,
|
||||
env: env,
|
||||
timerRunner: _timerRunner,
|
||||
).timeout(
|
||||
_initTimeout,
|
||||
onTimeout: () {
|
||||
return Future.error(
|
||||
TimeoutException(
|
||||
'Превышено время ожидания инициализации зависимостей',
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
final diContainer = await _initDependencies(
|
||||
debugService: _debugService,
|
||||
env: env,
|
||||
timerRunner: _timerRunner,
|
||||
);
|
||||
// Инициализация метода обработки ошибок
|
||||
_initErrorHandlers(_debugService);
|
||||
runApp(AppRoot(diContainer: diContainer, router: router));
|
||||
await _onAppLoaded();
|
||||
} on Object catch (e, stackTrace) {
|
||||
await _onAppLoaded();
|
||||
_timerRunner.stop();
|
||||
|
||||
/// Если произошла ошибка при инициализации приложения,
|
||||
/// то запускаем экран ошибки
|
||||
runApp(ErrorScreen(error: e, stackTrace: stackTrace, onRetry: run));
|
||||
runApp(
|
||||
ErrorScreen(
|
||||
error: e,
|
||||
stackTrace: stackTrace,
|
||||
onRetry: () => run(arguments),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,20 +111,32 @@ class AppRunner {
|
||||
}) async {
|
||||
debugService.log(() => 'Тип сборки: ${env.name}');
|
||||
final diContainer = DiContainer(env: env, dService: debugService);
|
||||
await diContainer.init(
|
||||
onProgress: (name) => timerRunner.logOnProgress(name),
|
||||
onComplete: (name) {
|
||||
timerRunner
|
||||
..logOnComplete(name)
|
||||
..stop();
|
||||
},
|
||||
onError: (message, error, [stackTrace]) {
|
||||
timerRunner.stop();
|
||||
_debugService.logError(message, error: error, stackTrace: stackTrace);
|
||||
throw Exception('Ошибка инициализации зависимостей: $message');
|
||||
},
|
||||
);
|
||||
//throw Exception('Test error');
|
||||
await diContainer
|
||||
.init(
|
||||
onProgress: (name) => timerRunner.logOnProgress(name),
|
||||
onComplete: (name) {
|
||||
timerRunner
|
||||
..logOnComplete(name)
|
||||
..stop();
|
||||
},
|
||||
onError: (message, error, [stackTrace]) {
|
||||
timerRunner.stop();
|
||||
_debugService.logError(
|
||||
message,
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
throw Exception('Ошибка инициализации зависимостей: $message');
|
||||
},
|
||||
)
|
||||
.timeout(
|
||||
const Duration(seconds: 7),
|
||||
onTimeout: () {
|
||||
throw Exception(
|
||||
'Превышено время ожидания инициализации зависимостей',
|
||||
);
|
||||
},
|
||||
);
|
||||
return diContainer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ part of 'app_runner.dart';
|
||||
void _initErrorHandlers(IDebugService debugService) {
|
||||
// Обработка ошибок в приложении
|
||||
FlutterError.onError = (details) {
|
||||
_showErrorScreen(details.exception, details.stack);
|
||||
debugService.logError(
|
||||
() => 'FlutterError.onError: ${details.exceptionAsString()}',
|
||||
error: details.exception,
|
||||
@@ -13,7 +12,6 @@ void _initErrorHandlers(IDebugService debugService) {
|
||||
};
|
||||
// Обработка асинхронных ошибок в приложении
|
||||
PlatformDispatcher.instance.onError = (error, stack) {
|
||||
_showErrorScreen(error, stack);
|
||||
debugService.logError(
|
||||
() => 'PlatformDispatcher.instance.onError',
|
||||
error: error,
|
||||
@@ -22,14 +20,3 @@ void _initErrorHandlers(IDebugService debugService) {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/// Метод для показа экрана ошибки
|
||||
void _showErrorScreen(Object error, StackTrace? stackTrace) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await AppRouter.rootNavigatorKey.currentState?.push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => ErrorScreen(error: error, stackTrace: stackTrace),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,6 +24,11 @@ class TimerRunner {
|
||||
);
|
||||
}
|
||||
|
||||
/// Метод для сброса секундомера
|
||||
void reset() {
|
||||
_stopwatch.reset();
|
||||
}
|
||||
|
||||
/// Метод для обработки прогресса инициализации зависимостей
|
||||
void logOnProgress(String name) {
|
||||
_debugService.log(
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import 'package:friflex_starter/runner/app_runner.dart';
|
||||
|
||||
void main() => AppRunner(.dev).run();
|
||||
void main(List<String> arguments) => AppRunner(.dev).run(arguments);
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import 'package:friflex_starter/runner/app_runner.dart';
|
||||
|
||||
void main() => AppRunner(.prod).run();
|
||||
void main(List<String> arguments) => AppRunner(.prod).run(arguments);
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import 'package:friflex_starter/runner/app_runner.dart';
|
||||
|
||||
void main() => AppRunner(.stage).run();
|
||||
void main(List<String> arguments) => AppRunner(.stage).run(arguments);
|
||||
|
||||
Reference in New Issue
Block a user