├── .fvm └── fvm_config.json ├── .gitattributes ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── com │ └── cleveroad │ └── cr_logger │ └── cr_logger │ ├── CrLogger.kt │ └── CrLoggerPlugin.kt ├── assets ├── arrow_down.png ├── content_copy.png ├── ic_back.png └── ic_menu.png ├── docs ├── .last_build_id ├── assets │ ├── AssetManifest.bin │ ├── AssetManifest.bin.json │ ├── AssetManifest.json │ ├── FontManifest.json │ ├── NOTICES │ ├── assets │ │ ├── ic_debug.png │ │ ├── ic_debug_native.png │ │ ├── ic_error.png │ │ ├── ic_error_android.png │ │ ├── ic_error_ios.png │ │ ├── ic_http.png │ │ ├── ic_json.png │ │ ├── ic_warning.png │ │ ├── ic_warning_android.png │ │ └── ic_warning_ios.png │ ├── fonts │ │ ├── Epilogue-Medium.ttf │ │ ├── Epilogue-Regular.ttf │ │ └── MaterialIcons-Regular.otf │ ├── packages │ │ ├── cr_logger │ │ │ ├── assets │ │ │ │ ├── arrow_down.png │ │ │ │ ├── content_copy.png │ │ │ │ ├── ic_back.png │ │ │ │ └── ic_menu.png │ │ │ └── fonts │ │ │ │ ├── Epilogue-Medium.ttf │ │ │ │ └── Epilogue-Regular.ttf │ │ └── flutter_dropzone_web │ │ │ └── assets │ │ │ └── flutter_dropzone.js │ └── shaders │ │ └── ink_sparkle.frag ├── canvaskit │ ├── canvaskit.js │ ├── canvaskit.js.symbols │ ├── canvaskit.wasm │ ├── chromium │ │ ├── canvaskit.js │ │ ├── canvaskit.js.symbols │ │ └── canvaskit.wasm │ ├── skwasm.js │ ├── skwasm.js.symbols │ ├── skwasm.wasm │ └── skwasm.worker.js ├── flutter.js ├── flutter_service_worker.js ├── index.html ├── main.dart.js ├── styles.css └── version.json ├── example ├── .gitignore ├── README.md ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── cleveroad │ │ │ │ │ └── cr_logger │ │ │ │ │ └── cr_logger_example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── assets │ ├── ic_debug.png │ ├── ic_debug_native.png │ ├── ic_error.png │ ├── ic_error_android.png │ ├── ic_error_ios.png │ ├── ic_http.png │ ├── ic_json.png │ ├── ic_warning.png │ ├── ic_warning_android.png │ └── ic_warning_ios.png ├── fonts │ ├── Epilogue-Medium.ttf │ └── Epilogue-Regular.ttf ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ ├── generated │ │ └── assets.dart │ ├── main.dart │ ├── rest_client.dart │ ├── styles.dart │ ├── utils │ │ └── try_cast.dart │ └── widgets │ │ └── example_btn.dart ├── pubspec.yaml └── web │ ├── index.html │ └── styles.css ├── fonts ├── Epilogue-Medium.ttf └── Epilogue-Regular.ttf ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── CrLogger.swift │ ├── CrLoggerPlugin.h │ ├── CrLoggerPlugin.m │ └── SwiftCrLoggerPlugin.swift └── cr_logger.podspec ├── lib ├── cr_logger.dart ├── cr_logger_web.dart ├── generated │ └── assets.dart └── src │ ├── base │ └── base_page_with_progress.dart │ ├── constants.dart │ ├── controllers │ ├── logs_mode.dart │ └── logs_mode_controller.dart │ ├── cr_logger.dart │ ├── cr_logger_helper.dart │ ├── cr_logger_wrapper.dart │ ├── data │ ├── bean │ │ ├── error_bean.dart │ │ ├── http_bean.dart │ │ ├── log_bean.dart │ │ ├── request_bean.dart │ │ └── response_bean.dart │ ├── models │ │ └── log_type.dart │ └── sqflite_db │ │ ├── converters │ │ ├── http_enitity_converter.dart │ │ └── log_entity_converters.dart │ │ ├── entities │ │ ├── http_entity.dart │ │ └── log_entity.dart │ │ ├── log_module.dart │ │ └── sqflite_repository.dart │ ├── dio_log.dart │ ├── extensions │ ├── ansi_color_ext.dart │ ├── card_theme_ext.dart │ ├── cast.dart │ ├── date_time.dart │ ├── do_post_frame.dart │ ├── extensions.dart │ ├── image_ext.dart │ ├── int_ext.dart │ └── theme_data_ext.dart │ ├── interceptor │ ├── chopper_log_interceptor.dart │ ├── cr_http_adapter.dart │ ├── cr_http_client_adapter.dart │ └── dio_log_interceptor.dart │ ├── js │ ├── console_output_worker.dart │ ├── error_worker_scripts.dart │ ├── http_pretty_output_scripts.dart │ ├── request_worker_scripts.dart │ ├── response_worker_scripts.dart │ └── scripts.dart │ ├── log_message_wrapper.dart │ ├── managers │ ├── http_log_manager.dart │ ├── log_manager.dart │ └── transfer_manager.dart │ ├── models │ ├── http_log_type.dart │ └── request_status.dart │ ├── overlay_draggable_button.dart │ ├── page │ ├── actions_and_values │ │ ├── actions_and_values_page.dart │ │ ├── actions_manager.dart │ │ ├── models │ │ │ ├── action_model.dart │ │ │ └── notifier_data.dart │ │ ├── notifiers_manager.dart │ │ └── widgets │ │ │ ├── action_item.dart │ │ │ └── value_notifier_item.dart │ ├── app_info_page.dart │ ├── http_logs │ │ ├── http_log_details_page.dart │ │ └── http_logs_page.dart │ ├── log_main │ │ ├── log_main.dart │ │ ├── log_main_mobile.dart │ │ ├── log_main_web.dart │ │ └── widgets │ │ │ ├── cr_web_appbar_widget.dart │ │ │ ├── mobile_header_widget.dart │ │ │ └── web_header_widget.dart │ ├── logs │ │ ├── log_local_detail_page.dart │ │ └── log_page.dart │ ├── search │ │ ├── http_search_page.dart │ │ ├── log_search_page.dart │ │ ├── search_page.dart │ │ └── widgets │ │ │ └── path_widget.dart │ └── widgets │ │ ├── app_info_item.dart │ │ ├── clear_logs_content_widget.dart │ │ ├── cupertino_search_field.dart │ │ ├── http_item.dart │ │ ├── json_details_widget.dart │ │ ├── local_log_item.dart │ │ ├── popup_menu.dart │ │ └── progress_widget.dart │ ├── providers │ └── sqflite_provider.dart │ ├── res │ ├── colors.dart │ ├── styles.dart │ └── theme.dart │ ├── utils │ ├── console_log_output.dart │ ├── copy_clipboard.dart │ ├── enum_ext.dart │ ├── hide_values_in_map.dart │ ├── html_stub.dart │ ├── json_utils.dart │ ├── map_ext.dart │ ├── nothing_log_filter.dart │ ├── pair.dart │ ├── paramas_detector │ │ ├── parameter_model.dart │ │ └── params_detector.dart │ ├── parsers │ │ ├── isolate_parser.dart │ │ └── url_parser.dart │ ├── pretty_cr_logger.dart │ ├── pretty_cr_printer.dart │ ├── show_info_dialog.dart │ ├── show_log_snack_bar.dart │ ├── show_remove_log_bottom_sheet.dart │ ├── show_remove_log_snack_bar.dart │ ├── text_with_params_widget.dart │ ├── unfocus.dart │ └── web_utils.dart │ └── widget │ ├── adaptive_layout │ ├── adaptive_layout_widget.dart │ └── layout_types.dart │ ├── body_expansion_tile.dart │ ├── build_number.dart │ ├── copy_widget.dart │ ├── cr_app_bar.dart │ ├── cr_back_button.dart │ ├── cr_inspector.dart │ ├── delete_log_confirm_widget.dart │ ├── error_value_widget.dart │ ├── expand_arrow_button.dart │ ├── headers_expansion_tile.dart │ ├── http_error_widget.dart │ ├── http_request_widget.dart │ ├── http_response_widget.dart │ ├── json_widget │ ├── json_node_content.dart │ ├── json_tree_colors.dart │ └── json_widget.dart │ ├── options_buttons.dart │ ├── params_expansion_tile.dart │ ├── proxy_input_dialog.dart │ ├── remove_log_widget.dart │ ├── rounded_card.dart │ └── url_value_widget.dart ├── pubspec.yaml ├── screenshots ├── debug_log_screenshot.png ├── http-logs-example.gif ├── http_db_log_screenshot.png ├── http_error_screenshot.png ├── http_log_screenshot.png ├── http_request_screenshot.png ├── http_response_screenshot.png ├── http_search_screenshot.png ├── logs_search_screenshot.png ├── plugin_banner.png ├── quick_action_menu_screenshot.png ├── screenshot-web.png └── settings-screenshot.png └── test ├── parameter_detector_test.dart └── replace_curly_braces_test.dart /.fvm/fvm_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "flutterSdkVersion": "3.16.5", 3 | "flavors": {} 4 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.g.dart -diff 2 | *.svg -diff 3 | *.png -diff 4 | *.lock -diff 5 | *.xcconfig -diff 6 | ios/Runner.xcodeproj/* -diff 7 | ios/Runner.xcworkspace/* -diff 8 | lib/generated/* -diff 9 | ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json -diff 10 | android/app/src/main/res/drawable -diff 11 | *.idea/* -diff 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | *.ipa 7 | *.lock 8 | *.zip 9 | .DS_Store 10 | .atom/ 11 | .buildlog/ 12 | .history 13 | .svn/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | .gradle 27 | .metadata 28 | 29 | # Flutter/Dart/Pub related 30 | **/doc/api/ 31 | .dart_tool/ 32 | .flutter-plugins 33 | .flutter-plugins-dependencies 34 | .packages 35 | .pub-cache/ 36 | .pub/ 37 | .lock/ 38 | /build/ 39 | 40 | # Web related 41 | lib/generated_plugin_registrant.dart -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Cleveroad 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.cleveroad.cr_logger.cr_logger' 2 | version '1.0-SNAPSHOT' 3 | 4 | def localProperties = new Properties() 5 | def localPropertiesFile = rootProject.file('local.properties') 6 | if (localPropertiesFile.exists()) { 7 | localPropertiesFile.withReader('UTF-8') { reader -> 8 | localProperties.load(reader) 9 | } 10 | } 11 | 12 | 13 | buildscript { 14 | ext.kotlin_version = '1.6.10' 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | 20 | dependencies { 21 | classpath 'com.android.tools.build:gradle:4.2.2' 22 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 23 | } 24 | } 25 | 26 | rootProject.allprojects { 27 | repositories { 28 | google() 29 | mavenCentral() 30 | } 31 | } 32 | 33 | apply plugin: 'com.android.library' 34 | apply plugin: 'kotlin-android' 35 | 36 | android { 37 | compileSdkVersion 30 38 | 39 | compileOptions { 40 | sourceCompatibility JavaVersion.VERSION_1_8 41 | targetCompatibility JavaVersion.VERSION_1_8 42 | } 43 | 44 | kotlinOptions { 45 | jvmTarget = '1.8' 46 | } 47 | 48 | sourceSets { 49 | main.java.srcDirs += 'src/main/kotlin' 50 | } 51 | 52 | defaultConfig { 53 | minSdkVersion 21 54 | } 55 | } 56 | 57 | 58 | 59 | dependencies { 60 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 61 | } 62 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Oct 28 14:44:20 EEST 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'cr_logger' 2 | 3 | 4 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 5 | def properties = new Properties() 6 | 7 | assert localPropertiesFile.exists() 8 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 9 | 10 | def flutterSdkPath = properties.getProperty("flutter.sdk") 11 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 12 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/src/main/kotlin/com/cleveroad/cr_logger/cr_logger/CrLogger.kt: -------------------------------------------------------------------------------- 1 | package com.cleveroad.cr_logger.cr_logger 2 | 3 | import io.flutter.plugin.common.EventChannel 4 | 5 | class CrLogger { 6 | companion object CrLogger { 7 | private const val LOG_TYPE_DEBUG = "d" 8 | private const val LOG_TYPE_INFO = "i" 9 | private const val LOG_TYPE_ERROR = "e" 10 | 11 | private var eventChannel: EventChannel? = null 12 | private var eventSink: EventChannel.EventSink? = null 13 | 14 | fun init(eventChannel: EventChannel) { 15 | this.eventChannel = eventChannel 16 | this.eventChannel?.setStreamHandler( 17 | object : EventChannel.StreamHandler { 18 | override fun onListen(arguments: Any?, events: EventChannel.EventSink) { 19 | eventSink = events 20 | 21 | } 22 | 23 | override fun onCancel(arguments: Any?) { 24 | 25 | } 26 | }) 27 | 28 | } 29 | 30 | fun d(message: Any) { 31 | eventSink?.success(listOf(LOG_TYPE_DEBUG, message)) 32 | } 33 | 34 | fun i(message: Any) { 35 | eventSink?.success(listOf(LOG_TYPE_INFO, message)) 36 | } 37 | 38 | fun e(message: Any) { 39 | eventSink?.success(listOf(LOG_TYPE_ERROR, message)) 40 | } 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/com/cleveroad/cr_logger/cr_logger/CrLoggerPlugin.kt: -------------------------------------------------------------------------------- 1 | package com.cleveroad.cr_logger.cr_logger 2 | 3 | import androidx.annotation.NonNull 4 | 5 | import io.flutter.embedding.engine.plugins.FlutterPlugin 6 | import io.flutter.plugin.common.EventChannel 7 | import io.flutter.plugin.common.MethodCall 8 | import io.flutter.plugin.common.MethodChannel 9 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler 10 | import io.flutter.plugin.common.MethodChannel.Result 11 | 12 | /** CrLoggerPlugin */ 13 | class CrLoggerPlugin : FlutterPlugin, MethodCallHandler { 14 | /// The MethodChannel that will the communication between Flutter and native Android 15 | /// 16 | /// This local reference serves to register the plugin with the Flutter Engine and unregister it 17 | /// when the Flutter Engine is detached from the Activity 18 | private lateinit var channel: MethodChannel 19 | private lateinit var eventChannel: EventChannel 20 | 21 | override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { 22 | channel = MethodChannel( 23 | flutterPluginBinding.binaryMessenger, 24 | "com.cleveroad.cr_logger/method_channel" 25 | ) 26 | channel.setMethodCallHandler(this) 27 | eventChannel = 28 | EventChannel(flutterPluginBinding.binaryMessenger, "com.cleveroad.cr_logger/logger") 29 | CrLogger.init(eventChannel) 30 | } 31 | 32 | override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { 33 | if (call.method == "getPlatformVersion") { 34 | result.success("Android ${android.os.Build.VERSION.RELEASE}") 35 | } else { 36 | result.notImplemented() 37 | } 38 | } 39 | 40 | override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { 41 | channel.setMethodCallHandler(null) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /assets/arrow_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/assets/arrow_down.png -------------------------------------------------------------------------------- /assets/content_copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/assets/content_copy.png -------------------------------------------------------------------------------- /assets/ic_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/assets/ic_back.png -------------------------------------------------------------------------------- /assets/ic_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/assets/ic_menu.png -------------------------------------------------------------------------------- /docs/.last_build_id: -------------------------------------------------------------------------------- 1 | 846120b4c5c0513ff1e25f1352e8304a -------------------------------------------------------------------------------- /docs/assets/AssetManifest.bin: -------------------------------------------------------------------------------- 1 | assets/ic_debug.png  assetassets/ic_debug.pngassets/ic_debug_native.png  assetassets/ic_debug_native.pngassets/ic_error.png  assetassets/ic_error.pngassets/ic_error_android.png  assetassets/ic_error_android.pngassets/ic_error_ios.png  assetassets/ic_error_ios.pngassets/ic_http.png  assetassets/ic_http.pngassets/ic_json.png  assetassets/ic_json.pngassets/ic_warning.png  assetassets/ic_warning.pngassets/ic_warning_android.png  assetassets/ic_warning_android.pngassets/ic_warning_ios.png  assetassets/ic_warning_ios.pngfonts/Epilogue-Medium.ttf  assetfonts/Epilogue-Medium.ttffonts/Epilogue-Regular.ttf  assetfonts/Epilogue-Regular.ttf(packages/cr_logger/assets/arrow_down.png  asset(packages/cr_logger/assets/arrow_down.png*packages/cr_logger/assets/content_copy.png  asset*packages/cr_logger/assets/content_copy.png%packages/cr_logger/assets/ic_back.png  asset%packages/cr_logger/assets/ic_back.png%packages/cr_logger/assets/ic_menu.png  asset%packages/cr_logger/assets/ic_menu.png,packages/cr_logger/fonts/Epilogue-Medium.ttf  asset,packages/cr_logger/fonts/Epilogue-Medium.ttf-packages/cr_logger/fonts/Epilogue-Regular.ttf  asset-packages/cr_logger/fonts/Epilogue-Regular.ttf8packages/flutter_dropzone_web/assets/flutter_dropzone.js  asset8packages/flutter_dropzone_web/assets/flutter_dropzone.js -------------------------------------------------------------------------------- /docs/assets/AssetManifest.bin.json: -------------------------------------------------------------------------------- 1 | "DRMHE2Fzc2V0cy9pY19kZWJ1Zy5wbmcMAQ0BBwVhc3NldAcTYXNzZXRzL2ljX2RlYnVnLnBuZwcaYXNzZXRzL2ljX2RlYnVnX25hdGl2ZS5wbmcMAQ0BBwVhc3NldAcaYXNzZXRzL2ljX2RlYnVnX25hdGl2ZS5wbmcHE2Fzc2V0cy9pY19lcnJvci5wbmcMAQ0BBwVhc3NldAcTYXNzZXRzL2ljX2Vycm9yLnBuZwcbYXNzZXRzL2ljX2Vycm9yX2FuZHJvaWQucG5nDAENAQcFYXNzZXQHG2Fzc2V0cy9pY19lcnJvcl9hbmRyb2lkLnBuZwcXYXNzZXRzL2ljX2Vycm9yX2lvcy5wbmcMAQ0BBwVhc3NldAcXYXNzZXRzL2ljX2Vycm9yX2lvcy5wbmcHEmFzc2V0cy9pY19odHRwLnBuZwwBDQEHBWFzc2V0BxJhc3NldHMvaWNfaHR0cC5wbmcHEmFzc2V0cy9pY19qc29uLnBuZwwBDQEHBWFzc2V0BxJhc3NldHMvaWNfanNvbi5wbmcHFWFzc2V0cy9pY193YXJuaW5nLnBuZwwBDQEHBWFzc2V0BxVhc3NldHMvaWNfd2FybmluZy5wbmcHHWFzc2V0cy9pY193YXJuaW5nX2FuZHJvaWQucG5nDAENAQcFYXNzZXQHHWFzc2V0cy9pY193YXJuaW5nX2FuZHJvaWQucG5nBxlhc3NldHMvaWNfd2FybmluZ19pb3MucG5nDAENAQcFYXNzZXQHGWFzc2V0cy9pY193YXJuaW5nX2lvcy5wbmcHGWZvbnRzL0VwaWxvZ3VlLU1lZGl1bS50dGYMAQ0BBwVhc3NldAcZZm9udHMvRXBpbG9ndWUtTWVkaXVtLnR0ZgcaZm9udHMvRXBpbG9ndWUtUmVndWxhci50dGYMAQ0BBwVhc3NldAcaZm9udHMvRXBpbG9ndWUtUmVndWxhci50dGYHKHBhY2thZ2VzL2NyX2xvZ2dlci9hc3NldHMvYXJyb3dfZG93bi5wbmcMAQ0BBwVhc3NldAcocGFja2FnZXMvY3JfbG9nZ2VyL2Fzc2V0cy9hcnJvd19kb3duLnBuZwcqcGFja2FnZXMvY3JfbG9nZ2VyL2Fzc2V0cy9jb250ZW50X2NvcHkucG5nDAENAQcFYXNzZXQHKnBhY2thZ2VzL2NyX2xvZ2dlci9hc3NldHMvY29udGVudF9jb3B5LnBuZwclcGFja2FnZXMvY3JfbG9nZ2VyL2Fzc2V0cy9pY19iYWNrLnBuZwwBDQEHBWFzc2V0ByVwYWNrYWdlcy9jcl9sb2dnZXIvYXNzZXRzL2ljX2JhY2sucG5nByVwYWNrYWdlcy9jcl9sb2dnZXIvYXNzZXRzL2ljX21lbnUucG5nDAENAQcFYXNzZXQHJXBhY2thZ2VzL2NyX2xvZ2dlci9hc3NldHMvaWNfbWVudS5wbmcHLHBhY2thZ2VzL2NyX2xvZ2dlci9mb250cy9FcGlsb2d1ZS1NZWRpdW0udHRmDAENAQcFYXNzZXQHLHBhY2thZ2VzL2NyX2xvZ2dlci9mb250cy9FcGlsb2d1ZS1NZWRpdW0udHRmBy1wYWNrYWdlcy9jcl9sb2dnZXIvZm9udHMvRXBpbG9ndWUtUmVndWxhci50dGYMAQ0BBwVhc3NldActcGFja2FnZXMvY3JfbG9nZ2VyL2ZvbnRzL0VwaWxvZ3VlLVJlZ3VsYXIudHRmBzhwYWNrYWdlcy9mbHV0dGVyX2Ryb3B6b25lX3dlYi9hc3NldHMvZmx1dHRlcl9kcm9wem9uZS5qcwwBDQEHBWFzc2V0BzhwYWNrYWdlcy9mbHV0dGVyX2Ryb3B6b25lX3dlYi9hc3NldHMvZmx1dHRlcl9kcm9wem9uZS5qcw==" -------------------------------------------------------------------------------- /docs/assets/AssetManifest.json: -------------------------------------------------------------------------------- 1 | {"assets/ic_debug.png":["assets/ic_debug.png"],"assets/ic_debug_native.png":["assets/ic_debug_native.png"],"assets/ic_error.png":["assets/ic_error.png"],"assets/ic_error_android.png":["assets/ic_error_android.png"],"assets/ic_error_ios.png":["assets/ic_error_ios.png"],"assets/ic_http.png":["assets/ic_http.png"],"assets/ic_json.png":["assets/ic_json.png"],"assets/ic_warning.png":["assets/ic_warning.png"],"assets/ic_warning_android.png":["assets/ic_warning_android.png"],"assets/ic_warning_ios.png":["assets/ic_warning_ios.png"],"fonts/Epilogue-Medium.ttf":["fonts/Epilogue-Medium.ttf"],"fonts/Epilogue-Regular.ttf":["fonts/Epilogue-Regular.ttf"],"packages/cr_logger/assets/arrow_down.png":["packages/cr_logger/assets/arrow_down.png"],"packages/cr_logger/assets/content_copy.png":["packages/cr_logger/assets/content_copy.png"],"packages/cr_logger/assets/ic_back.png":["packages/cr_logger/assets/ic_back.png"],"packages/cr_logger/assets/ic_menu.png":["packages/cr_logger/assets/ic_menu.png"],"packages/cr_logger/fonts/Epilogue-Medium.ttf":["packages/cr_logger/fonts/Epilogue-Medium.ttf"],"packages/cr_logger/fonts/Epilogue-Regular.ttf":["packages/cr_logger/fonts/Epilogue-Regular.ttf"],"packages/flutter_dropzone_web/assets/flutter_dropzone.js":["packages/flutter_dropzone_web/assets/flutter_dropzone.js"]} -------------------------------------------------------------------------------- /docs/assets/FontManifest.json: -------------------------------------------------------------------------------- 1 | [{"family":"MaterialIcons","fonts":[{"asset":"fonts/MaterialIcons-Regular.otf"}]},{"family":"Epilogue","fonts":[{"asset":"fonts/Epilogue-Medium.ttf"},{"asset":"fonts/Epilogue-Regular.ttf"}]},{"family":"packages/cr_logger/Epilogue","fonts":[{"asset":"packages/cr_logger/fonts/Epilogue-Medium.ttf"},{"asset":"packages/cr_logger/fonts/Epilogue-Regular.ttf"}]}] -------------------------------------------------------------------------------- /docs/assets/assets/ic_debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/assets/ic_debug.png -------------------------------------------------------------------------------- /docs/assets/assets/ic_debug_native.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/assets/ic_debug_native.png -------------------------------------------------------------------------------- /docs/assets/assets/ic_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/assets/ic_error.png -------------------------------------------------------------------------------- /docs/assets/assets/ic_error_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/assets/ic_error_android.png -------------------------------------------------------------------------------- /docs/assets/assets/ic_error_ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/assets/ic_error_ios.png -------------------------------------------------------------------------------- /docs/assets/assets/ic_http.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/assets/ic_http.png -------------------------------------------------------------------------------- /docs/assets/assets/ic_json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/assets/ic_json.png -------------------------------------------------------------------------------- /docs/assets/assets/ic_warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/assets/ic_warning.png -------------------------------------------------------------------------------- /docs/assets/assets/ic_warning_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/assets/ic_warning_android.png -------------------------------------------------------------------------------- /docs/assets/assets/ic_warning_ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/assets/ic_warning_ios.png -------------------------------------------------------------------------------- /docs/assets/fonts/Epilogue-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/fonts/Epilogue-Medium.ttf -------------------------------------------------------------------------------- /docs/assets/fonts/Epilogue-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/fonts/Epilogue-Regular.ttf -------------------------------------------------------------------------------- /docs/assets/fonts/MaterialIcons-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/fonts/MaterialIcons-Regular.otf -------------------------------------------------------------------------------- /docs/assets/packages/cr_logger/assets/arrow_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/packages/cr_logger/assets/arrow_down.png -------------------------------------------------------------------------------- /docs/assets/packages/cr_logger/assets/content_copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/packages/cr_logger/assets/content_copy.png -------------------------------------------------------------------------------- /docs/assets/packages/cr_logger/assets/ic_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/packages/cr_logger/assets/ic_back.png -------------------------------------------------------------------------------- /docs/assets/packages/cr_logger/assets/ic_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/packages/cr_logger/assets/ic_menu.png -------------------------------------------------------------------------------- /docs/assets/packages/cr_logger/fonts/Epilogue-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/packages/cr_logger/fonts/Epilogue-Medium.ttf -------------------------------------------------------------------------------- /docs/assets/packages/cr_logger/fonts/Epilogue-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/assets/packages/cr_logger/fonts/Epilogue-Regular.ttf -------------------------------------------------------------------------------- /docs/canvaskit/canvaskit.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/canvaskit/canvaskit.wasm -------------------------------------------------------------------------------- /docs/canvaskit/chromium/canvaskit.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/canvaskit/chromium/canvaskit.wasm -------------------------------------------------------------------------------- /docs/canvaskit/skwasm.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/canvaskit/skwasm.wasm -------------------------------------------------------------------------------- /docs/canvaskit/skwasm.worker.js: -------------------------------------------------------------------------------- 1 | "use strict";var Module={};var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";if(ENVIRONMENT_IS_NODE){var nodeWorkerThreads=require("worker_threads");var parentPort=nodeWorkerThreads.parentPort;parentPort.on("message",data=>onmessage({data:data}));var fs=require("fs");Object.assign(global,{self:global,require:require,Module:Module,location:{href:__filename},Worker:nodeWorkerThreads.Worker,importScripts:f=>(0,eval)(fs.readFileSync(f,"utf8")+"//# sourceURL="+f),postMessage:msg=>parentPort.postMessage(msg),performance:global.performance||{now:Date.now}})}var initializedJS=false;function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");if(ENVIRONMENT_IS_NODE){fs.writeSync(2,text+"\n");return}console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;self.alert=threadAlert;Module["instantiateWasm"]=(info,receiveInstance)=>{var module=Module["wasmModule"];Module["wasmModule"]=null;var instance=new WebAssembly.Instance(module,info);return receiveInstance(instance)};self.onunhandledrejection=e=>{throw e.reason??e};function handleMessage(e){try{if(e.data.cmd==="load"){let messageQueue=[];self.onmessage=e=>messageQueue.push(e);self.startWorker=instance=>{Module=instance;postMessage({"cmd":"loaded"});for(let msg of messageQueue){handleMessage(msg)}self.onmessage=handleMessage};Module["wasmModule"]=e.data.wasmModule;for(const handler of e.data.handlers){Module[handler]=(...args)=>{postMessage({cmd:"callHandler",handler:handler,args:args})}}Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;if(typeof e.data.urlOrBlob=="string"){importScripts(e.data.urlOrBlob)}else{var objectUrl=URL.createObjectURL(e.data.urlOrBlob);importScripts(objectUrl);URL.revokeObjectURL(objectUrl)}skwasm(Module)}else if(e.data.cmd==="run"){Module["__emscripten_thread_init"](e.data.pthread_ptr,/*isMainBrowserThread=*/0,/*isMainRuntimeThread=*/0,/*canBlock=*/1);Module["__emscripten_thread_mailbox_await"](e.data.pthread_ptr);Module["establishStackSpace"]();Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].threadInitTLS();if(!initializedJS){initializedJS=true}try{Module["invokeEntryPoint"](e.data.start_routine,e.data.arg)}catch(ex){if(ex!="unwind"){throw ex}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["__emscripten_thread_exit"](-1)}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="checkMailbox"){if(initializedJS){Module["checkMailbox"]()}}else if(e.data.cmd){err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){if(Module["__emscripten_thread_crashed"]){Module["__emscripten_thread_crashed"]()}throw ex}}self.onmessage=handleMessage; 2 | -------------------------------------------------------------------------------- /docs/styles.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/docs/styles.css -------------------------------------------------------------------------------- /docs/version.json: -------------------------------------------------------------------------------- 1 | {"app_name":"cr_logger_example","version":"1.0.0","build_number":"1","package_name":"cr_logger_example"} -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Android Studio will place build artifacts here 43 | /android/app/debug 44 | /android/app/profile 45 | /android/app/release 46 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # cr_logger_example 2 | 3 | Demonstrates how to use the cr_logger plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 33 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | 40 | sourceSets { 41 | main.java.srcDirs += 'src/main/kotlin' 42 | } 43 | 44 | defaultConfig { 45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 46 | applicationId "com.cleveroad.cr_logger.cr_logger_example" 47 | minSdkVersion 26 48 | targetSdkVersion 30 49 | versionCode flutterVersionCode.toInteger() 50 | versionName flutterVersionName 51 | } 52 | 53 | buildTypes { 54 | release { 55 | // TODO: Add your own signing config for the release build. 56 | // Signing with the debug keys for now, so `flutter run --release` works. 57 | signingConfig signingConfigs.debug 58 | } 59 | } 60 | } 61 | 62 | flutter { 63 | source '../..' 64 | } 65 | 66 | dependencies { 67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 68 | } 69 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 9 | 16 | 20 | 23 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/cleveroad/cr_logger/cr_logger_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.cleveroad.cr_logger.cr_logger_example 2 | 3 | import androidx.annotation.NonNull 4 | import com.cleveroad.cr_logger.cr_logger.CrLogger 5 | import io.flutter.embedding.android.FlutterActivity 6 | import io.flutter.embedding.engine.FlutterEngine 7 | import io.flutter.plugin.common.MethodChannel 8 | 9 | class MainActivity : FlutterActivity() { 10 | private val CHANNEL = "com.cleveroad.cr_logger_example/logs" 11 | 12 | override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { 13 | super.configureFlutterEngine(flutterEngine) 14 | MethodChannel( 15 | flutterEngine.dartExecutor.binaryMessenger, 16 | CHANNEL 17 | ).setMethodCallHandler { call, result -> 18 | when (call.method) { 19 | "debug" -> { 20 | CrLogger.d("Debug logs from native android") 21 | result.success(true) 22 | } 23 | "info" -> { 24 | CrLogger.i("Info logs from native android") 25 | result.success(true) 26 | } 27 | "error" -> { 28 | CrLogger.e("Error logs from native android") 29 | result.success(true) 30 | } 31 | "logJson" -> { 32 | val map = getOrderedMap() 33 | CrLogger.d(map) 34 | CrLogger.i(map) 35 | CrLogger.e(map) 36 | result.success(true) 37 | } 38 | } 39 | } 40 | } 41 | 42 | private fun getOrderedMap(): Map { 43 | val sessionMap = mapOf( 44 | "accessToken" to "gnrjknsmdkom232", 45 | "refreshToken" to "sdapasldofkds123", 46 | "tokenLifetime" to 123456789, 47 | ) 48 | val avatarMap = mapOf( 49 | "avatarUrl" to "https://image.example/userId/avatar.jpg", 50 | "smallAvatarUrl" to "https://image.example/userId/small_avatar.jpg", 51 | ) 52 | 53 | return mapOf( 54 | "id" to "mnjtnnjw542", 55 | "firstName" to "Steave", 56 | "lastName" to "Jobs", 57 | "session" to sessionMap, 58 | "avatar" to avatarMap, 59 | ) 60 | } 61 | } 62 | 63 | 64 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.9.22' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.4.2' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | tasks.register("clean", Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Oct 26 10:47:45 EEST 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /example/assets/ic_debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/assets/ic_debug.png -------------------------------------------------------------------------------- /example/assets/ic_debug_native.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/assets/ic_debug_native.png -------------------------------------------------------------------------------- /example/assets/ic_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/assets/ic_error.png -------------------------------------------------------------------------------- /example/assets/ic_error_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/assets/ic_error_android.png -------------------------------------------------------------------------------- /example/assets/ic_error_ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/assets/ic_error_ios.png -------------------------------------------------------------------------------- /example/assets/ic_http.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/assets/ic_http.png -------------------------------------------------------------------------------- /example/assets/ic_json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/assets/ic_json.png -------------------------------------------------------------------------------- /example/assets/ic_warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/assets/ic_warning.png -------------------------------------------------------------------------------- /example/assets/ic_warning_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/assets/ic_warning_android.png -------------------------------------------------------------------------------- /example/assets/ic_warning_ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/assets/ic_warning_ios.png -------------------------------------------------------------------------------- /example/fonts/Epilogue-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/fonts/Epilogue-Medium.ttf -------------------------------------------------------------------------------- /example/fonts/Epilogue-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/fonts/Epilogue-Regular.ttf -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/ephemeral/ 22 | Flutter/app.flx 23 | Flutter/app.zip 24 | Flutter/flutter_assets/ 25 | Flutter/flutter_export_environment.sh 26 | ServiceDefinitions.json 27 | Runner/GeneratedPluginRegistrant.* 28 | 29 | # Exceptions to above rules. 30 | !default.mode1v3 31 | !default.mode2v3 32 | !default.pbxuser 33 | !default.perspectivev3 34 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '11.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | import cr_logger 4 | 5 | let CHANNEL = "com.cleveroad.cr_logger_example/logs" 6 | 7 | @UIApplicationMain 8 | @objc class AppDelegate: FlutterAppDelegate { 9 | 10 | 11 | override func application( 12 | _ application: UIApplication, 13 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 14 | ) -> Bool { 15 | GeneratedPluginRegistrant.register(with: self) 16 | 17 | let controller : FlutterViewController = window?.rootViewController as! FlutterViewController 18 | let loggerChannel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: controller.binaryMessenger) 19 | 20 | loggerChannel.setMethodCallHandler {(call: FlutterMethodCall, result: FlutterResult) -> Void in 21 | switch call.method { 22 | case "debug": 23 | CrLogger.d(message: "Debug logs from native iOS") 24 | case "info": 25 | CrLogger.i(message: "Info logs from native iOS") 26 | break 27 | case "error": 28 | CrLogger.e(message: "Error logs from native iOS") 29 | break 30 | case "logJson": 31 | let keyValuePairsData = self.getOrderedMap() 32 | CrLogger.d(message: keyValuePairsData) 33 | CrLogger.i(message: keyValuePairsData) 34 | CrLogger.e(message: keyValuePairsData) 35 | 36 | default: 37 | result(FlutterMethodNotImplemented) 38 | } 39 | result(true) 40 | } 41 | 42 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 43 | } 44 | 45 | 46 | 47 | private func getOrderedMap() -> KeyValuePairs { 48 | let keyValueSession: KeyValuePairs = [ 49 | "accessToken": "gnrjknsmdkom232", 50 | "refreshToken": "sdapasldofkds123", 51 | "tokenLifetime": 123456789 52 | ] 53 | 54 | let keyValueAvatar : KeyValuePairs = [ 55 | "avatarUrl": "https://image.example/userId/avatar.jpg", 56 | "smallAvatarUrl":"https://image.example/userId/small_avatar.jpg" 57 | ] 58 | 59 | let keyValueUser : KeyValuePairs = [ 60 | "id" : "mnjtnnjw542", 61 | "firstName": "Steave", 62 | "lastName": "Jobs", 63 | "session": keyValueSession, 64 | "avatar": keyValueAvatar, 65 | ] 66 | 67 | 68 | return keyValueUser 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | cr_logger_example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/lib/generated/assets.dart: -------------------------------------------------------------------------------- 1 | ///This file is automatically generated. DO NOT EDIT, all your changes would be lost. 2 | class Assets { 3 | Assets._(); 4 | 5 | static const String assetsIcDebug = 'assets/ic_debug.png'; 6 | static const String assetsIcDebugNative = 'assets/ic_debug_native.png'; 7 | static const String assetsIcError = 'assets/ic_error.png'; 8 | static const String assetsIcErrorAndroid = 'assets/ic_error_android.png'; 9 | static const String assetsIcErrorIos = 'assets/ic_error_ios.png'; 10 | static const String assetsIcHttp = 'assets/ic_http.png'; 11 | static const String assetsIcJson = 'assets/ic_json.png'; 12 | static const String assetsIcWarning = 'assets/ic_warning.png'; 13 | static const String assetsIcWarningAndroid = 'assets/ic_warning_android.png'; 14 | static const String assetsIcWarningIos = 'assets/ic_warning_ios.png'; 15 | } 16 | -------------------------------------------------------------------------------- /example/lib/rest_client.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:chopper/chopper.dart'; 4 | import 'package:cr_logger/cr_logger.dart'; 5 | import 'package:dio/dio.dart'; 6 | import 'package:dio/io.dart'; 7 | 8 | class RestClient { 9 | RestClient._() { 10 | dio = Dio() 11 | ..options.receiveTimeout = const Duration(milliseconds: _serverTimeout) 12 | ..options.connectTimeout = const Duration(milliseconds: _serverTimeout) 13 | ..options.sendTimeout = const Duration(milliseconds: _serverTimeout); 14 | 15 | dio.interceptors.add( 16 | CRLoggerInitializer.instance.getDioInterceptor(), 17 | ); 18 | chopper = ChopperClient(interceptors: [ 19 | CRLoggerInitializer.instance.getChopperInterceptor(), 20 | ]); 21 | } 22 | 23 | static const _serverTimeout = 15000; 24 | static final RestClient instance = RestClient._(); 25 | 26 | late Dio dio; 27 | late ChopperClient chopper; 28 | 29 | /// Init proxy for Dio client. 30 | void initDioProxyForCharles(String proxy) { 31 | final split = proxy.split(':'); 32 | final ip = split.first; 33 | final port = split[1]; 34 | 35 | final proxyStr = 'PROXY $ip:$port; ' 36 | 'PROXY localhost:$port; DIRECT'; 37 | final adapter = dio.httpClientAdapter; 38 | if (adapter is IOHttpClientAdapter) { 39 | adapter.createHttpClient = () { 40 | final client = HttpClient(context: SecurityContext()) 41 | ..findProxy = (uri) { 42 | return proxyStr; 43 | } 44 | ..badCertificateCallback = ( 45 | X509Certificate cert, 46 | String host, 47 | int port, 48 | ) => 49 | true; 50 | 51 | return client; 52 | }; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /example/lib/styles.dart: -------------------------------------------------------------------------------- 1 | class LoggerStyle { 2 | LoggerStyle._(); 3 | } 4 | -------------------------------------------------------------------------------- /example/lib/utils/try_cast.dart: -------------------------------------------------------------------------------- 1 | T? tryCast(Object? x) => x is T ? x : null; 2 | -------------------------------------------------------------------------------- /example/lib/widgets/example_btn.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ExampleBtn extends StatelessWidget { 4 | const ExampleBtn({ 5 | required this.text, 6 | required this.assetName, 7 | required this.onTap, 8 | super.key, 9 | }); 10 | 11 | final String text; 12 | final String assetName; 13 | final VoidCallback? onTap; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return ElevatedButton( 18 | onPressed: onTap, 19 | style: ElevatedButton.styleFrom( 20 | backgroundColor: Colors.white, 21 | shape: RoundedRectangleBorder( 22 | borderRadius: BorderRadius.circular(10), 23 | ), 24 | elevation: 0, 25 | minimumSize: const Size(0, 77), 26 | ), 27 | child: SizedBox( 28 | width: double.infinity, 29 | child: Column( 30 | crossAxisAlignment: CrossAxisAlignment.start, 31 | children: [ 32 | const SizedBox(height: 14), 33 | Image.asset( 34 | assetName, 35 | height: 24, 36 | width: 24, 37 | ), 38 | const SizedBox(height: 8), 39 | Text( 40 | text, 41 | style: const TextStyle( 42 | fontWeight: FontWeight.w500, 43 | fontSize: 14, 44 | color: Colors.black, 45 | ), 46 | ), 47 | ], 48 | ), 49 | ), 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: cr_logger_example 2 | description: Demonstrates how to use the cr_logger plugin. 3 | version: 1.0.0+1 4 | 5 | publish_to: 'none' 6 | 7 | environment: 8 | sdk: ">=3.0.0 <4.0.0" 9 | flutter: ">=3.16.0" 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | 15 | cr_logger: 16 | path: ../ 17 | 18 | dio: 5.4.0 19 | chopper: 7.0.10 20 | logger: 2.0.2+1 21 | flutter_dropzone: 3.0.6 22 | share_plus: 7.2.1 23 | 24 | 25 | dev_dependencies: 26 | flutter_test: 27 | sdk: flutter 28 | 29 | flutter: 30 | uses-material-design: true 31 | assets: 32 | - assets/ 33 | 34 | fonts: 35 | - family: Epilogue 36 | fonts: 37 | - asset: fonts/Epilogue-Medium.ttf 38 | - asset: fonts/Epilogue-Regular.ttf -------------------------------------------------------------------------------- /example/web/styles.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/example/web/styles.css -------------------------------------------------------------------------------- /fonts/Epilogue-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/fonts/Epilogue-Medium.ttf -------------------------------------------------------------------------------- /fonts/Epilogue-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/fonts/Epilogue-Regular.ttf -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/ephemeral/ 38 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/CrLoggerPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface CrLoggerPlugin : NSObject 4 | @end 5 | -------------------------------------------------------------------------------- /ios/Classes/CrLoggerPlugin.m: -------------------------------------------------------------------------------- 1 | #import "CrLoggerPlugin.h" 2 | #if __has_include() 3 | #import 4 | #else 5 | // Support project import fallback if the generated compatibility header 6 | // is not copied when this plugin is created as a library. 7 | // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 8 | #import "cr_logger-Swift.h" 9 | #endif 10 | 11 | @implementation CrLoggerPlugin 12 | + (void)registerWithRegistrar:(NSObject*)registrar { 13 | [SwiftCrLoggerPlugin registerWithRegistrar:registrar]; 14 | } 15 | @end 16 | -------------------------------------------------------------------------------- /ios/Classes/SwiftCrLoggerPlugin.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | 4 | public class SwiftCrLoggerPlugin: NSObject, FlutterPlugin { 5 | 6 | public static func register(with registrar: FlutterPluginRegistrar) { 7 | let channel = FlutterMethodChannel(name: "cr_logger", binaryMessenger: registrar.messenger()) 8 | let instance = SwiftCrLoggerPlugin() 9 | FlutterEventChannel(name: "com.cleveroad.cr_logger/logger", binaryMessenger: registrar.messenger()) 10 | .setStreamHandler(CrLogger()) 11 | 12 | 13 | registrar.addMethodCallDelegate(instance, channel: channel) 14 | } 15 | 16 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 17 | result("iOS " + UIDevice.current.systemVersion) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ios/cr_logger.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint cr_logger.podspec` to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'cr_logger' 7 | s.version = '0.0.1' 8 | s.summary = 'A new Flutter project.' 9 | s.description = <<-DESC 10 | A new Flutter project. 11 | DESC 12 | s.homepage = 'http://example.com' 13 | s.license = { :file => '../LICENSE' } 14 | s.author = { 'Your Company' => 'email@example.com' } 15 | s.source = { :path => '.' } 16 | s.source_files = 'Classes/**/*' 17 | s.dependency 'Flutter' 18 | s.platform = :ios, '11.0' 19 | s.ios.deployment_target = '11.0' 20 | 21 | # Flutter.framework does not contain a i386 slice. 22 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } 23 | s.swift_version = '5.0' 24 | 25 | end 26 | -------------------------------------------------------------------------------- /lib/cr_logger.dart: -------------------------------------------------------------------------------- 1 | library cr_logger; 2 | 3 | export 'src/cr_logger.dart'; 4 | export 'src/cr_logger_wrapper.dart'; 5 | export 'src/data/bean/error_bean.dart'; 6 | export 'src/data/bean/http_bean.dart'; 7 | export 'src/data/bean/log_bean.dart'; 8 | export 'src/data/bean/request_bean.dart'; 9 | export 'src/data/bean/response_bean.dart'; 10 | export 'src/data/models/log_type.dart'; 11 | export 'src/dio_log.dart'; 12 | export 'src/extensions/ansi_color_ext.dart'; 13 | export 'src/managers/http_log_manager.dart'; 14 | export 'src/page/actions_and_values/models/notifier_data.dart'; 15 | export 'src/widget/cr_inspector.dart'; 16 | -------------------------------------------------------------------------------- /lib/cr_logger_web.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | // In order to *not* need this ignore, consider extracting the "web" version 3 | // of your plugin as a separate package, instead of inlining it in the same 4 | // package as the core of your plugin. 5 | // ignore: avoid_web_libraries_in_flutter 6 | import 'dart:html' as html show window; 7 | 8 | import 'package:flutter/services.dart'; 9 | import 'package:flutter_web_plugins/flutter_web_plugins.dart'; 10 | 11 | /// A web implementation of the CrDownloader plugin. 12 | final class CrLoggerWeb { 13 | static void registerWith(Registrar registrar) { 14 | final channel = MethodChannel( 15 | 'cr_logger', 16 | const StandardMethodCodec(), 17 | registrar, 18 | ); 19 | 20 | final pluginInstance = CrLoggerWeb(); 21 | channel.setMethodCallHandler(pluginInstance.handleMethodCall); 22 | } 23 | 24 | /// Handles method calls over the MethodChannel of this plugin. 25 | /// Note: Check the "federated" architecture for a new way of doing this: 26 | /// https://flutter.dev/go/federated-plugins 27 | Future handleMethodCall(MethodCall call) async { 28 | switch (call.method) { 29 | case 'getPlatformVersion': 30 | return getPlatformVersion(); 31 | default: 32 | throw PlatformException( 33 | code: 'Unimplemented', 34 | details: "cr_downloader for web doesn't implement '${call.method}'", 35 | ); 36 | } 37 | } 38 | 39 | /// Returns a [String] containing the version of the platform. 40 | Future getPlatformVersion() { 41 | final version = html.window.navigator.userAgent; 42 | 43 | return Future.value(version); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/generated/assets.dart: -------------------------------------------------------------------------------- 1 | ///This file is automatically generated. DO NOT EDIT, all your changes would be lost. 2 | class CRLoggerAssets { 3 | CRLoggerAssets._(); 4 | 5 | static const String assetsArrowDown = 'assets/arrow_down.png'; 6 | static const String assetsContentCopy = 'assets/content_copy.png'; 7 | static const String assetsIcBack = 'assets/ic_back.png'; 8 | static const String assetsIcMenu = 'assets/ic_menu.png'; 9 | } 10 | -------------------------------------------------------------------------------- /lib/src/base/base_page_with_progress.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/controllers/logs_mode.dart'; 2 | import 'package:cr_logger/src/controllers/logs_mode_controller.dart'; 3 | import 'package:cr_logger/src/res/colors.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | abstract class BasePageWithProgress extends State { 7 | final _logsModeController = LogsModeController.instance; 8 | 9 | LogsMode get currentLogsMode => _logsModeController.logMode.value; 10 | 11 | @override 12 | void initState() { 13 | super.initState(); 14 | _logsModeController.addListener(getCurrentLogs); 15 | } 16 | 17 | @override 18 | void dispose() { 19 | _logsModeController.removeListener(getCurrentLogs); 20 | 21 | super.dispose(); 22 | } 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return ValueListenableBuilder( 27 | valueListenable: _logsModeController.progressNotifier, 28 | //ignore: prefer-trailing-comma 29 | builder: (_, isProgressState, __) => isProgressState 30 | ? Container( 31 | color: CRLoggerColors.backgroundGrey.withOpacity(0.8), 32 | child: const Center( 33 | child: CircularProgressIndicator(), 34 | ), 35 | ) 36 | : Builder( 37 | builder: (_) => bodyWidget(context), 38 | ), 39 | ); 40 | } 41 | 42 | Widget bodyWidget(BuildContext context); 43 | 44 | Future getCurrentLogs(); 45 | } 46 | -------------------------------------------------------------------------------- /lib/src/constants.dart: -------------------------------------------------------------------------------- 1 | const kLoggerPackage = 'package:logger'; 2 | const kLogFileName = 'log'; 3 | 4 | const kMethodPost = 'POST'; 5 | const kHidden = 'Hidden'; 6 | 7 | const kMaxDuration = 3000; 8 | const kAverageDuration = 2000; 9 | const kMinDuration = 1000; 10 | const kWorkerId = 'worker1'; 11 | const kRequestWorkerId = 'requestWorker'; 12 | const kResponseWorkerId = 'responseWorker'; 13 | const kErrorWorkerId = 'errorWorker'; 14 | const kMs = 'ms'; 15 | const kSending = 'Sending'; 16 | 17 | /// Maximum number of each type of logs (http, debug, info, error) by default 18 | const kDefaultMaxLogsCount = 50; 19 | 20 | const kDefaultOfUrlCount = 4; 21 | 22 | /// Used to stop updating log list (if logs come too often) 23 | const kIndentForLoadingLogs = 100; 24 | 25 | final patternOfParamsRegex = RegExp(r'\{\{|\}\}'); 26 | -------------------------------------------------------------------------------- /lib/src/controllers/logs_mode.dart: -------------------------------------------------------------------------------- 1 | enum LogsMode { 2 | fromCurrentSession('Current logs'), 3 | fromDB('DB logs'); 4 | 5 | const LogsMode(this.appBarTitle); 6 | 7 | final String appBarTitle; 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/controllers/logs_mode_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:cr_logger/src/controllers/logs_mode.dart'; 3 | import 'package:cr_logger/src/managers/log_manager.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | 6 | final class LogsModeController extends ChangeNotifier { 7 | LogsModeController._(); 8 | 9 | static LogsModeController instance = LogsModeController._(); 10 | 11 | final logMode = ValueNotifier(LogsMode.fromCurrentSession); 12 | final progressNotifier = ValueNotifier(false); 13 | 14 | bool get isFromCurrentSession => logMode.value == LogsMode.fromCurrentSession; 15 | 16 | bool _logsAreLoaded = false; 17 | 18 | Future changeMode() async { 19 | progressNotifier.value = false; 20 | 21 | if (logMode.value == LogsMode.fromCurrentSession) { 22 | logMode.value = LogsMode.fromDB; 23 | if (!_logsAreLoaded) { 24 | progressNotifier.value = true; 25 | 26 | await _loadLogsFromDB(); 27 | progressNotifier.value = false; 28 | } 29 | _logsAreLoaded = true; 30 | } else { 31 | logMode.value = LogsMode.fromCurrentSession; 32 | } 33 | notifyListeners(); 34 | } 35 | 36 | Future _loadLogsFromDB() => Future.wait([ 37 | HttpLogManager.instance.loadLogsFromDB(), 38 | LogManager.instance.loadLogsFromDB(), 39 | ]); 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/cr_logger_helper.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:cr_logger/src/res/theme.dart'; 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:package_info_plus/package_info_plus.dart'; 6 | import 'package:shared_preferences/shared_preferences.dart'; 7 | import 'package:synchronized/synchronized.dart'; 8 | 9 | final class CRLoggerHelper { 10 | CRLoggerHelper._(); 11 | 12 | static const _proxySharedPrefKey = 'cr_logger_charles_proxy'; 13 | static CRLoggerHelper instance = CRLoggerHelper._(); 14 | 15 | final lock = Lock(); 16 | final inspectorNotifier = ValueNotifier(false); 17 | final loggerShowingNotifier = ValueNotifier(false); 18 | 19 | final maxLogsCount = CRLoggerInitializer.instance.maxCurrentLogsCount; 20 | 21 | final maxDBLogsCount = CRLoggerInitializer.instance.maxDBLogsCount; 22 | late final PackageInfo packageInfo; 23 | late final SharedPreferences _prefs; 24 | 25 | ThemeData theme = loggerTheme; 26 | 27 | bool get isLoggerShowing => loggerShowingNotifier.value; 28 | 29 | bool get useDB { 30 | final useCrLoggerInReleaseBuild = 31 | CRLoggerInitializer.instance.useCrLoggerInReleaseBuild; 32 | final useDB = CRLoggerInitializer.instance.useDB; 33 | 34 | return useDB && (useCrLoggerInReleaseBuild || !kReleaseMode) && !kIsWeb; 35 | } 36 | 37 | /// Condition that determines whether or not to print logs 38 | bool get printLogs { 39 | final printLogs = CRLoggerInitializer.instance.printLogs; 40 | final useCrLoggerInReleaseBuild = 41 | CRLoggerInitializer.instance.useCrLoggerInReleaseBuild; 42 | 43 | return printLogs && (useCrLoggerInReleaseBuild || !kReleaseMode); 44 | } 45 | 46 | Future init() async { 47 | _prefs = await SharedPreferences.getInstance(); 48 | packageInfo = await PackageInfo.fromPlatform(); 49 | } 50 | 51 | Future setProxyToSharedPref(String? proxy) async { 52 | return proxy != null 53 | ? _prefs.setString(_proxySharedPrefKey, proxy) 54 | : _prefs.remove(_proxySharedPrefKey); 55 | } 56 | 57 | String? getProxyFromSharedPref() => _prefs.getString(_proxySharedPrefKey); 58 | 59 | void showLogger() => loggerShowingNotifier.value = true; 60 | 61 | void hideLogger() => loggerShowingNotifier.value = false; 62 | } 63 | -------------------------------------------------------------------------------- /lib/src/cr_logger_wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/log_message_wrapper.dart'; 2 | import 'package:logger/logger.dart'; 3 | 4 | final class CRLoggerWrapper { 5 | CRLoggerWrapper._(); 6 | 7 | static final CRLoggerWrapper instance = CRLoggerWrapper._(); 8 | 9 | late Logger log; 10 | 11 | /// It is necessary to be careful with the use of the [showToast] parameter, 12 | /// because at a high refresh rate the logs with this parameter will be missed 13 | /// for a detailed view. 14 | void t( 15 | dynamic message, { 16 | dynamic error, 17 | StackTrace? stackTrace, 18 | bool showToast = false, 19 | }) { 20 | log.t( 21 | LogMessageWrapper( 22 | message: message, 23 | showToast: showToast, 24 | ), 25 | error: error, 26 | stackTrace: stackTrace, 27 | ); 28 | } 29 | 30 | void d( 31 | dynamic message, { 32 | dynamic error, 33 | StackTrace? stackTrace, 34 | bool showToast = false, 35 | }) { 36 | log.d( 37 | LogMessageWrapper( 38 | message: message, 39 | showToast: showToast, 40 | ), 41 | error: error, 42 | stackTrace: stackTrace, 43 | ); 44 | } 45 | 46 | void i( 47 | dynamic message, { 48 | dynamic error, 49 | StackTrace? stackTrace, 50 | bool showToast = false, 51 | }) { 52 | log.i( 53 | LogMessageWrapper( 54 | message: message, 55 | showToast: showToast, 56 | ), 57 | error: error, 58 | stackTrace: stackTrace, 59 | ); 60 | } 61 | 62 | void w( 63 | dynamic message, { 64 | dynamic error, 65 | StackTrace? stackTrace, 66 | bool showToast = false, 67 | }) { 68 | log.w( 69 | LogMessageWrapper( 70 | message: message, 71 | showToast: showToast, 72 | ), 73 | error: error, 74 | stackTrace: stackTrace, 75 | ); 76 | } 77 | 78 | void e( 79 | dynamic message, { 80 | dynamic error, 81 | StackTrace? stackTrace, 82 | bool showToast = false, 83 | }) { 84 | log.e( 85 | LogMessageWrapper( 86 | message: message, 87 | showToast: showToast, 88 | ), 89 | error: error, 90 | stackTrace: stackTrace, 91 | ); 92 | } 93 | 94 | void f( 95 | dynamic message, { 96 | dynamic error, 97 | StackTrace? stackTrace, 98 | bool showToast = false, 99 | }) { 100 | log.f( 101 | LogMessageWrapper( 102 | message: message, 103 | showToast: showToast, 104 | ), 105 | error: error, 106 | stackTrace: stackTrace, 107 | ); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /lib/src/data/bean/error_bean.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/data/bean/response_bean.dart'; 2 | 3 | final class ErrorBean { 4 | ErrorBean({ 5 | this.id, 6 | this.errorMessage, 7 | this.statusMessage, 8 | this.time, 9 | this.responseBean, 10 | this.duration, 11 | this.statusCode, 12 | this.baseUrl, 13 | this.url, 14 | this.errorData, 15 | }); 16 | 17 | factory ErrorBean.fromJson(Map json) { 18 | return ErrorBean( 19 | id: json['id'], 20 | errorMessage: json['errorMessage'], 21 | url: json['url'], 22 | time: DateTime.tryParse(json['time'] ?? '')?.toLocal(), 23 | responseBean: ResponseBean.fromJson(json['responseBean']), 24 | duration: json['duration'], 25 | errorData: json['errorData'], 26 | ); 27 | } 28 | 29 | int? id; 30 | dynamic errorData; 31 | String? errorMessage; 32 | String? statusMessage; 33 | String? baseUrl; 34 | String? url; 35 | DateTime? time; 36 | ResponseBean? responseBean; 37 | int? duration; 38 | int? statusCode; 39 | 40 | Map toJson() => { 41 | 'id': id, 42 | 'url': url, 43 | 'errorMessage': errorMessage, 44 | 'time': time?.toUtc().toString(), 45 | 'responseBean': responseBean?.toJson(), 46 | 'duration': duration, 47 | 'statusCode': statusCode, 48 | 'statusMessage': statusMessage, 49 | 'errorData': errorData?.toString(), 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /lib/src/data/bean/http_bean.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/data/bean/error_bean.dart' show ErrorBean; 2 | import 'package:cr_logger/src/data/bean/request_bean.dart' show RequestBean; 3 | import 'package:cr_logger/src/data/bean/response_bean.dart' show ResponseBean; 4 | import 'package:cr_logger/src/models/request_status.dart'; 5 | 6 | final class HttpBean { 7 | HttpBean({ 8 | this.key, 9 | this.request, 10 | this.response, 11 | this.error, 12 | }); 13 | 14 | int? key; 15 | RequestBean? request; 16 | ResponseBean? response; 17 | ErrorBean? error; 18 | } 19 | 20 | extension HttpBeanExtension on HttpBean { 21 | RequestStatus get status { 22 | if (error != null) { 23 | return isInternetError ? RequestStatus.noInternet : RequestStatus.error; 24 | } else { 25 | //ignore: prefer-conditional-expressions 26 | if (response != null) { 27 | return response?.statusCode == null 28 | ? RequestStatus.error 29 | : RequestStatus.success; 30 | } else { 31 | return RequestStatus.sending; 32 | } 33 | } 34 | } 35 | 36 | bool get isInternetError { 37 | if (error?.statusCode != null) { 38 | return false; 39 | } 40 | 41 | final errorMessage = error?.errorMessage; 42 | if (errorMessage != null && errorMessage.contains('Connecting timed out')) { 43 | return true; 44 | } 45 | 46 | final errorData = error?.errorData; 47 | if (errorData is Map) { 48 | final errorCode = errorData['OS Error code']; 49 | if (errorCode == 7 || 50 | errorCode == 8 || 51 | errorCode == 101 || 52 | errorCode == 103 || 53 | errorData['No internet'] == true) { 54 | return true; 55 | } 56 | } 57 | 58 | return false; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/src/data/bean/log_bean.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:cr_logger/src/res/colors.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:uuid/uuid.dart'; 5 | 6 | final class LogBean implements Comparable { 7 | LogBean({ 8 | required this.message, 9 | required this.time, 10 | required this.stackTrace, 11 | this.showToast = false, 12 | this.type, 13 | this.data = const {}, 14 | this.key, 15 | String? id, 16 | }) : id = id ?? const Uuid().v4(), 17 | color = type?.getColor() ?? CRLoggerColors.primaryColor; 18 | 19 | final int? key; 20 | final String id; 21 | final dynamic message; 22 | final DateTime time; 23 | final String? stackTrace; 24 | final Map? data; 25 | LogType? type; 26 | Color color; 27 | bool showToast; 28 | 29 | @override 30 | int compareTo(LogBean other) { 31 | return other.time.isAfter(time) ? 1 : -1; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/src/data/bean/response_bean.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:cr_logger/src/constants.dart'; 3 | import 'package:cr_logger/src/utils/hide_values_in_map.dart'; 4 | 5 | final class ResponseBean { 6 | ResponseBean({ 7 | this.id, 8 | this.statusCode, 9 | this.url, 10 | this.method, 11 | this.statusMessage, 12 | this.responseTime, 13 | this.duration, 14 | this.data, 15 | this.headers, 16 | }); 17 | 18 | factory ResponseBean.fromJson(Map json) { 19 | return ResponseBean( 20 | id: json['id'], 21 | statusCode: json['statusCode'], 22 | url: json['url'], 23 | method: json['method'], 24 | statusMessage: json['statusMessage'], 25 | responseTime: DateTime.tryParse(json['responseTime'] ?? '')?.toLocal(), 26 | duration: json['duration'], 27 | data: json['data'], 28 | headers: json['headers'], 29 | ); 30 | } 31 | 32 | int? id; 33 | int? statusCode; 34 | String? url; 35 | String? method; 36 | String? statusMessage; 37 | DateTime? responseTime; 38 | int? duration; 39 | dynamic data; 40 | Map? headers; 41 | 42 | Map toJson() { 43 | final headers = {}; 44 | this.headers?.forEach((k, list) => headers[k] = list.toString()); 45 | final changedHeaders = headers.map((key, value) { 46 | return CRLoggerInitializer.instance.hiddenHeaders.contains(key) 47 | ? MapEntry(key, kHidden) 48 | : MapEntry(key, value); 49 | }); 50 | Map? changedData; 51 | if (data is Map) { 52 | changedData = hideValuesInMap(data); 53 | } 54 | 55 | return { 56 | 'id': id, 57 | 'statusCode': statusCode, 58 | 'url': url, 59 | 'method': method, 60 | 'statusMessage': statusMessage, 61 | 'responseTime': responseTime?.toUtc().toString(), 62 | 'duration': duration, 63 | 'headers': changedHeaders, 64 | 'data': changedData, 65 | }; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/src/data/models/log_type.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/res/colors.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:logger/logger.dart'; 4 | 5 | enum LogType { 6 | http('HTTP'), 7 | debug('Debug'), 8 | info('Info'), 9 | error('Error'); 10 | 11 | const LogType(this.name); 12 | 13 | Color getColor() { 14 | switch (this) { 15 | case LogType.http: 16 | return CRLoggerColors.blueAccent; 17 | case LogType.debug: 18 | return CRLoggerColors.greenAccent; 19 | case LogType.info: 20 | return CRLoggerColors.blueAccent; 21 | case LogType.error: 22 | return CRLoggerColors.red; 23 | } 24 | } 25 | 26 | static LogType? getTypeFromLevel(Level level) { 27 | switch (level) { 28 | case Level.trace: 29 | case Level.debug: 30 | return LogType.debug; 31 | case Level.info: 32 | case Level.warning: 33 | return LogType.info; 34 | case Level.error: 35 | case Level.fatal: 36 | return LogType.error; 37 | default: 38 | return null; 39 | } 40 | } 41 | 42 | final String name; 43 | } 44 | -------------------------------------------------------------------------------- /lib/src/data/sqflite_db/converters/http_enitity_converter.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:cr_logger/src/data/sqflite_db/entities/http_entity.dart'; 3 | import 'package:cr_logger/src/utils/parsers/isolate_parser.dart'; 4 | 5 | final class HttpEntityConverter { 6 | final _parser = IsolateParser(); 7 | 8 | Future inToOut(HttpEntity inObject) async { 9 | final request = await _decode(inObject.request); 10 | final response = await _decode(inObject.response); 11 | final error = await _decode(inObject.error); 12 | 13 | return HttpBean( 14 | key: inObject.key, 15 | request: request != null ? RequestBean.fromJson(request) : null, 16 | response: response != null ? ResponseBean.fromJson(response) : null, 17 | error: error != null ? ErrorBean.fromJson(error) : null, 18 | ); 19 | } 20 | 21 | Future outToIn(HttpBean outObject) async { 22 | final request = outObject.request; 23 | final response = outObject.response; 24 | final error = outObject.error; 25 | 26 | return HttpEntity( 27 | key: outObject.key, 28 | request: await _encode(request, request?.toJson()), 29 | response: await _encode(response, response?.toJson()), 30 | error: await _encode(error, error?.toJson()), 31 | ); 32 | } 33 | 34 | dynamic _decode(String? json) async => 35 | json != null ? await _parser.decode(json) : null; 36 | 37 | Future _encode(dynamic object, Map? json) async => 38 | object != null ? await _parser.encode(json) : null; 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/data/sqflite_db/converters/log_entity_converters.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:cr_logger/src/data/sqflite_db/entities/log_entity.dart'; 3 | import 'package:cr_logger/src/utils/parsers/isolate_parser.dart'; 4 | 5 | final class LogEntityConverter { 6 | final _parser = IsolateParser(); 7 | 8 | Future inToOut(LogEntity inObject) async { 9 | final data = inObject.data; 10 | 11 | return LogBean( 12 | id: inObject.id, 13 | message: await _parser.decode(inObject.message), 14 | time: inObject.time?.toLocal() ?? DateTime.now(), 15 | stackTrace: inObject.stackTrace, 16 | data: data != null ? await _parser.decode(data) : null, 17 | key: inObject.key, 18 | type: inObject.type, 19 | ); 20 | } 21 | 22 | Future outToIn(LogBean outObject) async { 23 | final data = outObject.data; 24 | 25 | return LogEntity( 26 | message: await _parser.encode(outObject.message), 27 | time: outObject.time.toUtc(), 28 | stackTrace: outObject.stackTrace, 29 | data: data != null && data.isNotEmpty ? await _parser.encode(data) : null, 30 | type: outObject.type ?? LogType.info, 31 | id: outObject.id, 32 | key: outObject.key ?? 0, 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/src/data/sqflite_db/entities/http_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/utils/map_ext.dart'; 2 | 3 | final class HttpEntity { 4 | HttpEntity({ 5 | this.key, 6 | this.request, 7 | this.response, 8 | this.error, 9 | }); 10 | 11 | factory HttpEntity.fromJson(Map json) { 12 | return HttpEntity( 13 | /// Here it is checked for type because after importing the logs to the Web, 14 | /// in the json file the [key] is saved as a 'String'. 15 | /// But in the db the key is stored in 'int'. 16 | key: json['key'] is String ? int.tryParse(json['key']) : json['key'], 17 | request: json['request'], 18 | response: json['response'], 19 | error: json['error'], 20 | ); 21 | } 22 | 23 | int? key; 24 | String? request; 25 | String? response; 26 | String? error; 27 | 28 | Map toJson() { 29 | final json = { 30 | 'key': key?.toString(), 31 | 'request': request, 32 | 'response': response, 33 | 'error': error, 34 | }; 35 | 36 | // ignore: cascade_invocations 37 | json.clearAllNull(); 38 | 39 | return json; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/data/sqflite_db/entities/log_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:cr_logger/src/utils/enum_ext.dart'; 3 | import 'package:cr_logger/src/utils/map_ext.dart'; 4 | 5 | final class LogEntity { 6 | LogEntity({ 7 | required this.id, 8 | required this.message, 9 | required this.time, 10 | required this.stackTrace, 11 | required this.type, 12 | this.data = '', 13 | this.key, 14 | }); 15 | 16 | factory LogEntity.fromJson(Map json) { 17 | return LogEntity( 18 | key: json['key'], 19 | message: json['message'], 20 | time: DateTime.tryParse(json['time'])?.toLocal(), 21 | stackTrace: json['stacktrace'], 22 | type: LogType.values.valueOf(json['type']) ?? LogType.info, 23 | data: json['data'], 24 | id: json['id'], 25 | ); 26 | } 27 | 28 | final int? key; 29 | final String id; 30 | final String message; 31 | final DateTime? time; 32 | final String? stackTrace; 33 | final String? data; 34 | final LogType type; 35 | 36 | /// [key] should not be in this method, because the database increments it itself. 37 | Map toJson() { 38 | final json = { 39 | 'id': id, 40 | 'message': message.toString(), 41 | 'time': time?.toUtc().toString(), 42 | 'stacktrace': stackTrace?.toString(), 43 | 'data': data?.toString(), 44 | 'type': type.name.toString(), 45 | }; 46 | 47 | // ignore: cascade_invocations 48 | json.clearAllNull(); 49 | 50 | return json; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/src/data/sqflite_db/log_module.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:cr_logger/src/data/sqflite_db/converters/http_enitity_converter.dart'; 3 | import 'package:cr_logger/src/data/sqflite_db/converters/log_entity_converters.dart'; 4 | import 'package:cr_logger/src/data/sqflite_db/sqflite_repository.dart'; 5 | 6 | final class LogModule { 7 | LogModule._(); 8 | 9 | static LogModule instance = LogModule._(); 10 | 11 | final _httpLogConverter = HttpEntityConverter(); 12 | final _logConverter = LogEntityConverter(); 13 | 14 | final _sqlRepository = SqfliteRepository.instance; 15 | 16 | Future init() async { 17 | await _sqlRepository.initDB(); 18 | } 19 | 20 | /// Http Log 21 | Future> getAllSavedHttpLogs() async { 22 | final logs = await _sqlRepository.getAllHttpLogs(); 23 | final logModel = []; 24 | for (final log in logs) { 25 | logModel.add(await _httpLogConverter.inToOut(log)); 26 | } 27 | 28 | return logModel; 29 | } 30 | 31 | Future saveHttpLog(HttpBean httpLog) async => 32 | _sqlRepository.saveHTTPLog( 33 | await _httpLogConverter.outToIn(httpLog), 34 | ); 35 | 36 | Future updateHttpLog(HttpBean httpLog) async => 37 | _sqlRepository.updateHTTPLog( 38 | await _httpLogConverter.outToIn(httpLog), 39 | ); 40 | 41 | Future deleteAllHttpLogs() => _sqlRepository.deleteAllHttpLogs(); 42 | 43 | Future deleteHttpLogs(List logs) async { 44 | final ids = logs.map((e) => e.key).whereType().toList(); 45 | 46 | return _sqlRepository.deleteHttpLogs(ids); 47 | } 48 | 49 | /// Logs 50 | Future> getAllSavedLogs() async { 51 | final logs = await _sqlRepository.getAllLogs(); 52 | final logModel = []; 53 | for (final log in logs) { 54 | logModel.add(await _logConverter.inToOut(log)); 55 | } 56 | 57 | return logModel; 58 | } 59 | 60 | Future saveLog(LogBean log) async => _sqlRepository.saveLog( 61 | await _logConverter.outToIn(log), 62 | ); 63 | 64 | Future deleteAllLogs() => _sqlRepository.deleteAllLogs(); 65 | 66 | Future deleteLogs(List logs) async { 67 | final ids = logs.map((e) => e.key).whereType().toList(); 68 | 69 | return _sqlRepository.deleteLogs(ids); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/src/dio_log.dart: -------------------------------------------------------------------------------- 1 | export 'interceptor/chopper_log_interceptor.dart'; 2 | export 'interceptor/dio_log_interceptor.dart'; 3 | export 'managers/http_log_manager.dart'; 4 | export 'overlay_draggable_button.dart'; 5 | export 'utils/copy_clipboard.dart'; 6 | export 'utils/json_utils.dart'; 7 | export 'widget/http_error_widget.dart'; 8 | export 'widget/http_request_widget.dart'; 9 | export 'widget/http_response_widget.dart'; 10 | -------------------------------------------------------------------------------- /lib/src/extensions/ansi_color_ext.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:logger/logger.dart'; 4 | 5 | extension AnsiColorExt on AnsiColor { 6 | /// Return [AnsiColor] from provided [color] or null if [color] is null. 7 | static AnsiColor? fromColorOrNull(Color? color) => 8 | (color != null) ? fromColor(color) : null; 9 | 10 | /// Return [AnsiColor] from provided [color] 11 | static AnsiColor fromColor(Color color) { 12 | var xtermColor = ((color.red / 255).clamp(0.0, 1.0) * 5).toInt() * 36 + 13 | ((color.green / 255).clamp(0.0, 1.0) * 5).toInt() * 6 + 14 | ((color.blue / 255).clamp(0.0, 1.0) * 5).toInt() + 15 | 16; 16 | 17 | xtermColor = xtermColor < 0 18 | ? 0 19 | : xtermColor > 255 20 | ? 255 21 | : xtermColor; 22 | 23 | return AnsiColor.fg(xtermColor); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/src/extensions/card_theme_ext.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension CardThemeExt on CardTheme { 4 | BorderRadius get borderRadius => 5 | ((shape ?? const RoundedRectangleBorder()) as RoundedRectangleBorder) 6 | .borderRadius as BorderRadius; 7 | } 8 | -------------------------------------------------------------------------------- /lib/src/extensions/cast.dart: -------------------------------------------------------------------------------- 1 | T? cast(Object? x) => x is T ? x : null; 2 | -------------------------------------------------------------------------------- /lib/src/extensions/date_time.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/extensions/int_ext.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | extension DateTimeFormatter on DateTime { 5 | /// The same as [DateFormat('yyyy MM dd hh:mm:ss.SSS').format(this)] 6 | String formatTimeWithYear(BuildContext context) => 7 | '$year-${month.leading}-${day.leading} ${formatTime(context)}'; 8 | 9 | /// The same as [DateFormat('hh:mm:ss.SSS').format(this)] 10 | String formatTime(BuildContext context) { 11 | final userUse24HourFormat = MediaQuery.of(context).alwaysUse24HourFormat; 12 | 13 | return userUse24HourFormat 14 | ? '${hour.leading}:${minute.leading}:${second.leading}.${millisecond.leading3}' 15 | : formatted12HourString; 16 | } 17 | 18 | // TODO: make this using intl package later, when all packages move to 0.18.0 version 19 | /// Returns time from DateTime in 12H format (19:35 => 07:35 PM) 20 | String get formatted12HourString { 21 | final time = TimeOfDay.fromDateTime(this); 22 | final buffer = StringBuffer(); 23 | final hours12hFormat = time.hour - time.periodOffset; 24 | final hoursStr = 25 | hours12hFormat < 10 ? '0$hours12hFormat' : hours12hFormat.toString(); 26 | final minutesStr = 27 | time.minute < 10 ? '0${time.minute}' : time.minute.toString(); 28 | final periodStr = time.period.name; 29 | buffer.writeAll([ 30 | hoursStr, 31 | ':', 32 | minutesStr, 33 | ' ', 34 | periodStr, 35 | ]); 36 | 37 | return buffer.toString().toUpperCase(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/extensions/do_post_frame.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension StateExt on State { 4 | void doPostFrame(Function func) { 5 | WidgetsBinding.instance.addPostFrameCallback((timeStamp) { 6 | func(); 7 | }); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/src/extensions/extensions.dart: -------------------------------------------------------------------------------- 1 | export 'ansi_color_ext.dart'; 2 | export 'card_theme_ext.dart'; 3 | export 'cast.dart'; 4 | export 'date_time.dart'; 5 | export 'image_ext.dart'; 6 | export 'int_ext.dart'; 7 | export 'theme_data_ext.dart'; 8 | -------------------------------------------------------------------------------- /lib/src/extensions/image_ext.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension ImageExt on Image { 4 | static Image fromPackage( 5 | String name, { 6 | double width = 24, 7 | double height = 24, 8 | }) => 9 | Image.asset( 10 | name, 11 | package: 'cr_logger', 12 | width: width, 13 | height: height, 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /lib/src/extensions/int_ext.dart: -------------------------------------------------------------------------------- 1 | extension IntExt on int { 2 | String toStringWithLeading([int width = 2, String symbol = '0']) => 3 | toString().padLeft(width, symbol); 4 | 5 | String get leading => toStringWithLeading(); 6 | 7 | String get leading3 => toStringWithLeading(3); 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/extensions/theme_data_ext.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension ThemeDataExt on ThemeData { 4 | /// Return ThemeData with default values of [cardTheme] fields 5 | ThemeData copyWithDefaultCardTheme(CardTheme defaultCardTheme) { 6 | return copyWith( 7 | cardTheme: cardTheme.copyWith( 8 | clipBehavior: cardTheme.clipBehavior ?? defaultCardTheme.clipBehavior, 9 | color: cardTheme.color ?? defaultCardTheme.color, 10 | shadowColor: cardTheme.shadowColor ?? defaultCardTheme.shadowColor, 11 | elevation: cardTheme.elevation ?? defaultCardTheme.elevation, 12 | margin: cardTheme.margin ?? defaultCardTheme.margin, 13 | shape: cardTheme.shape ?? defaultCardTheme.shape, 14 | ), 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/interceptor/chopper_log_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | 4 | import 'package:chopper/chopper.dart'; 5 | import 'package:cr_logger/cr_logger.dart'; 6 | import 'package:http/http.dart' as http; 7 | 8 | final class ChopperLogInterceptor 9 | implements ResponseInterceptor, RequestInterceptor { 10 | final logManager = HttpLogManager.instance; 11 | 12 | @override 13 | FutureOr onRequest(Request request) async { 14 | final reqOpt = RequestBean() 15 | ..id = _getRequestHashCode(await request.toBaseRequest()) 16 | ..url = request.url.toString() 17 | ..method = request.method 18 | ..contentType = request.headers['Content-Type'].toString() 19 | ..requestTime = DateTime.now() 20 | ..params = request.parameters 21 | ..body = request.body 22 | ..headers = request.headers; 23 | logManager.onRequest(reqOpt); 24 | 25 | return request; 26 | } 27 | 28 | @override 29 | FutureOr onResponse(Response response) { 30 | var data = {}; 31 | try { 32 | data = json.decode(response.body as String); 33 | } catch (error, stackTrace) { 34 | log.e( 35 | 'Chopper interceptor error', 36 | error: error, 37 | stackTrace: stackTrace, 38 | ); 39 | } 40 | 41 | final requestId = _getRequestHashCode(response.base.request!); 42 | final statusCode = response.statusCode; 43 | final isError = statusCode < 200 || statusCode >= 300; 44 | 45 | /// In error case, do not put data in ResponseBean. 46 | final dynamic responseData; 47 | //ignore: prefer-conditional-expressions 48 | if (isError) { 49 | responseData = null; 50 | } else { 51 | responseData = data.isNotEmpty ? data : response.body; 52 | } 53 | 54 | final responseBean = ResponseBean() 55 | ..id = requestId 56 | ..responseTime = DateTime.now() 57 | ..statusCode = response.statusCode 58 | ..url = response.base.request?.url.toString() 59 | ..method = response.base.request?.method 60 | ..statusMessage = response.base.reasonPhrase 61 | ..data = responseData 62 | ..headers = response.headers; 63 | logManager.onResponse(responseBean); 64 | 65 | /// On error 66 | if (isError) { 67 | final errorBean = ErrorBean() 68 | ..id = requestId 69 | ..errorData = response.error 70 | ..statusCode = statusCode 71 | ..statusMessage = response.base.reasonPhrase 72 | ..url = response.base.request?.url.toString() 73 | ..time = DateTime.now(); 74 | logManager.onError(errorBean); 75 | } 76 | 77 | return response; 78 | } 79 | 80 | /// Creates hashcode based on request 81 | int _getRequestHashCode(http.BaseRequest baseRequest) { 82 | var hashCodeSum = 0; 83 | 84 | hashCodeSum += baseRequest.url.hashCode; 85 | hashCodeSum += baseRequest.method.hashCode; 86 | if (baseRequest.headers.isNotEmpty) { 87 | baseRequest.headers.forEach((key, value) { 88 | hashCodeSum += key.hashCode; 89 | hashCodeSum += value.hashCode; 90 | }); 91 | } 92 | if (baseRequest.contentLength != null) { 93 | hashCodeSum += baseRequest.contentLength.hashCode; 94 | } 95 | 96 | return hashCodeSum.hashCode; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/src/interceptor/cr_http_adapter.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:http/http.dart' as http; 3 | 4 | final class CRHttpAdapter { 5 | HttpLogManager logManager = HttpLogManager.instance; 6 | 7 | /// Handles http response. It creates both request and response from http call 8 | void onResponse(http.Response response, Object? body) { 9 | if (response.request == null) { 10 | return; 11 | } 12 | final request = response.request!; 13 | 14 | final requestHeaders = {}; 15 | 16 | request.headers.forEach((header, value) { 17 | requestHeaders[header] = value; 18 | }); 19 | 20 | String? contentType = 'unknown'; 21 | if (requestHeaders.containsKey('Content-Type')) { 22 | contentType = requestHeaders['Content-Type'] as String?; 23 | } 24 | 25 | final requestBean = RequestBean() 26 | ..id = request.hashCode 27 | ..url = request.url.toString() 28 | ..method = request.method 29 | ..contentType = contentType 30 | ..requestTime = DateTime.now() 31 | ..body = body 32 | ..headers = requestHeaders; 33 | logManager.onRequest(requestBean); 34 | 35 | final responseHeaders = {}; 36 | 37 | response.headers.forEach((header, value) { 38 | responseHeaders[header] = value; 39 | }); 40 | 41 | final statusCode = response.statusCode; 42 | final isError = statusCode < 200 || statusCode >= 300; 43 | 44 | final responseBean = ResponseBean() 45 | ..id = request.hashCode 46 | ..responseTime = DateTime.now() 47 | ..url = request.url.toString() 48 | ..method = request.method 49 | ..statusCode = response.statusCode 50 | ..statusMessage = response.reasonPhrase 51 | 52 | /// In error case, do not put data in ResponseBean. 53 | ..data = isError ? null : body 54 | ..headers = responseHeaders; 55 | logManager.onResponse(responseBean); 56 | 57 | /// On error 58 | if (isError) { 59 | final errorBean = ErrorBean() 60 | ..id = request.hashCode 61 | ..url = request.url.toString() 62 | ..time = DateTime.now() 63 | ..statusCode = response.statusCode 64 | ..statusMessage = response.reasonPhrase; 65 | logManager.onError(errorBean); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/src/interceptor/cr_http_client_adapter.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:cr_logger/cr_logger.dart'; 4 | 5 | final class CRHttpClientAdapter { 6 | HttpLogManager logManager = HttpLogManager.instance; 7 | 8 | /// Handles httpClientRequest and creates http alice call from it 9 | void onRequest(HttpClientRequest request, Object? body) { 10 | final headers = {}; 11 | 12 | request.headers.forEach((header, value) { 13 | headers[header] = value; 14 | }); 15 | 16 | String? contentType = 'unknown'; 17 | if (headers.containsKey('Content-Type')) { 18 | contentType = headers['Content-Type'] as String?; 19 | } 20 | 21 | final reqOpt = RequestBean() 22 | ..id = request.hashCode 23 | ..url = request.uri.toString() 24 | ..method = request.method 25 | ..contentType = contentType 26 | ..requestTime = DateTime.now() 27 | ..body = body 28 | ..headers = headers; 29 | 30 | logManager.onRequest(reqOpt); 31 | } 32 | 33 | /// Handles httpClientRequest and adds response to http alice call 34 | void onResponse( 35 | HttpClientResponse response, 36 | HttpClientRequest request, 37 | Object? body, 38 | ) { 39 | final headers = {}; 40 | 41 | response.headers.forEach((header, value) { 42 | headers[header] = value; 43 | }); 44 | 45 | final statusCode = response.statusCode; 46 | final isError = statusCode < 200 || statusCode >= 300; 47 | 48 | final resOpt = ResponseBean() 49 | ..id = request.hashCode 50 | ..responseTime = DateTime.now() 51 | ..url = request.uri.toString() 52 | ..method = request.method 53 | ..statusCode = response.statusCode 54 | ..statusMessage = response.reasonPhrase 55 | 56 | /// In error case, do not put data in ResponseBean. 57 | ..data = isError ? null : body 58 | ..headers = headers; 59 | logManager.onResponse(resOpt); 60 | 61 | /// On error 62 | if (isError) { 63 | final errorBean = ErrorBean() 64 | ..id = request.hashCode 65 | ..url = request.uri.toString() 66 | ..time = DateTime.now() 67 | ..statusCode = response.statusCode 68 | ..statusMessage = response.reasonPhrase; 69 | logManager.onError(errorBean); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/src/js/console_output_worker.dart: -------------------------------------------------------------------------------- 1 | @JS() 2 | library console_output; 3 | 4 | // ignore: avoid_web_libraries_in_flutter 5 | import 'package:cr_logger/src/utils/html_stub.dart' 6 | if (dart.library.js) 'package:js/js.dart'; 7 | 8 | @JS('printLogs') 9 | external void printLogs(List lines); 10 | 11 | @JS('printRequestLog') 12 | external void printRequestLog(String requestBean); 13 | 14 | @JS('printResponseLog') 15 | external void printResponseLog(String responseBean); 16 | 17 | @JS('printErrorLog') 18 | external void printErrorLog(String errorBean); 19 | 20 | @JS('downloadLogsWeb') 21 | external void downloadLogsWeb(String fileName, String text); 22 | -------------------------------------------------------------------------------- /lib/src/js/error_worker_scripts.dart: -------------------------------------------------------------------------------- 1 | const errorWorkerScript = r''' 2 | self.onmessage = function (e) { 3 | let errorBean = JSON.parse(e.data) 4 | console.log(errorBean) 5 | if (errorBean["responseBean"] != null) { 6 | let uri = errorBean["url"]; 7 | printBoxed( 8 | `Error ║ Status: ${errorBean["statusCode"]} ${errorBean["statusMessage"]}`, 9 | uri.toString(), 10 | ); 11 | if (errorBean["errorMessage"] != null && errorBean["responseBean"] != null) { 12 | let responseBean = errorBean["responseBean"] 13 | printMapAsTable(new Map(Object.entries(responseBean["headers"])), 'Headers'); 14 | console.log('╔ Body'); 15 | console.log('║') 16 | if (responseBean["data"] != null) { 17 | if (responseBean["data"] instanceof Object) { 18 | printPrettyMap(new Map(Object.entries(responseBean["data"]))); 19 | } else if (responseBean["data"] instanceof Array) { 20 | console.log(`║ [`); 21 | printList(responseBean["data"]); 22 | console.log(`║ [`); 23 | } else { 24 | printBlock(responseBean["data"].toString()); 25 | } 26 | } 27 | console.log('║'); 28 | printLine('╚'); 29 | } else { 30 | printLine('╚'); 31 | console.log(''); 32 | } 33 | } else { 34 | printBoxed( 35 | 'Error ║ ', 36 | errorBean["errorMessage"], 37 | ); 38 | } 39 | }; 40 | '''; 41 | 42 | const createErrorWorkerScript = ''' 43 | var errorBlob = new Blob([ 44 | document.querySelector('#errorWorker').textContent 45 | ], { type: "text/javascript" }) 46 | 47 | var errorWorker = new Worker(window.URL.createObjectURL(errorBlob)); 48 | '''; 49 | 50 | const printErrorLogScript = ''' 51 | function printErrorLog(errorBean) { 52 | errorWorker.postMessage(errorBean); 53 | } 54 | '''; 55 | -------------------------------------------------------------------------------- /lib/src/js/request_worker_scripts.dart: -------------------------------------------------------------------------------- 1 | const requestWorkerScript = ''' 2 | self.onmessage = function (e) { 3 | let requestBean = JSON.parse(e.data) 4 | 5 | printRequestHeader(requestBean) 6 | printMapAsTable(new Map(Object.entries(requestBean["params"])), 'Query Parameters'); 7 | printMapAsTable(new Map(Object.entries(requestBean["headers"])), 'Headers'); 8 | 9 | if (requestBean["method"] != 'GET') { 10 | let data = requestBean["body"]; 11 | if (data != null) { 12 | if (data instanceof Object) { 13 | printMapAsTable(new Map(Object.entries(data)), 'Body'); 14 | } 15 | else { 16 | printBlock(data.toString()); 17 | } 18 | } 19 | } 20 | }; 21 | '''; 22 | 23 | const createRequestWorkerScript = ''' 24 | var requestBlob = new Blob([ 25 | document.querySelector('#requestWorker').textContent 26 | ], { type: "text/javascript" }) 27 | 28 | var requestWorker = new Worker(window.URL.createObjectURL(requestBlob)); 29 | '''; 30 | 31 | const printRequestLogScript = ''' 32 | function printRequestLog(requestBean) { 33 | requestWorker.postMessage(requestBean); 34 | } 35 | '''; 36 | -------------------------------------------------------------------------------- /lib/src/js/response_worker_scripts.dart: -------------------------------------------------------------------------------- 1 | const responseWorkerScript = ''' 2 | self.onmessage = function (e) { 3 | let responseBean = JSON.parse(e.data) 4 | 5 | printResponse(responseBean) 6 | }; 7 | '''; 8 | 9 | const createResponseWorkerScript = ''' 10 | var responseBlob = new Blob([ 11 | document.querySelector('#responseWorker').textContent 12 | ], { type: "text/javascript" }) 13 | 14 | var responseWorker = new Worker(window.URL.createObjectURL(responseBlob)); 15 | '''; 16 | 17 | const printResponseLogScript = ''' 18 | function printResponseLog(responseBean) { 19 | responseWorker.postMessage(responseBean); 20 | } 21 | '''; 22 | -------------------------------------------------------------------------------- /lib/src/js/scripts.dart: -------------------------------------------------------------------------------- 1 | const workerScript = ''' 2 | self.onmessage = function(e) { 3 | e.data.forEach( 4 | function(line) { 5 | console.log(line); 6 | } 7 | ); 8 | }; 9 | '''; 10 | 11 | const createWorkerScript = ''' 12 | var blob = new Blob([ 13 | document.querySelector('#worker1').textContent 14 | ], { type: "text/javascript" }) 15 | 16 | var worker = new Worker(window.URL.createObjectURL(blob)); 17 | '''; 18 | 19 | const printLogsScript = ''' 20 | function printLogs(lines) { 21 | worker.postMessage(lines); // Start the worker. 22 | } 23 | '''; 24 | 25 | const downloadLogsWebScript = ''' 26 | function downloadLogsWeb(fileName, text) { 27 | var element = document.createElement('a'); 28 | element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); 29 | element.setAttribute('download', fileName); 30 | 31 | element.style.display = 'none'; 32 | document.body.appendChild(element); 33 | element.click() 34 | element.remove() 35 | } 36 | '''; 37 | -------------------------------------------------------------------------------- /lib/src/log_message_wrapper.dart: -------------------------------------------------------------------------------- 1 | /// Log message wrapper allowing to add new parameters 2 | class LogMessageWrapper { 3 | LogMessageWrapper({ 4 | required this.message, 5 | required this.showToast, 6 | }); 7 | 8 | dynamic message; 9 | bool showToast; 10 | } 11 | -------------------------------------------------------------------------------- /lib/src/models/http_log_type.dart: -------------------------------------------------------------------------------- 1 | enum HttpLogType { 2 | request, 3 | response, 4 | error, 5 | } 6 | 7 | extension HttpLogTypeExt on HttpLogType { 8 | String asString() { 9 | switch (this) { 10 | case HttpLogType.request: 11 | return 'Request'; 12 | case HttpLogType.response: 13 | return 'Response'; 14 | case HttpLogType.error: 15 | return 'Error'; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/models/request_status.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/res/colors.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | enum RequestStatus { 5 | sending(CRLoggerColors.yellow), 6 | success(CRLoggerColors.green), 7 | error(CRLoggerColors.red), 8 | noInternet(CRLoggerColors.red); 9 | 10 | const RequestStatus(this.color); 11 | 12 | final Color color; 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/page/actions_and_values/actions_manager.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/page/actions_and_values/models/action_model.dart'; 2 | import 'package:flutter/foundation.dart'; 3 | 4 | /// Manager through which methods are added to the page 5 | final class ActionsManager { 6 | ActionsManager._(); 7 | 8 | static final List actions = []; 9 | 10 | static void addActionButton( 11 | String text, 12 | VoidCallback action, { 13 | String? connectedWidgetId, 14 | }) { 15 | actions.add(ActionModel( 16 | text: text, 17 | action: action, 18 | connectedWidgetId: connectedWidgetId, 19 | )); 20 | } 21 | 22 | static void removeActionButtonsById(String connectedWidgetId) { 23 | actions.removeWhere((action) { 24 | return action.connectedWidgetId == connectedWidgetId; 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/src/page/actions_and_values/models/action_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | 3 | /// Action data model for [ActionsManager] 4 | final class ActionModel { 5 | ActionModel({ 6 | required this.text, 7 | required this.action, 8 | this.connectedWidgetId, 9 | }); 10 | 11 | final String text; 12 | final VoidCallback action; 13 | final String? connectedWidgetId; 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/page/actions_and_values/models/notifier_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// Model value notifier 4 | final class NotifierData { 5 | NotifierData({ 6 | required this.valueNotifier, 7 | this.widget, 8 | this.name, 9 | this.connectedWidgetId, 10 | }) : assert( 11 | valueNotifier == null && name == null && widget != null || 12 | valueNotifier != null && name != null && widget == null, 13 | "if widget is null, then name and valueNotifier can't be null and conversely", 14 | ); 15 | 16 | final String? name; 17 | final ValueNotifier? valueNotifier; 18 | final Widget? widget; 19 | final String? connectedWidgetId; 20 | } 21 | -------------------------------------------------------------------------------- /lib/src/page/actions_and_values/notifiers_manager.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/page/actions_and_values/models/notifier_data.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | 4 | /// Manager through which value notifiers are added to the page 5 | final class NotifiersManager { 6 | NotifiersManager._(); 7 | 8 | static final List valueNotifiers = []; 9 | 10 | static void addNotifier({ 11 | ValueNotifier? notifier, 12 | Widget? widget, 13 | String? name, 14 | String? connectedWidgetId, 15 | }) { 16 | valueNotifiers.add( 17 | NotifierData( 18 | name: name, 19 | valueNotifier: notifier, 20 | widget: widget, 21 | connectedWidgetId: connectedWidgetId, 22 | ), 23 | ); 24 | } 25 | 26 | static void removeNotifiersById(String connectedWidgetId) { 27 | valueNotifiers.removeWhere((notifier) { 28 | return notifier.connectedWidgetId == connectedWidgetId; 29 | }); 30 | } 31 | 32 | static void clear() { 33 | valueNotifiers.clear(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/src/page/actions_and_values/widgets/action_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/page/actions_and_values/models/action_model.dart'; 2 | import 'package:cr_logger/src/res/colors.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class ActionItem extends StatelessWidget { 6 | const ActionItem({ 7 | required this.actionModel, 8 | super.key, 9 | }); 10 | 11 | final ActionModel actionModel; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return ElevatedButton( 16 | onPressed: actionModel.action, 17 | style: ElevatedButton.styleFrom( 18 | backgroundColor: Colors.white, 19 | shape: RoundedRectangleBorder( 20 | borderRadius: BorderRadius.circular(10), 21 | ), 22 | elevation: 0, 23 | minimumSize: const Size(0, 77), 24 | ), 25 | child: Text( 26 | actionModel.text, 27 | maxLines: 2, 28 | overflow: TextOverflow.ellipsis, 29 | style: const TextStyle(color: CRLoggerColors.primaryColor), 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/src/page/actions_and_values/widgets/value_notifier_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:cr_logger/src/res/styles.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class ValueNotifierItem extends StatelessWidget { 6 | const ValueNotifierItem({ 7 | required this.notifierData, 8 | super.key, 9 | }); 10 | 11 | final NotifierData notifierData; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | final notifier = notifierData.valueNotifier; 16 | 17 | return notifier != null 18 | ? ValueListenableBuilder( 19 | valueListenable: notifier, 20 | //ignore: prefer-trailing-comma 21 | builder: (_, value, __) { 22 | return Row( 23 | children: [ 24 | Expanded( 25 | child: Text( 26 | notifierData.name ?? '', 27 | style: CRStyle.bodyBlackRegular14, 28 | ), 29 | ), 30 | const SizedBox(width: 20), 31 | Expanded( 32 | child: GestureDetector( 33 | onLongPress: () => copyClipboard( 34 | context, 35 | value.toString(), 36 | ), 37 | child: Text( 38 | value.toString(), 39 | style: CRStyle.bodyBlackRegular14, 40 | ), 41 | ), 42 | ), 43 | ], 44 | ); 45 | }, 46 | ) 47 | : notifierData.widget ?? const SizedBox(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/src/page/app_info_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/cr_logger.dart'; 2 | import 'package:cr_logger/src/cr_logger_helper.dart'; 3 | import 'package:cr_logger/src/page/widgets/app_info_item.dart'; 4 | import 'package:cr_logger/src/res/colors.dart'; 5 | import 'package:cr_logger/src/res/styles.dart'; 6 | import 'package:cr_logger/src/widget/cr_app_bar.dart'; 7 | import 'package:flutter/material.dart'; 8 | 9 | class AppInfoPage extends StatelessWidget { 10 | const AppInfoPage({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | final packageInfo = CRLoggerHelper.instance.packageInfo; 15 | final packageName = packageInfo.packageName; 16 | final version = packageInfo.version; 17 | final buildNumber = packageInfo.buildNumber; 18 | final customInfoItems = CRLoggerInitializer.instance.appInfo.entries; 19 | 20 | return Theme( 21 | data: CRLoggerHelper.instance.theme, 22 | child: Scaffold( 23 | backgroundColor: CRLoggerColors.backgroundGrey, 24 | appBar: const CRAppBar(title: 'App info'), 25 | body: ListView( 26 | padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), 27 | children: [ 28 | /// Package name 29 | RichText( 30 | text: TextSpan( 31 | text: 'Package name: ', 32 | style: CRStyle.bodyBlackSemiBold14, 33 | children: [ 34 | TextSpan( 35 | text: packageName, 36 | style: CRStyle.bodyBlackRegular14, 37 | ), 38 | ], 39 | ), 40 | ), 41 | const SizedBox(height: 12), 42 | 43 | /// Version 44 | RichText( 45 | textDirection: TextDirection.ltr, 46 | text: TextSpan( 47 | text: 'Version: ', 48 | style: CRStyle.bodyBlackSemiBold14, 49 | children: [ 50 | TextSpan( 51 | text: '$version+$buildNumber', 52 | style: CRStyle.bodyBlackRegular14, 53 | ), 54 | ], 55 | ), 56 | ), 57 | const SizedBox(height: 6), 58 | 59 | /// Custom app info 60 | if (customInfoItems.isNotEmpty) ...[ 61 | const Divider(), 62 | ...customInfoItems.map( 63 | (item) { 64 | return AppInfoItem( 65 | name: item.key, 66 | value: item.value, 67 | ); 68 | }, 69 | ).toList(), 70 | ], 71 | ], 72 | ), 73 | ), 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/src/page/log_main/log_main.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:cr_logger/src/cr_logger_helper.dart'; 3 | import 'package:cr_logger/src/managers/log_manager.dart'; 4 | import 'package:cr_logger/src/page/log_main/log_main_mobile.dart'; 5 | import 'package:cr_logger/src/page/log_main/log_main_web.dart'; 6 | import 'package:cr_logger/src/widget/adaptive_layout/adaptive_layout_widget.dart'; 7 | import 'package:flutter/material.dart'; 8 | 9 | class MainLogPage extends StatefulWidget { 10 | const MainLogPage({ 11 | required this.navigationKey, 12 | required this.onLoggerClose, 13 | super.key, 14 | }); 15 | 16 | final GlobalKey navigationKey; 17 | final VoidCallback onLoggerClose; 18 | 19 | static void cleanLogs() { 20 | cleanDebug(); 21 | cleanError(); 22 | cleanInfo(); 23 | cleanHttpLogs(); 24 | } 25 | 26 | static void cleanHttpLogs() { 27 | HttpLogManager.instance.cleanAllLogs(); 28 | } 29 | 30 | static void cleanDebug() { 31 | LogManager.instance.cleanDebug(); 32 | } 33 | 34 | static void cleanInfo() { 35 | LogManager.instance.cleanInfo(); 36 | } 37 | 38 | static void cleanError() { 39 | LogManager.instance.cleanError(); 40 | } 41 | 42 | @override 43 | _MainLogPageState createState() => _MainLogPageState(); 44 | } 45 | 46 | class _MainLogPageState extends State { 47 | @override 48 | Widget build(BuildContext context) { 49 | return Theme( 50 | data: CRLoggerHelper.instance.theme, 51 | child: ValueListenableBuilder( 52 | valueListenable: CRLoggerHelper.instance.loggerShowingNotifier, 53 | // ignore: prefer-trailing-comma 54 | builder: (_, showLogger, __) => Offstage( 55 | offstage: !showLogger, 56 | child: Navigator( 57 | key: widget.navigationKey, 58 | onGenerateRoute: _onGenerateRoute, 59 | ), 60 | ), 61 | ), 62 | ); 63 | } 64 | 65 | Route? _onGenerateRoute(RouteSettings settings) { 66 | return MaterialPageRoute( 67 | builder: (context) => AdaptiveLayoutWidget( 68 | mobileLayoutWidget: MainLogMobilePage( 69 | onLoggerClose: widget.onLoggerClose, 70 | ), 71 | webLayoutWidget: MainLogWebPage( 72 | onLoggerClose: widget.onLoggerClose, 73 | ), 74 | ), 75 | settings: settings, 76 | ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lib/src/page/log_main/widgets/cr_web_appbar_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/page/widgets/popup_menu.dart'; 2 | import 'package:cr_logger/src/res/styles.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class CRWebAppBar extends StatelessWidget { 6 | const CRWebAppBar({ 7 | required this.popupKey, 8 | required this.onLoggerClose, 9 | super.key, 10 | }); 11 | 12 | final GlobalKey popupKey; 13 | final VoidCallback onLoggerClose; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return SizedBox( 18 | height: 68, 19 | child: Row( 20 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 21 | children: [ 22 | PopupMenu( 23 | popupKey: popupKey, 24 | child: const Padding( 25 | padding: EdgeInsets.all(8), 26 | child: Row( 27 | children: [ 28 | Icon(Icons.view_headline), 29 | SizedBox(width: 10), 30 | Padding( 31 | padding: EdgeInsets.only(top: 2), 32 | child: Text( 33 | 'Menu', 34 | style: CRStyle.subtitle1BlackSemiBold16, 35 | ), 36 | ), 37 | ], 38 | ), 39 | ), 40 | ), 41 | InkWell( 42 | onTap: onLoggerClose, 43 | child: const Padding( 44 | padding: EdgeInsets.all(8), 45 | child: Row( 46 | children: [ 47 | Text( 48 | 'Close', 49 | style: CRStyle.subtitle1BlackSemiBold16, 50 | ), 51 | SizedBox(width: 10), 52 | Icon(Icons.close), 53 | ], 54 | ), 55 | ), 56 | ), 57 | ], 58 | ), 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/src/page/log_main/widgets/mobile_header_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/controllers/logs_mode.dart'; 2 | import 'package:cr_logger/src/controllers/logs_mode_controller.dart'; 3 | import 'package:cr_logger/src/res/colors.dart'; 4 | import 'package:cr_logger/src/res/styles.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class MobileHeaderWidget extends StatelessWidget { 8 | const MobileHeaderWidget({ 9 | required this.onClear, 10 | required this.onAllClear, 11 | super.key, 12 | }); 13 | 14 | final VoidCallback onClear; 15 | final VoidCallback onAllClear; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Row( 20 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 21 | children: [ 22 | const Row( 23 | children: [ 24 | SizedBox(width: 16), 25 | Text( 26 | 'Logs', 27 | style: CRStyle.subtitle1BlackSemiBold16, 28 | ), 29 | ], 30 | ), 31 | Row( 32 | children: [ 33 | TextButton( 34 | onPressed: onClear, 35 | onLongPress: onAllClear, 36 | style: TextButton.styleFrom( 37 | minimumSize: const Size(0, 20), 38 | padding: const EdgeInsets.symmetric( 39 | vertical: 4, 40 | horizontal: 16, 41 | ), 42 | foregroundColor: CRLoggerColors.red, 43 | ), 44 | child: ValueListenableBuilder( 45 | valueListenable: LogsModeController.instance.logMode, 46 | //ignore: prefer-trailing-comma 47 | builder: (_, value, __) => Text( 48 | value == LogsMode.fromCurrentSession 49 | ? 'Clear logs' 50 | : 'Clear logs from DB', 51 | style: CRStyle.bodyRedMedium14, 52 | ), 53 | ), 54 | ), 55 | ], 56 | ), 57 | ], 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/src/page/log_main/widgets/web_header_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/res/colors.dart'; 2 | import 'package:cr_logger/src/res/styles.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class WebHeaderWidget extends StatelessWidget { 6 | const WebHeaderWidget({ 7 | required this.onClear, 8 | required this.onAllClear, 9 | super.key, 10 | }); 11 | 12 | final VoidCallback onClear; 13 | final VoidCallback onAllClear; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Row( 18 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 19 | children: [ 20 | const Row( 21 | children: [ 22 | SizedBox(width: 16), 23 | Text( 24 | 'Logs', 25 | style: CRStyle.h2BlackSemibold, 26 | ), 27 | ], 28 | ), 29 | Row( 30 | children: [ 31 | TextButton( 32 | onPressed: onClear, 33 | onLongPress: onAllClear, 34 | style: TextButton.styleFrom( 35 | minimumSize: const Size(0, 20), 36 | padding: const EdgeInsets.symmetric( 37 | vertical: 4, 38 | horizontal: 16, 39 | ), 40 | foregroundColor: CRLoggerColors.red, 41 | ), 42 | child: const Text( 43 | 'Clear logs', 44 | style: CRStyle.subtitle1RedMedium16, 45 | ), 46 | ), 47 | ], 48 | ), 49 | ], 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/src/page/search/search_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/controllers/logs_mode.dart'; 2 | import 'package:cr_logger/src/controllers/logs_mode_controller.dart'; 3 | import 'package:cr_logger/src/cr_logger_helper.dart'; 4 | import 'package:cr_logger/src/page/search/http_search_page.dart'; 5 | import 'package:cr_logger/src/page/search/log_search_page.dart'; 6 | import 'package:cr_logger/src/res/colors.dart'; 7 | import 'package:cr_logger/src/res/styles.dart'; 8 | import 'package:cr_logger/src/widget/cr_back_button.dart'; 9 | import 'package:cr_logger/src/widget/options_buttons.dart'; 10 | import 'package:flutter/material.dart'; 11 | 12 | class SearchPage extends StatefulWidget { 13 | const SearchPage({super.key}); 14 | 15 | @override 16 | _SearchPageState createState() => _SearchPageState(); 17 | } 18 | 19 | class _SearchPageState extends State { 20 | final _searchCtrl = TextEditingController(); 21 | 22 | final _pageController = PageController(); 23 | 24 | final _logsMode = LogsModeController.instance.logMode.value; 25 | 26 | bool get _isCurrentLogMode => _logsMode == LogsMode.fromCurrentSession; 27 | 28 | @override 29 | void dispose() { 30 | _searchCtrl.dispose(); 31 | _pageController.dispose(); 32 | super.dispose(); 33 | } 34 | 35 | @override 36 | Widget build(BuildContext context) => Theme( 37 | data: CRLoggerHelper.instance.theme, 38 | child: SafeArea( 39 | child: Scaffold( 40 | backgroundColor: CRLoggerColors.backgroundGrey, 41 | appBar: AppBar( 42 | centerTitle: true, 43 | elevation: 0, 44 | backgroundColor: CRLoggerColors.backgroundGrey, 45 | 46 | /// App bar title 47 | title: Text( 48 | _isCurrentLogMode ? 'Search in logs' : 'Search log in DB', 49 | style: CRStyle.subtitle1BlackSemiBold17, 50 | ), 51 | leading: const CRBackButton(), 52 | 53 | /// Tabs: HTTP and Logs 54 | bottom: PreferredSize( 55 | preferredSize: const Size( 56 | double.infinity, 57 | kToolbarHeight, 58 | ), 59 | child: Padding( 60 | padding: const EdgeInsets.only( 61 | top: 8, 62 | left: 16, 63 | right: 16, 64 | bottom: 14, 65 | ), 66 | child: OptionsButtons( 67 | titles: const ['HTTP', 'Logs'], 68 | onSelected: _onChangedTab, 69 | ), 70 | ), 71 | ), 72 | ), 73 | body: Column( 74 | children: [ 75 | /// Correct page dependens on the tab. HTTP page or Logs page 76 | Expanded( 77 | child: PageView( 78 | physics: const NeverScrollableScrollPhysics(), 79 | controller: _pageController, 80 | children: const [ 81 | HttpSearchPage(), 82 | LogSearchPage(), 83 | ], 84 | ), 85 | ), 86 | ], 87 | ), 88 | ), 89 | ), 90 | ); 91 | 92 | void _onChangedTab(int index) { 93 | _pageController.jumpToPage(index); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /lib/src/page/search/widgets/path_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/res/colors.dart'; 2 | import 'package:cr_logger/src/res/styles.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class PathWidget extends StatelessWidget { 6 | const PathWidget({ 7 | required this.onSearchUrl, 8 | required this.path, 9 | super.key, 10 | }); 11 | 12 | final VoidCallback onSearchUrl; 13 | final String path; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return GestureDetector( 18 | onTap: onSearchUrl, 19 | child: Container( 20 | margin: const EdgeInsets.all(2), 21 | padding: const EdgeInsets.all(8), 22 | decoration: BoxDecoration( 23 | borderRadius: BorderRadius.circular(16), 24 | border: Border.all( 25 | color: CRLoggerColors.progressBackground, 26 | ), 27 | color: CRLoggerColors.white, 28 | ), 29 | child: Text( 30 | path, 31 | style: CRStyle.bodyBlackSemiDefault13, 32 | ), 33 | ), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/page/widgets/app_info_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/res/styles.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class AppInfoItem extends StatelessWidget { 5 | const AppInfoItem({ 6 | required this.name, 7 | required this.value, 8 | super.key, 9 | }); 10 | 11 | final String name; 12 | final String value; 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Padding( 17 | padding: const EdgeInsets.symmetric(vertical: 6), 18 | child: Column( 19 | crossAxisAlignment: CrossAxisAlignment.start, 20 | children: [ 21 | Text(name, style: CRStyle.bodyBlackSemiBold14), 22 | Padding( 23 | padding: const EdgeInsets.only(left: 14, top: 6), 24 | child: Text(value, style: CRStyle.bodyBlackRegular14), 25 | ), 26 | ], 27 | ), 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/page/widgets/clear_logs_content_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/res/colors.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class ClearLogsContentWidget extends StatefulWidget { 5 | const ClearLogsContentWidget({ 6 | required this.clearLogsFromDB, 7 | super.key, 8 | }); 9 | 10 | final ValueChanged clearLogsFromDB; 11 | 12 | @override 13 | State createState() => _ClearLogsContentWidgetState(); 14 | } 15 | 16 | class _ClearLogsContentWidgetState extends State { 17 | bool _clearLogsFromDB = false; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Column( 22 | crossAxisAlignment: CrossAxisAlignment.start, 23 | mainAxisSize: MainAxisSize.min, 24 | children: [ 25 | const Text('Do you want to clear all logs?'), 26 | const SizedBox(height: 4), 27 | CheckboxListTile( 28 | contentPadding: const EdgeInsets.only(left: 2), 29 | activeColor: CRLoggerColors.blue, 30 | value: _clearLogsFromDB, 31 | onChanged: _onChanged, 32 | title: const Text( 33 | 'Clear the database', 34 | style: TextStyle(fontSize: 14), 35 | ), 36 | ), 37 | ], 38 | ); 39 | } 40 | 41 | void _onChanged(bool? value) { 42 | if (value != null) { 43 | setState(() { 44 | _clearLogsFromDB = value; 45 | }); 46 | widget.clearLogsFromDB(_clearLogsFromDB); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/src/page/widgets/cupertino_search_field.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/res/colors.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class CupertinoSearchField extends StatefulWidget { 6 | const CupertinoSearchField({ 7 | required this.searchController, 8 | required this.onSearch, 9 | required this.onClear, 10 | required this.placeholderText, 11 | super.key, 12 | }); 13 | 14 | final TextEditingController searchController; 15 | final VoidCallback onSearch; 16 | final VoidCallback onClear; 17 | final String placeholderText; 18 | 19 | @override 20 | State createState() => _CupertinoSearchFieldState(); 21 | } 22 | 23 | class _CupertinoSearchFieldState extends State { 24 | final _focusNode = FocusNode(); 25 | 26 | @override 27 | void dispose() { 28 | _focusNode.dispose(); 29 | super.dispose(); 30 | } 31 | 32 | @override 33 | Widget build(BuildContext context) { 34 | return Padding( 35 | padding: const EdgeInsets.symmetric(horizontal: 16), 36 | child: Row( 37 | children: [ 38 | /// Cupertino search field 39 | Expanded( 40 | child: CupertinoTextField( 41 | focusNode: _focusNode, 42 | controller: widget.searchController, 43 | padding: const EdgeInsets.symmetric( 44 | vertical: 8, 45 | horizontal: 12, 46 | ), 47 | onChanged: (_) => widget.onSearch(), 48 | suffixMode: OverlayVisibilityMode.editing, 49 | suffix: IconButton( 50 | padding: EdgeInsets.zero, 51 | onPressed: widget.onClear, 52 | icon: const Icon(Icons.clear), 53 | ), 54 | placeholder: widget.placeholderText, 55 | cursorWidth: 1, 56 | cursorColor: CRLoggerColors.primaryColor, 57 | decoration: BoxDecoration( 58 | color: CRLoggerColors.white, 59 | borderRadius: BorderRadius.circular(21), 60 | ), 61 | ), 62 | ), 63 | ], 64 | ), 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/src/page/widgets/json_details_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/res/styles.dart'; 2 | import 'package:cr_logger/src/widget/expand_arrow_button.dart'; 3 | import 'package:cr_logger/src/widget/json_widget/json_widget.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class JsonDetailsWidget extends StatefulWidget { 7 | const JsonDetailsWidget({ 8 | required this.logMessage, 9 | super.key, 10 | }); 11 | 12 | final Map logMessage; 13 | 14 | @override 15 | State createState() => _JsonDetailsWidgetState(); 16 | } 17 | 18 | class _JsonDetailsWidgetState extends State { 19 | final _allExpandedNodesNotifier = ValueNotifier(true); 20 | 21 | @override 22 | void dispose() { 23 | _allExpandedNodesNotifier.dispose(); 24 | 25 | super.dispose(); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return ValueListenableBuilder( 31 | valueListenable: _allExpandedNodesNotifier, 32 | builder: ( 33 | BuildContext context, 34 | bool isAllNodesExpanded, 35 | Widget? child, 36 | ) { 37 | return Column( 38 | children: [ 39 | Row( 40 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 41 | children: [ 42 | /// Json title 43 | const Text( 44 | 'JSON data', 45 | style: CRStyle.subtitle1BlackSemiBold16, 46 | ), 47 | 48 | /// Json expand button 49 | ExpandArrowButton( 50 | isExpanded: isAllNodesExpanded, 51 | onTap: _onExpandArrowTap, 52 | ), 53 | ], 54 | ), 55 | 56 | /// Json widget 57 | JsonWidget( 58 | widget.logMessage, 59 | allExpandedNodes: isAllNodesExpanded, 60 | ), 61 | ], 62 | ); 63 | }, 64 | ); 65 | } 66 | 67 | void _onExpandArrowTap() { 68 | _allExpandedNodesNotifier.value = !_allExpandedNodesNotifier.value; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/src/page/widgets/progress_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/res/colors.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class ProgressWidget extends StatelessWidget { 5 | const ProgressWidget({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Container( 10 | alignment: Alignment.center, 11 | color: CRLoggerColors.backgroundGrey.withOpacity(0.8), 12 | child: const CircularProgressIndicator(strokeWidth: 2), 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/src/providers/sqflite_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:cr_logger/src/data/sqflite_db/log_module.dart'; 3 | 4 | final class SqfliteProvider { 5 | SqfliteProvider._(); 6 | 7 | static final instance = SqfliteProvider._(); 8 | 9 | final _module = LogModule.instance; 10 | 11 | Future init() => _module.init(); 12 | 13 | /// HTTP logs 14 | Future saveHttpLog(HttpBean httpLog) => _module.saveHttpLog(httpLog); 15 | 16 | Future updateHttpLog(HttpBean httpLog) => 17 | _module.updateHttpLog(httpLog); 18 | 19 | Future> getAllSavedHttpLogs() => _module.getAllSavedHttpLogs(); 20 | 21 | Future deleteAllHttpLogs() => _module.deleteAllHttpLogs(); 22 | 23 | Future deleteHttpLogs(List logs) => 24 | _module.deleteHttpLogs(logs); 25 | 26 | /// Logs 27 | Future saveLog(LogBean log) => _module.saveLog(log); 28 | 29 | Future> getAllSavedLogs() => _module.getAllSavedLogs(); 30 | 31 | Future deleteAllLogs() => _module.deleteAllLogs(); 32 | 33 | Future deleteLogs(List logs) => _module.deleteLogs(logs); 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/res/colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | final class CRLoggerColors { 4 | CRLoggerColors._(); 5 | 6 | static const primaryColor = Color(0xFF151923); 7 | static const accentColor = Color(0xffff9f00); 8 | static const black = Colors.black; 9 | static const white = Colors.white; 10 | static const whitish = Color(0xFFDADCE0); 11 | static const grey = Color(0xFF77788A); 12 | static const orange = Color(0xFFDE5507); 13 | static const red = Color(0xFFDF3731); 14 | static const blue = Color(0xFF1B6CFF); 15 | static const darkMagenta = Color(0xFFBB5BC3); 16 | static const blueAccent = Color(0xFF0980C2); 17 | static const lightRed = Color(0xFFFCC6C2); 18 | static const linkBlue = Color(0xFFABCAFF); 19 | static const linkColor = Color(0xFFBB5BC3); 20 | static const green = Color(0xFF199B4D); 21 | static const greenAccent = Color(0xFF1EB35D); 22 | static const progressBackground = Color(0xFFB8C7CB); 23 | static const backgroundGrey = Color(0xFFF3F5F6); 24 | 25 | static const yellow = Colors.orangeAccent; 26 | 27 | static Brightness brightness = Brightness.dark; 28 | } 29 | -------------------------------------------------------------------------------- /lib/src/res/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/res/colors.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | ThemeData get loggerTheme => ThemeData( 5 | primaryColor: CRLoggerColors.primaryColor, 6 | brightness: Brightness.light, 7 | indicatorColor: CRLoggerColors.accentColor, 8 | iconTheme: const IconThemeData( 9 | color: CRLoggerColors.primaryColor, 10 | ), 11 | textSelectionTheme: const TextSelectionThemeData( 12 | cursorColor: CRLoggerColors.black, 13 | ), 14 | dialogTheme: const DialogTheme( 15 | backgroundColor: Colors.white, 16 | contentTextStyle: TextStyle( 17 | color: Colors.black87, 18 | fontSize: 16, 19 | ), 20 | titleTextStyle: TextStyle( 21 | color: Colors.black, 22 | fontSize: 18, 23 | fontWeight: FontWeight.w600, 24 | ), 25 | shape: Border(), 26 | ), 27 | cardTheme: CardTheme( 28 | shape: RoundedRectangleBorder( 29 | borderRadius: BorderRadius.circular(4), 30 | ), 31 | elevation: 6, 32 | ), 33 | appBarTheme: const AppBarTheme( 34 | backgroundColor: CRLoggerColors.backgroundGrey, 35 | elevation: 0, 36 | ), 37 | canvasColor: CRLoggerColors.white, 38 | popupMenuTheme: const PopupMenuThemeData(color: CRLoggerColors.white), 39 | colorScheme: ColorScheme.fromSwatch().copyWith( 40 | secondary: CRLoggerColors.accentColor, 41 | onSecondary: CRLoggerColors.primaryColor, 42 | ), 43 | scaffoldBackgroundColor: CRLoggerColors.backgroundGrey, 44 | fontFamily: 'Epilogue', 45 | ); 46 | -------------------------------------------------------------------------------- /lib/src/utils/console_log_output.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/constants.dart'; 2 | import 'package:cr_logger/src/cr_logger_helper.dart'; 3 | import 'package:cr_logger/src/js/console_output_worker.dart'; 4 | import 'package:cr_logger/src/js/scripts.dart'; 5 | import 'package:cr_logger/src/utils/html_stub.dart' 6 | if (dart.library.js) 'dart:html' as html; 7 | import 'package:flutter/foundation.dart'; 8 | import 'package:logger/logger.dart'; 9 | import 'package:worker_manager/worker_manager.dart'; 10 | 11 | final class ConsoleLogOutput extends LogOutput { 12 | ConsoleLogOutput() { 13 | if (kIsWeb) { 14 | _createWorker(); 15 | } 16 | } 17 | 18 | @override 19 | Future output(OutputEvent event) async { 20 | await CRLoggerHelper.instance.lock.synchronized(() async { 21 | if (kIsWeb) { 22 | if (kReleaseMode || kProfileMode) { 23 | final src = html.ScriptElement()..text = printLogsScript; 24 | html.document.body?.append(src); 25 | printLogs(event.lines); 26 | src.remove(); 27 | } else { 28 | // ignore: avoid_print 29 | event.lines.forEach(print); 30 | } 31 | } else { 32 | await workerManager.execute(() async => isolatePrintLog(event.lines)); 33 | } 34 | }); 35 | } 36 | 37 | void _createWorker() { 38 | final srcWorker = html.ScriptElement() 39 | ..id = kWorkerId 40 | ..text = workerScript; 41 | html.document.body?.append(srcWorker); 42 | final srcCreateWorker = html.ScriptElement()..text = createWorkerScript; 43 | html.document.body?.append(srcCreateWorker); 44 | } 45 | } 46 | 47 | Object isolatePrintLog(dynamic data) { 48 | if (data is List) { 49 | // ignore: avoid_print 50 | data.forEach(print); 51 | } 52 | 53 | return ''; 54 | } 55 | -------------------------------------------------------------------------------- /lib/src/utils/copy_clipboard.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | 4 | void copyClipboard( 5 | BuildContext context, 6 | String value, { 7 | Color selectValueColor = Colors.white, 8 | }) { 9 | final snackBar = SnackBar( 10 | content: RichText( 11 | overflow: TextOverflow.ellipsis, 12 | maxLines: 4, 13 | text: TextSpan( 14 | children: [ 15 | const TextSpan(text: 'Copy '), 16 | TextSpan( 17 | text: '"$value"', 18 | style: TextStyle( 19 | color: selectValueColor, 20 | ), 21 | ), 22 | const TextSpan(text: ' to clipboard'), 23 | ], 24 | ), 25 | ), 26 | ); 27 | ScaffoldMessenger.of(context).clearSnackBars(); 28 | ScaffoldMessenger.of(context).showSnackBar(snackBar); 29 | Clipboard.setData(ClipboardData(text: value)); 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/utils/enum_ext.dart: -------------------------------------------------------------------------------- 1 | extension EnumValueOf on Iterable { 2 | T? valueOf(String? name) { 3 | if (name != null) { 4 | final lowerCase = name.toLowerCase(); 5 | 6 | return firstWhere((value) => value.name == lowerCase); 7 | } 8 | 9 | return null; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/utils/hide_values_in_map.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:cr_logger/src/constants.dart'; 3 | 4 | Map hideValuesInMap(Map body) { 5 | return body.map((key, value) { 6 | if (CRLoggerInitializer.instance.hiddenFields.contains(key)) { 7 | return MapEntry(key, kHidden); 8 | } else { 9 | if (value is Map) { 10 | return MapEntry(key, hideValuesInMap(value)); 11 | } 12 | if (value is List) { 13 | final list = value.map((e) { 14 | return e is Map ? hideValuesInMap(e) : e; 15 | }).toList(); 16 | 17 | return MapEntry(key, list); 18 | } 19 | 20 | return MapEntry(key, value); 21 | } 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/utils/html_stub.dart: -------------------------------------------------------------------------------- 1 | final class ScriptElement { 2 | late String text; 3 | late String id; 4 | late bool defer; 5 | 6 | // ignore: no-empty-block 7 | void remove() {} 8 | } 9 | 10 | late DocStub document; 11 | 12 | final class DocStub { 13 | BodyStub? body; 14 | } 15 | 16 | class BodyStub { 17 | // ignore: avoid-unused-parameters 18 | // ignore: no-empty-block 19 | void append(_) {} 20 | } 21 | 22 | final class JS { 23 | const JS([this.name]); 24 | 25 | final String? name; 26 | } 27 | -------------------------------------------------------------------------------- /lib/src/utils/json_utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | String toJson(Object? data) { 4 | const je = JsonEncoder.withIndent(' '); 5 | final json = je.convert(data); 6 | 7 | return json; 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/utils/map_ext.dart: -------------------------------------------------------------------------------- 1 | extension MapExt on Map { 2 | // ignore: avoid_annotating_with_dynamic 3 | void putIfNotNull(dynamic value, T key) { 4 | if (value != null) { 5 | this[key] = value; 6 | } 7 | } 8 | 9 | void clearAllNull() { 10 | removeWhere((key, value) => value == null); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/src/utils/nothing_log_filter.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | 3 | final class NothingLogFilter extends LogFilter { 4 | @override 5 | bool shouldLog(LogEvent event) { 6 | return false; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/utils/pair.dart: -------------------------------------------------------------------------------- 1 | final class Pair { 2 | Pair({ 3 | required this.first, 4 | required this.second, 5 | }); 6 | 7 | final A first; 8 | final B second; 9 | } 10 | -------------------------------------------------------------------------------- /lib/src/utils/paramas_detector/parameter_model.dart: -------------------------------------------------------------------------------- 1 | final class ParameterModel { 2 | ParameterModel({ 3 | required this.name, 4 | required this.locationStart, 5 | required this.locationEnd, 6 | }); 7 | 8 | final int locationStart; 9 | final int locationEnd; 10 | final String name; 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/utils/paramas_detector/params_detector.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/constants.dart'; 2 | import 'package:cr_logger/src/utils/paramas_detector/parameter_model.dart'; 3 | 4 | final class ParameterDetector { 5 | static final _parameterRegex = RegExp(r'\{\{[^{}]+\}\}'); 6 | 7 | List getParams(String text) { 8 | final params = []; 9 | 10 | if (_parameterRegex.hasMatch(text)) { 11 | final matches = _parameterRegex.allMatches(text); 12 | if (matches.isNotEmpty) { 13 | for (final match in matches) { 14 | final param = match.group(0); 15 | if (param != null) { 16 | params.add( 17 | ParameterModel( 18 | name: param.replaceAll(patternOfParamsRegex, ''), 19 | locationStart: match.start, 20 | locationEnd: match.end, 21 | ), 22 | ); 23 | } 24 | } 25 | } 26 | } 27 | 28 | return params; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/utils/parsers/isolate_parser.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:worker_manager/worker_manager.dart'; 4 | 5 | /// This class is for converting a [String] into a [Map] and conversely. 6 | final class IsolateParser { 7 | /// [data] is a string, but may contain a map, so a decoding attempt is made first. If it fails, [data] is returned as a string. 8 | dynamic decode(String data) => 9 | workerManager.execute(() async => _decode(data)); 10 | 11 | /// [data] can be any type, so there is a check if it is a map, then [json.encode] is called, 12 | /// otherwise [data] is converted into a string and returned. 13 | Future encode(dynamic data) => 14 | workerManager.execute(() async => _encode(data)); 15 | } 16 | 17 | dynamic _decode(String data) { 18 | try { 19 | return json.decode(data); 20 | } catch (_) { 21 | return Future.value(data); 22 | } 23 | } 24 | 25 | String _encode(dynamic data) => 26 | data is Map ? json.encode(data) : data.toString(); 27 | -------------------------------------------------------------------------------- /lib/src/utils/parsers/url_parser.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | 3 | String getUrlWithHiddenParams(String url, {bool showFullPath = false}) { 4 | final uri = Uri.tryParse(url); 5 | 6 | /// Get path without scheme. 7 | /// E.g. httpbin/anything instead of https://httpbin/anything 8 | final absolutePath = '${uri?.host}${uri?.path}'; 9 | 10 | /// Get all parameters 11 | final parameters = uri?.queryParameters; 12 | 13 | /// Separate the base url and parameters to avoid replacing parts of the base url 14 | final resultUrl = url.split('?'); 15 | 16 | if (!showFullPath) { 17 | resultUrl.first = absolutePath; 18 | } 19 | 20 | /// If there is no parameters then nothing to hide and just return url 21 | if (resultUrl.length < 2) { 22 | return resultUrl.join(); 23 | } 24 | 25 | /// Replace 26 | CRLoggerInitializer.instance.hiddenFields.forEach((element) { 27 | final value = parameters?[element]; 28 | if (value != null) { 29 | /// = needed to avoid replacing keys if parameter have the same name as key 30 | /// 31 | /// E.g. https://....?test=test 32 | resultUrl[1] = resultUrl[1].replaceAll('=$value', '=...'); 33 | } 34 | }); 35 | 36 | return resultUrl.join('?'); 37 | } 38 | -------------------------------------------------------------------------------- /lib/src/utils/show_info_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/cr_logger_helper.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | Future showInfoDialog({ 5 | required BuildContext context, 6 | Widget? title, 7 | Widget? content, 8 | }) => 9 | showDialog( 10 | context: context, 11 | builder: (context) => Theme( 12 | data: CRLoggerHelper.instance.theme, 13 | child: AlertDialog( 14 | title: title, 15 | content: content, 16 | actions: [ 17 | TextButton( 18 | onPressed: () => Navigator.pop(context), 19 | child: const Text('CLOSE'), 20 | ), 21 | ], 22 | ), 23 | ), 24 | ); 25 | -------------------------------------------------------------------------------- /lib/src/utils/show_log_snack_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// Displays the snack bar when a log is added with the snack bar display option 4 | /// Pressing the "OPEN" button opens the logger, the tab in which the log 5 | /// belongs and the log details page 6 | void showLogSnackBar( 7 | ScaffoldMessengerState scaffoldMessengerState, 8 | VoidCallback? onOpen, 9 | String message, 10 | ) { 11 | final scaffold = scaffoldMessengerState; 12 | scaffold 13 | ..hideCurrentSnackBar() 14 | ..showSnackBar( 15 | SnackBar( 16 | content: Text( 17 | 'Log: $message', 18 | maxLines: 1, 19 | overflow: TextOverflow.ellipsis, 20 | ), 21 | action: SnackBarAction( 22 | label: 'OPEN', 23 | textColor: Colors.green, 24 | //ignore:prefer-extracting-callbacks 25 | onPressed: () { 26 | scaffold.hideCurrentSnackBar(); 27 | onOpen?.call(); 28 | }, 29 | ), 30 | ), 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /lib/src/utils/show_remove_log_bottom_sheet.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/utils/web_utils.dart'; 2 | import 'package:cr_logger/src/widget/delete_log_confirm_widget.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | Future showRemoveLogBottomSheet( 6 | BuildContext context, { 7 | required String message, 8 | final Color textColor = Colors.black, 9 | }) async { 10 | final bottomSheetWidth = 11 | MediaQuery.of(context).size.width > kWidthTrashHoldForMobileLayout 12 | ? 400.0 13 | : double.infinity; 14 | 15 | final result = await showModalBottomSheet( 16 | context: context, 17 | shape: const RoundedRectangleBorder( 18 | borderRadius: BorderRadius.only( 19 | topRight: Radius.circular(20), 20 | topLeft: Radius.circular(20), 21 | ), 22 | ), 23 | constraints: BoxConstraints(maxWidth: bottomSheetWidth), 24 | builder: (context) => DeleteLogConfirmWidget( 25 | message: message, 26 | textColor: textColor, 27 | ), 28 | ); 29 | 30 | return result == DeleteLogConfirmation.ok; 31 | } 32 | -------------------------------------------------------------------------------- /lib/src/utils/show_remove_log_snack_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// Displays the snack bar after deleting the log 4 | /// Pressing the "UNDO" button restores the deleted log 5 | void showRemoveLogSnackBar(BuildContext context, VoidCallback? onUndo) { 6 | final scaffold = ScaffoldMessenger.of(context); 7 | scaffold 8 | ..hideCurrentSnackBar() 9 | ..showSnackBar( 10 | SnackBar( 11 | content: const Text( 12 | 'Log has been deleted', 13 | maxLines: 1, 14 | overflow: TextOverflow.ellipsis, 15 | ), 16 | action: SnackBarAction( 17 | label: 'UNDO', 18 | textColor: Colors.green, 19 | //ignore:prefer-extracting-callbacks 20 | onPressed: () { 21 | scaffold.hideCurrentSnackBar(); 22 | onUndo?.call(); 23 | }, 24 | ), 25 | ), 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /lib/src/utils/unfocus.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | void unfocus() => WidgetsBinding.instance.focusManager.primaryFocus?.unfocus(); 4 | -------------------------------------------------------------------------------- /lib/src/utils/web_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | 3 | const double kMaxWebContentWidth = 1264; 4 | 5 | const double kDefaultDesktopWebContentWidth = 500; 6 | 7 | const double kWidthTrashHoldForMobileLayout = 1100; 8 | 9 | final kIsWebMobile = kIsWeb && 10 | (defaultTargetPlatform == TargetPlatform.iOS || 11 | defaultTargetPlatform == TargetPlatform.android); 12 | 13 | final kUserMobileLayoutForWeb = !kIsWeb || kIsWebMobile; 14 | -------------------------------------------------------------------------------- /lib/src/widget/adaptive_layout/adaptive_layout_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/utils/web_utils.dart'; 2 | import 'package:cr_logger/src/widget/adaptive_layout/layout_types.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class AdaptiveLayoutWidget extends StatefulWidget { 6 | const AdaptiveLayoutWidget({ 7 | required this.mobileLayoutWidget, 8 | required this.webLayoutWidget, 9 | this.onLayoutChange, 10 | super.key, 11 | }); 12 | 13 | final Widget mobileLayoutWidget; 14 | final Widget webLayoutWidget; 15 | final ValueChanged? onLayoutChange; 16 | 17 | @override 18 | _AdaptiveLayoutWidgetState createState() => _AdaptiveLayoutWidgetState(); 19 | } 20 | 21 | class _AdaptiveLayoutWidgetState extends State { 22 | LayoutType _layoutType = LayoutType.web; 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return LayoutBuilder( 27 | builder: (ctx, constraints) { 28 | final layoutType = _getLayoutTypeFromConstraints(constraints); 29 | 30 | // Use callback only when layout changed. 31 | if (_layoutType != layoutType) { 32 | _layoutType = layoutType; 33 | widget.onLayoutChange?.call(_layoutType); 34 | } 35 | 36 | return _layoutType.isWebLayout 37 | ? widget.webLayoutWidget 38 | : widget.mobileLayoutWidget; 39 | }, 40 | ); 41 | } 42 | 43 | LayoutType _getLayoutTypeFromConstraints(BoxConstraints constraints) { 44 | return constraints.maxWidth > kWidthTrashHoldForMobileLayout 45 | ? LayoutType.web 46 | : LayoutType.mobile; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/src/widget/adaptive_layout/layout_types.dart: -------------------------------------------------------------------------------- 1 | enum LayoutType { 2 | web, 3 | mobile, 4 | } 5 | 6 | extension LayoutTypeExt on LayoutType { 7 | bool get isWebLayout => this == LayoutType.web; 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/widget/body_expansion_tile.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:cr_logger/src/res/colors.dart'; 3 | import 'package:cr_logger/src/res/styles.dart'; 4 | import 'package:cr_logger/src/widget/expand_arrow_button.dart'; 5 | import 'package:cr_logger/src/widget/json_widget/json_widget.dart'; 6 | import 'package:cr_logger/src/widget/rounded_card.dart'; 7 | import 'package:dio/dio.dart'; 8 | import 'package:flutter/material.dart'; 9 | 10 | class BodyExpansionTile extends StatefulWidget { 11 | const BodyExpansionTile({ 12 | required this.request, 13 | super.key, 14 | }); 15 | 16 | final RequestBean? request; 17 | 18 | @override 19 | State createState() => _BodyExpansionTileState(); 20 | } 21 | 22 | class _BodyExpansionTileState extends State { 23 | bool _isExpanded = false; 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | const _jsonWidgetBodyValueKey = ValueKey('RequestPageBody'); 28 | final bodyIsString = widget.request?.body is String; 29 | final bodyLength = 30 | bodyIsString ? 1 : _tryGetBodyAsMap(widget.request)?.length ?? 0; 31 | final bodyIsNotEmpty = bodyLength != 0; 32 | 33 | return RoundedCard( 34 | padding: const EdgeInsets.only( 35 | left: 16, 36 | top: 10, 37 | right: 16, 38 | bottom: 10, 39 | ), 40 | child: Column( 41 | children: [ 42 | Row( 43 | children: [ 44 | const Expanded( 45 | child: Text( 46 | 'Body', 47 | style: CRStyle.subtitle1BlackSemiBold16, 48 | ), 49 | ), 50 | Text( 51 | '$bodyLength', 52 | style: CRStyle.subtitle1BlackSemiBold16.copyWith( 53 | color: CRLoggerColors.grey, 54 | ), 55 | ), 56 | const SizedBox(width: 6), 57 | ExpandArrowButton( 58 | isExpanded: _isExpanded && bodyIsNotEmpty, 59 | onTap: bodyIsNotEmpty ? _expand : null, 60 | ), 61 | ], 62 | ), 63 | if (bodyIsNotEmpty && !bodyIsString) 64 | JsonWidget( 65 | _tryGetBodyAsMap(widget.request), 66 | allExpandedNodes: _isExpanded, 67 | key: _jsonWidgetBodyValueKey, 68 | ), 69 | if (bodyIsString && _isExpanded) Text(widget.request?.body), 70 | ], 71 | ), 72 | ); 73 | } 74 | 75 | Map? _tryGetBodyAsMap(request) { 76 | if (request?.body is FormData) { 77 | return request?.getFormData() ?? ''; 78 | } else if (request?.body is List) { 79 | return request?.body ?? ''; 80 | } else if (request?.body is Map) { 81 | return request?.body ?? ''; 82 | } else { 83 | return null; 84 | } 85 | } 86 | 87 | void _expand() => setState(() => _isExpanded = !_isExpanded); 88 | } 89 | -------------------------------------------------------------------------------- /lib/src/widget/build_number.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:package_info_plus/package_info_plus.dart'; 3 | 4 | class BuildNumber extends StatelessWidget { 5 | const BuildNumber({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Material( 10 | type: MaterialType.transparency, 11 | child: FutureBuilder( 12 | future: PackageInfo.fromPlatform(), 13 | builder: ( 14 | BuildContext context, 15 | AsyncSnapshot snapshot, 16 | ) { 17 | final buildNumber = 18 | snapshot.hasData ? snapshot.data?.buildNumber : null; 19 | 20 | return Text( 21 | buildNumber ?? '', 22 | style: const TextStyle( 23 | fontSize: 18, 24 | fontWeight: FontWeight.normal, 25 | color: Colors.white, 26 | ), 27 | ); 28 | }, 29 | ), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/src/widget/copy_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/generated/assets.dart'; 2 | import 'package:cr_logger/src/extensions/extensions.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class CopyWidget extends StatelessWidget { 6 | const CopyWidget({ 7 | required this.onCopy, 8 | super.key, 9 | }); 10 | 11 | final VoidCallback? onCopy; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return SizedBox.square( 16 | dimension: 28, 17 | child: IconButton( 18 | onPressed: onCopy, 19 | icon: ImageExt.fromPackage( 20 | CRLoggerAssets.assetsContentCopy, 21 | height: 20, 22 | width: 20, 23 | ), 24 | iconSize: 20, 25 | splashRadius: 20, 26 | padding: EdgeInsets.zero, 27 | ), 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/widget/cr_app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/res/colors.dart'; 2 | import 'package:cr_logger/src/res/styles.dart'; 3 | import 'package:cr_logger/src/widget/cr_back_button.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class CRAppBar extends StatelessWidget implements PreferredSizeWidget { 7 | const CRAppBar({ 8 | this.title = '', 9 | this.titleWidget, 10 | this.showBackButton, 11 | this.backButtonColor, 12 | this.onBackPressed, 13 | this.actions, 14 | this.reverse = false, 15 | this.centerTitle = true, 16 | super.key, 17 | }); 18 | 19 | final String title; 20 | final Widget? titleWidget; 21 | final bool centerTitle; 22 | final bool reverse; 23 | final bool? showBackButton; 24 | final Color? backButtonColor; 25 | final VoidCallback? onBackPressed; 26 | final List? actions; 27 | 28 | @override 29 | Size get preferredSize => const Size.fromHeight(kToolbarHeight); 30 | 31 | @override 32 | Widget build(BuildContext context) => AppBar( 33 | centerTitle: centerTitle, 34 | automaticallyImplyLeading: false, 35 | leading: CRBackButton( 36 | color: backButtonColor, 37 | showBackButton: showBackButton, 38 | onPressed: onBackPressed, 39 | ), 40 | title: titleWidget ?? 41 | Text( 42 | title, 43 | style: CRStyle.subtitle1BlackSemiBold17, 44 | ), 45 | elevation: 0, 46 | backgroundColor: CRLoggerColors.backgroundGrey, 47 | actions: actions, 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /lib/src/widget/cr_back_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/generated/assets.dart'; 2 | import 'package:cr_logger/src/extensions/image_ext.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class CRBackButton extends StatelessWidget { 6 | const CRBackButton({ 7 | super.key, 8 | this.color, 9 | this.onPressed, 10 | this.showBackButton, 11 | }); 12 | 13 | final bool? showBackButton; 14 | final Color? color; 15 | final VoidCallback? onPressed; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return (showBackButton == null 20 | ? ModalRoute.of(context)?.canPop == true 21 | : (showBackButton ?? false)) 22 | ? IconButton( 23 | icon: ImageExt.fromPackage(CRLoggerAssets.assetsIcBack), 24 | color: color, 25 | tooltip: MaterialLocalizations.of(context).backButtonTooltip, 26 | onPressed: () => _onBackPressed(context), 27 | ) 28 | : const SizedBox(); 29 | } 30 | 31 | void _onBackPressed(BuildContext context) { 32 | if (onPressed != null) { 33 | onPressed?.call(); 34 | } else { 35 | Navigator.maybePop(context); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/widget/cr_inspector.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/cr_logger_helper.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:inspector/inspector.dart'; 4 | 5 | class CrInspector extends StatelessWidget { 6 | const CrInspector({ 7 | required this.child, 8 | super.key, 9 | }); 10 | 11 | final Widget child; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return ValueListenableBuilder( 16 | valueListenable: CRLoggerHelper.instance.inspectorNotifier, 17 | // ignore: Prefer-trailing-comma 18 | builder: (_, enabled, __) => Inspector( 19 | isEnabled: enabled, 20 | child: child, 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/src/widget/error_value_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:cr_logger/src/res/colors.dart'; 3 | import 'package:cr_logger/src/res/styles.dart'; 4 | import 'package:cr_logger/src/utils/parsers/url_parser.dart'; 5 | import 'package:cr_logger/src/widget/copy_widget.dart'; 6 | import 'package:cr_logger/src/widget/expand_arrow_button.dart'; 7 | import 'package:cr_logger/src/widget/rounded_card.dart'; 8 | import 'package:flutter/material.dart'; 9 | 10 | class ErrorValueWidget extends StatefulWidget { 11 | const ErrorValueWidget({ 12 | required this.errorBean, 13 | super.key, 14 | }); 15 | 16 | final ErrorBean errorBean; 17 | 18 | @override 19 | _ErrorValueWidgetState createState() => _ErrorValueWidgetState(); 20 | } 21 | 22 | class _ErrorValueWidgetState extends State { 23 | bool _expanded = false; 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | final statusCode = widget.errorBean.statusCode; 28 | final statusMessage = widget.errorBean.statusMessage; 29 | final url = widget.errorBean.url; 30 | final urlWithHiddenParams = getUrlWithHiddenParams(url.toString()); 31 | 32 | return RoundedCard( 33 | child: Column( 34 | crossAxisAlignment: CrossAxisAlignment.start, 35 | children: [ 36 | Row( 37 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 38 | children: [ 39 | const Text('Error', style: CRStyle.subtitle1BlackSemiBold16), 40 | CopyWidget(onCopy: () => copyClipboard(context, url ?? '')), 41 | ], 42 | ), 43 | const SizedBox(height: 4), 44 | 45 | /// Status code and message 46 | if (statusCode != null && statusMessage != null) 47 | Row( 48 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 49 | children: [ 50 | const Text( 51 | 'Status', 52 | style: CRStyle.bodyBlackMedium14, 53 | ), 54 | Container( 55 | decoration: BoxDecoration( 56 | color: CRLoggerColors.red, 57 | borderRadius: BorderRadius.circular(4), 58 | ), 59 | padding: const EdgeInsets.symmetric( 60 | horizontal: 10, 61 | vertical: 4, 62 | ), 63 | child: Text( 64 | '$statusCode $statusMessage', 65 | style: CRStyle.bodyWhiteSemiBold14, 66 | ), 67 | ), 68 | ], 69 | ), 70 | const Divider(height: 20), 71 | 72 | /// Link 73 | Row( 74 | crossAxisAlignment: CrossAxisAlignment.start, 75 | children: [ 76 | Expanded( 77 | child: Padding( 78 | padding: const EdgeInsets.only(top: 6, bottom: 4), 79 | child: Text( 80 | urlWithHiddenParams, 81 | maxLines: _expanded ? null : 1, 82 | overflow: _expanded ? null : TextOverflow.ellipsis, 83 | style: CRStyle.bodyBlackRegular14, 84 | ), 85 | ), 86 | ), 87 | const SizedBox(width: 4), 88 | ExpandArrowButton( 89 | isExpanded: _expanded, 90 | onTap: () => setState(() => _expanded = !_expanded), 91 | ), 92 | ], 93 | ), 94 | ], 95 | ), 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/src/widget/expand_arrow_button.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | import 'package:cr_logger/generated/assets.dart'; 4 | import 'package:cr_logger/src/extensions/extensions.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class ExpandArrowButton extends StatelessWidget { 8 | const ExpandArrowButton({ 9 | required this.isExpanded, 10 | this.onTap, 11 | super.key, 12 | }); 13 | 14 | final bool isExpanded; 15 | final VoidCallback? onTap; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return SizedBox.square( 20 | dimension: 28, 21 | child: Transform.rotate( 22 | angle: isExpanded ? math.pi : 0, 23 | child: IconButton( 24 | onPressed: onTap, 25 | icon: ImageExt.fromPackage( 26 | CRLoggerAssets.assetsArrowDown, 27 | height: 28, 28 | width: 28, 29 | ), 30 | iconSize: 28, 31 | splashRadius: 20, 32 | padding: EdgeInsets.zero, 33 | ), 34 | ), 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/src/widget/http_error_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:cr_logger/src/res/styles.dart'; 3 | import 'package:cr_logger/src/widget/error_value_widget.dart'; 4 | import 'package:cr_logger/src/widget/json_widget/json_widget.dart'; 5 | import 'package:cr_logger/src/widget/rounded_card.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | class HttpErrorWidget extends StatefulWidget { 9 | const HttpErrorWidget(this.httpBean, {super.key}); 10 | 11 | final HttpBean httpBean; 12 | 13 | @override 14 | _HttpErrorWidgetState createState() => _HttpErrorWidgetState(); 15 | } 16 | 17 | class _HttpErrorWidgetState extends State 18 | with AutomaticKeepAliveClientMixin { 19 | final _jsonWidgetErrorValueKey = const ValueKey('ErrorPageParams'); 20 | 21 | @override 22 | bool get wantKeepAlive => true; 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | super.build(context); 27 | 28 | final errorBean = widget.httpBean.error; 29 | 30 | return errorBean == null 31 | ? const Center( 32 | child: Text( 33 | 'No error', 34 | style: CRStyle.bodyGreyMedium14, 35 | ), 36 | ) 37 | : SingleChildScrollView( 38 | padding: const EdgeInsets.only( 39 | left: 16, 40 | right: 16, 41 | bottom: 16, 42 | ), 43 | child: Column( 44 | crossAxisAlignment: CrossAxisAlignment.start, 45 | children: [ 46 | ErrorValueWidget(errorBean: errorBean), 47 | const SizedBox(height: 12), 48 | 49 | /// Error response 50 | RoundedCard( 51 | padding: const EdgeInsets.all(16), 52 | child: Column( 53 | crossAxisAlignment: CrossAxisAlignment.start, 54 | children: [ 55 | const Text( 56 | 'Response:', 57 | style: CRStyle.subtitle1BlackSemiBold16, 58 | ), 59 | const SizedBox(height: 12), 60 | JsonWidget( 61 | _getJsonObj(errorBean), 62 | allExpandedNodes: true, 63 | key: _jsonWidgetErrorValueKey, 64 | ), 65 | ], 66 | ), 67 | ), 68 | ], 69 | ), 70 | ); 71 | } 72 | 73 | Map? _getJsonObj(ErrorBean? error) { 74 | final errorData = error?.errorData; 75 | 76 | return errorData is Map 77 | ? errorData 78 | : {'Error': error?.errorData.toString() ?? ''}; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/src/widget/json_widget/json_tree_colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const nullColor = Colors.grey; 4 | const boolColor = Colors.orange; 5 | 6 | const jsonTreeColor = Color(0xFF2D45C3); 7 | const intColor = Color(0xFF199B4D); 8 | const doubleColor = Color(0xFF199B4D); 9 | const stringColor = Color(0xFFCD44D9); 10 | -------------------------------------------------------------------------------- /lib/src/widget/json_widget/json_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_json_widget/cr_json_recycler.dart'; 2 | import 'package:cr_logger/cr_logger.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class JsonWidget extends StatefulWidget { 6 | const JsonWidget( 7 | this.jsonObj, { 8 | this.notRoot, 9 | this.caption, 10 | this.allExpandedNodes = false, 11 | this.uncovered = 1, 12 | super.key, 13 | }); 14 | 15 | final Map? jsonObj; 16 | final bool? notRoot; 17 | final Widget? caption; 18 | final bool allExpandedNodes; 19 | final int uncovered; 20 | 21 | @override 22 | JsonWidgetState createState() => JsonWidgetState(); 23 | } 24 | 25 | class JsonWidgetState extends State { 26 | late final _jsonCtr = JsonRecyclerController(isExpanded: false); 27 | 28 | Map? _jsonWithHiddenParameters; 29 | 30 | @override 31 | void initState() { 32 | super.initState(); 33 | _updateNodes(); 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | final jsonObj = widget.jsonObj; 39 | 40 | return jsonObj == null || jsonObj.isEmpty 41 | ? const SizedBox() 42 | : Padding( 43 | padding: 44 | EdgeInsets.only(left: (widget.notRoot ?? false) ? 14.0 : 0), 45 | child: Column( 46 | mainAxisSize: MainAxisSize.min, 47 | crossAxisAlignment: CrossAxisAlignment.start, 48 | children: [ 49 | if (widget.caption != null) 50 | Padding( 51 | padding: const EdgeInsets.only(bottom: 3), 52 | child: widget.caption, 53 | ), 54 | 55 | /// JsonTreeView 56 | CustomScrollView( 57 | physics: const NeverScrollableScrollPhysics(), 58 | shrinkWrap: true, 59 | slivers: [ 60 | CrJsonRecyclerSliver( 61 | jsonController: _jsonCtr, 62 | json: _jsonWithHiddenParameters, 63 | rootExpanded: true, 64 | ), 65 | ], 66 | ), 67 | ], 68 | ), 69 | ); 70 | } 71 | 72 | @override 73 | void didUpdateWidget(covariant JsonWidget oldWidget) { 74 | super.didUpdateWidget(oldWidget); 75 | _updateNodes(); 76 | if (oldWidget.allExpandedNodes != widget.allExpandedNodes) { 77 | _jsonCtr.changeState(); 78 | } 79 | } 80 | 81 | /// Create Nodes3 = {map entry} "Test3" -> "Hidden"4 = {map entry} "Test4" -3 = {map entry} "Test3" -> "Hidden"> [_InternalLinkedHashMap] 82 | Map? _toTreeJson(Map jsonObj) { 83 | for (final obj in jsonObj.keys) { 84 | final isHidden = CRLoggerInitializer.instance.hiddenFields.contains(obj); 85 | if (isHidden) { 86 | jsonObj[obj] = 'Hidden'; 87 | } 88 | } 89 | 90 | return jsonObj; 91 | } 92 | 93 | void _updateNodes() { 94 | if (widget.jsonObj != null) { 95 | _jsonWithHiddenParameters = _toTreeJson(widget.jsonObj!); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/src/widget/params_expansion_tile.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:cr_logger/src/res/colors.dart'; 3 | import 'package:cr_logger/src/res/styles.dart'; 4 | import 'package:cr_logger/src/widget/expand_arrow_button.dart'; 5 | import 'package:cr_logger/src/widget/json_widget/json_widget.dart'; 6 | import 'package:cr_logger/src/widget/rounded_card.dart'; 7 | import 'package:flutter/material.dart'; 8 | 9 | class ParamsExpansionTile extends StatefulWidget { 10 | const ParamsExpansionTile({ 11 | required this.request, 12 | super.key, 13 | }); 14 | 15 | final RequestBean? request; 16 | 17 | @override 18 | State createState() => _ParamsExpansionTileState(); 19 | } 20 | 21 | class _ParamsExpansionTileState extends State { 22 | bool _isExpanded = false; 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | final paramsLength = widget.request?.params?.length ?? 0; 27 | final paramsIsNotEmpty = paramsLength != 0; 28 | const _jsonWidgetParamsValueKey = ValueKey('RequestPageParams'); 29 | 30 | return RoundedCard( 31 | padding: const EdgeInsets.only( 32 | left: 16, 33 | top: 10, 34 | right: 16, 35 | bottom: 10, 36 | ), 37 | child: Column( 38 | children: [ 39 | Row( 40 | children: [ 41 | const Expanded( 42 | child: Text( 43 | 'Params', 44 | style: CRStyle.subtitle1BlackSemiBold16, 45 | ), 46 | ), 47 | Text( 48 | '$paramsLength', 49 | style: CRStyle.subtitle1BlackSemiBold16.copyWith( 50 | color: CRLoggerColors.grey, 51 | ), 52 | ), 53 | const SizedBox(width: 6), 54 | ExpandArrowButton( 55 | isExpanded: _isExpanded && paramsIsNotEmpty, 56 | onTap: paramsIsNotEmpty ? _expand : null, 57 | ), 58 | ], 59 | ), 60 | if (paramsIsNotEmpty) 61 | JsonWidget( 62 | widget.request?.params, 63 | key: _jsonWidgetParamsValueKey, 64 | allExpandedNodes: _isExpanded, 65 | ), 66 | ], 67 | ), 68 | ); 69 | } 70 | 71 | void _expand() => setState(() => _isExpanded = !_isExpanded); 72 | } 73 | -------------------------------------------------------------------------------- /lib/src/widget/proxy_input_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/cr_logger_helper.dart'; 2 | import 'package:cr_logger/src/res/colors.dart'; 3 | import 'package:cr_logger/src/res/styles.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class ProxyInputDialog extends StatefulWidget { 7 | const ProxyInputDialog({super.key}); 8 | 9 | @override 10 | _ProxyInputDialogState createState() => _ProxyInputDialogState(); 11 | } 12 | 13 | class _ProxyInputDialogState extends State { 14 | late TextEditingController iPCtrl; 15 | late TextEditingController portCtrl; 16 | 17 | @override 18 | void initState() { 19 | String? ip; 20 | String? port; 21 | 22 | final proxy = CRLoggerHelper.instance.getProxyFromSharedPref(); 23 | if (proxy != null) { 24 | final items = proxy.split(':'); 25 | ip = items.first; 26 | port = items[1]; 27 | } 28 | 29 | iPCtrl = TextEditingController(text: ip ?? ''); 30 | portCtrl = TextEditingController(text: port ?? '8888'); 31 | super.initState(); 32 | } 33 | 34 | @override 35 | void dispose() { 36 | iPCtrl.dispose(); 37 | portCtrl.dispose(); 38 | super.dispose(); 39 | } 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | return AlertDialog( 44 | title: const Text('Proxy settings for Charles'), 45 | content: Column( 46 | crossAxisAlignment: CrossAxisAlignment.stretch, 47 | mainAxisSize: MainAxisSize.min, 48 | children: [ 49 | const Text( 50 | 'Changes will be applied after restarting the app', 51 | style: CRStyle.captionGreyMedium12, 52 | ), 53 | TextField( 54 | // ignore: no-empty-block 55 | onChanged: (_) => setState(() {}), 56 | controller: iPCtrl, 57 | decoration: const InputDecoration(hintText: 'Enter new IP address'), 58 | ), 59 | TextField( 60 | // ignore: no-empty-block 61 | onChanged: (_) => setState(() {}), 62 | controller: portCtrl, 63 | decoration: const InputDecoration(hintText: 'Enter port'), 64 | ), 65 | ], 66 | ), 67 | actions: [ 68 | TextButton( 69 | onPressed: _clearProxy, 70 | child: const DefaultTextStyle( 71 | style: TextStyle(color: CRLoggerColors.red), 72 | child: Text('CLEAR'), 73 | ), 74 | ), 75 | TextButton( 76 | onPressed: _closeDialog, 77 | child: const Text('CANCEL'), 78 | ), 79 | TextButton( 80 | onPressed: 81 | iPCtrl.text.isEmpty || portCtrl.text.isEmpty ? null : _saveProxy, 82 | child: const Text('SAVE'), 83 | ), 84 | ], 85 | ); 86 | } 87 | 88 | void _closeDialog() => Navigator.of(context).pop(); 89 | 90 | Future _saveProxy() async { 91 | final ip = iPCtrl.text.trim(); 92 | final port = portCtrl.text.trim(); 93 | 94 | if (ip.isNotEmpty && port.isNotEmpty) { 95 | final proxy = '$ip:$port'; 96 | await CRLoggerHelper.instance.setProxyToSharedPref(proxy); 97 | } 98 | _closeDialog(); 99 | } 100 | 101 | Future _clearProxy() async { 102 | await CRLoggerHelper.instance.setProxyToSharedPref(null); 103 | _closeDialog(); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /lib/src/widget/remove_log_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class RemoveLogWidget extends StatelessWidget { 4 | const RemoveLogWidget({ 5 | this.onRemove, 6 | super.key, 7 | }); 8 | 9 | final VoidCallback? onRemove; 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return SizedBox.square( 14 | dimension: 28, 15 | child: IconButton( 16 | onPressed: onRemove, 17 | icon: const Icon( 18 | Icons.delete_outline, 19 | color: Colors.black45, 20 | ), 21 | iconSize: 24, 22 | splashRadius: 20, 23 | padding: EdgeInsets.zero, 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/src/widget/rounded_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/res/colors.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class RoundedCard extends StatelessWidget { 5 | const RoundedCard({ 6 | required this.child, 7 | this.onTap, 8 | this.onLongTap, 9 | this.padding, 10 | super.key, 11 | }); 12 | 13 | final Widget child; 14 | final VoidCallback? onTap; 15 | final VoidCallback? onLongTap; 16 | final EdgeInsetsGeometry? padding; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Card( 21 | elevation: 0, 22 | margin: EdgeInsets.zero, 23 | shape: RoundedRectangleBorder( 24 | borderRadius: BorderRadius.circular(10), 25 | ), 26 | child: InkWell( 27 | onTap: onTap, 28 | onLongPress: onLongTap, 29 | borderRadius: BorderRadius.circular(10), 30 | child: Ink( 31 | decoration: BoxDecoration( 32 | color: CRLoggerColors.white, 33 | borderRadius: BorderRadius.circular(10), 34 | ), 35 | padding: padding ?? 36 | const EdgeInsets.only( 37 | left: 16, 38 | top: 10, 39 | right: 16, 40 | bottom: 10, 41 | ), 42 | child: child, 43 | ), 44 | ), 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/src/widget/url_value_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/cr_logger.dart'; 2 | import 'package:cr_logger/src/extensions/extensions.dart'; 3 | import 'package:cr_logger/src/res/styles.dart'; 4 | import 'package:cr_logger/src/utils/parsers/url_parser.dart'; 5 | import 'package:cr_logger/src/widget/expand_arrow_button.dart'; 6 | import 'package:cr_logger/src/widget/rounded_card.dart'; 7 | import 'package:flutter/material.dart'; 8 | 9 | class UrlValueWidget extends StatefulWidget { 10 | const UrlValueWidget({ 11 | required this.url, 12 | this.requestTime, 13 | this.responseTime, 14 | super.key, 15 | }); 16 | 17 | final String? url; 18 | final DateTime? requestTime; 19 | final DateTime? responseTime; 20 | 21 | @override 22 | _UrlValueWidgetState createState() => _UrlValueWidgetState(); 23 | } 24 | 25 | class _UrlValueWidgetState extends State { 26 | bool _isExpanded = false; 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | final urlWithHiddenParams = getUrlWithHiddenParams( 31 | widget.url.toString(), 32 | showFullPath: _isExpanded, 33 | ); 34 | final requestTime = widget.requestTime; 35 | final responseTime = widget.responseTime; 36 | 37 | return RoundedCard( 38 | onTap: () => setState(() => _isExpanded = !_isExpanded), 39 | onLongTap: () => copyClipboard(context, widget.url ?? ''), 40 | padding: const EdgeInsets.only( 41 | left: 16, 42 | top: 10, 43 | right: 16, 44 | bottom: 10, 45 | ), 46 | child: Row( 47 | crossAxisAlignment: CrossAxisAlignment.start, 48 | children: [ 49 | Expanded( 50 | child: Column( 51 | crossAxisAlignment: CrossAxisAlignment.start, 52 | children: [ 53 | const SizedBox(height: 8), 54 | 55 | /// URL 56 | Text( 57 | urlWithHiddenParams, 58 | maxLines: _isExpanded ? null : 1, 59 | overflow: _isExpanded ? null : TextOverflow.ellipsis, 60 | style: CRStyle.bodyBlackRegular14, 61 | ), 62 | const SizedBox(height: 4), 63 | 64 | /// Request time 65 | if (_isExpanded && requestTime != null) 66 | Padding( 67 | padding: const EdgeInsets.only(top: 6), 68 | child: Text( 69 | 'Request time: ${requestTime.formatTime(context)}', 70 | style: CRStyle.bodyGreyRegular14, 71 | ), 72 | ), 73 | 74 | /// Response time 75 | if (_isExpanded && responseTime != null) 76 | Padding( 77 | padding: const EdgeInsets.only(top: 6), 78 | child: Text( 79 | 'Response time: ${responseTime.formatTime(context)}', 80 | style: CRStyle.bodyGreyRegular14, 81 | ), 82 | ), 83 | ], 84 | ), 85 | ), 86 | const SizedBox(width: 4), 87 | 88 | /// Arrow button 89 | ExpandArrowButton(isExpanded: _isExpanded), 90 | ], 91 | ), 92 | ); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: cr_logger 2 | description: Powerful logging plugin. Supports android, ios and web platforms. 3 | 4 | version: 2.4.2 5 | homepage: https://github.com/Cleveroad/cr_logger 6 | 7 | topics: 8 | - tool 9 | - network 10 | - debug 11 | - logging 12 | - dio 13 | 14 | environment: 15 | sdk: ">=3.0.0 <4.0.0" 16 | flutter: ">=3.16.0" 17 | 18 | dependencies: 19 | flutter: 20 | sdk: flutter 21 | flutter_web_plugins: 22 | sdk: flutter 23 | http: ^1.1.2 24 | 25 | # MIT 26 | cr_json_widget: ^1.1.1 27 | 28 | # Network 29 | # MIT 30 | dio: ^5.4.0 31 | # MIT 32 | chopper: ^7.0.10 33 | # MIT 34 | logger: ^2.0.2+1 35 | 36 | # BSD 37 | package_info_plus: ^5.0.1 38 | 39 | # MIT 40 | uuid: ^3.0.7 41 | 42 | # MIT 43 | app_settings: ^5.1.1 44 | 45 | # BSD 46 | path_provider: ^2.1.2 47 | # BSD-3-Clause 48 | path: ^1.8.3 49 | 50 | # MIT 51 | inspector: ^2.1.0 52 | 53 | # BSD-3-Clause 54 | shared_preferences: ^2.2.2 55 | # MIT 56 | split_view: ^3.2.1 57 | 58 | # unknown 59 | # https://pub.dev/packages/synchronized/license 60 | synchronized: ^3.1.0 61 | 62 | # BSD-3-Clause 63 | js: ^0.6.7 64 | 65 | # BSD-3-Clause 66 | # https://stackoverflow.com/a/70757512 67 | platform: ^3.1.2 68 | 69 | # Apache-2.0 70 | infinite_scroll_pagination: ^4.0.0 71 | # MIT 72 | worker_manager: ^6.3.1 73 | 74 | # BSD-2-Clause 75 | sqflite: ^2.3.0 76 | 77 | dev_dependencies: 78 | flutter_test: 79 | sdk: flutter 80 | flutter_lints: ^3.0.1 81 | 82 | build_runner: ^2.4.7 83 | 84 | flutter: 85 | uses-material-design: true 86 | assets: 87 | - assets/ 88 | 89 | fonts: 90 | - family: Epilogue 91 | fonts: 92 | - asset: fonts/Epilogue-Medium.ttf 93 | - asset: fonts/Epilogue-Regular.ttf 94 | 95 | plugin: 96 | platforms: 97 | android: 98 | package: com.cleveroad.cr_logger.cr_logger 99 | pluginClass: CrLoggerPlugin 100 | ios: 101 | pluginClass: CrLoggerPlugin 102 | web: 103 | pluginClass: CrLoggerWeb 104 | fileName: cr_logger_web.dart 105 | 106 | -------------------------------------------------------------------------------- /screenshots/debug_log_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/screenshots/debug_log_screenshot.png -------------------------------------------------------------------------------- /screenshots/http-logs-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/screenshots/http-logs-example.gif -------------------------------------------------------------------------------- /screenshots/http_db_log_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/screenshots/http_db_log_screenshot.png -------------------------------------------------------------------------------- /screenshots/http_error_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/screenshots/http_error_screenshot.png -------------------------------------------------------------------------------- /screenshots/http_log_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/screenshots/http_log_screenshot.png -------------------------------------------------------------------------------- /screenshots/http_request_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/screenshots/http_request_screenshot.png -------------------------------------------------------------------------------- /screenshots/http_response_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/screenshots/http_response_screenshot.png -------------------------------------------------------------------------------- /screenshots/http_search_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/screenshots/http_search_screenshot.png -------------------------------------------------------------------------------- /screenshots/logs_search_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/screenshots/logs_search_screenshot.png -------------------------------------------------------------------------------- /screenshots/plugin_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/screenshots/plugin_banner.png -------------------------------------------------------------------------------- /screenshots/quick_action_menu_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/screenshots/quick_action_menu_screenshot.png -------------------------------------------------------------------------------- /screenshots/screenshot-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/screenshots/screenshot-web.png -------------------------------------------------------------------------------- /screenshots/settings-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cleveroad/cr_logger/ad324c1aec547aca793690643999a6463fc69fc1/screenshots/settings-screenshot.png -------------------------------------------------------------------------------- /test/replace_curly_braces_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:cr_logger/src/constants.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | void main() { 5 | group('patternOfParamsRegex tests', () { 6 | test('replace for string without parameters', () { 7 | const text = 'This is a test string.'; 8 | 9 | expect(text.replaceAll(patternOfParamsRegex, ''), equals(text)); 10 | }); 11 | 12 | test('replace for string with one parameter', () { 13 | const text = 'Hello, {{name}}!'; 14 | 15 | expect( 16 | text.replaceAll(patternOfParamsRegex, ''), 17 | equals('Hello, name!'), 18 | ); 19 | }); 20 | 21 | test('replace for string with several parameter', () { 22 | const text = 'The {{animal}} jumped over the {{object}}.'; 23 | 24 | expect( 25 | text.replaceAll(patternOfParamsRegex, ''), 26 | equals('The animal jumped over the object.'), 27 | ); 28 | }); 29 | 30 | test( 31 | 'replace for string with one parameter, when parameter is a start of sentence', 32 | () { 33 | const text = '{{curly-braces}} are not allowed as parameter names.'; 34 | 35 | expect( 36 | text.replaceAll(patternOfParamsRegex, ''), 37 | equals('curly-braces are not allowed as parameter names.'), 38 | ); 39 | }, 40 | ); 41 | 42 | test('replace for string with nested parameters', () { 43 | const text = 'This {{has {{nested}}}} parameters.'; 44 | 45 | expect( 46 | text.replaceAll(patternOfParamsRegex, ''), 47 | equals('This has nested parameters.'), 48 | ); 49 | }); 50 | 51 | test('replace for string, where parameters are adjacent to each other', () { 52 | const text = '{{param1}}{{param2}}'; 53 | 54 | expect(text.replaceAll(patternOfParamsRegex, ''), equals('param1param2')); 55 | }); 56 | 57 | test('replace for string when parameter in curly braces ', () { 58 | const text = 'This is a {{{parameter}}} in curly braces'; 59 | expect( 60 | text.replaceAll(patternOfParamsRegex, ''), 61 | equals('This is a {parameter} in curly braces'), 62 | ); 63 | }); 64 | }); 65 | } 66 | --------------------------------------------------------------------------------