mirror of
https://github.com/smmarty/friflex_flutter_starter.git
synced 2026-02-05 03:32:18 +00:00
init
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
import '../../domain/repository/i_auth_repository.dart';
|
||||
|
||||
/// {@template AuthMockRepository}
|
||||
///
|
||||
/// {@endtemplate}
|
||||
final class AuthMockRepository implements IAuthRepository {
|
||||
@override
|
||||
String get name => 'AuthMockRepository';
|
||||
}
|
||||
15
lib/features/auth/data/repository/auth_repository.dart
Normal file
15
lib/features/auth/data/repository/auth_repository.dart
Normal 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';
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import 'package:friflex_starter/di/di_base_repo.dart';
|
||||
|
||||
/// {@template IAuthRepository}
|
||||
///
|
||||
/// {@endtemplate}
|
||||
abstract interface class IAuthRepository with DiBaseRepo {}
|
||||
21
lib/features/auth/presentation/screens/auth_screen.dart
Normal file
21
lib/features/auth/presentation/screens/auth_screen.dart
Normal 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'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
60
lib/features/debug/app_debug_service.dart
Normal file
60
lib/features/debug/app_debug_service.dart
Normal 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),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
32
lib/features/debug/debug_routes.dart
Normal file
32
lib/features/debug/debug_routes.dart
Normal 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,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
45
lib/features/debug/debug_screen.dart
Normal file
45
lib/features/debug/debug_screen.dart
Normal 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');
|
||||
}
|
||||
}
|
||||
60
lib/features/debug/i_debug_service.dart
Normal file
60
lib/features/debug/i_debug_service.dart
Normal 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,
|
||||
});
|
||||
}
|
||||
23
lib/features/error/error_screen.dart
Normal file
23
lib/features/error/error_screen.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import '../../domain/repository/i_main_repository.dart';
|
||||
|
||||
/// {@template MainMockRepository}
|
||||
///
|
||||
/// {@endtemplate}
|
||||
final class MainMockRepository implements IMainRepository {
|
||||
@override
|
||||
String get name => 'MainMockRepository';
|
||||
}
|
||||
15
lib/features/main/data/repository/main_repository.dart
Normal file
15
lib/features/main/data/repository/main_repository.dart
Normal 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';
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import 'package:friflex_starter/di/di_base_repo.dart';
|
||||
|
||||
/// {@template IMainRepository}
|
||||
///
|
||||
/// {@endtemplate}
|
||||
abstract interface class IMainRepository with DiBaseRepo{}
|
||||
32
lib/features/main/presentation/main_routes.dart
Normal file
32
lib/features/main/presentation/main_routes.dart
Normal 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,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
73
lib/features/main/presentation/screens/main_screen.dart
Normal file
73
lib/features/main/presentation/screens/main_screen.dart
Normal 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}',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
32
lib/features/root/root_screen.dart
Normal file
32
lib/features/root/root_screen.dart
Normal 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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user