├── lib ├── features │ ├── network │ │ ├── network.dart │ │ ├── ui │ │ │ ├── raw_data_viewer │ │ │ │ ├── models │ │ │ │ │ └── raw_data_view.dart │ │ │ │ └── bloc │ │ │ │ │ ├── raw_data_viewer_event.dart │ │ │ │ │ ├── raw_data_viewer_state.dart │ │ │ │ │ └── raw_data_viewer_bloc.dart │ │ │ ├── details │ │ │ │ ├── utils │ │ │ │ │ └── interceptor_details_helper.dart │ │ │ │ ├── bloc │ │ │ │ │ ├── interceptor_details_event.dart │ │ │ │ │ ├── interceptor_details_state.dart │ │ │ │ │ └── interceptor_details_bloc.dart │ │ │ │ ├── models │ │ │ │ │ └── details_topic_data.dart │ │ │ │ ├── widgets │ │ │ │ │ └── details_row_widget.dart │ │ │ │ ├── components │ │ │ │ │ ├── interceptor_details_error.dart │ │ │ │ │ ├── interceptor_details_request.dart │ │ │ │ │ └── interceptor_details_response.dart │ │ │ │ └── screen │ │ │ │ │ └── interceptor_details_screen.dart │ │ │ └── list │ │ │ │ ├── components │ │ │ │ ├── trailing_widget.dart │ │ │ │ └── expansion_widget.dart │ │ │ │ ├── bloc │ │ │ │ ├── networks_list_state.dart │ │ │ │ └── networks_list_event.dart │ │ │ │ ├── desktop_components │ │ │ │ ├── state_helpers.dart │ │ │ │ └── draggable_cell.dart │ │ │ │ ├── models │ │ │ │ └── network_action.dart │ │ │ │ └── screen │ │ │ │ ├── networks_list_screen.dart │ │ │ │ └── desktop_networks_list_screen.dart │ │ └── models │ │ │ ├── infospect_network_error.dart │ │ │ ├── infospect_network_response.dart │ │ │ └── infospect_form_data.dart │ ├── launch │ │ ├── bloc │ │ │ ├── launch_event.dart │ │ │ ├── launch_state.dart │ │ │ └── launch_bloc.dart │ │ ├── models │ │ │ └── navigation_tab_data.dart │ │ └── screen │ │ │ ├── infospect_launch_screen.dart │ │ │ └── launch_mobile_screen.dart │ └── logger │ │ ├── ui │ │ └── logs_list │ │ │ ├── models │ │ │ └── logs_action.dart │ │ │ ├── screen │ │ │ ├── logs_list_screen.dart │ │ │ └── desktop_logs_list_screen.dart │ │ │ └── bloc │ │ │ ├── logs_list_event.dart │ │ │ └── logs_list_state.dart │ │ └── models │ │ └── infospect_log.dart ├── utils │ ├── extensions │ │ ├── double_extension.dart │ │ ├── map_extension.dart │ │ ├── date_time_extension.dart │ │ ├── infospect_log │ │ │ └── infospect_log_extension.dart │ │ ├── infospect_network │ │ │ ├── network_request_extension.dart │ │ │ └── network_response_extension.dart │ │ └── int_extension.dart │ ├── common_widgets │ │ ├── divider.dart │ │ ├── conditional_widget.dart │ │ ├── app_bottom_bar.dart │ │ ├── app_search_bar.dart │ │ ├── app_adaptive_dialog.dart │ │ └── highlight_text_widget.dart │ └── models │ │ └── action_model.dart ├── styling │ └── themes │ │ └── infospect_theme.dart ├── infospect.dart ├── helpers │ ├── desktop_theme_cubit │ │ ├── desktop_theme_cubit.dart │ │ └── desktop_theme_state.dart │ └── log_helper.dart └── routes │ └── routes.dart ├── example ├── linux │ ├── .gitignore │ ├── main.cc │ ├── flutter │ │ ├── generated_plugin_registrant.h │ │ ├── generated_plugin_registrant.cc │ │ ├── generated_plugins.cmake │ │ └── CMakeLists.txt │ └── my_application.h ├── README.md ├── analysis_options.yaml ├── ios │ ├── Runner │ │ ├── Runner-Bridging-Header.h │ │ ├── Assets.xcassets │ │ │ ├── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── 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-1024x1024@1x.png │ │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ │ └── Contents.json │ │ ├── AppDelegate.swift │ │ ├── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Flutter │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── AppFrameworkInfo.plist │ ├── Runner.xcodeproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── RunnerTests │ │ └── RunnerTests.swift │ ├── .gitignore │ ├── Podfile.lock │ └── Podfile ├── web │ ├── favicon.png │ ├── icons │ │ ├── Icon-192.png │ │ ├── Icon-512.png │ │ ├── Icon-maskable-192.png │ │ └── Icon-maskable-512.png │ ├── manifest.json │ └── index.html ├── android │ ├── gradle.properties │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── 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 │ │ │ │ │ ├── drawable │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── drawable-v21 │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── values │ │ │ │ │ │ └── styles.xml │ │ │ │ │ └── values-night │ │ │ │ │ │ └── styles.xml │ │ │ │ ├── kotlin │ │ │ │ │ └── com │ │ │ │ │ │ └── example │ │ │ │ │ │ └── example │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── AndroidManifest.xml │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── .gitignore │ ├── settings.gradle │ └── build.gradle ├── macos │ ├── Runner │ │ ├── Configs │ │ │ ├── Debug.xcconfig │ │ │ ├── Release.xcconfig │ │ │ ├── Warnings.xcconfig │ │ │ └── AppInfo.xcconfig │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ ├── app_icon_16.png │ │ │ │ ├── app_icon_32.png │ │ │ │ ├── app_icon_64.png │ │ │ │ ├── app_icon_1024.png │ │ │ │ ├── app_icon_128.png │ │ │ │ ├── app_icon_256.png │ │ │ │ ├── app_icon_512.png │ │ │ │ └── Contents.json │ │ ├── AppDelegate.swift │ │ ├── Release.entitlements │ │ ├── MainFlutterWindow.swift │ │ ├── DebugProfile.entitlements │ │ ├── RunnerProfile.entitlements │ │ └── Info.plist │ ├── .gitignore │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ ├── Flutter-Release.xcconfig │ │ └── GeneratedPluginRegistrant.swift │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner.xcodeproj │ │ └── project.xcworkspace │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── RunnerTests │ │ └── RunnerTests.swift │ ├── Podfile.lock │ └── Podfile ├── windows │ ├── runner │ │ ├── resources │ │ │ └── app_icon.ico │ │ ├── resource.h │ │ ├── utils.h │ │ ├── runner.exe.manifest │ │ ├── flutter_window.h │ │ ├── main.cpp │ │ ├── CMakeLists.txt │ │ ├── utils.cpp │ │ ├── flutter_window.cpp │ │ └── Runner.rc │ ├── .gitignore │ └── flutter │ │ ├── generated_plugin_registrant.h │ │ ├── generated_plugin_registrant.cc │ │ └── generated_plugins.cmake ├── pubspec.yaml ├── .vscode │ └── launch.json ├── .gitignore └── .metadata ├── images ├── logs_style.png ├── preview │ ├── ios.gif │ ├── mac.gif │ ├── windows.gif │ └── linux(ubuntu vm).gif └── screenshot │ ├── logs.png │ ├── linux.png │ ├── share.png │ ├── expanded.png │ ├── windows.png │ ├── logs_desktop.png │ ├── share_desktop.png │ ├── desktop_multi_window.png │ ├── network_calls_screen.png │ └── network_calls_screen_desktop.png ├── .github ├── pr-labeler.yml ├── release-drafter.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── pull_request_opened.yml │ ├── publish.yml │ ├── commit_push.yml │ ├── create_tag.yml │ ├── check_version.yml │ └── trigger_on_pr_merge.yml ├── analysis_options.yaml ├── windows └── flutter │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake ├── .metadata ├── .vscode └── launch.json ├── SECURITY.md ├── .gitignore ├── .pubignore ├── LICENSE ├── test └── infospect_test.dart ├── CHANGELOG.md ├── pubspec.yaml └── CONTRIBUTION.md /lib/features/network/network.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /example/linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | A new Flutter project. 4 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /images/logs_style.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/images/logs_style.png -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/web/favicon.png -------------------------------------------------------------------------------- /images/preview/ios.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/images/preview/ios.gif -------------------------------------------------------------------------------- /images/preview/mac.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/images/preview/mac.gif -------------------------------------------------------------------------------- /images/preview/windows.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/images/preview/windows.gif -------------------------------------------------------------------------------- /images/screenshot/logs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/images/screenshot/logs.png -------------------------------------------------------------------------------- /images/screenshot/linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/images/screenshot/linux.png -------------------------------------------------------------------------------- /images/screenshot/share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/images/screenshot/share.png -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /images/screenshot/expanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/images/screenshot/expanded.png -------------------------------------------------------------------------------- /images/screenshot/windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/images/screenshot/windows.png -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /images/preview/linux(ubuntu vm).gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/images/preview/linux(ubuntu vm).gif -------------------------------------------------------------------------------- /images/screenshot/logs_desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/images/screenshot/logs_desktop.png -------------------------------------------------------------------------------- /images/screenshot/share_desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/images/screenshot/share_desktop.png -------------------------------------------------------------------------------- /example/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /images/screenshot/desktop_multi_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/images/screenshot/desktop_multi_window.png -------------------------------------------------------------------------------- /images/screenshot/network_calls_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/images/screenshot/network_calls_screen.png -------------------------------------------------------------------------------- /lib/utils/extensions/double_extension.dart: -------------------------------------------------------------------------------- 1 | extension DoubleExtension on double { 2 | String get formattedString => toStringAsFixed(2); 3 | } 4 | -------------------------------------------------------------------------------- /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/windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /images/screenshot/network_calls_screen_desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/images/screenshot/network_calls_screen_desktop.png -------------------------------------------------------------------------------- /.github/pr-labeler.yml: -------------------------------------------------------------------------------- 1 | bug: hotfix/* 2 | documentation: docs/* 3 | feature: ['feature/*', 'feat/*'] 4 | fix: fix/* 5 | chore: chore/* 6 | path: path/* 7 | build: build/* 8 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/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/kushalmahapatro/infospect/HEAD/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/kushalmahapatro/infospect/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/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/kushalmahapatro/infospect/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | # Additional information about this file can be found at 4 | # https://dart.dev/guides/language/analysis-options 5 | -------------------------------------------------------------------------------- /lib/utils/extensions/map_extension.dart: -------------------------------------------------------------------------------- 1 | extension MapExtension on Map? { 2 | Map getMap() { 3 | return this != null ? this!.cast() : {}; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/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/kushalmahapatro/infospect/HEAD/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/kushalmahapatro/infospect/HEAD/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/kushalmahapatro/infospect/HEAD/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/kushalmahapatro/infospect/HEAD/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/kushalmahapatro/infospect/HEAD/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/kushalmahapatro/infospect/HEAD/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/kushalmahapatro/infospect/HEAD/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/kushalmahapatro/infospect/HEAD/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/kushalmahapatro/infospect/HEAD/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/kushalmahapatro/infospect/HEAD/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/kushalmahapatro/infospect/HEAD/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/kushalmahapatro/infospect/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kushalmahapatro/infospect/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | categories: 2 | - title: 'Enhancements' 3 | labels: 4 | - 'enhancement' 5 | - title: 'Fixes' 6 | labels: 7 | - 'bug' 8 | - 'security' 9 | change-template: '* $TITLE — #$NUMBER' 10 | template: $CHANGES -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | 10 | void RegisterPlugins(flutter::PluginRegistry* registry) { 11 | } 12 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 6 | -------------------------------------------------------------------------------- /example/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @main 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/features/launch/bloc/launch_event.dart: -------------------------------------------------------------------------------- 1 | part of 'launch_bloc.dart'; 2 | 3 | @immutable 4 | abstract class LaunchEvent { 5 | const LaunchEvent(); 6 | } 7 | 8 | class TabChanged extends LaunchEvent { 9 | final int selectedTab; 10 | 11 | const TabChanged({required this.selectedTab}); 12 | } 13 | -------------------------------------------------------------------------------- /lib/styling/themes/infospect_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class InfospectTheme { 4 | // 5 | InfospectTheme._(); 6 | 7 | static final ThemeData lightTheme = ThemeData(useMaterial3: true); 8 | static final ThemeData darkTheme = ThemeData.dark(useMaterial3: true); 9 | } 10 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/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/macos/Runner.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.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: f92f44110e87bad5ff168335c36da6f6053036e6 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /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/ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /lib/utils/extensions/date_time_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:infospect/utils/extensions/int_extension.dart'; 2 | 3 | extension DateTimeExtension on DateTime { 4 | String get formatTime { 5 | return "${hour.formatTimeUnit}:" 6 | "${minute.formatTimeUnit}:" 7 | "${second.formatTimeUnit}:" 8 | "${millisecond.formatTimeUnit}"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/features/network/ui/raw_data_viewer/models/raw_data_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | enum RawDataView { 4 | beautified(value: 'Beautified', icon: Icons.code), 5 | treeView(value: 'Tree View', icon: Icons.list); 6 | 7 | final String value; 8 | final IconData icon; 9 | const RawDataView({required this.value, required this.icon}); 10 | } 11 | -------------------------------------------------------------------------------- /example/macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import Cocoa 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /example/windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /lib/features/network/ui/details/utils/interceptor_details_helper.dart: -------------------------------------------------------------------------------- 1 | extension MapExtension on Map? { 2 | String get contentType { 3 | if (this != null && 4 | (this!.containsKey('content-type') || 5 | this!.containsKey('Content-Type'))) { 6 | return (this!['content-type'] ?? this!['Content-Type'] ?? 'unknown') 7 | .toString(); 8 | } 9 | return 'unknown'; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /example/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A new Flutter project. 3 | publish_to: 'none' 4 | version: 0.1.0 5 | 6 | environment: 7 | sdk: '>=3.0.0 <4.0.0' 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | infospect: 13 | path: ../ 14 | path_provider: any 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | flutter_lints: ^2.0.0 20 | 21 | flutter: 22 | uses-material-design: true 23 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/utils/extensions/infospect_log/infospect_log_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:infospect/infospect.dart'; 2 | 3 | extension InfospectLogExtension on InfospectLog { 4 | String get sharableData { 5 | String data = 'Level: ${level.name}\n' 6 | '$timestamp: $message\n'; 7 | if (error != null) { 8 | data += 'Error: $error\n'; 9 | } 10 | if (stackTrace != null) { 11 | data += 'Stack Trace: $stackTrace\n'; 12 | } 13 | return data; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /example/macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.client 10 | 11 | com.apple.security.network.server 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /example/macos/Runner/RunnerProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.client 10 | 11 | com.apple.security.network.server 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /lib/features/launch/models/navigation_tab_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 2 | import 'package:infospect/utils/common_widgets/app_bottom_bar.dart'; 3 | 4 | /// An class that provides navigation tab data for bottom navigation. 5 | class NavigationTabData { 6 | static List get tabs { 7 | return [ 8 | (icon: FontAwesomeIcons.globe, title: "Network calls"), 9 | (icon: FontAwesomeIcons.list, title: "Logs") 10 | ]; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /example/windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /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/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /lib/features/network/ui/details/bloc/interceptor_details_event.dart: -------------------------------------------------------------------------------- 1 | part of 'interceptor_details_bloc.dart'; 2 | 3 | @immutable 4 | abstract class InterceptorDetailsEvent { 5 | const InterceptorDetailsEvent(); 6 | } 7 | 8 | class DetailsTabChanged extends InterceptorDetailsEvent { 9 | final int selectedTab; 10 | const DetailsTabChanged({required this.selectedTab}); 11 | } 12 | 13 | class RequestBodyTypeChanged extends InterceptorDetailsEvent { 14 | final int index; 15 | const RequestBodyTypeChanged({required this.index}); 16 | } 17 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /lib/features/launch/bloc/launch_state.dart: -------------------------------------------------------------------------------- 1 | part of 'launch_bloc.dart'; 2 | 3 | @immutable 4 | class LaunchState extends Equatable { 5 | final int selectedTab; 6 | 7 | const LaunchState({this.selectedTab = 0}); 8 | 9 | @override 10 | List get props => [ 11 | selectedTab, 12 | ]; 13 | 14 | LaunchState copyWith({ 15 | int? selectedTab, 16 | List? networkCalls, 17 | }) { 18 | return LaunchState( 19 | selectedTab: selectedTab ?? this.selectedTab, 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/utils/common_widgets/divider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | abstract class AppDivider { 4 | static Widget horizontal({double? height, Color? color}) { 5 | return Container( 6 | height: height ?? 1, 7 | width: double.infinity, 8 | color: color ?? Colors.black, 9 | ); 10 | } 11 | 12 | static Widget vertical({double? width, Color? color}) { 13 | return Container( 14 | width: width ?? 1, 15 | height: double.infinity, 16 | color: color ?? Colors.black, 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/utils/common_widgets/conditional_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ConditionalWidget extends StatelessWidget { 4 | final bool condition; 5 | final Widget ifTrue; 6 | final Widget ifFalse; 7 | const ConditionalWidget({ 8 | super.key, 9 | required this.condition, 10 | required this.ifTrue, 11 | required this.ifFalse, 12 | }); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | if (condition) { 17 | return ifTrue; 18 | } else { 19 | return ifFalse; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/features/network/ui/details/bloc/interceptor_details_state.dart: -------------------------------------------------------------------------------- 1 | part of 'interceptor_details_bloc.dart'; 2 | 3 | @immutable 4 | class InterceptorDetailsState extends Equatable { 5 | final int selectedTab; 6 | 7 | const InterceptorDetailsState({ 8 | this.selectedTab = 0, 9 | }); 10 | 11 | @override 12 | List get props => [selectedTab]; 13 | 14 | InterceptorDetailsState copyWith({ 15 | int? selectedTab, 16 | int? requestBodyType, 17 | }) { 18 | return InterceptorDetailsState( 19 | selectedTab: selectedTab ?? this.selectedTab, 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/features/network/ui/raw_data_viewer/bloc/raw_data_viewer_event.dart: -------------------------------------------------------------------------------- 1 | part of 'raw_data_viewer_bloc.dart'; 2 | 3 | abstract class RawDataViewerEvent extends Equatable { 4 | const RawDataViewerEvent(); 5 | } 6 | 7 | class SearchValueChanged extends RawDataViewerEvent { 8 | final String value; 9 | 10 | const SearchValueChanged(this.value); 11 | 12 | @override 13 | List get props => [value]; 14 | } 15 | 16 | class RawDataViewChanged extends RawDataViewerEvent { 17 | final RawDataView view; 18 | 19 | const RawDataViewChanged(this.view); 20 | 21 | @override 22 | List get props => [view]; 23 | } 24 | -------------------------------------------------------------------------------- /lib/utils/extensions/infospect_network/network_request_extension.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:infospect/features/network/models/infospect_network_request.dart'; 4 | 5 | extension NetworkRequestExtension on InfospectNetworkRequest { 6 | Map get bodyMap { 7 | if (body is String && body.toString().isNotEmpty) { 8 | try { 9 | return jsonDecode(body) as Map; 10 | } catch (_) { 11 | return {}; 12 | } 13 | } else if (body is Map) { 14 | return (body as Map).cast(); 15 | } 16 | return {}; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | 8 | { 9 | "name": "example", 10 | "cwd": "example", 11 | "request": "launch", 12 | "type": "dart" 13 | }, 14 | { 15 | "name": "pokedex", 16 | "cwd": "flutter_pokedex-master", 17 | "request": "launch", 18 | "type": "dart" 19 | }, 20 | 21 | ] 22 | } -------------------------------------------------------------------------------- /lib/features/network/ui/raw_data_viewer/bloc/raw_data_viewer_state.dart: -------------------------------------------------------------------------------- 1 | part of 'raw_data_viewer_bloc.dart'; 2 | 3 | class RawDataViewerState extends Equatable { 4 | const RawDataViewerState({ 5 | this.view = RawDataView.beautified, 6 | this.searchValue = '', 7 | }); 8 | 9 | final RawDataView view; 10 | final String searchValue; 11 | 12 | @override 13 | List get props => [view, searchValue]; 14 | 15 | RawDataViewerState copyWith({ 16 | RawDataView? view, 17 | String? searchValue, 18 | }) { 19 | return RawDataViewerState( 20 | view: view ?? this.view, 21 | searchValue: searchValue ?? this.searchValue, 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = example 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /example/macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import desktop_multi_window 9 | import package_info_plus 10 | import path_provider_foundation 11 | import share_plus 12 | 13 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 14 | FlutterMultiWindowPlugin.register(with: registry.registrar(forPlugin: "FlutterMultiWindowPlugin")) 15 | FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) 16 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 17 | SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) 18 | } 19 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.3.0' 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 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | tasks.register("clean", Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /example/windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | /pubspec.lock 27 | **/doc/api/ 28 | .dart_tool/ 29 | .packages 30 | build/ 31 | .flutter-plugins 32 | .flutter-plugins-dependencies 33 | .fvm/flutter_sdk 34 | .fvm/fvm_config.json 35 | .vscode/settings.json 36 | -------------------------------------------------------------------------------- /.pubignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | /pubspec.lock 27 | **/doc/api/ 28 | .dart_tool/ 29 | .packages 30 | build/ 31 | .flutter-plugins 32 | .flutter-plugins-dependencies 33 | .fvm/flutter_sdk 34 | .fvm/fvm_config.json 35 | .vscode/settings.json 36 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | void RegisterPlugins(flutter::PluginRegistry* registry) { 14 | DesktopMultiWindowPluginRegisterWithRegistrar( 15 | registry->GetRegistrarForPlugin("DesktopMultiWindowPlugin")); 16 | SharePlusWindowsPluginCApiRegisterWithRegistrar( 17 | registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); 18 | UrlLauncherWindowsRegisterWithRegistrar( 19 | registry->GetRegistrarForPlugin("UrlLauncherWindows")); 20 | } 21 | -------------------------------------------------------------------------------- /lib/features/launch/screen/infospect_launch_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:infospect/features/launch/screen/launch_desktop_screen.dart'; 3 | import 'package:infospect/helpers/infospect_helper.dart'; 4 | 5 | import 'launch_mobile_screen.dart'; 6 | 7 | class InfospectLaunchScreen extends StatelessWidget { 8 | final Infospect infospect; 9 | const InfospectLaunchScreen(this.infospect, {super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return LayoutBuilder( 14 | builder: (context, constraints) { 15 | if (constraints.maxWidth > 600) { 16 | return LaunchDesktopScreen(infospect); 17 | } else { 18 | return LaunchMobileScreen(infospect); 19 | } 20 | }, 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/features/launch/bloc/launch_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:bloc/bloc.dart'; 4 | import 'package:equatable/equatable.dart'; 5 | import 'package:infospect/features/network/models/infospect_network_call.dart'; 6 | import 'package:meta/meta.dart'; 7 | 8 | part 'launch_event.dart'; 9 | part 'launch_state.dart'; 10 | 11 | /// BLoC responsible for handling the state of the application's launch phase, especially related to tab changes. 12 | class LaunchBloc extends Bloc { 13 | LaunchBloc() : super(const LaunchState()) { 14 | /// Listen to tab changes. 15 | on(_onTabChanged); 16 | } 17 | 18 | FutureOr _onTabChanged(TabChanged event, Emitter emit) { 19 | emit(state.copyWith(selectedTab: event.selectedTab)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /example/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "example", 9 | "request": "launch", 10 | "type": "dart" 11 | }, 12 | { 13 | "name": "example (profile mode)", 14 | "request": "launch", 15 | "type": "dart", 16 | "flutterMode": "profile" 17 | }, 18 | { 19 | "name": "example (release mode)", 20 | "request": "launch", 21 | "type": "dart", 22 | "flutterMode": "release" 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /lib/features/network/ui/details/models/details_topic_data.dart: -------------------------------------------------------------------------------- 1 | export 'request_details_topic_helper.dart'; 2 | export 'response_details_topic_helper.dart'; 3 | 4 | typedef TopicData = ({String topic, TopicDetailsBody body}); 5 | 6 | typedef TrailingData = ({ 7 | String trailing, 8 | Map data, 9 | bool beautificationRequired 10 | }); 11 | 12 | typedef ListData = ({String title, String subtitle, String? other}); 13 | 14 | sealed class TopicDetailsBody {} 15 | 16 | class TopicDetailsBodyMap extends TopicDetailsBody { 17 | final Map map; 18 | final TrailingData? trailing; 19 | 20 | TopicDetailsBodyMap({required this.map, this.trailing}); 21 | } 22 | 23 | class TopicDetailsBodyList extends TopicDetailsBody { 24 | final List list; 25 | 26 | TopicDetailsBodyList(this.list); 27 | } 28 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | 12 | void fl_register_plugins(FlPluginRegistry* registry) { 13 | g_autoptr(FlPluginRegistrar) desktop_multi_window_registrar = 14 | fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopMultiWindowPlugin"); 15 | desktop_multi_window_plugin_register_with_registrar(desktop_multi_window_registrar); 16 | g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = 17 | fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); 18 | url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); 19 | } 20 | -------------------------------------------------------------------------------- /lib/infospect.dart: -------------------------------------------------------------------------------- 1 | export 'package:infospect/features/invoker/infospect_desktop_invoker.dart'; 2 | export 'package:infospect/features/invoker/infospect_invoker.dart'; 3 | export 'package:infospect/features/logger/models/infospect_log.dart'; 4 | export 'package:infospect/helpers/infospect_helper.dart'; 5 | export 'package:infospect/infospect.dart'; 6 | export 'package:infospect/styling/themes/infospect_theme.dart'; 7 | // common widgets 8 | export 'package:infospect/utils/common_widgets/action_widget.dart'; 9 | export 'package:infospect/utils/common_widgets/app_bottom_bar.dart'; 10 | export 'package:infospect/utils/common_widgets/app_search_bar.dart'; 11 | export 'package:infospect/utils/common_widgets/conditional_widget.dart'; 12 | export 'package:infospect/utils/common_widgets/divider.dart'; 13 | export 'package:infospect/utils/common_widgets/highlight_text_widget.dart'; 14 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | ) 7 | 8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 9 | ) 10 | 11 | set(PLUGIN_BUNDLED_LIBRARIES) 12 | 13 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 14 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 15 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 18 | endforeach(plugin) 19 | 20 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 23 | endforeach(ffi_plugin) 24 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Symbolication related 36 | app.*.symbols 37 | 38 | # Obfuscation related 39 | app.*.map.json 40 | 41 | # Android Studio will place build artifacts here 42 | /android/app/debug 43 | /android/app/profile 44 | /android/app/release 45 | -------------------------------------------------------------------------------- /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 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | desktop_multi_window 7 | url_launcher_linux 8 | ) 9 | 10 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 11 | ) 12 | 13 | set(PLUGIN_BUNDLED_LIBRARIES) 14 | 15 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 16 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 17 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 18 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 19 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 20 | endforeach(plugin) 21 | 22 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 23 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 24 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 25 | endforeach(ffi_plugin) 26 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | desktop_multi_window 7 | share_plus 8 | url_launcher_windows 9 | ) 10 | 11 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 12 | ) 13 | 14 | set(PLUGIN_BUNDLED_LIBRARIES) 15 | 16 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 17 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 18 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 19 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 20 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 21 | endforeach(plugin) 22 | 23 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 24 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 25 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 26 | endforeach(ffi_plugin) 27 | -------------------------------------------------------------------------------- /lib/features/network/ui/list/components/trailing_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:infospect/infospect.dart'; 3 | import 'package:infospect/routes/routes.dart'; 4 | 5 | class TrailingWidget extends StatelessWidget { 6 | final String text; 7 | final Infospect infospect; 8 | final Map data; 9 | final bool beautificationRequired; 10 | const TrailingWidget( 11 | {super.key, 12 | required this.text, 13 | required this.infospect, 14 | required this.data, 15 | this.beautificationRequired = false}); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return TextButton( 20 | onPressed: () => mobileRoutes.rawData( 21 | context, 22 | data, 23 | beautificationRequired, 24 | ), 25 | child: Text( 26 | text, 27 | style: Theme.of(context).textTheme.labelSmall, 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /example/windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lib/helpers/desktop_theme_cubit/desktop_theme_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | 4 | part 'desktop_theme_state.dart'; 5 | 6 | /// `DesktopThemeCubit` manages the theme state for a desktop application. 7 | /// It is responsible for handling theme-related actions and updating the state accordingly. 8 | class DesktopThemeCubit extends Cubit { 9 | /// Constructor initializes the `DesktopThemeCubit` with a default dark theme. 10 | DesktopThemeCubit() : super(const DarkDesktopTheme()); 11 | 12 | /// Sets the theme state based on the provided value. 13 | /// 14 | /// - `isDark`: A boolean indicating whether the theme should be set to dark or not. 15 | /// If `true`, it sets the theme to dark, otherwise it sets to light. 16 | void setTheme(bool isDark) { 17 | if (isDark) { 18 | emit(const DarkDesktopTheme()); 19 | } else { 20 | emit(const LightDesktopTheme()); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/features/network/ui/details/bloc/interceptor_details_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:bloc/bloc.dart'; 4 | import 'package:equatable/equatable.dart'; 5 | import 'package:meta/meta.dart'; 6 | 7 | part 'interceptor_details_event.dart'; 8 | part 'interceptor_details_state.dart'; 9 | 10 | class InterceptorDetailsBloc 11 | extends Bloc { 12 | InterceptorDetailsBloc() : super(const InterceptorDetailsState()) { 13 | on(_onTabChanged); 14 | on(_onRequestBodyTypeChanged); 15 | } 16 | 17 | FutureOr _onTabChanged( 18 | DetailsTabChanged event, Emitter emit) { 19 | emit(state.copyWith(selectedTab: event.selectedTab)); 20 | } 21 | 22 | FutureOr _onRequestBodyTypeChanged( 23 | RequestBodyTypeChanged event, Emitter emit) { 24 | emit(state.copyWith(requestBodyType: event.index)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/pull_request_opened.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request 2 | 3 | on: 4 | pull_request: 5 | types: [ opened, edited, reopened ] 6 | 7 | permissions: 8 | contents: read 9 | issues: write 10 | pull-requests: write 11 | discussions: write 12 | 13 | jobs: 14 | assign_author: 15 | name: Assign Author to the Pull Request 16 | permissions: 17 | contents: read 18 | pull-requests: write 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Assign Author to the Pull Request 22 | uses: technote-space/assign-author@6252488282b99652aef47451a353eb88aea24666 23 | 24 | pr-labeler: 25 | name: Label the PR based on the branch 26 | permissions: 27 | contents: read 28 | pull-requests: write 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Label the Pull Request 32 | uses: TimonVS/pr-labeler-action@bd0b592a410983316a454e3d48444608f028ec8e 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /lib/utils/models/action_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class ActionModel { 5 | final IconData icon; 6 | final List> actions; 7 | 8 | ActionModel({ 9 | required this.icon, 10 | required this.actions, 11 | }); 12 | } 13 | 14 | class PopupAction extends Equatable { 15 | final T id; 16 | final T? parentId; 17 | final String name; 18 | final List> subActions; 19 | final bool isSelected; 20 | 21 | const PopupAction({ 22 | required this.name, 23 | required this.id, 24 | this.isSelected = false, 25 | this.subActions = const [], 26 | this.parentId, 27 | }); 28 | 29 | PopupAction setParentId(T parentId) => PopupAction( 30 | id: id, 31 | name: name, 32 | isSelected: isSelected, 33 | subActions: subActions, 34 | parentId: parentId ?? this.parentId, 35 | ); 36 | 37 | @override 38 | List get props => [id, parentId, name, isSelected, subActions]; 39 | } 40 | -------------------------------------------------------------------------------- /lib/utils/extensions/int_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:infospect/utils/extensions/double_extension.dart'; 2 | 3 | extension IntExtension on int { 4 | String get toReadableTime { 5 | if (this < 0) { 6 | return "-1 ms"; 7 | } 8 | if (this <= 1000) { 9 | return "$this ms"; 10 | } 11 | if (this <= 60000) { 12 | return "${(this / 1000).formattedString} s"; 13 | } 14 | 15 | final Duration duration = Duration(milliseconds: this); 16 | 17 | return "${duration.inMinutes} min ${duration.inSeconds.remainder(60)} s " 18 | "${duration.inMilliseconds.remainder(1000)} ms"; 19 | } 20 | 21 | String get toReadableBytes { 22 | if (this < 0) { 23 | return "-1 B"; 24 | } 25 | if (this <= 1000) { 26 | return "$this B"; 27 | } 28 | if (this <= 1000000) { 29 | return "${(this / 1000).formattedString} kB"; 30 | } 31 | 32 | return "${(this / 1000000).formattedString} MB"; 33 | } 34 | 35 | String get formatTimeUnit { 36 | return (this < 10) ? "0$this" : "$this"; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "short_name": "example", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/publish.yml 2 | name: Publish to pub.dev 3 | 4 | on: 5 | push: 6 | tags: 7 | - 'v[0-9]+.[0-9]+.[0-9]+*' # tag pattern on pub.dev: 'v' 8 | 9 | # Publish using custom workflow 10 | jobs: 11 | publish: 12 | permissions: 13 | id-token: write # Required for authentication using OIDC 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | 18 | - uses: subosito/flutter-action@v2 19 | with: 20 | channel: 'stable' 21 | - run: flutter --version 22 | 23 | - name: Install dependencies 24 | run: flutter pub get 25 | 26 | - name: Analyze project source 27 | run: flutter analyze 28 | 29 | - name: Run tests 30 | run: flutter test 31 | 32 | - name: Run dart publish (dry run) 33 | run: flutter pub publish --dry-run 34 | 35 | - name: Publish 36 | uses: k-paxian/dart-package-publisher@v1.5.1 37 | with: 38 | credentialJson: ${{ secrets.CREDENTIAL_JSON }} 39 | flutter: true 40 | skipTests: true -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Kushal Mahapatro 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. -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - package_info_plus (0.4.5): 4 | - Flutter 5 | - path_provider_foundation (0.0.1): 6 | - Flutter 7 | - FlutterMacOS 8 | - share_plus (0.0.1): 9 | - Flutter 10 | 11 | DEPENDENCIES: 12 | - Flutter (from `Flutter`) 13 | - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) 14 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) 15 | - share_plus (from `.symlinks/plugins/share_plus/ios`) 16 | 17 | EXTERNAL SOURCES: 18 | Flutter: 19 | :path: Flutter 20 | package_info_plus: 21 | :path: ".symlinks/plugins/package_info_plus/ios" 22 | path_provider_foundation: 23 | :path: ".symlinks/plugins/path_provider_foundation/darwin" 24 | share_plus: 25 | :path: ".symlinks/plugins/share_plus/ios" 26 | 27 | SPEC CHECKSUMS: 28 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 29 | package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c 30 | path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 31 | share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad 32 | 33 | PODFILE CHECKSUM: edb6eae8eaaa8fdc95467f977e6cda7818ba81cf 34 | 35 | COCOAPODS: 1.15.2 36 | -------------------------------------------------------------------------------- /example/macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /lib/features/network/ui/raw_data_viewer/bloc/raw_data_viewer_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:equatable/equatable.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:infospect/features/network/ui/raw_data_viewer/models/raw_data_view.dart'; 6 | 7 | part 'raw_data_viewer_event.dart'; 8 | part 'raw_data_viewer_state.dart'; 9 | 10 | typedef RawDataViewerSelector 11 | = BlocSelector; 12 | typedef RawDataViewerBuilder 13 | = BlocBuilder; 14 | 15 | class RawDataViewerBloc extends Bloc { 16 | RawDataViewerBloc() : super(const RawDataViewerState()) { 17 | on(_onSearchValueChanged); 18 | on(_onRawDataViewChanged); 19 | } 20 | 21 | FutureOr _onSearchValueChanged( 22 | SearchValueChanged event, Emitter emit) { 23 | emit(state.copyWith(searchValue: event.value)); 24 | } 25 | 26 | FutureOr _onRawDataViewChanged( 27 | RawDataViewChanged event, Emitter emit) { 28 | emit(state.copyWith(view: event.view)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/helpers/desktop_theme_cubit/desktop_theme_state.dart: -------------------------------------------------------------------------------- 1 | part of 'desktop_theme_cubit.dart'; 2 | 3 | /// A `sealed class` (in other languages) or abstract class in Dart representing different theme states 4 | /// for a desktop application. This class makes it easy to distinguish between dark and light themes. 5 | abstract class DesktopThemeState extends Equatable { 6 | /// Constructs a `DesktopThemeState` with the provided theme information. 7 | /// 8 | /// - `isDarkTheme`: A boolean value indicating whether the theme is dark. 9 | const DesktopThemeState({required this.isDarkTheme}); 10 | 11 | final bool isDarkTheme; 12 | 13 | @override 14 | List get props => [isDarkTheme]; 15 | } 16 | 17 | /// Represents the dark theme state. 18 | /// 19 | /// This class extends `DesktopThemeState` and sets `isDarkTheme` to `true`. 20 | final class DarkDesktopTheme extends DesktopThemeState { 21 | const DarkDesktopTheme({super.isDarkTheme = true}); 22 | } 23 | 24 | /// Represents the light theme state. 25 | /// 26 | /// This class extends `DesktopThemeState` and sets `isDarkTheme` to `false`. 27 | final class LightDesktopTheme extends DesktopThemeState { 28 | const LightDesktopTheme({super.isDarkTheme = false}); 29 | } 30 | -------------------------------------------------------------------------------- /lib/utils/common_widgets/app_bottom_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:cuberto_bottom_bar/cuberto_bottom_bar.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class AppBottomBar extends StatelessWidget { 5 | const AppBottomBar( 6 | {super.key, 7 | required this.tabs, 8 | required this.selectedIndex, 9 | this.tabChangedCallback}); 10 | 11 | final List tabs; 12 | final ValueChanged? tabChangedCallback; 13 | final int selectedIndex; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return CubertoBottomBar( 18 | key: const Key("BottomBar"), 19 | barShadow: const [BoxShadow(blurRadius: 0)], 20 | selectedTab: selectedIndex, 21 | tabs: tabs.map((e) => TabData(iconData: e.icon, title: e.title)).toList(), 22 | inactiveIconColor: Theme.of(context).colorScheme.onSurface, 23 | barBackgroundColor: Theme.of(context).colorScheme.surface, 24 | textColor: Theme.of(context).colorScheme.onPrimary, 25 | onTabChangedListener: (int position, String title, Color? tabColor) { 26 | tabChangedCallback?.call(position); 27 | }, 28 | ); 29 | } 30 | } 31 | 32 | typedef BottomBarItem = ({IconData icon, String title}); 33 | -------------------------------------------------------------------------------- /.github/workflows/commit_push.yml: -------------------------------------------------------------------------------- 1 | name: Commit Push 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - '*' 8 | 9 | env: 10 | flutter_version: "3.22.0" 11 | java_version: "12.x" 12 | 13 | permissions: 14 | contents: read 15 | issues: write 16 | pull-requests: write 17 | discussions: write 18 | 19 | jobs: 20 | 21 | check-branch: 22 | runs-on: ubuntu-latest 23 | name: Check branch 24 | steps: 25 | - uses: actions/checkout@v3 26 | - name: Cache Flutter SDK 27 | uses: actions/cache@v3 28 | with: 29 | path: /opt/hostedtoolcache/flutter 30 | key: ${{ runner.OS }}-flutter-install-cache-${{ env.flutter_version }} 31 | - uses: subosito/flutter-action@v2 32 | with: 33 | flutter-version: ${{ env.flutter_version }} 34 | - run: flutter --version 35 | 36 | - name: Install dependencies 37 | run: flutter pub get 38 | 39 | - name: Analyze project source 40 | run: flutter analyze 41 | 42 | - name: Run tests 43 | run: flutter test 44 | 45 | - name: Run dart publish (dry run) 46 | if: ${{ github.event.pull_request.base.ref == 'main' }} 47 | run: flutter pub publish --dry-run 48 | -------------------------------------------------------------------------------- /lib/features/logger/ui/logs_list/models/logs_action.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:infospect/utils/models/action_model.dart'; 3 | 4 | /// Enum that lists the types of actions that can be performed related to logs. 5 | enum LogsActionType { 6 | level, 7 | share, 8 | clear, 9 | } 10 | 11 | /// An abstract class that provides data models for actions related to logs. 12 | abstract class LogsAction { 13 | static ActionModel get filterModel { 14 | return ActionModel( 15 | icon: Icons.filter_alt_outlined, 16 | actions: [ 17 | PopupAction( 18 | id: LogsActionType.level, 19 | name: "Log Level", 20 | subActions: DiagnosticLevel.values 21 | .map( 22 | (e) => PopupAction( 23 | id: e.name, 24 | name: e.name.toUpperCase(), 25 | ), 26 | ) 27 | .toList(), 28 | ), 29 | ], 30 | ); 31 | } 32 | 33 | static ActionModel get menuModel { 34 | return ActionModel( 35 | icon: Icons.more_vert, 36 | actions: [ 37 | const PopupAction( 38 | id: LogsActionType.share, 39 | name: "Share", 40 | ), 41 | const PopupAction( 42 | id: LogsActionType.clear, 43 | name: "Clear", 44 | ), 45 | ], 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /example/windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"example", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /lib/features/network/ui/list/bloc/networks_list_state.dart: -------------------------------------------------------------------------------- 1 | part of 'networks_list_bloc.dart'; 2 | 3 | final class NetworksListState extends Equatable { 4 | const NetworksListState({ 5 | this.calls = const [], 6 | this.filteredCalls = const [], 7 | this.searchedText = '', 8 | this.filters = const [], 9 | }); 10 | 11 | final List calls; 12 | final List filteredCalls; 13 | final String searchedText; 14 | final List filters; 15 | 16 | @override 17 | List get props => [calls, searchedText, filteredCalls, filters]; 18 | 19 | NetworksListState copyWith({ 20 | List? calls, 21 | List? filteredCalls, 22 | String? searchedText, 23 | List? filters, 24 | }) { 25 | return NetworksListState( 26 | calls: calls ?? this.calls, 27 | filteredCalls: filteredCalls ?? this.filteredCalls, 28 | searchedText: searchedText ?? this.searchedText, 29 | filters: filters ?? this.filters, 30 | ); 31 | } 32 | } 33 | 34 | final class CompressedNetworkCallLogsFile extends NetworksListState { 35 | const CompressedNetworkCallLogsFile({ 36 | super.calls, 37 | super.filteredCalls, 38 | super.searchedText, 39 | super.filters, 40 | required this.sharableFile, 41 | }); 42 | 43 | final File sharableFile; 44 | 45 | @override 46 | List get props => super.props..add(sharableFile); 47 | } 48 | -------------------------------------------------------------------------------- /lib/features/network/ui/details/widgets/details_row_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class DetailsRowWidget extends StatelessWidget { 4 | final String name; 5 | final String value; 6 | final String? other; 7 | final bool showDivider; 8 | 9 | const DetailsRowWidget(this.name, this.value, 10 | {super.key, this.other, this.showDivider = true}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Column( 15 | crossAxisAlignment: CrossAxisAlignment.start, 16 | children: [ 17 | const SizedBox(height: 8), 18 | if (name.isNotEmpty) ...[ 19 | SelectableText( 20 | name, 21 | style: Theme.of(context).textTheme.titleMedium, 22 | ), 23 | const SizedBox(height: 4), 24 | ], 25 | SelectableText( 26 | value, 27 | style: Theme.of(context).textTheme.bodyMedium, 28 | ), 29 | if (other != null) ...[ 30 | const SizedBox(height: 4), 31 | SelectableText( 32 | other ?? '', 33 | style: Theme.of(context).textTheme.bodyMedium, 34 | ), 35 | ], 36 | const SizedBox(height: 8), 37 | Container( 38 | height: 1, 39 | width: double.maxFinite, 40 | color: showDivider 41 | ? Theme.of(context).colorScheme.outline 42 | : Colors.transparent, 43 | ), 44 | ], 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/features/network/ui/list/bloc/networks_list_event.dart: -------------------------------------------------------------------------------- 1 | part of 'networks_list_bloc.dart'; 2 | 3 | sealed class NetworksListEvent extends Equatable { 4 | const NetworksListEvent(); 5 | } 6 | 7 | final class CallsChanged extends NetworksListEvent { 8 | final List calls; 9 | 10 | const CallsChanged({required this.calls}); 11 | 12 | @override 13 | List get props => [calls]; 14 | } 15 | 16 | final class NetworkLogsSearched extends NetworksListEvent { 17 | final String text; 18 | 19 | const NetworkLogsSearched({required this.text}); 20 | 21 | @override 22 | List get props => [text]; 23 | } 24 | 25 | final class NetworkLogsFilterAdded extends NetworksListEvent { 26 | final PopupAction action; 27 | 28 | const NetworkLogsFilterAdded({required this.action}); 29 | 30 | @override 31 | List get props => [action]; 32 | } 33 | 34 | final class NetworkLogsFilterRemoved extends NetworksListEvent { 35 | final PopupAction action; 36 | 37 | const NetworkLogsFilterRemoved({required this.action}); 38 | 39 | @override 40 | List get props => [action]; 41 | } 42 | 43 | final class ShareNetworkLogsClicked extends NetworksListEvent { 44 | const ShareNetworkLogsClicked(); 45 | 46 | @override 47 | List get props => []; 48 | } 49 | 50 | final class ClearNetworkLogsClicked extends NetworksListEvent { 51 | const ClearNetworkLogsClicked(); 52 | 53 | @override 54 | List get props => []; 55 | } 56 | -------------------------------------------------------------------------------- /lib/utils/common_widgets/app_search_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class AppSearchBar extends StatelessWidget { 5 | const AppSearchBar({ 6 | super.key, 7 | required this.controller, 8 | required this.focusNode, 9 | required this.onChanged, 10 | this.isDesktop = false, 11 | }); 12 | 13 | final TextEditingController controller; 14 | final FocusNode focusNode; 15 | final ValueChanged onChanged; 16 | final bool isDesktop; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return SizedBox( 21 | height: isDesktop ? 28 : null, 22 | child: CupertinoSearchTextField( 23 | padding: isDesktop 24 | ? EdgeInsets.zero 25 | : const EdgeInsetsDirectional.fromSTEB(5.5, 8, 5.5, 8), 26 | style: isDesktop 27 | ? Theme.of(context).textTheme.labelSmall 28 | : Theme.of(context).textTheme.labelLarge, 29 | placeholderStyle: isDesktop 30 | ? Theme.of(context) 31 | .textTheme 32 | .labelSmall 33 | ?.copyWith(color: Colors.grey) 34 | : null, 35 | controller: controller, 36 | focusNode: focusNode, 37 | itemSize: isDesktop ? 16 : 20, 38 | prefixInsets: isDesktop 39 | ? const EdgeInsetsDirectional.only(start: 8, end: 4, bottom: 2) 40 | : const EdgeInsetsDirectional.fromSTEB(6, 0, 0, 3), 41 | onChanged: (value) { 42 | onChanged.call(value); 43 | }, 44 | ), 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/infospect_test.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs, sort_constructors_first 2 | import 'package:bloc_test/bloc_test.dart'; 3 | import 'package:equatable/equatable.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | 6 | void main() { 7 | blocTest( 8 | 'emits [MyState] when MyEvent is added.', 9 | build: () => TestBloc(), 10 | act: (bloc) => bloc.add(const TestEvent(1)), 11 | expect: () => const [ 12 | TestState([ 13 | A([B(1)]) 14 | ]) 15 | ], 16 | ); 17 | } 18 | 19 | class TestBloc extends Bloc { 20 | TestBloc() 21 | : super(const TestState([ 22 | A([B(0)]) 23 | ])) { 24 | on( 25 | (event, emit) { 26 | emit(state.copyWith(a: [ 27 | A([B(event.id)]) 28 | ])); 29 | }, 30 | ); 31 | } 32 | } 33 | 34 | class TestEvent { 35 | final int id; 36 | const TestEvent(this.id); 37 | } 38 | 39 | class TestState extends Equatable { 40 | final List a; 41 | 42 | const TestState(this.a); 43 | 44 | TestState copyWith({List? a}) { 45 | return TestState(a ?? this.a); 46 | } 47 | 48 | @override 49 | List get props => [a]; 50 | } 51 | 52 | class A extends Equatable { 53 | final List b; 54 | const A(this.b); 55 | @override 56 | List get props => [b]; 57 | 58 | @override 59 | String toString() { 60 | return 'A{b: $b}'; 61 | } 62 | } 63 | 64 | class B extends Equatable { 65 | final int id; 66 | const B(this.id); 67 | 68 | @override 69 | List get props => [id]; 70 | } 71 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /example/macos/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - desktop_multi_window (0.0.1): 3 | - FlutterMacOS 4 | - FlutterMacOS (1.0.0) 5 | - package_info_plus (0.0.1): 6 | - FlutterMacOS 7 | - path_provider_foundation (0.0.1): 8 | - Flutter 9 | - FlutterMacOS 10 | - share_plus (0.0.1): 11 | - FlutterMacOS 12 | 13 | DEPENDENCIES: 14 | - desktop_multi_window (from `Flutter/ephemeral/.symlinks/plugins/desktop_multi_window/macos`) 15 | - FlutterMacOS (from `Flutter/ephemeral`) 16 | - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) 17 | - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) 18 | - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) 19 | 20 | EXTERNAL SOURCES: 21 | desktop_multi_window: 22 | :path: Flutter/ephemeral/.symlinks/plugins/desktop_multi_window/macos 23 | FlutterMacOS: 24 | :path: Flutter/ephemeral 25 | package_info_plus: 26 | :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos 27 | path_provider_foundation: 28 | :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin 29 | share_plus: 30 | :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos 31 | 32 | SPEC CHECKSUMS: 33 | desktop_multi_window: 566489c048b501134f9d7fb6a2354c60a9126486 34 | FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 35 | package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c 36 | path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 37 | share_plus: 36537c04ce0c3e3f5bd297ce4318b6d5ee5fd6cf 38 | 39 | PODFILE CHECKSUM: f4e9b4167f25de2cc4d7731c862661b6e734da0d 40 | 41 | COCOAPODS: 1.15.2 42 | -------------------------------------------------------------------------------- /lib/helpers/log_helper.dart: -------------------------------------------------------------------------------- 1 | part of 'infospect_helper.dart'; 2 | 3 | /// `InfospectLogHelper` is a utility class designed to assist `Infospect` in handling logging tasks. 4 | /// This class acts as an intermediary between `Infospect` and its logger, encapsulating 5 | /// the logic for adding logs, aggregating multiple logs, and clearing all logs. 6 | class InfospectLogHelper { 7 | /// The private constructor for the `InfospectLogHelper` class. 8 | /// 9 | /// - `infospect`: The reference to the parent `Infospect` instance. 10 | const InfospectLogHelper._(Infospect infospect) : _infospect = infospect; 11 | 12 | final Infospect _infospect; 13 | 14 | /// Adds a single `InfospectLog` entry to the logger of the `Infospect` instance. 15 | /// 16 | /// - `log`: The log entry to be added. 17 | void addLog(InfospectLog log) { 18 | // Adds the log entry to the logger. 19 | _infospect.infospectLogger.add(log); 20 | 21 | // Sends the log to any potential subscribers or handlers. 22 | _infospect.sendLogs([log]); 23 | } 24 | 25 | /// Aggregates and adds multiple `InfospectLog` entries to the logger of the `Infospect` instance. 26 | /// 27 | /// - `logs`: The list of log entries to be added. 28 | void addLogs(List logs) { 29 | // Appends the list of logs to the logger. 30 | _infospect.infospectLogger.logs.addAll(logs); 31 | 32 | // Sends the logs to any potential subscribers or handlers. 33 | _infospect.sendLogs(logs); 34 | } 35 | 36 | /// Clears all the logs from the logger of the `Infospect` instance. 37 | void clearAllLogs() { 38 | _infospect.infospectLogger.logs.clear(); 39 | _infospect.infospectLogger.clear(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/features/network/ui/list/desktop_components/state_helpers.dart: -------------------------------------------------------------------------------- 1 | extension ListExt on List { 2 | double width(CellType cell) { 3 | final CellType cellId = 4 | CellType.values.firstWhere((element) => element == cell); 5 | return this[cellId.id].width; 6 | } 7 | } 8 | 9 | class DataCellState { 10 | final double minWidth; 11 | final double maxWidth; 12 | final double width; 13 | final int id; 14 | final String label; 15 | 16 | const DataCellState({ 17 | this.width = 75, 18 | this.minWidth = 50, 19 | this.maxWidth = 100, 20 | required this.id, 21 | required this.label, 22 | }); 23 | 24 | DataCellState copyWith({ 25 | double? minWidth, 26 | double? maxWidth, 27 | double? width, 28 | int? id, 29 | String? label, 30 | }) { 31 | return DataCellState( 32 | minWidth: minWidth ?? this.minWidth, 33 | maxWidth: maxWidth ?? this.maxWidth, 34 | width: width ?? this.width, 35 | id: id ?? this.id, 36 | label: label ?? this.label, 37 | ); 38 | } 39 | } 40 | 41 | enum CellType { 42 | columnState(0, '', minWidth: 26, maxWidth: 26), 43 | columnId(1, 'Id', minWidth: 30, maxWidth: 40), 44 | columnUrl(2, 'Url', maxWidth: 400, minWidth: 300), 45 | columnClient(3, 'Client'), 46 | columnMethod(4, 'Method'), 47 | columnStatus(5, 'Status'), 48 | columnCode(6, 'Code'), 49 | columnTime(7, 'Time', minWidth: 80, maxWidth: 200), 50 | columnDuration(8, 'Duration'), 51 | columnSecure(9, 'Secure'); 52 | 53 | final int id; 54 | final String label; 55 | final double minWidth; 56 | final double maxWidth; 57 | const CellType(this.id, this.label, 58 | {this.minWidth = 50, this.maxWidth = 100}); 59 | } 60 | -------------------------------------------------------------------------------- /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/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 14 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /lib/utils/extensions/infospect_network/network_response_extension.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:infospect/features/network/models/infospect_network_response.dart'; 5 | 6 | extension NetworkResponseExtension on InfospectNetworkResponse { 7 | String get statusString { 8 | int status = this.status ?? -1; 9 | if (status == -1) { 10 | return "ERR"; 11 | } else if (status < 200) { 12 | return status.toString(); 13 | } else if (status >= 200 && status < 300) { 14 | return "$status OK"; 15 | } else if (status >= 300 && status < 400) { 16 | return status.toString(); 17 | } else if (status >= 400 && status < 600) { 18 | return status.toString(); 19 | } else { 20 | return "ERR"; 21 | } 22 | } 23 | 24 | Color? getStatusTextColor(BuildContext context) { 25 | int status = this.status ?? -1; 26 | if (status == -1) { 27 | return Colors.red[400]; 28 | } else if (status < 200) { 29 | return Theme.of(context).textTheme.bodyLarge!.color; 30 | } else if (status >= 200 && status < 300) { 31 | return Colors.green[400]; 32 | } else if (status >= 300 && status < 400) { 33 | return Colors.orange[400]; 34 | } else if (status >= 400 && status < 600) { 35 | return Colors.red[400]; 36 | } else { 37 | return Theme.of(context).textTheme.bodyLarge!.color; 38 | } 39 | } 40 | 41 | Map get bodyMap { 42 | if (body is String && body.toString().isNotEmpty) { 43 | try { 44 | return jsonDecode(body) as Map; 45 | } catch (_) { 46 | return {}; 47 | } 48 | } else if (body is Map) { 49 | return (body as Map).cast(); 50 | } 51 | return {}; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/features/network/models/infospect_network_error.dart: -------------------------------------------------------------------------------- 1 | /// Represents an Network error for the Infospect application. 2 | class InfospectNetworkError { 3 | /// The error object. 4 | final dynamic error; 5 | 6 | /// The stack trace associated with the error, if available. 7 | final StackTrace? stackTrace; 8 | 9 | /// Creates an instance of the `InfospectNetworkError` class. 10 | /// 11 | /// Parameters: 12 | /// - [error]: The error object (can be of any type). 13 | /// - [stackTrace]: The stack trace associated with the error (optional). 14 | const InfospectNetworkError({ 15 | this.error, 16 | this.stackTrace, 17 | }); 18 | 19 | /// Converts the `InfospectNetworkError` object into a Map representation. 20 | /// 21 | /// Returns a Map with the following key-value pairs: 22 | /// - 'error': The string representation of the error object using the `toString()` method. 23 | /// - 'stackTrace': The string representation of the stack trace, if available, using `toString()`. 24 | Map toMap() { 25 | return { 26 | 'error': error.toString(), 27 | 'stackTrace': stackTrace?.toString(), 28 | }; 29 | } 30 | 31 | /// Creates an instance of the `InfospectNetworkError` class from a Map representation. 32 | /// 33 | /// Parameters: 34 | /// - [map]: A Map containing the key-value pairs representing the `InfospectNetworkError` object. 35 | /// 36 | /// Returns an instance of the `InfospectNetworkError` class with the data populated from the provided Map. 37 | factory InfospectNetworkError.fromMap(Map map) { 38 | return InfospectNetworkError( 39 | error: map['error'] as dynamic, 40 | stackTrace: map['stackTrace'] != null 41 | ? StackTrace.fromString(map['stackTrace']) 42 | : null, 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: f92f44110e87bad5ff168335c36da6f6053036e6 8 | channel: stable 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: f92f44110e87bad5ff168335c36da6f6053036e6 17 | base_revision: f92f44110e87bad5ff168335c36da6f6053036e6 18 | - platform: android 19 | create_revision: f92f44110e87bad5ff168335c36da6f6053036e6 20 | base_revision: f92f44110e87bad5ff168335c36da6f6053036e6 21 | - platform: ios 22 | create_revision: f92f44110e87bad5ff168335c36da6f6053036e6 23 | base_revision: f92f44110e87bad5ff168335c36da6f6053036e6 24 | - platform: linux 25 | create_revision: f92f44110e87bad5ff168335c36da6f6053036e6 26 | base_revision: f92f44110e87bad5ff168335c36da6f6053036e6 27 | - platform: macos 28 | create_revision: f92f44110e87bad5ff168335c36da6f6053036e6 29 | base_revision: f92f44110e87bad5ff168335c36da6f6053036e6 30 | - platform: web 31 | create_revision: f92f44110e87bad5ff168335c36da6f6053036e6 32 | base_revision: f92f44110e87bad5ff168335c36da6f6053036e6 33 | - platform: windows 34 | create_revision: f92f44110e87bad5ff168335c36da6f6053036e6 35 | base_revision: f92f44110e87bad5ff168335c36da6f6053036e6 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | CADisableMinimumFrameDurationOnPhone 47 | 48 | UIApplicationSupportsIndirectInputEvents 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /example/windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /lib/features/launch/screen/launch_mobile_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:infospect/features/launch/bloc/launch_bloc.dart'; 4 | import 'package:infospect/features/launch/models/navigation_tab_data.dart'; 5 | import 'package:infospect/features/logger/ui/logs_list/screen/logs_list_screen.dart'; 6 | import 'package:infospect/features/network/ui/list/screen/networks_list_screen.dart'; 7 | import 'package:infospect/infospect.dart'; 8 | 9 | class LaunchMobileScreen extends StatelessWidget { 10 | final Infospect infospect; 11 | const LaunchMobileScreen(this.infospect, {super.key}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Scaffold( 16 | body: BlocSelector( 17 | selector: (state) => state.selectedTab, 18 | builder: (context, index) { 19 | return IndexedStack( 20 | index: index, 21 | children: [ 22 | NetworksListScreen(infospect), 23 | LogsListScreen(infospect), 24 | ], 25 | ); 26 | }, 27 | ), 28 | bottomNavigationBar: const BottomNavBarWidget(), 29 | ); 30 | } 31 | } 32 | 33 | class BottomNavBarWidget extends StatelessWidget { 34 | const BottomNavBarWidget({ 35 | super.key, 36 | }); 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return BlocSelector( 41 | selector: (state) => state.selectedTab, 42 | builder: (context, index) { 43 | return AppBottomBar( 44 | selectedIndex: index, 45 | tabs: NavigationTabData.tabs, 46 | tabChangedCallback: (value) { 47 | context.read().add( 48 | TabChanged( 49 | selectedTab: value, 50 | ), 51 | ); 52 | }, 53 | ); 54 | }, 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/features/logger/ui/logs_list/screen/logs_list_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:infospect/features/logger/ui/logs_list/bloc/logs_list_bloc.dart'; 4 | import 'package:infospect/features/logger/ui/logs_list/components/log_item_widget.dart'; 5 | import 'package:infospect/features/logger/ui/logs_list/components/logs_list_app_bar.dart'; 6 | import 'package:infospect/helpers/infospect_helper.dart'; 7 | import 'package:share_plus/share_plus.dart'; 8 | 9 | class LogsListScreen extends StatelessWidget { 10 | const LogsListScreen(this.infospect, {super.key}); 11 | 12 | final Infospect infospect; 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | final logsListBloc = context.watch(); 17 | return Scaffold( 18 | appBar: LogsListAppBar( 19 | hasBottom: logsListBloc.state.filters.isNotEmpty, 20 | infospect: infospect, 21 | ), 22 | body: BlocConsumer( 23 | listenWhen: (previous, current) => current is CompressedLogsFile, 24 | listener: (context, state) { 25 | if (state is CompressedLogsFile) { 26 | if (Infospect.instance.onShareAllLogs != null) { 27 | Infospect 28 | .instance.onShareAllNetworkCalls!(state.sharableFile.path); 29 | return; 30 | } 31 | final XFile file = XFile(state.sharableFile.path); 32 | Share.shareXFiles([file]); 33 | } 34 | }, 35 | builder: (context, state) { 36 | return ListView.builder( 37 | itemCount: state.filteredLogs.length, 38 | itemBuilder: (context, index) { 39 | return LogItemWidget( 40 | log: state.filteredLogs[index], 41 | searchedText: state.searchedText, 42 | ); 43 | }, 44 | ); 45 | }, 46 | ), 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/features/network/ui/list/models/network_action.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:infospect/utils/models/action_model.dart'; 3 | 4 | enum NetworkActionType { 5 | method, 6 | status, 7 | share, 8 | clear, 9 | } 10 | 11 | abstract class NetworkAction { 12 | static ActionModel get filterModel { 13 | return ActionModel( 14 | icon: Icons.filter_alt_outlined, 15 | actions: const [ 16 | PopupAction( 17 | id: NetworkActionType.method, 18 | name: "Method", 19 | subActions: [ 20 | PopupAction( 21 | id: 'get', 22 | name: "GET", 23 | ), 24 | PopupAction( 25 | id: 'post', 26 | name: "POST", 27 | ), 28 | PopupAction( 29 | id: 'put', 30 | name: "PUT", 31 | ), 32 | PopupAction( 33 | id: 'delete', 34 | name: "DELETE", 35 | ), 36 | PopupAction( 37 | id: 'option', 38 | name: "OPTION", 39 | ), 40 | ], 41 | ), 42 | PopupAction( 43 | id: NetworkActionType.status, 44 | name: "Status", 45 | subActions: [ 46 | PopupAction( 47 | id: 'success', 48 | name: "Success", 49 | ), 50 | PopupAction( 51 | id: 'error', 52 | name: "Error", 53 | ), 54 | ], 55 | ), 56 | ], 57 | ); 58 | } 59 | 60 | static ActionModel get menuModel { 61 | return ActionModel( 62 | icon: Icons.more_vert, 63 | actions: const [ 64 | PopupAction( 65 | id: NetworkActionType.share, 66 | name: "Share", 67 | ), 68 | PopupAction( 69 | id: NetworkActionType.clear, 70 | name: "Clear", 71 | ), 72 | ], 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.1.4 2 | ##### Fixes 3 | - Bumped some outdated dependencies 4 | 5 | ## 0.1.3 6 | ##### Fixes 7 | - Bumped version of cuberto_bottom_bar to 3.1.0 to address the issue with bottom navigation bar selection index. [Fluter issue](https://github.com/flutter/flutter/issues/148983) 8 | - Back button in Desktop layout of Infospect for Tabs 9 | - Fixed width for row cell added to Desktop layout of Infospect 10 | - Upgraded the version of archive 11 | 12 | ## 0.1.2 13 | ##### Fixes 14 | - Fixes for issue [#3](https://github.com/kushalmahapatro/infospect/issues/3) 15 | 16 | ##### Added 17 | - Infospect desktop sub-window restricted to be opened only once 18 | 19 | ## 0.1.1 20 | ##### Added 21 | - Http and Dio interceptor added in example 22 | - Readme updated with InfospectInvoker functionality and usage 23 | 24 | ## 0.1.0 25 | ##### Added 26 | 27 | - Documentation 28 | - Gif preview of iOS, macOs, Linux (Ubuntu VM in mac) and Windows 10 (VM in mac) 29 | 30 | ##### Fixes 31 | 32 | - Analysis issue fixes 33 | 34 | ## 0.0.1-alpha 35 | #### Initial version. 36 | 37 | ##### Added 38 | 39 | - Network Calls 40 | - Interception for Dio and http 41 | - Filtering the calls based on Success/Error and also the method type 42 | - Sharing of respective network call with CURL data 43 | - Details screen with request , response and error 44 | - Filtering of the network call with the searched text (matching to the url) 45 | - Logs 46 | - capturing of logs and showing them in list 47 | - Filtering out the logs based on the DiagnosticLevel like info, debug, warning, error, hint, summary, fine , hidden, off. 48 | - Filtering of the logs with the searched text (matching to the log message/error/stacktrace) 49 | - Invoker 50 | - Widget to open the Infospect screen from anywhere in the app 51 | - Multi-window support in desktop and invoking the Infospect screen in the same window in mobile 52 | - Shortcut to invoke the infospect screen in Desktop app (Ctrl + Shift + I), and also an option provided to open up within the app. -------------------------------------------------------------------------------- /lib/features/logger/ui/logs_list/screen/desktop_logs_list_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:infospect/features/logger/ui/logs_list/bloc/logs_list_bloc.dart'; 4 | import 'package:infospect/features/logger/ui/logs_list/components/log_item_widget.dart'; 5 | import 'package:infospect/features/logger/ui/logs_list/components/logs_list_app_bar.dart'; 6 | import 'package:infospect/helpers/infospect_helper.dart'; 7 | import 'package:share_plus/share_plus.dart'; 8 | 9 | class DesktopLogsListScreen extends StatelessWidget { 10 | const DesktopLogsListScreen(this.infospect, {super.key}); 11 | 12 | final Infospect infospect; 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | final logsListBloc = context.watch(); 17 | return Scaffold( 18 | appBar: LogsListAppBar.desktop( 19 | hasBottom: logsListBloc.state.filters.isNotEmpty, 20 | infospect: infospect, 21 | ), 22 | body: BlocConsumer( 23 | listenWhen: (previous, current) => current is CompressedLogsFile, 24 | listener: (context, state) { 25 | if (state is CompressedLogsFile) { 26 | if (Infospect.instance.onShareAllLogs != null) { 27 | Infospect 28 | .instance.onShareAllNetworkCalls!(state.sharableFile.path); 29 | return; 30 | } 31 | final XFile file = XFile(state.sharableFile.path); 32 | Share.shareXFiles([file]); 33 | } 34 | }, 35 | builder: (context, state) { 36 | return ListView.builder( 37 | itemCount: state.filteredLogs.length, 38 | itemBuilder: (context, index) { 39 | return LogItemWidget( 40 | log: state.filteredLogs[index], 41 | searchedText: state.searchedText, 42 | ); 43 | }, 44 | ); 45 | }, 46 | ), 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.github/workflows/create_tag.yml: -------------------------------------------------------------------------------- 1 | name: Create Tag and Publish to pub.dev 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["Trigger Create Tag and Publish Workflow on PR Merge"] 6 | types: 7 | - completed 8 | 9 | jobs: 10 | placeholder-job: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Placeholder Step 16 | run: echo "This is a placeholder job" 17 | 18 | create-tag-and-publish: 19 | permissions: 20 | repository-projects: write 21 | packages: write 22 | contents: read 23 | pull-requests: write 24 | 25 | runs-on: ubuntu-latest 26 | needs: [placeholder-job] 27 | 28 | steps: 29 | - uses: actions/checkout@v3 30 | 31 | - name: Get version 32 | id: yq 33 | uses: mikefarah/yq@master 34 | with: 35 | cmd: yq -r '.version' 'pubspec.yaml' 36 | 37 | - name: Print version 38 | run: echo ${{ steps.yq.outputs.result }} 39 | 40 | - name: Create tag 41 | uses: actions/github-script@v3 42 | id: tagCreate 43 | env: 44 | TAG: 'v${{ steps.yq.outputs.result }}' 45 | with: 46 | github-token: ${{ github.token }} 47 | script: | 48 | github.git.createRef({ 49 | owner: context.repo.owner, 50 | repo: context.repo.repo, 51 | ref: "refs/tags/v${{ steps.yq.outputs.result }}", 52 | sha: context.sha 53 | }) 54 | - name: Print tag update 55 | run: echo ${{ steps.tagCreate.outputs.result }} 56 | 57 | - name: Publish 58 | uses: subosito/flutter-action@v2 59 | with: 60 | channel: 'stable' 61 | 62 | - name: Flutter Version 63 | run: flutter --version 64 | 65 | - name: Publish to Dart Package 66 | uses: k-paxian/dart-package-publisher@v1.5.1 67 | with: 68 | credentialJson: ${{ secrets.CREDENTIAL_JSON }} 69 | flutter: true 70 | skipTests: true 71 | -------------------------------------------------------------------------------- /example/windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length <= 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | example 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | return true; 35 | } 36 | 37 | void FlutterWindow::OnDestroy() { 38 | if (flutter_controller_) { 39 | flutter_controller_ = nullptr; 40 | } 41 | 42 | Win32Window::OnDestroy(); 43 | } 44 | 45 | LRESULT 46 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 47 | WPARAM const wparam, 48 | LPARAM const lparam) noexcept { 49 | // Give Flutter, including plugins, an opportunity to handle window messages. 50 | if (flutter_controller_) { 51 | std::optional result = 52 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 53 | lparam); 54 | if (result) { 55 | return *result; 56 | } 57 | } 58 | 59 | switch (message) { 60 | case WM_FONTCHANGE: 61 | flutter_controller_->engine()->ReloadSystemFonts(); 62 | break; 63 | } 64 | 65 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 66 | } 67 | -------------------------------------------------------------------------------- /lib/features/network/ui/details/components/interceptor_details_error.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:infospect/features/network/models/infospect_network_call.dart'; 3 | import 'package:infospect/features/network/ui/details/widgets/details_row_widget.dart'; 4 | import 'package:infospect/features/network/ui/list/components/expansion_widget.dart'; 5 | import 'package:infospect/helpers/infospect_helper.dart'; 6 | import 'package:infospect/utils/common_widgets/conditional_widget.dart'; 7 | 8 | class InterceptorDetailsError extends StatelessWidget { 9 | final InfospectNetworkCall call; 10 | final Infospect infospect; 11 | const InterceptorDetailsError( 12 | this.call, { 13 | super.key, 14 | required this.infospect, 15 | }); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return ConditionalWidget( 20 | condition: call.loading || call.response == null, 21 | ifTrue: const Center( 22 | child: Column( 23 | mainAxisAlignment: MainAxisAlignment.center, 24 | children: [CircularProgressIndicator(), Text("Waiting for response")], 25 | ), 26 | ), 27 | ifFalse: ListView( 28 | children: [ 29 | Column( 30 | crossAxisAlignment: CrossAxisAlignment.start, 31 | children: [ 32 | /// Errors 33 | if (call.error != null) ...[ 34 | ExpansionWidget( 35 | title: 'Error', 36 | children: [ 37 | DetailsRowWidget( 38 | 'Message', 39 | call.error?.error.toString() ?? '', 40 | showDivider: call.error?.stackTrace != null, 41 | ), 42 | if (call.error?.stackTrace != null) 43 | DetailsRowWidget( 44 | 'Stack Trace', 45 | call.error?.stackTrace.toString() ?? '', 46 | showDivider: false, 47 | ), 48 | ], 49 | ), 50 | ], 51 | ], 52 | ), 53 | ], 54 | ), 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /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 | namespace "com.example.example" 30 | compileSdkVersion flutter.compileSdkVersion 31 | ndkVersion flutter.ndkVersion 32 | 33 | compileOptions { 34 | sourceCompatibility JavaVersion.VERSION_1_8 35 | targetCompatibility JavaVersion.VERSION_1_8 36 | } 37 | 38 | kotlinOptions { 39 | jvmTarget = '1.8' 40 | } 41 | 42 | sourceSets { 43 | main.java.srcDirs += 'src/main/kotlin' 44 | } 45 | 46 | defaultConfig { 47 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 48 | applicationId "com.example.example" 49 | minSdkVersion 19 50 | targetSdkVersion flutter.targetSdkVersion 51 | versionCode flutterVersionCode.toInteger() 52 | versionName flutterVersionName 53 | } 54 | 55 | buildTypes { 56 | release { 57 | // TODO: Add your own signing config for the release build. 58 | // Signing with the debug keys for now, so `flutter run --release` works. 59 | signingConfig signingConfigs.debug 60 | } 61 | } 62 | } 63 | 64 | flutter { 65 | source '../..' 66 | } 67 | 68 | dependencies { 69 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 70 | } 71 | -------------------------------------------------------------------------------- /example/macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.14' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | target 'RunnerTests' do 35 | inherit! :search_paths 36 | end 37 | end 38 | 39 | post_install do |installer| 40 | installer.pods_project.targets.each do |target| 41 | flutter_additional_macos_build_settings(target) 42 | end 43 | 44 | installer.aggregate_targets.each do |target| 45 | target.xcconfigs.each do |variant, xcconfig| 46 | xcconfig_path = target.client_root + target.xcconfig_relative_path(variant) 47 | IO.write(xcconfig_path, IO.read(xcconfig_path).gsub("DT_TOOLCHAIN_DIR", "TOOLCHAIN_DIR")) 48 | end 49 | end 50 | 51 | installer.pods_project.targets.each do |target| 52 | target.build_configurations.each do |config| 53 | if config.base_configuration_reference.is_a? Xcodeproj::Project::Object::PBXFileReference 54 | xcconfig_path = config.base_configuration_reference.real_path 55 | IO.write(xcconfig_path, IO.read(xcconfig_path).gsub("DT_TOOLCHAIN_DIR", "TOOLCHAIN_DIR")) 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/features/logger/ui/logs_list/bloc/logs_list_event.dart: -------------------------------------------------------------------------------- 1 | part of 'logs_list_bloc.dart'; 2 | 3 | /// `LogsListEvent` is a sealed class representing various events related 4 | /// to logs management. These events are used by the `LogsListBloc` to 5 | /// manage the logs state. 6 | sealed class LogsListEvent { 7 | const LogsListEvent(); 8 | } 9 | 10 | /// Event triggered when logs are changed. 11 | /// 12 | /// This event should be dispatched with the new list of logs. 13 | final class LogsChanged extends LogsListEvent { 14 | /// List of updated logs. 15 | final List logs; 16 | 17 | /// Creates a new instance of `LogsChanged`. 18 | /// 19 | /// - [logs]: The updated logs list. 20 | const LogsChanged({required this.logs}); 21 | } 22 | 23 | /// Event triggered when a search is performed on logs. 24 | final class TextSearched extends LogsListEvent { 25 | /// The text used for searching within the logs. 26 | final String text; 27 | 28 | /// Creates a new instance of `TextSearched`. 29 | /// 30 | /// - [text]: The text to search. 31 | const TextSearched({required this.text}); 32 | } 33 | 34 | /// Event triggered when a filter is added to the logs list. 35 | final class LogsFilterAdded extends LogsListEvent { 36 | /// The filter action to be added. 37 | final PopupAction action; 38 | 39 | /// Creates a new instance of `LogsFilterAdded`. 40 | /// 41 | /// - [action]: The filter action to be added. 42 | const LogsFilterAdded({required this.action}); 43 | } 44 | 45 | /// Event triggered when a filter is removed from the logs list. 46 | final class LogsFilterRemoved extends LogsListEvent { 47 | /// The filter action to be removed. 48 | final PopupAction action; 49 | 50 | /// Creates a new instance of `LogsFilterRemoved`. 51 | /// 52 | /// - [action]: The filter action to be removed. 53 | const LogsFilterRemoved({required this.action}); 54 | } 55 | 56 | /// Event triggered when a request is made to share all logs. 57 | final class ShareAllLogsClicked extends LogsListEvent { 58 | /// Creates a new instance of `ShareAllLogsClicked`. 59 | const ShareAllLogsClicked(); 60 | } 61 | 62 | /// Event triggered when a request is made to clear all logs. 63 | final class ClearAllLogsClicked extends LogsListEvent { 64 | /// Creates a new instance of `ClearAllLogsClicked`. 65 | const ClearAllLogsClicked(); 66 | } 67 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '12.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 | target 'RunnerTests' do 36 | inherit! :search_paths 37 | end 38 | end 39 | 40 | 41 | post_install do |installer| 42 | installer.pods_project.targets.each do |target| 43 | flutter_additional_ios_build_settings(target) 44 | end 45 | 46 | installer.aggregate_targets.each do |target| 47 | target.xcconfigs.each do |variant, xcconfig| 48 | xcconfig_path = target.client_root + target.xcconfig_relative_path(variant) 49 | IO.write(xcconfig_path, IO.read(xcconfig_path).gsub("DT_TOOLCHAIN_DIR", "TOOLCHAIN_DIR")) 50 | end 51 | end 52 | 53 | installer.pods_project.targets.each do |target| 54 | target.build_configurations.each do |config| 55 | if config.base_configuration_reference.is_a? Xcodeproj::Project::Object::PBXFileReference 56 | xcconfig_path = config.base_configuration_reference.real_path 57 | IO.write(xcconfig_path, IO.read(xcconfig_path).gsub("DT_TOOLCHAIN_DIR", "TOOLCHAIN_DIR")) 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: infospect 2 | description: Infospect meaning Information Inspector is a Flutter plugin for that empowers the developers to debug the network calls and logs in the app. 3 | version: 0.1.4 4 | repository: https://github.com/kushalmahapatro/infospect 5 | issue_tracker: https://github.com/kushalmahapatro/infospect/issues 6 | topics: 7 | - interceptor 8 | - dio 9 | - http 10 | - information-inspector 11 | - multi-window 12 | 13 | environment: 14 | sdk: ">=3.0.0 <4.0.0" 15 | flutter: ">=1.17.0" 16 | 17 | screenshots: 18 | - description: "Infospect Desktop Multi Window- macOS" 19 | path: images/screenshot/desktop_multi_window.png 20 | - description: "Infospect Desktop Multi Window- Linux" 21 | path: images/screenshot/linux.png 22 | - description: "Infospect Desktop Multi Window- Windows" 23 | path: images/screenshot/windows.png 24 | - description: "Infospect network calls screen - Mobile" 25 | path: images/screenshot/network_calls_screen.png 26 | - description: "Infospect logs screen - Mobile" 27 | path: images/screenshot/logs.png 28 | - description: "Infospect network calls screen - Desktop" 29 | path: images/screenshot/network_calls_screen_desktop.png 30 | - description: "Infospect logs screen - Desktop" 31 | path: images/screenshot/logs_desktop.png 32 | - description: "Infospect invoker open state" 33 | path: images/screenshot/expanded.png 34 | - description: "Infospect share UI - Mobile" 35 | path: images/screenshot/share.png 36 | - description: "Infospect share UI - Desktop" 37 | path: images/screenshot/share_desktop.png 38 | 39 | dependencies: 40 | # sdk 41 | flutter: 42 | sdk: flutter 43 | flutter_localizations: 44 | sdk: flutter 45 | 46 | archive: ^3.6.1 47 | bloc: ^8.1.4 48 | collection: ^1.18.0 49 | cuberto_bottom_bar: ^3.1.0 50 | cupertino_icons: ^1.0.8 51 | desktop_multi_window: ^0.2.0 52 | dio: ^5.6.0 53 | equatable: ^2.0.5 54 | flutter_bloc: ^8.1.6 55 | flutter_json_view: ^1.1.2 56 | font_awesome_flutter: ^10.7.0 57 | http: ^1.2.2 58 | menu_bar: ^0.5.3 59 | meta: ^1.12.0 60 | package_info_plus: ^8.0.2 61 | path: ^1.9.0 62 | path_provider: ^2.1.4 63 | rxdart: ^0.28.0 64 | share_plus: ^10.0.1 65 | 66 | dev_dependencies: 67 | flutter_test: 68 | sdk: flutter 69 | flutter_lints: ^4.0.0 70 | bloc_test: ^9.1.7 71 | lints: ^4.0.0 72 | -------------------------------------------------------------------------------- /lib/utils/common_widgets/app_adaptive_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | /// A utility class that provides a static method to show platform-specific alert dialogs. 7 | class AppAdaptiveDialog { 8 | static void show( 9 | BuildContext context, { 10 | required String tag, 11 | required String title, 12 | required String body, 13 | required VoidCallback onPositiveActionClick, 14 | }) { 15 | /// Displays a platform-specific alert dialog. 16 | /// 17 | /// - `context`: BuildContext required for showing the dialog. 18 | /// - `tag`: A unique identifier for the dialog. 19 | /// - `title`: The title of the dialog. 20 | /// - `body`: The body content of the dialog. 21 | /// - `onPositiveActionClick`: A callback that gets executed when the positive action button is clicked. 22 | showDialog( 23 | context: context, 24 | builder: (context) { 25 | if (Platform.isMacOS || Platform.isIOS) { 26 | return CupertinoAlertDialog( 27 | title: Text(title), 28 | content: Text(body), 29 | actions: [ 30 | TextButton( 31 | onPressed: () => Navigator.pop(context, '${tag}_cancel'), 32 | child: const Text('Cancel'), 33 | ), 34 | TextButton( 35 | onPressed: () { 36 | onPositiveActionClick.call(); 37 | Navigator.pop(context, '${tag}_clear'); 38 | }, 39 | child: const Text('Go ahead'), 40 | ), 41 | ], 42 | ); 43 | } else { 44 | return AlertDialog( 45 | title: Text(title), 46 | content: Text(body), 47 | actions: [ 48 | TextButton( 49 | onPressed: () => Navigator.pop(context, '${tag}_cancel'), 50 | child: const Text('Cancel'), 51 | ), 52 | TextButton( 53 | onPressed: () { 54 | onPositiveActionClick.call(); 55 | Navigator.pop(context, '${tag}_clear'); 56 | }, 57 | child: const Text('Go ahead'), 58 | ), 59 | ], 60 | ); 61 | } 62 | }, 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/features/network/ui/list/desktop_components/draggable_cell.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:infospect/features/network/ui/list/desktop_components/desktop_call_list_states.dart'; 3 | 4 | class DraggableCell extends StatefulWidget { 5 | const DraggableCell({ 6 | required this.text, 7 | required this.onColumnWidthChanged, 8 | this.maxWidth = 100, 9 | this.minWidth = 50, 10 | this.width, 11 | super.key, 12 | }); 13 | 14 | final String text; 15 | final ValueChanged onColumnWidthChanged; 16 | final double minWidth; 17 | final double maxWidth; 18 | final double? width; 19 | 20 | @override 21 | State createState() => _DraggableCellState(); 22 | } 23 | 24 | class _DraggableCellState extends DraggableCellState { 25 | @override 26 | Widget build(BuildContext context) { 27 | if (widget.width != null) { 28 | columnWidth = widget.width!; 29 | } 30 | return SizedBox( 31 | width: columnWidth, 32 | child: Row( 33 | children: [ 34 | Expanded( 35 | child: Container( 36 | margin: const EdgeInsetsDirectional.only(start: 4), 37 | alignment: AlignmentDirectional.centerStart, 38 | child: Text( 39 | widget.text, 40 | maxLines: 1, 41 | overflow: TextOverflow.ellipsis, 42 | ), // Header text 43 | ), 44 | ), 45 | if (minimumColumnWidth != maximumColumnWidth) 46 | GestureDetector( 47 | onPanStart: (details) => updateInitX(details.globalPosition.dx), 48 | onPanUpdate: updateColumnWidth, 49 | child: MouseRegion( 50 | cursor: columnWidth == minimumColumnWidth 51 | ? SystemMouseCursors.resizeRight 52 | : columnWidth == maximumColumnWidth 53 | ? SystemMouseCursors.resizeLeft 54 | : SystemMouseCursors.resizeLeftRight, 55 | child: Container( 56 | color: Colors.black, 57 | width: 1, 58 | margin: const EdgeInsetsDirectional.only( 59 | top: 4, 60 | bottom: 4, 61 | start: 8, 62 | ), 63 | height: double.infinity, 64 | ), 65 | ), 66 | ) 67 | ], 68 | ), 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/features/network/ui/list/screen/networks_list_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:infospect/features/network/models/infospect_network_call.dart'; 4 | import 'package:infospect/features/network/ui/list/bloc/networks_list_bloc.dart'; 5 | import 'package:infospect/features/network/ui/list/components/network_call_app_bar.dart'; 6 | import 'package:infospect/features/network/ui/list/components/network_call_item.dart'; 7 | import 'package:infospect/helpers/infospect_helper.dart'; 8 | import 'package:infospect/routes/routes.dart'; 9 | import 'package:share_plus/share_plus.dart'; 10 | 11 | class NetworksListScreen extends StatelessWidget { 12 | final Infospect infospect; 13 | const NetworksListScreen(this.infospect, {super.key}); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | final networkListBloc = context.watch(); 18 | 19 | return Scaffold( 20 | appBar: NetworkCallAppBar( 21 | hasBottom: networkListBloc.state.filters.isNotEmpty, 22 | infospect: infospect, 23 | ), 24 | body: BlocConsumer( 25 | listenWhen: (previous, current) => 26 | current is CompressedNetworkCallLogsFile, 27 | listener: (context, state) { 28 | if (state is CompressedNetworkCallLogsFile) { 29 | if (Infospect.instance.onShareAllNetworkCalls != null) { 30 | Infospect 31 | .instance.onShareAllNetworkCalls!(state.sharableFile.path); 32 | return; 33 | } 34 | final XFile file = XFile(state.sharableFile.path); 35 | Share.shareXFiles([file]); 36 | } 37 | }, 38 | builder: (context, state) { 39 | if (state.filteredCalls.isEmpty) { 40 | return const Center(child: Text("No network calls")); 41 | } 42 | 43 | return ListView.builder( 44 | itemCount: state.filteredCalls.length, 45 | itemBuilder: (context, index) { 46 | return NetworkCallItem( 47 | networkCall: state.filteredCalls[index], 48 | searchedText: state.searchedText, 49 | onItemClicked: (InfospectNetworkCall call) { 50 | mobileRoutes.logsList(context, infospect, call); 51 | }, 52 | ); 53 | }, 54 | ); 55 | }, 56 | ), 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/routes/routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/scheduler.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:infospect/features/launch/screen/infospect_launch_screen.dart'; 5 | import 'package:infospect/features/network/models/infospect_network_call.dart'; 6 | import 'package:infospect/features/network/ui/details/bloc/interceptor_details_bloc.dart'; 7 | import 'package:infospect/features/network/ui/details/screen/interceptor_details_screen.dart'; 8 | import 'package:infospect/features/network/ui/raw_data_viewer/screen/raw_data_viewer_screen.dart'; 9 | import 'package:infospect/infospect.dart'; 10 | 11 | class MobileRoutes { 12 | ThemeData? _themeData; 13 | set themeData(ThemeData themeData) => _themeData = themeData; 14 | 15 | MobileRoutes._(); 16 | 17 | /// launch screen 18 | Widget launch(Infospect infospect) => 19 | _themeWidget(InfospectLaunchScreen(infospect)); 20 | 21 | /// network details list 22 | Future logsList(BuildContext context, Infospect infospect, 23 | InfospectNetworkCall call) => 24 | Navigator.push( 25 | infospect.context ?? context, 26 | MaterialPageRoute( 27 | builder: (context) => BlocProvider( 28 | create: (context) => InterceptorDetailsBloc(), 29 | child: _themeWidget( 30 | InterceptorDetailsScreen(infospect, call), 31 | ), 32 | ), 33 | ), 34 | ); 35 | 36 | /// raw data screen 37 | Future rawData(BuildContext context, Map data, 38 | bool beautificationRequired) => 39 | Navigator.push( 40 | context, 41 | MaterialPageRoute( 42 | builder: (_) { 43 | return _themeWidget( 44 | RawDataViewerScreen( 45 | data: data, 46 | beautificationRequired: beautificationRequired, 47 | ), 48 | ); 49 | }, 50 | ), 51 | ); 52 | 53 | Widget _themeWidget(Widget widget) { 54 | if (_themeData == null) { 55 | final Brightness brightness = 56 | SchedulerBinding.instance.platformDispatcher.platformBrightness; 57 | _themeData = InfospectTheme.darkTheme; 58 | if (brightness == Brightness.light) { 59 | _themeData = InfospectTheme.lightTheme; 60 | } 61 | } 62 | 63 | return Theme( 64 | data: _themeData!, 65 | child: widget, 66 | ); 67 | } 68 | } 69 | 70 | final mobileRoutes = MobileRoutes._(); 71 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/features/network/ui/list/components/expansion_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:infospect/features/network/ui/details/widgets/details_row_widget.dart'; 4 | 5 | class ExpansionWidget extends StatelessWidget { 6 | final String title; 7 | final List children; 8 | final Widget? trailing; 9 | final bool initiallyExpanded; 10 | 11 | factory ExpansionWidget.map({ 12 | required Map map, 13 | required String title, 14 | Widget? trailing, 15 | bool initiallyExpanded = true, 16 | Key? key, 17 | }) { 18 | return ExpansionWidget( 19 | key: key, 20 | title: title, 21 | trailing: trailing, 22 | initiallyExpanded: initiallyExpanded, 23 | children: [ 24 | ...map.entries.mapIndexed( 25 | (i, e) => DetailsRowWidget( 26 | e.key, 27 | e.value.toString(), 28 | showDivider: i != map.length - 1, 29 | ), 30 | ) 31 | ], 32 | ); 33 | } 34 | 35 | const ExpansionWidget({ 36 | super.key, 37 | required this.title, 38 | required this.children, 39 | this.trailing, 40 | this.initiallyExpanded = true, 41 | }); 42 | 43 | @override 44 | Widget build(BuildContext context) { 45 | return Container( 46 | padding: const EdgeInsets.symmetric(horizontal: 6), 47 | margin: const EdgeInsets.all(6), 48 | decoration: BoxDecoration( 49 | borderRadius: BorderRadius.circular(8), 50 | border: Border.all( 51 | color: Theme.of(context).colorScheme.onSurface, 52 | width: 1, 53 | ), 54 | ), 55 | child: ExpansionTile( 56 | initiallyExpanded: initiallyExpanded, 57 | tilePadding: const EdgeInsets.symmetric(horizontal: 8), 58 | childrenPadding: const EdgeInsets.symmetric(horizontal: 10), 59 | expandedAlignment: Alignment.center, 60 | controlAffinity: ListTileControlAffinity.platform, 61 | shape: Border.all(color: Colors.transparent), 62 | iconColor: Theme.of(context).colorScheme.primary, 63 | collapsedIconColor: Theme.of(context).colorScheme.primary, 64 | title: Row( 65 | children: [ 66 | Expanded( 67 | child: Text( 68 | title, 69 | style: Theme.of(context).textTheme.titleLarge, 70 | ), 71 | ), 72 | trailing ?? const SizedBox.shrink(), 73 | ], 74 | ), 75 | children: children, 76 | ), 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /.github/workflows/check_version.yml: -------------------------------------------------------------------------------- 1 | name: Check Version 2 | 3 | on: 4 | workflow_call: 5 | secrets: 6 | TRIGGER_NEXT_ACTION: 7 | required: true 8 | description: 'Trigger Next Action' 9 | 10 | pull_request: 11 | branches: 12 | - main 13 | 14 | jobs: 15 | check-version: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout code 20 | uses: actions/checkout@v3 21 | 22 | - name: Get version 23 | id: yq 24 | uses: mikefarah/yq@master 25 | 26 | with: 27 | cmd: yq -r '.version' 'pubspec.yaml' 28 | 29 | - name: Print version 30 | run: echo ${{ steps.yq.outputs.result }} 31 | 32 | - name: Get Latest Tag 33 | id: get_tag 34 | run: | 35 | REPO_NAME=${{ github.repository }} 36 | LATEST_TAG=$(curl -s "https://api.github.com/repos/$REPO_NAME/releases" | jq -r '.[0].tag_name') 37 | echo "latest=${LATEST_TAG}">> $GITHUB_OUTPUT 38 | 39 | - name: Print version 40 | run: echo "provided_version=${{ steps.yq.outputs.result }} and latest_tag=${{ steps.get_tag.outputs.latest }}" 41 | 42 | - name: Check Version 43 | id: check_version 44 | run: | 45 | latest_tag=${{ steps.get_tag.outputs.latest }} 46 | provided_tag=${{ steps.yq.outputs.result }} 47 | 48 | # Extract numerical version parts from the tags 49 | latest_version=$(echo "$latest_tag" | sed 's/v\?\([0-9.]*\)[-a-zA-Z]*$/\1/') 50 | provided_version=$(echo "$provided_tag" | sed 's/v\?\([0-9.]*\)[-a-zA-Z]*$/\1/') 51 | 52 | echo "latest_version=${latest_version} and provided_version=${provided_version}" 53 | echo "$provided_version" == "$latest_version" 54 | 55 | if [[ "$provided_version" == "$latest_version" ]]; then 56 | echo "Provided version is equal to latest version" 57 | echo "trigger_next_action=false">> $GITHUB_OUTPUT 58 | exit 1 59 | elif [[ "$provided_version" < "$latest_version" ]]; then 60 | echo "Provided version is less than latest version" 61 | echo "trigger_next_action=false">> $GITHUB_OUTPUT 62 | exit 1 63 | else 64 | echo "trigger_next_action=true">> $GITHUB_OUTPUT 65 | fi 66 | 67 | - name: Trigger Next Action 68 | run: | 69 | echo "Trigger Next Action ${{ steps.check_version.outputs.trigger_next_action }}" 70 | echo "set-env:TRIGGER_NEXT_ACTION=${{ steps.check_version.outputs.trigger_next_action}}" >> $GITHUB_ENV 71 | env: 72 | TRIGGER_NEXT_ACTION: ${{ steps.check_version.outputs.trigger_next_action }} -------------------------------------------------------------------------------- /lib/features/network/ui/details/components/interceptor_details_request.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:infospect/features/network/models/infospect_network_call.dart'; 4 | import 'package:infospect/features/network/ui/details/models/details_topic_data.dart'; 5 | import 'package:infospect/features/network/ui/details/widgets/details_row_widget.dart'; 6 | import 'package:infospect/features/network/ui/list/components/expansion_widget.dart'; 7 | import 'package:infospect/features/network/ui/list/components/trailing_widget.dart'; 8 | import 'package:infospect/helpers/infospect_helper.dart'; 9 | 10 | class InterceptorDetailsRequest extends StatelessWidget { 11 | final InfospectNetworkCall call; 12 | final Infospect infospect; 13 | 14 | const InterceptorDetailsRequest( 15 | this.call, { 16 | super.key, 17 | required this.infospect, 18 | }); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | final RequestDetailsTopicHelper topicHelper = 23 | RequestDetailsTopicHelper(call); 24 | 25 | return ListView( 26 | shrinkWrap: true, 27 | children: topicHelper.topics.map((e) { 28 | return switch (e.body) { 29 | /// expansion widget with map 30 | TopicDetailsBodyMap( 31 | map: Map map, 32 | trailing: TrailingData? trailing 33 | ) => 34 | _getExpansionMap(e, map, trailing), 35 | 36 | /// expansion widget with list 37 | TopicDetailsBodyList(list: List list) => 38 | _getExpansionList(e, list) 39 | }; 40 | }).toList(), 41 | ); 42 | } 43 | 44 | ExpansionWidget _getExpansionList(TopicData e, List list) { 45 | return ExpansionWidget( 46 | title: e.topic, 47 | children: list 48 | .mapIndexed( 49 | (index, e) => DetailsRowWidget( 50 | e.title, 51 | e.subtitle, 52 | other: e.other, 53 | showDivider: index != list.length - 1, 54 | ), 55 | ) 56 | .toList(), 57 | ); 58 | } 59 | 60 | ExpansionWidget _getExpansionMap( 61 | TopicData e, Map map, TrailingData? trailing) { 62 | return ExpansionWidget.map( 63 | title: e.topic, 64 | map: map, 65 | trailing: trailing != null 66 | ? TrailingWidget( 67 | text: trailing.trailing, 68 | infospect: infospect, 69 | data: trailing.data, 70 | beautificationRequired: trailing.beautificationRequired, 71 | ) 72 | : null, 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/features/network/ui/list/screen/desktop_networks_list_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:infospect/features/network/ui/details/screen/desktop_details_screen.dart'; 4 | import 'package:infospect/features/network/ui/list/bloc/networks_list_bloc.dart'; 5 | import 'package:infospect/features/network/ui/list/components/network_call_app_bar.dart'; 6 | import 'package:infospect/features/network/ui/list/desktop_components/desktop_call_list_states.dart'; 7 | import 'package:infospect/features/network/ui/list/desktop_components/draggable_table.dart'; 8 | import 'package:infospect/helpers/infospect_helper.dart'; 9 | import 'package:infospect/utils/common_widgets/divider.dart'; 10 | 11 | class DesktopNetworksListScreen extends StatefulWidget { 12 | final Infospect infospect; 13 | const DesktopNetworksListScreen(this.infospect, {super.key}); 14 | 15 | @override 16 | State createState() => 17 | _DesktopNetworksListScreenState(); 18 | } 19 | 20 | class _DesktopNetworksListScreenState 21 | extends DesktopNetworksListScreenState { 22 | @override 23 | Widget build(BuildContext context) { 24 | final networkListBloc = context.watch(); 25 | 26 | return Scaffold( 27 | appBar: NetworkCallAppBar.desktop( 28 | infospect: widget.infospect, 29 | hasBottom: networkListBloc.state.filters.isNotEmpty, 30 | ), 31 | body: Column( 32 | mainAxisAlignment: MainAxisAlignment.start, 33 | crossAxisAlignment: CrossAxisAlignment.start, 34 | children: [ 35 | Expanded( 36 | child: LayoutBuilder( 37 | builder: (context, constraints) { 38 | return DraggableTable( 39 | infospect: widget.infospect, 40 | onCallSelected: (call) => updateSelectedCall(call), 41 | selectedCall: selectedCall, 42 | constraints: constraints, 43 | ); 44 | }, 45 | ), 46 | ), 47 | AppDivider.horizontal(), 48 | Expanded( 49 | child: DesktopDetailsScreen( 50 | selectedCall: selectedCall, 51 | infospect: widget.infospect, 52 | topicHelper: topicHelper, 53 | responseTopicHelper: responseTopicHelper, 54 | selectedTopic: selectedTopic, 55 | selectedResponseTopic: selectedResponseTopic, 56 | onResponseTopicSelected: (value) => 57 | updateSelectedResponseTopic(value), 58 | onTopicSelected: (value) => updateSelectedTopic(value), 59 | ), 60 | ), 61 | ], 62 | ), 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/features/logger/ui/logs_list/bloc/logs_list_state.dart: -------------------------------------------------------------------------------- 1 | part of 'logs_list_bloc.dart'; 2 | 3 | /// Represents the state of the logs list in the application. 4 | /// 5 | /// This state contains information about the logs, the currently filtered 6 | /// logs, the text searched, and the filters applied. 7 | class LogsListState extends Equatable { 8 | /// Creates a new instance of [LogsListState]. 9 | /// 10 | /// - [logs]: The list of all logs. 11 | /// - [filteredLogs]: The list of logs that match the current search and filters. 12 | /// - [searchedText]: The text that was searched. 13 | /// - [filters]: The list of filters applied. 14 | const LogsListState({ 15 | this.logs = const [], 16 | this.filteredLogs = const [], 17 | this.searchedText = '', 18 | this.filters = const [], 19 | }); 20 | 21 | /// The list of all logs. 22 | final List logs; 23 | 24 | /// The list of logs that match the current search and filters. 25 | final List filteredLogs; 26 | 27 | /// The text that was searched. 28 | final String searchedText; 29 | 30 | /// The list of filters applied. 31 | final List filters; 32 | 33 | @override 34 | List get props => [logs, filteredLogs, searchedText, filters]; 35 | 36 | /// Creates a copy of this [LogsListState] but with the given fields replaced with the new values. 37 | LogsListState copyWith({ 38 | List? logs, 39 | List? filteredLogs, 40 | String? searchedText, 41 | List? filters, 42 | }) { 43 | return LogsListState( 44 | logs: logs ?? this.logs, 45 | filteredLogs: filteredLogs ?? this.filteredLogs, 46 | searchedText: searchedText ?? this.searchedText, 47 | filters: filters ?? this.filters, 48 | ); 49 | } 50 | } 51 | 52 | /// Represents a state that includes information about a compressed file 53 | /// containing the logs. 54 | /// 55 | /// This state extends [LogsListState] and adds an additional property 56 | /// representing the compressed file that contains the logs and can be shared. 57 | final class CompressedLogsFile extends LogsListState { 58 | /// Creates a new instance of [CompressedLogsFile]. 59 | /// 60 | /// - [logs]: The list of all logs. 61 | /// - [filteredLogs]: The list of logs that match the current search and filters. 62 | /// - [searchedText]: The text that was searched. 63 | /// - [filters]: The list of filters applied. 64 | /// - [sharableFile]: The compressed file that can be shared. 65 | const CompressedLogsFile({ 66 | super.logs, 67 | super.filteredLogs, 68 | super.searchedText, 69 | super.filters, 70 | required this.sharableFile, 71 | }); 72 | 73 | /// The compressed file that can be shared. 74 | final File sharableFile; 75 | 76 | @override 77 | List get props => super.props..add(sharableFile); 78 | } 79 | -------------------------------------------------------------------------------- /example/linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.10) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | 12 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 13 | # which isn't available in 3.10. 14 | function(list_prepend LIST_NAME PREFIX) 15 | set(NEW_LIST "") 16 | foreach(element ${${LIST_NAME}}) 17 | list(APPEND NEW_LIST "${PREFIX}${element}") 18 | endforeach(element) 19 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 20 | endfunction() 21 | 22 | # === Flutter Library === 23 | # System-level dependencies. 24 | find_package(PkgConfig REQUIRED) 25 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 26 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 27 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 28 | 29 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 30 | 31 | # Published to parent scope for install step. 32 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 33 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 34 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 35 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 36 | 37 | list(APPEND FLUTTER_LIBRARY_HEADERS 38 | "fl_basic_message_channel.h" 39 | "fl_binary_codec.h" 40 | "fl_binary_messenger.h" 41 | "fl_dart_project.h" 42 | "fl_engine.h" 43 | "fl_json_message_codec.h" 44 | "fl_json_method_codec.h" 45 | "fl_message_codec.h" 46 | "fl_method_call.h" 47 | "fl_method_channel.h" 48 | "fl_method_codec.h" 49 | "fl_method_response.h" 50 | "fl_plugin_registrar.h" 51 | "fl_plugin_registry.h" 52 | "fl_standard_message_codec.h" 53 | "fl_standard_method_codec.h" 54 | "fl_string_codec.h" 55 | "fl_value.h" 56 | "fl_view.h" 57 | "flutter_linux.h" 58 | ) 59 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 60 | add_library(flutter INTERFACE) 61 | target_include_directories(flutter INTERFACE 62 | "${EPHEMERAL_DIR}" 63 | ) 64 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 65 | target_link_libraries(flutter INTERFACE 66 | PkgConfig::GTK 67 | PkgConfig::GLIB 68 | PkgConfig::GIO 69 | ) 70 | add_dependencies(flutter flutter_assemble) 71 | 72 | # === Flutter tool backend === 73 | # _phony_ is a non-existent file to force this command to run every time, 74 | # since currently there's no way to get a full input/output list from the 75 | # flutter tool. 76 | add_custom_command( 77 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 78 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 79 | COMMAND ${CMAKE_COMMAND} -E env 80 | ${FLUTTER_TOOL_ENVIRONMENT} 81 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 82 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 83 | VERBATIM 84 | ) 85 | add_custom_target(flutter_assemble DEPENDS 86 | "${FLUTTER_LIBRARY}" 87 | ${FLUTTER_LIBRARY_HEADERS} 88 | ) 89 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/features/network/ui/details/components/interceptor_details_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:infospect/features/network/models/infospect_network_call.dart'; 4 | import 'package:infospect/features/network/ui/details/models/details_topic_data.dart'; 5 | import 'package:infospect/features/network/ui/details/widgets/details_row_widget.dart'; 6 | import 'package:infospect/features/network/ui/list/components/expansion_widget.dart'; 7 | import 'package:infospect/features/network/ui/list/components/trailing_widget.dart'; 8 | import 'package:infospect/helpers/infospect_helper.dart'; 9 | import 'package:infospect/utils/common_widgets/conditional_widget.dart'; 10 | 11 | class InterceptorDetailsResponse extends StatelessWidget { 12 | final InfospectNetworkCall call; 13 | final Infospect infospect; 14 | 15 | const InterceptorDetailsResponse( 16 | this.call, { 17 | super.key, 18 | required this.infospect, 19 | }); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return ConditionalWidget( 24 | condition: call.loading || call.response == null, 25 | ifTrue: const Center( 26 | child: Column( 27 | mainAxisAlignment: MainAxisAlignment.center, 28 | children: [CircularProgressIndicator(), Text("Awaiting response")], 29 | ), 30 | ), 31 | ifFalse: Builder( 32 | builder: (context) { 33 | final ResponseDetailsTopicHelper topicHelper = 34 | ResponseDetailsTopicHelper(call); 35 | return ListView( 36 | children: topicHelper.topics.map( 37 | (e) { 38 | return switch (e.body) { 39 | /// expansion widget with map 40 | TopicDetailsBodyMap( 41 | map: Map map, 42 | trailing: TrailingData? trailing 43 | ) => 44 | _getExpansionMap(e, map, trailing), 45 | 46 | /// expansion widget with list 47 | TopicDetailsBodyList(list: List list) => 48 | _getExpansionList(e, list) 49 | }; 50 | }, 51 | ).toList(), 52 | ); 53 | }, 54 | ), 55 | ); 56 | } 57 | 58 | ExpansionWidget _getExpansionList(TopicData e, List list) { 59 | return ExpansionWidget( 60 | title: e.topic, 61 | children: list 62 | .mapIndexed( 63 | (index, e) => DetailsRowWidget( 64 | e.title, 65 | e.subtitle, 66 | other: e.other, 67 | showDivider: index != list.length - 1, 68 | ), 69 | ) 70 | .toList(), 71 | ); 72 | } 73 | 74 | ExpansionWidget _getExpansionMap( 75 | TopicData e, Map map, TrailingData? trailing) { 76 | return ExpansionWidget.map( 77 | title: e.topic, 78 | map: map, 79 | trailing: trailing != null 80 | ? TrailingWidget( 81 | text: trailing.trailing, 82 | infospect: infospect, 83 | data: trailing.data, 84 | beautificationRequired: trailing.beautificationRequired, 85 | ) 86 | : null, 87 | ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/features/network/models/infospect_network_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:infospect/utils/extensions/map_extension.dart'; 3 | 4 | /// Represents an Network response data for the Infospect application. 5 | class InfospectNetworkResponse { 6 | /// The Network status code of the response. 7 | final int? status; 8 | 9 | /// The size of the response data in bytes. 10 | final int size; 11 | 12 | /// The timestamp when the response was received. 13 | final DateTime time; 14 | 15 | /// The body of the Network response. 16 | final dynamic body; 17 | 18 | /// The headers of the Network response. 19 | final Map? headers; 20 | 21 | /// Creates an instance of the `InfospectNetworkResponse` class. 22 | /// 23 | /// Parameters: 24 | /// - [status]: The Network status code of the response (optional). 25 | /// - [size]: The size of the response data in bytes (default is 0). 26 | /// - [responseTime]: The timestamp when the response was received (default is the current time). 27 | /// - [body]: The body of the Network response (can be of any type). 28 | /// - [headers]: The headers of the Network response (optional). 29 | InfospectNetworkResponse({ 30 | this.status, 31 | this.size = 0, 32 | DateTime? responseTime, 33 | this.body = '', 34 | this.headers, 35 | }) : time = responseTime ?? DateTime.now(); 36 | 37 | InfospectNetworkResponse copyWith({ 38 | int? status, 39 | int? size, 40 | DateTime? time, 41 | dynamic body, 42 | Map? headers, 43 | }) { 44 | return InfospectNetworkResponse( 45 | status: status ?? this.status, 46 | size: size ?? this.size, 47 | responseTime: time ?? this.time, 48 | body: body ?? this.body, 49 | headers: headers ?? this.headers, 50 | ); 51 | } 52 | 53 | /// Converts the `InfospectNetworkResponse` object into a Map representation. 54 | /// 55 | /// Returns a Map with the following key-value pairs: 56 | /// - 'status': The Network status code of the response (can be null if not set). 57 | /// - 'size': The size of the response data in bytes. 58 | /// - 'time': The timestamp when the response was received (in microseconds since epoch). 59 | /// - 'body': The body of the Network response. 60 | /// - 'headers': The headers of the Network response (can be null if not set). 61 | Map toMap() { 62 | return { 63 | 'status': status, 64 | 'size': size, 65 | 'time': time.microsecondsSinceEpoch, 66 | 'body': body is ResponseBody ? body.runtimeType.toString() : body, 67 | 'headers': headers, 68 | }; 69 | } 70 | 71 | /// Creates an instance of the `InfospectNetworkResponse` class from a Map representation. 72 | /// 73 | /// Parameters: 74 | /// - [map]: A Map containing the key-value pairs representing the `InfospectNetworkResponse` object. 75 | /// 76 | /// Returns an instance of the `InfospectNetworkResponse` class with the data populated from the provided Map. 77 | factory InfospectNetworkResponse.fromMap(Map map) { 78 | return InfospectNetworkResponse( 79 | status: map['status'] != null ? map['status'] as int : null, 80 | size: map['size'] ?? 0, 81 | responseTime: DateTime.fromMicrosecondsSinceEpoch(map['time']), 82 | body: map['body'] as dynamic, 83 | headers: (map['headers'] as Map?)?.getMap(), 84 | ); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/features/network/ui/details/screen/interceptor_details_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:infospect/features/network/models/infospect_network_call.dart'; 4 | import 'package:infospect/features/network/ui/details/bloc/interceptor_details_bloc.dart'; 5 | import 'package:infospect/features/network/ui/details/components/interceptor_details_error.dart'; 6 | import 'package:infospect/features/network/ui/details/components/interceptor_details_request.dart'; 7 | import 'package:infospect/features/network/ui/details/components/interceptor_details_response.dart'; 8 | import 'package:infospect/helpers/infospect_helper.dart'; 9 | import 'package:infospect/utils/common_widgets/app_bottom_bar.dart'; 10 | import 'package:infospect/utils/extensions/infospect_network/network_call_extension.dart'; 11 | import 'package:share_plus/share_plus.dart'; 12 | 13 | class InterceptorDetailsScreen extends StatelessWidget { 14 | final Infospect infospect; 15 | final InfospectNetworkCall call; 16 | const InterceptorDetailsScreen(this.infospect, this.call, {super.key}); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Scaffold( 21 | appBar: AppBar( 22 | elevation: 0, 23 | title: Text( 24 | call.server, 25 | maxLines: 1, 26 | overflow: TextOverflow.ellipsis, 27 | ), 28 | ), 29 | body: BlocSelector( 30 | selector: (state) => state.selectedTab, 31 | builder: (context, index) { 32 | return IndexedStack( 33 | index: index, 34 | children: [ 35 | InterceptorDetailsRequest(call, infospect: infospect), 36 | InterceptorDetailsResponse(call, infospect: infospect), 37 | InterceptorDetailsError(call, infospect: infospect), 38 | ], 39 | ); 40 | }, 41 | ), 42 | bottomNavigationBar: BottomNavBarWidget(call), 43 | ); 44 | } 45 | } 46 | 47 | class BottomNavBarWidget extends StatelessWidget { 48 | final InfospectNetworkCall call; 49 | const BottomNavBarWidget( 50 | this.call, { 51 | super.key, 52 | }); 53 | 54 | @override 55 | Widget build(BuildContext context) { 56 | final tabs = [ 57 | (icon: Icons.arrow_upward, title: "Request"), 58 | (icon: Icons.arrow_downward, title: "Response"), 59 | (icon: Icons.warning, title: "Error"), 60 | (icon: Icons.share, title: "Share") 61 | ]; 62 | if (call.error == null) { 63 | tabs.removeAt(2); 64 | } 65 | return BlocSelector( 66 | selector: (state) => state.selectedTab, 67 | builder: (context, index) { 68 | return AppBottomBar( 69 | selectedIndex: index, 70 | tabs: tabs, 71 | tabChangedCallback: (position) async { 72 | if (position == tabs.length - 1) { 73 | Share.share( 74 | await call.sharableData, 75 | subject: 'Request Details', 76 | ); 77 | } else { 78 | context.read().add( 79 | DetailsTabChanged( 80 | selectedTab: position, 81 | ), 82 | ); 83 | } 84 | }, 85 | ); 86 | }, 87 | ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/features/network/models/infospect_form_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | /// Represents a file attachment for the Infospect application. 4 | class InfospectFormDataFile extends Equatable { 5 | final String? fileName; // The name of the file. 6 | final String contentType; // The content type (MIME type) of the file. 7 | final int length; // The length (size) of the file in bytes. 8 | 9 | /// Creates an instance of the `InfospectFormDataFile` class with the provided [fileName], [contentType], and [length]. 10 | /// 11 | /// Parameters: 12 | /// - [fileName]: The name of the file (optional). Can be null if the file is not available or has no name. 13 | /// - [contentType]: The content type (MIME type) of the file. 14 | /// - [length]: The length (size) of the file in bytes. 15 | const InfospectFormDataFile(this.fileName, this.contentType, this.length); 16 | 17 | /// Converts the `InfospectFormDataFile` object into a Map representation. 18 | /// 19 | /// Returns a Map with the following key-value pairs: 20 | /// - 'fileName': The name of the file. 21 | /// - 'contentType': The content type (MIME type) of the file. 22 | /// - 'length': The length (size) of the file in bytes. 23 | Map toMap() { 24 | return { 25 | 'fileName': fileName, 26 | 'contentType': contentType, 27 | 'length': length, 28 | }; 29 | } 30 | 31 | /// Creates an instance of the `InfospectFormDataFile` class from a Map representation. 32 | /// 33 | /// Parameters: 34 | /// - [map]: A Map containing the key-value pairs representing the `InfospectFormDataFile` object. 35 | /// 36 | /// Returns an instance of the `InfospectFormDataFile` class with the data populated from the provided Map. 37 | factory InfospectFormDataFile.fromMap(Map map) { 38 | return InfospectFormDataFile( 39 | map['fileName'] as String?, 40 | map['contentType'] as String, 41 | map['length'] as int, 42 | ); 43 | } 44 | 45 | @override 46 | List get props => [fileName, contentType, length]; 47 | } 48 | 49 | /// Represents a data field for the Infospect application. 50 | class InfospectFormDataField extends Equatable { 51 | final String name; // The name of the data field. 52 | final String value; // The value of the data field. 53 | 54 | /// Creates an instance of the `InfospectFormDataField` class with the provided [name] and [value]. 55 | const InfospectFormDataField(this.name, this.value); 56 | 57 | /// Converts the `InfospectFormDataField` object into a Map representation. 58 | /// 59 | /// Returns a Map with the following key-value pairs: 60 | /// - 'name': The name of the data field. 61 | /// - 'value': The value of the data field. 62 | Map toMap() { 63 | return { 64 | 'name': name, 65 | 'value': value, 66 | }; 67 | } 68 | 69 | /// Creates an instance of the `InfospectFormDataField` class from a Map representation. 70 | /// 71 | /// Parameters: 72 | /// - [map]: A Map containing the key-value pairs representing the `InfospectFormDataField` object. 73 | /// 74 | /// Returns an instance of the `InfospectFormDataField` class with the data populated from the provided Map. 75 | factory InfospectFormDataField.fromMap(Map map) { 76 | return InfospectFormDataField( 77 | map['name'] as String, 78 | map['value'] as String, 79 | ); 80 | } 81 | 82 | @override 83 | List get props => [name, value]; 84 | } 85 | -------------------------------------------------------------------------------- /example/windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) 64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0,0 67 | #endif 68 | 69 | #if defined(FLUTTER_VERSION) 70 | #define VERSION_AS_STRING FLUTTER_VERSION 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.example" "\0" 93 | VALUE "FileDescription", "example" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "example" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2023 com.example. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "example.exe" "\0" 98 | VALUE "ProductName", "example" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /CONTRIBUTION.md: -------------------------------------------------------------------------------- 1 | # Contributing to [Infospect] 2 | 3 | Thank you for considering contributing to [Infospect]! We appreciate your interest and support. This document outlines the guidelines and process for contributing to this project. By participating in this project, you agree to abide by these guidelines. 4 | 5 | ## Table of Contents 6 | 7 | 1. [Getting Started](#getting-started) 8 | - [Prerequisites](#prerequisites) 9 | - [Setting up the Development Environment](#setting-up-the-development-environment) 10 | 2. [Submitting Contributions](#submitting-contributions) 11 | - [Creating an Issue](#creating-an-issue) 12 | - [Working on an Issue](#working-on-an-issue) 13 | - [Creating a Pull Request](#creating-a-pull-request) 14 | 3. [Code Guidelines](#code-guidelines) 15 | - [Coding Style](#coding-style) 16 | - [Documentation](#documentation) 17 | 4. [Community Guidelines](#community-guidelines) 18 | - [Code of Conduct](#code-of-conduct) 19 | - [Communication Channels](#communication-channels) 20 | 5. [License](#license) 21 | 22 | ## Getting Started 23 | 24 | ### Prerequisites 25 | 26 | - List any prerequisites here, such as programming languages, frameworks, or tools required to contribute to the project. 27 | 28 | ### Setting up the Development Environment 29 | 30 | 1. Fork the repository to your own GitHub account. 31 | 2. Clone the forked repository to your local machine. 32 | 3. Install any necessary dependencies. 33 | 4. Build and run the project locally to ensure everything is working correctly. 34 | 35 | ## Submitting Contributions 36 | 37 | ### Creating an Issue 38 | 39 | Before making any major changes or contributions, please create an issue to discuss the proposed changes. This helps ensure that your efforts align with the project's goals and avoids duplication of work. Include as much detail as possible in your issue, such as the problem you intend to solve or the feature you want to add. 40 | 41 | ### Working on an Issue 42 | 43 | If you find an existing issue that you'd like to work on, assign yourself to the issue to indicate that you're working on it. Make sure to communicate your intentions and progress regularly by commenting on the issue. 44 | 45 | ### Creating a Pull Request 46 | 47 | 1. Create a new branch based on the latest `main` branch. 48 | 2. Make your changes, following the project's coding guidelines and best practices. 49 | 3. Test your changes thoroughly to ensure they don't introduce any new issues. 50 | 4. Commit your changes with clear and concise commit messages. 51 | 5. Push your branch to your forked repository. 52 | 6. Create a pull request (PR) against the `main` branch of the original repository. 53 | 7. Include a description of the changes, along with any relevant information or context. 54 | 55 | ## Code Guidelines 56 | 57 | ### Coding Style 58 | 59 | - Follow the established coding style and conventions used in the project. 60 | - Maintain consistent indentation, variable naming, and formatting. 61 | 62 | ### Documentation 63 | 64 | - Document any new features, classes, methods, or significant changes you introduce. 65 | - Update existing documentation if necessary to reflect your changes. 66 | 67 | ## Community Guidelines 68 | 69 | ### Code of Conduct 70 | 71 | - Treat all project participants with respect and kindness. 72 | - Be open to constructive feedback and discussions. 73 | - Harassment, hate speech, and other offensive behavior will not be tolerated. 74 | 75 | ### Communication Channels 76 | 77 | - Stay connected and engage in discussions related to the project. 78 | - Ask questions, seek help, and provide assistance where you can. 79 | 80 | ## License 81 | 82 | [Infospect] is licensed under the [MIT License]. See the [LICENSE](./LICENSE) file for more details. -------------------------------------------------------------------------------- /.github/workflows/trigger_on_pr_merge.yml: -------------------------------------------------------------------------------- 1 | name: Trigger Create Tag and Publish Workflow on PR Merge 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - closed 7 | 8 | 9 | jobs: 10 | check-version: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v3 16 | 17 | - name: Get version 18 | id: yq 19 | uses: mikefarah/yq@master 20 | 21 | with: 22 | cmd: yq -r '.version' 'pubspec.yaml' 23 | 24 | - name: Print version 25 | run: echo ${{ steps.yq.outputs.result }} 26 | 27 | - name: Get Latest Tag 28 | id: get_tag 29 | run: | 30 | REPO_NAME=${{ github.repository }} 31 | LATEST_TAG=$(curl -s "https://api.github.com/repos/$REPO_NAME/releases" | jq -r '.[0].tag_name') 32 | echo "latest=${LATEST_TAG}">> $GITHUB_OUTPUT 33 | 34 | - name: Print version 35 | run: echo "provided_version=${{ steps.yq.outputs.result }} and latest_tag=${{ steps.get_tag.outputs.latest }}" 36 | 37 | - name: Check Version 38 | id: check_version 39 | run: | 40 | latest_tag=${{ steps.get_tag.outputs.latest }} 41 | provided_tag=${{ steps.yq.outputs.result }} 42 | 43 | # Extract numerical version parts from the tags 44 | latest_version=$(echo "$latest_tag" | sed 's/v\?\([0-9.]*\)[-a-zA-Z]*$/\1/') 45 | provided_version=$(echo "$provided_tag" | sed 's/v\?\([0-9.]*\)[-a-zA-Z]*$/\1/') 46 | 47 | echo "latest_version=${latest_version} and provided_version=${provided_version}" 48 | echo "$provided_version" == "$latest_version" 49 | 50 | if [[ "$provided_version" == "$latest_version" ]]; then 51 | echo "Provided version is equal to latest version" 52 | echo "trigger_next_action=false">> $GITHUB_OUTPUT 53 | exit 1 54 | elif [[ "$provided_version" < "$latest_version" ]]; then 55 | echo "Provided version is less than latest version" 56 | echo "trigger_next_action=false">> $GITHUB_OUTPUT 57 | exit 1 58 | else 59 | echo "trigger_next_action=true">> $GITHUB_OUTPUT 60 | fi 61 | 62 | - name: Trigger Next Action 63 | run: | 64 | echo "Trigger Next Action ${{ steps.check_version.outputs.trigger_next_action }}" 65 | echo "set-env:TRIGGER_NEXT_ACTION=${{ steps.check_version.outputs.trigger_next_action}}" >> $GITHUB_ENV 66 | env: 67 | TRIGGER_NEXT_ACTION: ${{ steps.check_version.outputs.trigger_next_action }} 68 | 69 | trigger-create-tag-and-publish: 70 | runs-on: ubuntu-latest 71 | 72 | steps: 73 | - name: Check if PR is merged into main 74 | id: check_merged 75 | run: | 76 | sleep 30 77 | if [ "${{ github.event.pull_request.merged }}" = "true" ] && [ "${{ github.event.pull_request.base.ref }}" = "main" ]; then 78 | echo "true" 79 | else 80 | echo "false" 81 | fi 82 | 83 | - name: Set trigger_create_tag_and_publish output 84 | id: set_output 85 | run: echo "set-env:TRIGGER_CREATE_TAG_AND_PUBLISH=${{ steps.check_merged.outputs.result}}" >> $GITHUB_ENV 86 | env: 87 | TRIGGER_CREATE_TAG_AND_PUBLISH: ${{ steps.check_merged.outputs.result}} 88 | 89 | - name: Trigger dependent workflow 90 | run: | 91 | if [ "$TRIGGER_CREATE_TAG_AND_PUBLISH" = "true" && "$TRIGGER_NEXT_ACTION" = "true" ]; then 92 | curl -X POST \ 93 | -H "Accept: application/vnd.github.v3+json" \ 94 | -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ 95 | https://api.github.com/repos/${{ github.repository }}/actions/workflows/create_tag.yml/dispatches \ 96 | -d '{"ref": "main"}' 97 | fi 98 | 99 | -------------------------------------------------------------------------------- /lib/utils/common_widgets/highlight_text_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:infospect/utils/common_widgets/conditional_widget.dart'; 3 | 4 | /// A widget that displays text with a highlighted section. 5 | /// 6 | /// This widget provides an easy way to display a piece of text with 7 | /// highlighted sections. The highlight is based on the string provided 8 | /// to the [highlight] parameter. 9 | class HighlightText extends StatelessWidget { 10 | /// The text to display. 11 | final String text; 12 | 13 | /// The text to be highlighted. 14 | final String? highlight; 15 | 16 | /// The base style for the text. 17 | final TextStyle? style; 18 | 19 | /// The style for the highlighted text. If not provided, [highlightColor] will be used. 20 | final TextStyle? highlightStyle; 21 | 22 | /// The background color of the highlighted text. 23 | final Color? highlightColor; 24 | 25 | /// Determines if the text matching should be case insensitive. 26 | final bool ignoreCase; 27 | 28 | /// Whether the text can be selected or not. 29 | final bool selectable; 30 | 31 | final int? maxLines; 32 | final TextOverflow? overflow; 33 | final bool? softWrap; 34 | 35 | /// Creates a [HighlightText] widget. 36 | const HighlightText({ 37 | super.key, 38 | required this.text, 39 | this.highlight, 40 | this.style, 41 | this.highlightColor = const Color.fromRGBO(255, 255, 0, 0.3), 42 | this.highlightStyle, 43 | this.ignoreCase = false, 44 | this.selectable = true, 45 | this.maxLines, 46 | this.overflow, 47 | this.softWrap, 48 | }); 49 | 50 | @override 51 | Widget build(BuildContext context) { 52 | if ((highlight?.isEmpty ?? true) || text.isEmpty) { 53 | return ConditionalWidget( 54 | condition: selectable, 55 | ifTrue: SelectableText( 56 | text, 57 | style: style, 58 | maxLines: maxLines, 59 | ), 60 | ifFalse: Text( 61 | text, 62 | style: style, 63 | maxLines: maxLines, 64 | overflow: overflow, 65 | softWrap: softWrap, 66 | ), 67 | ); 68 | } 69 | 70 | final String sourceText = ignoreCase ? text.toLowerCase() : text; 71 | final String targetHighlight = 72 | ignoreCase ? highlight!.toLowerCase() : highlight!; 73 | 74 | List spans = []; 75 | int start = 0; 76 | int indexOfHighlight; 77 | do { 78 | indexOfHighlight = sourceText.indexOf(targetHighlight, start); 79 | if (indexOfHighlight < 0) { 80 | // no highlight 81 | spans.add(_normalSpan(text.substring(start))); 82 | break; 83 | } 84 | if (indexOfHighlight > start) { 85 | spans.add(_normalSpan(text.substring(start, indexOfHighlight))); 86 | } 87 | start = indexOfHighlight + highlight!.length; 88 | spans.add(_highlightSpan(text.substring(indexOfHighlight, start))); 89 | } while (true); 90 | 91 | return ConditionalWidget( 92 | condition: selectable, 93 | ifTrue: SelectableText.rich( 94 | TextSpan(children: spans), 95 | maxLines: maxLines, 96 | ), 97 | ifFalse: Text.rich( 98 | TextSpan(children: spans), 99 | maxLines: maxLines, 100 | overflow: overflow, 101 | softWrap: softWrap, 102 | ), 103 | ); 104 | } 105 | 106 | TextSpan _highlightSpan(String content) { 107 | return TextSpan( 108 | text: content, 109 | style: highlightStyle ?? 110 | style?.copyWith(backgroundColor: highlightColor) ?? 111 | TextStyle(backgroundColor: highlightColor), 112 | ); 113 | } 114 | 115 | TextSpan _normalSpan(String content) { 116 | return TextSpan(text: content, style: style); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /lib/features/logger/models/infospect_log.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:flutter/foundation.dart'; 3 | 4 | /// Represents a log entry for the Infospect application. 5 | @immutable 6 | class InfospectLog { 7 | /// The log level indicating the severity of the log. 8 | final DiagnosticLevel level; 9 | 10 | /// The timestamp when the log was created. 11 | final DateTime timestamp; 12 | 13 | /// The log message. 14 | final String message; 15 | 16 | /// The error associated with the log (if any). 17 | final dynamic error; 18 | 19 | /// The stack trace associated with the log (if available). 20 | final StackTrace? stackTrace; 21 | 22 | /// Creates an instance of the `InfospectLog` class. 23 | /// 24 | /// Parameters: 25 | /// - [level]: The log level indicating the severity of the log (default is [DiagnosticLevel.info]). 26 | /// - [timestamp]: The timestamp when the log was created (default is the current time). 27 | /// - [message]: The log message. 28 | /// - [error]: The error associated with the log (optional). 29 | /// - [stackTrace]: The stack trace associated with the log (optional). 30 | InfospectLog({ 31 | this.level = DiagnosticLevel.info, 32 | DateTime? timestamp, 33 | required this.message, 34 | this.error, 35 | this.stackTrace, 36 | }) : assert(timestamp == null || !timestamp.isUtc), 37 | timestamp = timestamp ?? DateTime.now(); 38 | 39 | /// Generates a hash code for the `InfospectLog` object. 40 | @override 41 | int get hashCode => Object.hash(level, timestamp, message, error, stackTrace); 42 | 43 | /// Checks if this `InfospectLog` object is equal to another object. 44 | @override 45 | bool operator ==(Object other) { 46 | return other is InfospectLog && 47 | level == other.level && 48 | timestamp == other.timestamp && 49 | message == other.message && 50 | error == other.error && 51 | stackTrace == other.stackTrace; 52 | } 53 | 54 | /// Converts the `InfospectLog` object into a Map representation. 55 | /// 56 | /// Returns a Map with the following key-value pairs: 57 | /// - 'level': The name of the log level as a String. 58 | /// - 'timestamp': The timestamp when the log was created in milliseconds since epoch. 59 | /// - 'message': The log message as a String. 60 | /// - 'error': The error associated with the log as a String representation (if available). 61 | /// - 'stackTrace': The stack trace associated with the log as a String representation (if available). 62 | Map toMap() { 63 | Map map = {}; 64 | map['level'] = level.name; 65 | map['timestamp'] = timestamp.millisecondsSinceEpoch.toString(); 66 | map['message'] = message; 67 | map['error'] = error.toString(); 68 | map['stackTrace'] = stackTrace.toString(); 69 | return map; 70 | } 71 | 72 | /// Creates an instance of the `InfospectLog` class from a Map representation. 73 | /// 74 | /// Parameters: 75 | /// - [map]: A Map containing the key-value pairs representing the `InfospectLog` object. 76 | /// 77 | /// Returns an instance of the `InfospectLog` class with the data populated from the provided Map. 78 | static InfospectLog fromMap(Map map) { 79 | return InfospectLog( 80 | message: map['message'] ?? '', 81 | level: DiagnosticLevel.values 82 | .firstWhereOrNull((element) => element.name == map['level']) ?? 83 | DiagnosticLevel.info, 84 | timestamp: map['timestamp'] != null 85 | ? DateTime.fromMillisecondsSinceEpoch( 86 | int.tryParse(map['timestamp'].toString()) ?? 0) 87 | : null, 88 | error: map['error'] ?? '', 89 | stackTrace: map['stackTrace'] != null 90 | ? StackTrace.fromString(map['stackTrace'].toString()) 91 | : null, 92 | ); 93 | } 94 | } 95 | --------------------------------------------------------------------------------