mirror of
https://github.com/pese-git/cherrypick.git
synced 2026-01-24 05:25:19 +00:00
feat(logging): add talker_dio_logger and talker_bloc_logger integration, improve cherrypick logger structure, add UI log screen for DI and network/bloc debug
This commit is contained in:
@@ -2,6 +2,7 @@ import 'package:cherrypick/cherrypick.dart';
|
||||
import 'package:cherrypick_annotations/cherrypick_annotations.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:talker/talker.dart';
|
||||
|
||||
import 'domain/repository/post_repository.dart';
|
||||
import 'presentation/bloc/post_bloc.dart';
|
||||
@@ -9,26 +10,38 @@ import 'router/app_router.dart';
|
||||
|
||||
part 'app.inject.cherrypick.g.dart';
|
||||
|
||||
class TalkerProvider extends InheritedWidget {
|
||||
final Talker talker;
|
||||
const TalkerProvider({required this.talker, required Widget child, Key? key}) : super(key: key, child: child);
|
||||
static Talker of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<TalkerProvider>()!.talker;
|
||||
@override
|
||||
bool updateShouldNotify(TalkerProvider oldWidget) => oldWidget.talker != talker;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
class MyApp extends StatelessWidget with _$MyApp {
|
||||
final _appRouter = AppRouter();
|
||||
final Talker talker;
|
||||
|
||||
@named('repo')
|
||||
@inject()
|
||||
late final PostRepository repository;
|
||||
|
||||
MyApp({super.key}) {
|
||||
MyApp({super.key, required this.talker}) {
|
||||
_inject(this);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (_) => PostBloc(repository),
|
||||
child: MaterialApp.router(
|
||||
routeInformationParser: _appRouter.defaultRouteParser(),
|
||||
routerDelegate: _appRouter.delegate(),
|
||||
theme: ThemeData.light(),
|
||||
return TalkerProvider(
|
||||
talker: talker,
|
||||
child: BlocProvider(
|
||||
create: (_) => PostBloc(repository),
|
||||
child: MaterialApp.router(
|
||||
routeInformationParser: _appRouter.defaultRouteParser(),
|
||||
routerDelegate: _appRouter.delegate(),
|
||||
theme: ThemeData.light(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import 'package:cherrypick_annotations/cherrypick_annotations.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:cherrypick/cherrypick.dart';
|
||||
import 'package:talker_dio_logger/talker_dio_logger_interceptor.dart';
|
||||
import 'package:talker_dio_logger/talker_dio_logger_settings.dart';
|
||||
import 'package:talker_flutter/talker_flutter.dart';
|
||||
import '../data/network/json_placeholder_api.dart';
|
||||
import '../data/post_repository_impl.dart';
|
||||
import '../domain/repository/post_repository.dart';
|
||||
@@ -9,6 +12,18 @@ part 'app_module.module.cherrypick.g.dart';
|
||||
|
||||
@module()
|
||||
abstract class AppModule extends Module {
|
||||
@provide()
|
||||
@singleton()
|
||||
TalkerDioLoggerSettings talkerDioLoggerSettings() => TalkerDioLoggerSettings(
|
||||
printRequestHeaders: true,
|
||||
printResponseHeaders: true,
|
||||
printResponseMessage: true,
|
||||
);
|
||||
|
||||
@provide()
|
||||
@singleton()
|
||||
TalkerDioLogger talkerDioLogger(Talker talker, TalkerDioLoggerSettings settings) => TalkerDioLogger(talker: talker, settings: settings);
|
||||
|
||||
@instance()
|
||||
int timeout() => 1000;
|
||||
|
||||
@@ -35,8 +50,8 @@ abstract class AppModule extends Module {
|
||||
@provide()
|
||||
@singleton()
|
||||
@named('dio')
|
||||
Dio dio(@named('baseUrl') String baseUrl) =>
|
||||
Dio(BaseOptions(baseUrl: baseUrl));
|
||||
Dio dio(@named('baseUrl') String baseUrl, TalkerDioLogger logger) =>
|
||||
Dio(BaseOptions(baseUrl: baseUrl))..interceptors.add(logger);
|
||||
|
||||
@provide()
|
||||
@singleton()
|
||||
|
||||
13
examples/postly/lib/di/core_module.dart
Normal file
13
examples/postly/lib/di/core_module.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'package:cherrypick/cherrypick.dart';
|
||||
import 'package:talker_flutter/talker_flutter.dart';
|
||||
|
||||
class CoreModule extends Module {
|
||||
final Talker _talker;
|
||||
|
||||
CoreModule({required Talker talker}) : _talker = talker;
|
||||
|
||||
@override
|
||||
void builder(Scope currentScope) {
|
||||
bind<Talker>().toProvide(() => _talker).singleton();
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,30 @@
|
||||
import 'package:cherrypick/cherrypick.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:postly/app.dart';
|
||||
import 'package:postly/di/core_module.dart';
|
||||
import 'package:talker_bloc_logger/talker_bloc_logger_observer.dart';
|
||||
import 'package:talker_flutter/talker_flutter.dart';
|
||||
import 'di/app_module.dart';
|
||||
import 'package:talker_cherrypick_logger/talker_cherrypick_logger.dart';
|
||||
|
||||
void main() {
|
||||
final talker = Talker();
|
||||
final talkerLogger = TalkerCherryPickLogger(talker);
|
||||
|
||||
|
||||
Bloc.observer = TalkerBlocObserver(talker: talker);
|
||||
|
||||
CherryPick.setGlobalLogger(talkerLogger);
|
||||
// Включаем cycle-detection только в debug/test
|
||||
if (kDebugMode) {
|
||||
CherryPick.setGlobalLogger(PrintLogger());
|
||||
CherryPick.enableGlobalCycleDetection();
|
||||
CherryPick.enableGlobalCrossScopeCycleDetection();
|
||||
}
|
||||
|
||||
// Используем safe root scope для гарантии защиты
|
||||
CherryPick.openRootScope().installModules([$AppModule()]);
|
||||
runApp(MyApp());
|
||||
CherryPick.openRootScope().installModules([CoreModule(talker: talker), $AppModule()]);
|
||||
|
||||
runApp(MyApp(talker: talker,));
|
||||
}
|
||||
|
||||
15
examples/postly/lib/presentation/pages/logs_page.dart
Normal file
15
examples/postly/lib/presentation/pages/logs_page.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:talker_flutter/talker_flutter.dart';
|
||||
import '../../app.dart';
|
||||
|
||||
@RoutePage()
|
||||
class LogsPage extends StatelessWidget {
|
||||
const LogsPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final talker = TalkerProvider.of(context);
|
||||
return TalkerScreen(talker: talker);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:postly/app.dart';
|
||||
|
||||
import '../../router/app_router.gr.dart';
|
||||
import '../bloc/post_bloc.dart';
|
||||
@@ -15,7 +16,18 @@ class PostsPage extends StatelessWidget {
|
||||
create: (context) =>
|
||||
context.read<PostBloc>()..add(const PostEvent.fetchAll()),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(title: const Text('Posts')),
|
||||
appBar: AppBar(
|
||||
title: const Text('Posts'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.bug_report),
|
||||
tooltip: 'Open logs',
|
||||
onPressed: () {
|
||||
AutoRouter.of(context).push(const LogsRoute());
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: BlocBuilder<PostBloc, PostState>(
|
||||
builder: (context, state) {
|
||||
return state.when(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
|
||||
import '../presentation/pages/logs_page.dart';
|
||||
import 'app_router.gr.dart';
|
||||
|
||||
@AutoRouterConfig()
|
||||
@@ -8,5 +8,6 @@ class AppRouter extends RootStackRouter {
|
||||
List<AutoRoute> get routes => [
|
||||
AutoRoute(page: PostsRoute.page, initial: true),
|
||||
AutoRoute(page: PostDetailsRoute.page),
|
||||
AutoRoute(page: LogsRoute.page),
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user