This commit is contained in:
petrovyuri
2025-01-21 14:24:31 +03:00
parent e7b2c31e86
commit 17d096baac
96 changed files with 3575 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
import '../../domain/repository/i_auth_repository.dart';
/// {@template AuthMockRepository}
///
/// {@endtemplate}
final class AuthMockRepository implements IAuthRepository {
@override
String get name => 'AuthMockRepository';
}

View File

@@ -0,0 +1,15 @@
import 'package:friflex_starter/app/http/i_http_client.dart';
import '../../domain/repository/i_auth_repository.dart';
/// {@template AuthRepository}
///
/// {@endtemplate}
final class AuthRepository implements IAuthRepository {
final IHttpClient httpClient;
AuthRepository({required this.httpClient});
@override
String get name => 'AuthRepository';
}

View File

@@ -0,0 +1,6 @@
import 'package:friflex_starter/di/di_base_repo.dart';
/// {@template IAuthRepository}
///
/// {@endtemplate}
abstract interface class IAuthRepository with DiBaseRepo {}

View File

@@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
/// {@template AuthScreen}
///
/// {@endtemplate}
class AuthScreen extends StatelessWidget {
/// {@macro AuthScreen}
const AuthScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AuthScreen'),
),
body: const Center(
child: Text('AuthScreen'),
),
);
}
}

View File

@@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:friflex_starter/features/debug/i_debug_service.dart';
import 'package:talker_bloc_logger/talker_bloc_logger.dart';
import 'package:talker_dio_logger/talker_dio_logger_interceptor.dart';
import 'package:talker_flutter/talker_flutter.dart';
/// Класс реализации интерфейса дебаг сервиса
class AppDebugService implements IDebugService {
/// Наименование сервиса
static const name = 'GmsDebugService';
final Talker _talker = TalkerFlutter.init();
@override
TalkerBlocObserver createBlocObserver() =>
TalkerBlocObserver(talker: _talker);
@override
TalkerDioLogger createHttpInterceptor() => TalkerDioLogger(talker: _talker);
@override
TalkerRouteObserver createRouterObserver() => TalkerRouteObserver(_talker);
@override
void error(String msg, [Object? exception, StackTrace? stackTrace]) {
_talker.error(msg, exception, stackTrace);
}
@override
void handleError(Object error, [StackTrace? stackTrace, String? message]) {
_talker.handle(error, stackTrace, message);
}
@override
void info(String message) {
_talker.info(message);
}
@override
void log(String message) {
_talker.log(message);
}
@override
void warning(String message) {
_talker.warning(message);
}
@override
Future<T?> openDebugScreen<T>(
BuildContext context, {
bool useRootNavigator = false,
}) {
return Navigator.of(context).push<T>(
MaterialPageRoute(
builder: (context) => TalkerScreen(talker: _talker),
),
);
}
}

View File

@@ -0,0 +1,32 @@
import 'package:flutter/widgets.dart';
import 'package:friflex_starter/features/debug/debug_screen.dart';
import 'package:go_router/go_router.dart';
abstract final class DebugRoutes {
/// Название роута страницы профиля пользователя
static const String debugScreenName = 'debug_screen';
/// Путь роута страницы профиля пользователя
static const String _debugScreenPath = '/debug';
/// Метод для построения ветки роутов по фиче профиля пользователя
///
/// Принимает:
/// - [routes] - вложенные роуты
static StatefulShellBranch buildShellBranch({
List<RouteBase> routes = const [],
List<NavigatorObserver>? observers,
}) =>
StatefulShellBranch(
initialLocation: _debugScreenPath,
observers: observers,
routes: [
GoRoute(
path: _debugScreenPath,
name: debugScreenName,
builder: (context, state) => const DebugScreen(),
routes: routes,
),
],
);
}

View File

@@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import 'package:friflex_starter/app/app_context_ext.dart';
class DebugScreen extends StatelessWidget {
const DebugScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Debug Screen')),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
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('Вызывать Talker'),
),
],
),
),
);
}
Future<void> callError() async {
throw Exception('Тестовая ошибка Exception для отладки PlatformDispatcher');
}
}

View File

@@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
/// Интерфейс для сервиса отладки
abstract interface class IDebugService {
/// Наименование сервиса
static const name = 'IDebugService';
/// Метод для создания обработчика для BLoC
Object createBlocObserver();
/// Метод для создания обработчика для роутера
NavigatorObserver createRouterObserver();
/// Метод для создания обработчика для http-клиентов
Object createHttpInterceptor();
/// Метод для логгирования предупреждений
///
/// Принимает:
/// - [message] - сообщение для логгирования в формате [String]
void warning(String message);
/// Метод для логгирования ошибок
///
/// Принимает:
/// - [message] - сообщение для логгирования в формате [String]
/// - [exception] - исключение
/// - [stackTrace] - стек вызова
void error(String message, [Object? exception, StackTrace? stackTrace]);
/// Метод для обработки ошибок
///
/// Принимает:
/// - [error] - исключение
/// - [stackTrace] - стек вызова
/// - [message] - сообщение для логгирования в формате [String]
void handleError(Object error, [StackTrace? stackTrace, String? message]);
/// Метод для логгирования информативных сообщений
///
/// Принимает:
/// - [message] - сообщение для логгирования в формате [String]
void info(String message);
/// Метод для логгирования сообщений
///
/// Принимает:
/// - [message] - сообщение для логгирования в формате [String]
void log(String message);
/// Метод для открытия окна отладки
///
/// Принимает:
/// - [context] - для определения навигатора по нему
/// - [useRootNavigator] - при true, открывает окно в корневом навигаторе
Future<T?> openDebugScreen<T>(
BuildContext context, {
bool useRootNavigator = false,
});
}

View File

@@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
/// {@template ErrorScreen}
/// Экран, когда в приложении произошла фатальная ошибка
/// {@endtemplate}
class ErrorScreen extends StatelessWidget {
/// {@macro ErrorScreen}
const ErrorScreen({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: Text(
'Что-то пошло не так, попробуйте перезагрузить приложение',
textAlign: TextAlign.center,
),
),
),
);
}
}

View File

@@ -0,0 +1,9 @@
import '../../domain/repository/i_main_repository.dart';
/// {@template MainMockRepository}
///
/// {@endtemplate}
final class MainMockRepository implements IMainRepository {
@override
String get name => 'MainMockRepository';
}

View File

@@ -0,0 +1,15 @@
import 'package:friflex_starter/app/http/i_http_client.dart';
import '../../domain/repository/i_main_repository.dart';
/// {@template MainRepository}
///
/// {@endtemplate}
final class MainRepository implements IMainRepository {
final IHttpClient httpClient;
MainRepository({required this.httpClient});
@override
String get name => 'MainRepository';
}

View File

@@ -0,0 +1,6 @@
import 'package:friflex_starter/di/di_base_repo.dart';
/// {@template IMainRepository}
///
/// {@endtemplate}
abstract interface class IMainRepository with DiBaseRepo{}

View File

@@ -0,0 +1,32 @@
import 'package:flutter/widgets.dart';
import 'package:friflex_starter/features/main/presentation/screens/main_screen.dart';
import 'package:go_router/go_router.dart';
abstract final class MainRoutes {
/// Название роута главной страницы
static const String mainScreenName = 'main_screen';
/// Путь роута страницы профиля пользователя
static const String _mainScreenPath = '/main';
/// Метод для построения ветки роутов по фиче профиля пользователя
///
/// Принимает:
/// - [routes] - вложенные роуты
static StatefulShellBranch buildShellBranch({
List<RouteBase> routes = const [],
List<NavigatorObserver>? observers,
}) =>
StatefulShellBranch(
initialLocation: _mainScreenPath,
observers: observers,
routes: [
GoRoute(
path: _mainScreenPath,
name: mainScreenName,
builder: (context, state) => const MainScreen(),
routes: routes,
),
],
);
}

View File

@@ -0,0 +1,73 @@
import 'package:flutter/material.dart';
import 'package:friflex_starter/app/app_context_ext.dart';
import 'package:friflex_starter/app/theme/app_colors_scheme.dart';
class MainScreen extends StatelessWidget {
const MainScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Main Screen'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(height: 16),
Text(
'Окружение: ${context.di.appConfig.env.name}',
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
context.theme.changeTheme();
},
child: const Text('Сменить тему'),
),
const SizedBox(height: 16),
ColoredBox(
color: context.colors.testColor,
child: const SizedBox(height: 100, width: 100),
),
const SizedBox(height: 16),
Text(
'Текущая тема: ${context.theme.themeMode}',
),
const SizedBox(height: 16),
Text(
'Текущий репозиторий: ${context.di.repositories.authRepository.name}',
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
context.localization.changeLocal(
const Locale('ru', 'RU'),
);
},
child: const Text('Сменить язык на Rусский'),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
context.localization.changeLocal(
const Locale('en', 'EN'),
);
},
child: const Text('Сменить язык на Английский'),
),
const SizedBox(height: 16),
Text(
'Тестовое слово: ${context.l10n.helloWorld}',
),
const SizedBox(height: 16),
Text(
'Текущий язык: ${context.l10n.localeName}',
),
],
),
),
);
}
}

View File

@@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
/// Класс для реализации корневой страницы приложения
class RootScreen extends StatelessWidget {
/// Создает корневую страницу приложения
///
/// Принимает:
/// - [navigationShell] - текущая ветка навигации
const RootScreen({
super.key,
required this.navigationShell,
});
/// Текущая ветка навигации
final StatefulNavigationShell navigationShell;
@override
Widget build(BuildContext context) {
return Scaffold(
body: navigationShell,
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.bug_report), label: 'Debug'),
],
currentIndex: navigationShell.currentIndex,
onTap: navigationShell.goBranch,
),
);
}
}