From ba1348935d6ada4f116925c145d83fef888f4eaf Mon Sep 17 00:00:00 2001 From: natrayansf Date: Fri, 12 Dec 2025 16:59:01 +0530 Subject: [PATCH] Updated latest changes to the flutter examples repository --- android/app/src/main/AndroidManifest.xml | 15 +- .../example/flutter_examples/MainActivity.kt | 17 + .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/local.properties | 4 +- android/settings.gradle | 2 +- lib/meta_tag/meta_tag.dart | 11 + lib/meta_tag/meta_tag_default.dart | 11 + lib/meta_tag/meta_tag_web.dart | 56 + lib/model/helper.dart | 10 + lib/model/showcase_application.dart | 51 +- lib/model/web_view.dart | 77 +- lib/sample_details.json | 4 +- .../smart_chart_ai_generator.dart | 2 +- .../ai_charts/stock_forecasting.dart | 2 +- .../ai_datagrid/anomaly_detection.dart | 9 + .../ai_datagrid/predictive_data_entry.dart | 38 +- .../ai_datagrid/semantic_filtering.dart | 167 +- .../ai_samples/ai_maps/location_finder.dart | 5 +- lib/samples/barcodes/data_matrix.dart | 7 +- lib/samples/barcodes/qr_code.dart | 245 ++- lib/samples/calendar/agenda_view.dart | 2 +- lib/samples/calendar/airfare.dart | 2 +- lib/samples/calendar/appointment_editor.dart | 1410 +++++++++-------- lib/samples/calendar/calendar_loadmore.dart | 2 +- lib/samples/calendar/customization.dart | 2 +- .../calendar/drag_and_drop_calendar.dart | 2 +- lib/samples/calendar/getting_started.dart | 126 +- lib/samples/calendar/recurrence.dart | 2 +- lib/samples/calendar/resizing_calendar.dart | 2 +- lib/samples/calendar/schedule_view.dart | 2 +- lib/samples/calendar/shift_scheduler.dart | 6 +- lib/samples/calendar/special_regions.dart | 2 +- lib/samples/calendar/timeline_views.dart | 2 +- .../axis_features/axis_animation.dart | 28 +- .../axis_features/axis_crossing.dart | 28 +- .../axis_features/customized_axis_label.dart | 2 +- .../axis_features/interval_type.dart | 2 +- .../maximum_width_for_labels.dart | 68 +- .../axis_features/plot_band.dart | 354 ++++- .../axis_features/plot_offset.dart | 244 ++- .../axis_features/positioning_axis_label.dart | 6 + .../axis_features/range_padding.dart | 52 +- .../axis_types/category/label_placement.dart | 53 +- .../area/animation_area_chart.dart | 2 +- .../chart_types/area/area_with_axis_base.dart | 39 +- .../chart_types/bar/animation_bar_chart.dart | 2 +- .../bar/bar_width_and_spacing.dart | 59 +- .../bubble/animation_bubble_chart.dart | 4 +- .../column/animation_column_chart.dart | 2 +- .../column/column_width_and_spacing.dart | 40 +- .../column/column_with_axis_base.dart | 37 +- .../financial_charts/candle_chart.dart | 91 +- .../chart_types/histogram.dart | 27 +- .../line/animation_line_chart.dart | 2 +- .../line/customized_line_chart.dart | 2 +- .../chart_types/range_area.dart | 6 +- .../animation_range_column_chart.dart | 2 +- .../scatter/animation_scatter_chart.dart | 2 +- .../spline/animation_spline_chart.dart | 2 +- .../spline/customized_spline_chart.dart | 2 +- .../chart_types/spline/spline_types.dart | 1 + .../step_line/animation_step_line_chart.dart | 2 +- .../cartesian_charts/infinite_scrolling.dart | 2 +- .../legend/legend_various_options.dart | 124 +- .../add_remove_data/add_remove_points.dart | 2 +- .../add_remove_data/add_remove_series.dart | 2 +- .../live_update/real_time_line_chart.dart | 2 +- .../live_update/vertical_live_chart.dart | 2 +- .../real_time_charts/update_data_source.dart | 2 +- .../data_label/default_data_labels.dart | 154 +- .../series_features/sorting.dart | 77 +- .../trendline/default_trendline.dart | 14 +- .../trendline/trendline_forecast.dart | 60 +- .../user_interactions/auto_scrolling.dart | 2 +- .../user_interactions/crosshair.dart | 2 +- .../doughnut/semi_doughnut_chart.dart | 3 +- .../chart_types/pie/pie_with_image.dart | 8 +- .../pie/pie_with_smart_data_label.dart | 1 + .../chart_types/pie/point_render_mode.dart | 78 +- .../chart_types/pie/semi_pie_chart.dart | 60 +- .../legend/legend_with_various_options.dart | 76 +- .../funnel_charts/default_funnel_chart.dart | 3 +- .../funnel_with_smart_labels.dart | 181 +-- .../pyramid_with_smart_labels.dart | 189 +-- .../customer_datagridsource.dart | 2 +- .../datagridsource/dealer_datagridsource.dart | 2 +- .../orderinfo_datagridsource.dart | 2 +- .../product_datagridsource.dart | 2 +- .../realtime_datagridsource.dart | 2 +- .../datagridsource/stock_datagridsource.dart | 2 +- .../stockinfo_datagridsource.dart | 2 +- .../datagridsource/team_datagridsource.dart | 2 +- .../date_picker/blackout_date_picker.dart | 2 +- .../date_picker/customized_date_picker.dart | 2 +- .../date_picker_getting_started.dart | 572 +++---- lib/samples/date_picker/hijri_calendar.dart | 504 +++--- .../date_picker/vertical_calendar.dart | 61 +- .../radial_range_slider.dart | 31 +- .../gauge/pointers/marker_pointer.dart | 43 +- lib/samples/pdf/conformance.dart | 17 +- lib/samples/pdf/digital_signature.dart | 63 +- lib/samples/pdf/encryption.dart | 21 +- lib/samples/pdf/form.dart | 24 +- lib/samples/pdf/helper/save_file_mobile.dart | 17 +- .../import_and_export_annotation_data.dart | 53 +- .../pdf/import_and_export_form_data.dart | 54 +- .../pdf_viewer/pdf_viewer_custom_toolbar.dart | 3 - .../pdf_viewer/shared/toolbar_widgets.dart | 2 +- .../basic_features/state.dart | 1 + .../radial_slider/basic_features/state.dart | 1 + .../range_selector_label_customization.dart | 2 +- .../range_selector_with_zooming.dart | 2 +- ...ertical_range_slider_tooltip_position.dart | 3 +- .../vertical_slider_tooltip_position.dart | 5 +- lib/samples/sparkline/customization.dart | 193 ++- lib/samples/sparkline/live_update.dart | 2 +- lib/samples/treemap/hierarchical.dart | 53 +- .../expense_tracker/helper/common_helper.dart | 2 +- .../expense_tracker/pages/base_home.dart | 23 + .../pages/settings/sections/appearance.dart | 42 +- .../pages/settings/settings.dart | 3 +- .../pages/welcome_screens/import_page.dart | 7 + .../pages/welcome_screens/setup_page.dart | 13 + .../stock_analysis/dialogs/settings.dart | 70 +- .../stock_analysis/screens/setup.dart | 10 + .../stock_analysis/screens/splash.dart | 7 + .../stock_chart_view/profile_menu_popup.dart | 7 + .../desktop_window/CHANGELOG.md | 30 + .../desktop_window/example/lib/main.dart | 180 +++ .../example/linux/CMakeLists.txt | 98 ++ .../example/linux/flutter/CMakeLists.txt | 86 + .../flutter/generated_plugin_registrant.cc | 15 + .../flutter/generated_plugin_registrant.h | 15 + .../linux/flutter/generated_plugins.cmake | 16 + .../desktop_window/example/linux/main.cc | 10 + .../example/linux/my_application.cc | 46 + .../example/linux/my_application.h | 18 + .../macos/Flutter/Flutter-Debug.xcconfig | 2 + .../macos/Flutter/Flutter-Release.xcconfig | 2 + .../Flutter/GeneratedPluginRegistrant.swift | 12 + .../desktop_window/example/macos/Podfile | 82 + .../desktop_window/example/macos/Podfile.lock | 23 + .../macos/Runner.xcodeproj/project.pbxproj | 642 ++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 101 ++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../desktop_window/example/pubspec.yaml | 71 + .../example/windows/CMakeLists.txt | 95 ++ .../example/windows/flutter/CMakeLists.txt | 101 ++ .../flutter/generated_plugin_registrant.cc | 14 + .../flutter/generated_plugin_registrant.h | 15 + .../windows/flutter/generated_plugins.cmake | 16 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 ++ .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 36 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 40 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 22 + .../example/windows/runner/utils.h | 8 + .../example/windows/runner/win32_window.cpp | 244 +++ .../example/windows/runner/win32_window.h | 98 ++ .../desktop_window/lib/desktop_window.dart | 80 + .../desktop_window/linux/CMakeLists.txt | 23 + .../linux/desktop_window_plugin.cc | 187 +++ .../desktop_window/desktop_window_plugin.h | 26 + .../macos/Classes/DesktopWindowPlugin.swift | 135 ++ .../macos/desktop_window.podspec | 22 + .../desktop_window/pubspec.yaml | 34 + .../desktop_window/windows/CMakeLists.txt | 24 + .../windows/desktop_window_plugin.cpp | 148 ++ .../windows/include/borders.cpp | 66 + .../desktop_window/windows/include/borders.h | 20 + .../desktop_window/desktop_window_plugin.h | 24 + .../windows/include/method_call.cpp | 260 +++ .../windows/include/method_call.h | 37 + .../device_info_plus/CHANGELOG.md | 524 ++++++ .../device_info_plus/android/build.gradle | 52 + .../android/gradle.properties | 1 + .../device_info_plus/android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 2 + .../plus/device_info/DeviceInfoPlusPlugin.kt | 32 + .../plus/device_info/MethodCallHandlerImpl.kt | 114 ++ .../example/android/app/build.gradle | 65 + .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 44 + .../deviceinfoexample/example/MainActivity.kt | 21 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + .../example/android/build.gradle | 18 + .../example/android/gradle.properties | 2 + .../gradle/wrapper/gradle-wrapper.properties | 6 + .../example/android/settings.gradle | 25 + .../device_info_plus_test.dart | 186 +++ .../ios/Flutter/AppFrameworkInfo.plist | 26 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + .../ios/Runner.xcodeproj/project.pbxproj | 723 +++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 98 ++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../example/ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 ++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../Runner/Base.lproj/LaunchScreen.storyboard | 37 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../example/ios/Runner/Info.plist | 51 + .../ios/Runner/Runner-Bridging-Header.h | 1 + .../example/ios/RunnerTests/RunnerTests.swift | 12 + .../device_info_plus/example/lib/main.dart | 279 ++++ .../example/linux/CMakeLists.txt | 106 ++ .../example/linux/flutter/CMakeLists.txt | 89 ++ .../device_info_plus/example/linux/main.cc | 11 + .../example/linux/my_application.cc | 45 + .../example/linux/my_application.h | 18 + .../macos/Flutter/Flutter-Debug.xcconfig | 2 + .../macos/Flutter/Flutter-Release.xcconfig | 2 + .../macos/Runner.xcodeproj/project.pbxproj | 791 +++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 98 ++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 102994 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 5680 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 520 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 14142 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1066 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 36406 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 2218 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 343 ++++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../macos/RunnerTests/RunnerTests.swift | 12 + .../device_info_plus/example/pubspec.yaml | 25 + .../device_info_plus/example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../device_info_plus/example/web/index.html | 23 + .../example/web/manifest.json | 23 + .../example/windows/CMakeLists.txt | 95 ++ .../example/windows/flutter/CMakeLists.txt | 103 ++ .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 ++ .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 41 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 38 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 63 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 237 +++ .../example/windows/runner/win32_window.h | 95 ++ .../ios/device_info_plus.podspec | 24 + .../ios/device_info_plus/Package.swift | 27 + .../device_info_plus/DeviceIdentifiers.m | 230 +++ .../FPPDeviceInfoPlusPlugin.m | 112 ++ .../device_info_plus/PrivacyInfo.xcprivacy | 14 + .../device_info_plus/DeviceIdentifiers.h | 13 + .../FPPDeviceInfoPlusPlugin.h | 8 + .../lib/device_info_plus.dart | 141 ++ .../lib/src/device_info_plus_linux.dart | 107 ++ .../lib/src/device_info_plus_macos.dart | 2 + .../lib/src/device_info_plus_web.dart | 59 + .../lib/src/device_info_plus_windows.dart | 187 +++ .../lib/src/model/android_device_info.dart | 395 +++++ .../lib/src/model/ios_device_info.dart | 222 +++ .../lib/src/model/linux_device_info.dart | 168 ++ .../lib/src/model/macos_device_info.dart | 151 ++ .../lib/src/model/web_browser_info.dart | 188 +++ .../lib/src/model/windows_device_info.dart | 184 +++ .../macos/device_info_plus.podspec | 23 + .../macos/device_info_plus/Package.swift | 24 + .../Sources/device_info_plus/CwlSysctl.swift | 152 ++ .../device_info_plus/DeviceIdentifiers.swift | 105 ++ .../DeviceInfoPlusMacosPlugin.swift | 54 + .../device_info_plus/PrivacyInfo.xcprivacy | 12 + .../Sources/device_info_plus/SystemUUID.swift | 16 + .../device_info_plus/pubspec.yaml | 53 + .../file_selector_linux/AUTHORS | 6 + .../file_selector_linux/CHANGELOG.md | 76 + .../example/lib/get_directory_page.dart | 76 + .../lib/get_multiple_directories_page.dart | 79 + .../example/lib/home_page.dart | 65 + .../file_selector_linux/example/lib/main.dart | 45 + .../example/lib/open_image_page.dart | 91 ++ .../lib/open_multiple_images_page.dart | 101 ++ .../example/lib/open_text_page.dart | 86 + .../example/lib/save_text_page.dart | 81 + .../example/linux/CMakeLists.txt | 111 ++ .../example/linux/flutter/CMakeLists.txt | 87 + .../linux/flutter/generated_plugins.cmake | 24 + .../file_selector_linux/example/linux/main.cc | 10 + .../example/linux/my_application.cc | 110 ++ .../example/linux/my_application.h | 22 + .../file_selector_linux/example/pubspec.yaml | 22 + .../lib/file_selector_linux.dart | 187 +++ .../lib/src/messages.g.dart | 261 +++ .../file_selector_linux/linux/CMakeLists.txt | 69 + .../linux/file_selector_plugin.cc | 202 +++ .../linux/file_selector_plugin_private.h | 26 + .../file_selector_plugin.h | 31 + .../file_selector_linux/linux/messages.g.cc | 570 +++++++ .../file_selector_linux/linux/messages.g.h | 269 ++++ .../file_selector_linux/pigeons/copyright.txt | 3 + .../file_selector_linux/pigeons/messages.dart | 71 + .../file_selector_linux/pubspec.yaml | 33 + .../image_picker_linux/AUTHORS | 7 + .../image_picker_linux/CHANGELOG.md | 22 + .../image_picker_linux/example/lib/main.dart | 564 +++++++ .../example/linux/CMakeLists.txt | 138 ++ .../example/linux/flutter/CMakeLists.txt | 88 + .../linux/flutter/generated_plugins.cmake | 24 + .../image_picker_linux/example/linux/main.cc | 10 + .../example/linux/my_application.cc | 111 ++ .../example/linux/my_application.h | 22 + .../image_picker_linux/example/pubspec.yaml | 29 + .../lib/image_picker_linux.dart | 193 +++ .../image_picker_linux/pubspec.yaml | 34 + .../path_provider_linux/AUTHORS | 66 + .../path_provider_linux/CHANGELOG.md | 109 ++ .../integration_test/path_provider_test.dart | 66 + .../path_provider_linux/example/lib/main.dart | 109 ++ .../example/linux/CMakeLists.txt | 106 ++ .../example/linux/flutter/CMakeLists.txt | 86 + .../linux/flutter/generated_plugins.cmake | 23 + .../path_provider_linux/example/linux/main.cc | 15 + .../example/linux/my_application.cc | 49 + .../example/linux/my_application.h | 22 + .../path_provider_linux/example/pubspec.yaml | 28 + .../example/test_driver/integration_test.dart | 7 + .../lib/path_provider_linux.dart | 5 + .../lib/src/get_application_id.dart | 9 + .../lib/src/get_application_id_real.dart | 78 + .../lib/src/get_application_id_stub.dart | 6 + .../lib/src/path_provider_linux.dart | 102 ++ .../path_provider_linux/pubspec.yaml | 33 + .../shared_preferences_linux/AUTHORS | 66 + .../shared_preferences_linux/CHANGELOG.md | 110 ++ .../shared_preferences_test.dart | 556 +++++++ .../example/lib/main.dart | 100 ++ .../example/linux/CMakeLists.txt | 95 ++ .../example/linux/flutter/CMakeLists.txt | 86 + .../linux/flutter/generated_plugins.cmake | 23 + .../example/linux/main.cc | 10 + .../example/linux/my_application.cc | 48 + .../example/linux/my_application.h | 22 + .../example/pubspec.yaml | 28 + .../example/test_driver/integration_test.dart | 7 + .../lib/shared_preferences_linux.dart | 420 +++++ .../shared_preferences_linux/pubspec.yaml | 34 + .../syncfusion_pdfviewer_linux/pubspec.yaml | 4 +- .../url_launcher_linux/AUTHORS | 66 + .../url_launcher_linux/CHANGELOG.md | 110 ++ .../integration_test/url_launcher_test.dart | 20 + .../url_launcher_linux/example/lib/main.dart | 91 ++ .../example/linux/CMakeLists.txt | 100 ++ .../example/linux/flutter/CMakeLists.txt | 87 + .../linux/flutter/generated_plugins.cmake | 24 + .../url_launcher_linux/example/linux/main.cc | 10 + .../example/linux/my_application.cc | 48 + .../example/linux/my_application.h | 22 + .../url_launcher_linux/example/pubspec.yaml | 28 + .../example/test_driver/integration_test.dart | 7 + .../lib/src/messages.g.dart | 119 ++ .../lib/url_launcher_linux.dart | 77 + .../url_launcher_linux/linux/CMakeLists.txt | 65 + .../url_launcher_linux/url_launcher_plugin.h | 31 + .../url_launcher_linux/linux/messages.g.cc | 300 ++++ .../url_launcher_linux/linux/messages.g.h | 121 ++ .../linux/url_launcher_plugin.cc | 109 ++ .../linux/url_launcher_plugin_private.h | 12 + .../url_launcher_linux/pigeons/copyright.txt | 3 + .../url_launcher_linux/pigeons/messages.dart | 23 + .../url_launcher_linux/pubspec.yaml | 34 + macos/Runner/DebugProfile.entitlements | 2 +- pubspec.yaml | 35 +- 438 files changed, 23557 insertions(+), 3208 deletions(-) create mode 100644 lib/meta_tag/meta_tag.dart create mode 100644 lib/meta_tag/meta_tag_default.dart create mode 100644 lib/meta_tag/meta_tag_web.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/CHANGELOG.md create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/lib/main.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/flutter/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/flutter/generated_plugin_registrant.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/flutter/generated_plugins.cmake create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/main.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/my_application.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/my_application.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Podfile create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Podfile.lock create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/AppDelegate.swift create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Configs/Release.xcconfig create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/DebugProfile.entitlements create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Info.plist create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/MainFlutterWindow.swift create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Release.entitlements create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/pubspec.yaml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/flutter/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/flutter/generated_plugin_registrant.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/flutter/generated_plugins.cmake create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/Runner.rc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/flutter_window.cpp create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/flutter_window.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/main.cpp create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/resource.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/resources/app_icon.ico create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/run_loop.cpp create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/run_loop.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/runner.exe.manifest create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/utils.cpp create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/utils.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/win32_window.cpp create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/win32_window.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/lib/desktop_window.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/linux/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/linux/desktop_window_plugin.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/linux/include/desktop_window/desktop_window_plugin.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/macos/Classes/DesktopWindowPlugin.swift create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/macos/desktop_window.podspec create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/pubspec.yaml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/desktop_window_plugin.cpp create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/borders.cpp create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/borders.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/desktop_window/desktop_window_plugin.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/method_call.cpp create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/method_call.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/CHANGELOG.md create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/build.gradle create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/gradle.properties create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/settings.gradle create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/src/main/AndroidManifest.xml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/src/main/kotlin/dev/fluttercommunity/plus/device_info/DeviceInfoPlusPlugin.kt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/src/main/kotlin/dev/fluttercommunity/plus/device_info/MethodCallHandlerImpl.kt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/build.gradle create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/debug/AndroidManifest.xml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/AndroidManifest.xml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/kotlin/io/flutter/plugins/deviceinfoexample/example/MainActivity.kt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/values/styles.xml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/profile/AndroidManifest.xml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/build.gradle create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/gradle.properties create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/settings.gradle create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/integration_test/device_info_plus_test.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Flutter/Debug.xcconfig create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Flutter/Release.xcconfig create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/AppDelegate.swift create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Info.plist create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Runner-Bridging-Header.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/RunnerTests/RunnerTests.swift create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/lib/main.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/flutter/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/main.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/my_application.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/my_application.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/AppDelegate.swift create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Configs/Release.xcconfig create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/DebugProfile.entitlements create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Info.plist create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/MainFlutterWindow.swift create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Release.entitlements create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/RunnerTests/RunnerTests.swift create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/pubspec.yaml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/web/favicon.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/web/icons/Icon-192.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/web/icons/Icon-512.png create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/web/index.html create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/web/manifest.json create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/flutter/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/Runner.rc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/flutter_window.cpp create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/flutter_window.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/main.cpp create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/resource.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/resources/app_icon.ico create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/run_loop.cpp create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/run_loop.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/runner.exe.manifest create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/utils.cpp create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/utils.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/win32_window.cpp create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/win32_window.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus.podspec create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Package.swift create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/DeviceIdentifiers.m create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/FPPDeviceInfoPlusPlugin.m create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/PrivacyInfo.xcprivacy create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/include/device_info_plus/DeviceIdentifiers.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/include/device_info_plus/FPPDeviceInfoPlusPlugin.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/device_info_plus.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/device_info_plus_linux.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/device_info_plus_macos.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/device_info_plus_web.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/device_info_plus_windows.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/android_device_info.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/ios_device_info.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/linux_device_info.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/macos_device_info.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/web_browser_info.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/windows_device_info.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus.podspec create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Package.swift create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/CwlSysctl.swift create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/DeviceIdentifiers.swift create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/DeviceInfoPlusMacosPlugin.swift create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/PrivacyInfo.xcprivacy create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/SystemUUID.swift create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/pubspec.yaml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/AUTHORS create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/CHANGELOG.md create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/get_directory_page.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/get_multiple_directories_page.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/home_page.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/main.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/open_image_page.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/open_multiple_images_page.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/open_text_page.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/save_text_page.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/flutter/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/flutter/generated_plugins.cmake create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/main.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/my_application.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/my_application.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/pubspec.yaml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/lib/file_selector_linux.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/lib/src/messages.g.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/file_selector_plugin.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/file_selector_plugin_private.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/include/file_selector_linux/file_selector_plugin.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/messages.g.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/messages.g.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/pigeons/copyright.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/pigeons/messages.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/pubspec.yaml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/AUTHORS create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/CHANGELOG.md create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/lib/main.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/flutter/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/flutter/generated_plugins.cmake create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/main.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/my_application.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/my_application.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/pubspec.yaml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/lib/image_picker_linux.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/pubspec.yaml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/AUTHORS create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/CHANGELOG.md create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/integration_test/path_provider_test.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/lib/main.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/flutter/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/flutter/generated_plugins.cmake create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/main.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/my_application.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/my_application.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/pubspec.yaml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/test_driver/integration_test.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/path_provider_linux.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/src/get_application_id.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/src/get_application_id_real.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/src/get_application_id_stub.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/src/path_provider_linux.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/pubspec.yaml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/AUTHORS create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/CHANGELOG.md create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/integration_test/shared_preferences_test.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/lib/main.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/flutter/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/flutter/generated_plugins.cmake create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/main.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/my_application.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/my_application.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/pubspec.yaml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/test_driver/integration_test.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/lib/shared_preferences_linux.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/pubspec.yaml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/AUTHORS create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/CHANGELOG.md create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/integration_test/url_launcher_test.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/lib/main.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/flutter/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/flutter/generated_plugins.cmake create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/main.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/my_application.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/my_application.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/pubspec.yaml create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/test_driver/integration_test.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/lib/src/messages.g.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/lib/url_launcher_linux.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/CMakeLists.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/include/url_launcher_linux/url_launcher_plugin.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/messages.g.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/messages.g.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/url_launcher_plugin.cc create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/url_launcher_plugin_private.h create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/pigeons/copyright.txt create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/pigeons/messages.dart create mode 100644 linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/pubspec.yaml diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6fa786f5..e962185d 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,9 +1,20 @@ - + + android:icon="@mipmap/ic_launcher" + android:allowBackup="false"> + + + + + + { GlobalKey sampleInputKey = GlobalKey(); GlobalKey sampleOutputKey = GlobalKey(); GlobalKey popUpKey = GlobalKey(); + final WebMetaTagUpdate metaTagUpdate = WebMetaTagUpdate(); late _SampleInputContainer inputContainer; late _SampleOutputContainer outputContainer; @@ -108,6 +109,10 @@ class _WebLayoutPageState extends State { model.addListener(_handleChange); super.initState(); + + // Updates meta tag details when navigating from the home page + // to a widget sample page. + metaTagUpdate.update(sample.title!, sample.control!.title!); } ///Notify the framework by calling this method @@ -573,6 +578,14 @@ class _SampleInputContainerState extends State<_SampleInputContainer> { widget.webLayoutPageState!.sample = item.subItems != null ? item.subItems![0] as SubItem : item; + + // Updates meta tag details when selecting a sample from the + // left panel in a widget page. + metaTagUpdate.update( + outputContainerState.sample.title!, + outputContainerState.sample.control!.title!, + ); + if (model.currentSampleKey == null || (item.key != null ? model.currentSampleKey != item.key @@ -751,6 +764,14 @@ class _SampleInputContainerState extends State<_SampleInputContainer> { list[i].title!; widget.webLayoutPageState!.selectSample = list[i].title; resetLocaleValue(model, outputContainerState.sample); + + // Updates meta tag details when selecting a sample from + // the left panel in a widget page. + metaTagUpdate.update( + outputContainerState.sample.title!, + outputContainerState.sample.control!.title!, + ); + if (model.currentSampleKey == null || (list[i].key != null ? model.currentSampleKey != list[i].key @@ -884,6 +905,10 @@ class _SampleInputContainerState extends State<_SampleInputContainer> { if (MediaQuery.of(context).size.width <= 768) { Navigator.pop(context); } + + // Sets default meta tag details when navigating back from a sample + // page to the home page. + metaTagUpdate.setDefault(); } } @@ -1844,6 +1869,13 @@ class SampleOutputContainerState extends State<_SampleOutputContainer> { outputContainerState.needTabs = true; outputContainerState.subItems = subItems; outputContainerState.tabIndex = value; + + // Updates meta tag details when switching between tab bar samples. + metaTagUpdate.update( + outputContainerState.sample.title!, + outputContainerState.sample.control!.title!, + ); + if (model.currentSampleKey == null || model.currentSampleKey != outputContainerState.sample.key) { outputContainerState.refresh(); @@ -1921,9 +1953,15 @@ class SampleOutputContainerState extends State<_SampleOutputContainer> { widget.webLayoutPageState!.popUpKey.currentState! as _PopupState; state._sampleDetails = sample; state._currentWidgetKey = model.currentRenderSample.key as GlobalKey; + final _OutputContainerState outputContainerState = _outputKey.currentState! as _OutputContainerState; + + // Updates meta tag details when expanding a sample to maximized view. + metaTagUpdate.update(sample.title!, sample.control!.title!); + outputContainerState.setState(() {}); + state.refresh(true); } @@ -1937,7 +1975,7 @@ class SampleOutputContainerState extends State<_SampleOutputContainer> { return Size(textPainter.width, textPainter.height); } - /// Get tabs which length is equal to list length + /// Get tabs which length is equal to list length. List _buildTabs(List list) { final List tabs = []; _tabTextWidth = 0; @@ -2537,6 +2575,14 @@ class _TileContainerState extends State<_TileContainer> { list[i].subItems != null ? list[i].subItems![0] as SubItem : list[i]; + + // Updates meta tag details when selecting a sample + // from the left panel in a widget page. + metaTagUpdate.update( + outputContainerState.sample.title!, + outputContainerState.sample.control!.title!, + ); + if (model.currentSampleKey == null || (list[i].key != null ? (model.currentSampleKey != list[i].key || @@ -2674,6 +2720,14 @@ class _TileContainerState extends State<_TileContainer> { list[i].subItems != null ? list[i].subItems![0] as SubItem : list[i]; + + // Updates meta tag details when selecting a sample from + // the left panel in a widget page. + metaTagUpdate.update( + outputContainerState.sample.title!, + outputContainerState.sample.control!.title!, + ); + if (model.currentSampleKey == null || (list[i].key != null ? (model.currentSampleKey != list[i].key || @@ -2898,6 +2952,15 @@ class _PopupState extends State<_Popup> { color: model!.drawerIconColor, ), onPressed: () { + if (_sampleDetails != null) { + // Updates meta tag details when closing + // the maximized sample view and returning + // to normal mode. + metaTagUpdate.update( + _sampleDetails!.title!, + _sampleDetails!.control!.title!, + ); + } model!.needToMaximize = false; final _OutputContainerState outputContainerState = @@ -2907,6 +2970,7 @@ class _PopupState extends State<_Popup> { outputContainerState.renderWidget = _currentWidgetKey?.currentWidget; }); + _sampleDetails = null; refresh(false); }, @@ -2916,7 +2980,14 @@ class _PopupState extends State<_Popup> { ], ), ), - body: _currentWidgetKey?.currentWidget, + body: ListenableBuilder( + listenable: model!, + builder: (BuildContext context, Widget? child) { + final Function? sampleView = + model!.sampleWidget[_sampleDetails!.key]; + return sampleView!(_currentWidgetKey); + }, + ), ) : Container(), ), diff --git a/lib/sample_details.json b/lib/sample_details.json index 21f88588..f976b8eb 100644 --- a/lib/sample_details.json +++ b/lib/sample_details.json @@ -345,6 +345,7 @@ "description": "Plot over 30 chart types ranging from line charts to financial charts", "image": "images/cartesian_types.png", "controlId": 1, + "status": "Updated", "subItems": [ { "type": "parent", @@ -1133,7 +1134,8 @@ "title": "Plot band", "key": "plot_band", "codeLink": "https://github.com/syncfusion/flutter-examples/blob/master/lib/samples/chart/cartesian_charts/axis_features/plot_band.dart", - "needsPropertyPanel": true + "needsPropertyPanel": true, + "status": "Updated" }, { "title": "Plot band recurrence", diff --git a/lib/samples/ai_samples/ai_charts/ai_smart_chart/smart_chart_ai_generator.dart b/lib/samples/ai_samples/ai_charts/ai_smart_chart/smart_chart_ai_generator.dart index 1eb1f49e..0d104bbf 100644 --- a/lib/samples/ai_samples/ai_charts/ai_smart_chart/smart_chart_ai_generator.dart +++ b/lib/samples/ai_samples/ai_charts/ai_smart_chart/smart_chart_ai_generator.dart @@ -725,7 +725,7 @@ class _ChartFromJsonState extends SampleViewState } void updateDataSource() { - final Random random = Random(); + final Random random = Random.secure(); for (final series in chartConfig.seriesList) { for (final data in series.data) { data.yValue = random.nextInt(100) + 50; diff --git a/lib/samples/ai_samples/ai_charts/stock_forecasting.dart b/lib/samples/ai_samples/ai_charts/stock_forecasting.dart index 3abd833e..f3568aae 100644 --- a/lib/samples/ai_samples/ai_charts/stock_forecasting.dart +++ b/lib/samples/ai_samples/ai_charts/stock_forecasting.dart @@ -862,7 +862,7 @@ class _StockForecastingSampleState extends SampleViewState List<_ChartData> _generateDataSource(List<_ChartData> stockData) { final List<_ChartData> items = []; - final Random random = Random(); + final Random random = Random.secure(); // Find the last date in the stock data final DateTime lastDate = stockData[stockData.length - 1].date; diff --git a/lib/samples/ai_samples/ai_datagrid/anomaly_detection.dart b/lib/samples/ai_samples/ai_datagrid/anomaly_detection.dart index 72c5d5cb..5e74236c 100644 --- a/lib/samples/ai_samples/ai_datagrid/anomaly_detection.dart +++ b/lib/samples/ai_samples/ai_datagrid/anomaly_detection.dart @@ -29,6 +29,7 @@ class _AnomalyDetectionSampleState extends SampleViewState late List _machineDetails; bool _isLoading = false; late bool _isWebOrDesktop; + late bool isMaterial3; @override void initState() { @@ -76,6 +77,8 @@ class _AnomalyDetectionSampleState extends SampleViewState model.isFirstTime = false; } }); + + isMaterial3 = model.themeData.useMaterial3; } String _generatePrompt() { @@ -197,6 +200,9 @@ class _AnomalyDetectionSampleState extends SampleViewState ), ), ], + elevation: 0, + scrolledUnderElevation: 0, + backgroundColor: isMaterial3 ? Colors.transparent : null, ), ), body: Stack( @@ -204,6 +210,9 @@ class _AnomalyDetectionSampleState extends SampleViewState SfDataGrid( source: _machineDataSource, columns: _machineDataSource._columns, + columnWidthMode: !_isWebOrDesktop && !model.isMobileResolution + ? ColumnWidthMode.fill + : ColumnWidthMode.none, ), if (_isLoading) const Center(child: CircularProgressIndicator()), ], diff --git a/lib/samples/ai_samples/ai_datagrid/predictive_data_entry.dart b/lib/samples/ai_samples/ai_datagrid/predictive_data_entry.dart index 829346f1..a8f89078 100644 --- a/lib/samples/ai_samples/ai_datagrid/predictive_data_entry.dart +++ b/lib/samples/ai_samples/ai_datagrid/predictive_data_entry.dart @@ -29,6 +29,7 @@ class _PredictiveDataSampleState extends SampleViewState late List _studentDetails; bool _isLoading = false; late bool _isWebOrDesktop; + late bool isMaterial3; @override void initState() { @@ -76,6 +77,8 @@ class _PredictiveDataSampleState extends SampleViewState model.isFirstTime = false; } }); + + isMaterial3 = model.themeData.useMaterial3; } String _generatePrompt() { @@ -213,6 +216,9 @@ class _PredictiveDataSampleState extends SampleViewState ), ), ], + elevation: 0, + scrolledUnderElevation: 0, + backgroundColor: isMaterial3 ? Colors.transparent : null, ), ), body: Stack( @@ -629,10 +635,10 @@ class StudentDataSource extends DataGridSource { columnWidthMode: !isWebOrDesktop ? ColumnWidthMode.none : ColumnWidthMode.fill, - width: !isWebOrDesktop - ? 120 - : (isWebOrDesktop && model.isMobileResolution) - ? 150.0 + width: isWebOrDesktop + ? double.nan + : (!isWebOrDesktop && model.isMobileResolution) + ? 120.0 : double.nan, label: Container( padding: const EdgeInsets.all(16.0), @@ -646,9 +652,9 @@ class StudentDataSource extends DataGridSource { columnWidthMode: !isWebOrDesktop ? ColumnWidthMode.none : ColumnWidthMode.fill, - width: !isWebOrDesktop - ? 150 - : (isWebOrDesktop && model.isMobileResolution) + width: isWebOrDesktop + ? double.nan + : (!isWebOrDesktop && model.isMobileResolution) ? 150.0 : double.nan, label: Container( @@ -663,9 +669,9 @@ class StudentDataSource extends DataGridSource { columnWidthMode: !isWebOrDesktop ? ColumnWidthMode.none : ColumnWidthMode.fill, - width: !isWebOrDesktop - ? 150 - : (isWebOrDesktop && model.isMobileResolution) + width: isWebOrDesktop + ? double.nan + : (!isWebOrDesktop && model.isMobileResolution) ? 150.0 : double.nan, label: Container( @@ -680,9 +686,9 @@ class StudentDataSource extends DataGridSource { columnWidthMode: !isWebOrDesktop ? ColumnWidthMode.none : ColumnWidthMode.fill, - width: !isWebOrDesktop - ? 150 - : (isWebOrDesktop && model.isMobileResolution) + width: isWebOrDesktop + ? double.nan + : (!isWebOrDesktop && model.isMobileResolution) ? 150.0 : double.nan, label: Container( @@ -697,9 +703,9 @@ class StudentDataSource extends DataGridSource { columnWidthMode: !isWebOrDesktop ? ColumnWidthMode.none : ColumnWidthMode.fill, - width: !isWebOrDesktop - ? 150 - : (isWebOrDesktop && model.isMobileResolution) + width: isWebOrDesktop + ? double.nan + : (!isWebOrDesktop && model.isMobileResolution) ? 150.0 : double.nan, label: Container( diff --git a/lib/samples/ai_samples/ai_datagrid/semantic_filtering.dart b/lib/samples/ai_samples/ai_datagrid/semantic_filtering.dart index ecc5680e..bcc7d624 100644 --- a/lib/samples/ai_samples/ai_datagrid/semantic_filtering.dart +++ b/lib/samples/ai_samples/ai_datagrid/semantic_filtering.dart @@ -25,6 +25,7 @@ class _SemanticFilteringSampleState extends SampleViewState final TextEditingController searchController = TextEditingController(); late bool _isWebOrDesktop; late ValueNotifier isLoadingNotifier; + late bool isMaterial3; @override void initState() { @@ -43,6 +44,7 @@ class _SemanticFilteringSampleState extends SampleViewState model: model, ); isLoadingNotifier = ValueNotifier(false); + isMaterial3 = model.themeData.useMaterial3; } String _generatePrompt() { @@ -139,64 +141,77 @@ Now, generate a similar list for "${searchController.text}". } } + Widget _buildSearchTextField() { + return SizedBox( + width: 250, + height: 45, + child: ValueListenableBuilder( + valueListenable: searchController, + builder: (context, value, child) { + return TextField( + controller: searchController, + onSubmitted: (value) { + if (value.trim().isNotEmpty) { + _sendChatMessage(_generatePrompt()); + } + }, + decoration: InputDecoration( + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 8, + ), + hintText: 'Search', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular( + _isWebOrDesktop || !isMaterial3 ? 4 : 100, + ), + ), + suffixIcon: searchController.text.isNotEmpty + ? IconButton( + icon: const Icon(Icons.clear), + onPressed: () { + searchController.clear(); + isLoadingNotifier.value = true; + + Future.delayed(const Duration(seconds: 1), () { + _patientDataSource.clearFilters(); + isLoadingNotifier.value = false; + }); + }, + ) + : null, + ), + onChanged: (value) { + if (value.trim().isEmpty) { + isLoadingNotifier.value = true; + + Future.delayed(const Duration(seconds: 1), () { + _patientDataSource.clearFilters(); + isLoadingNotifier.value = false; + }); + return; + } + }, + ); + }, + ), + ); + } + Widget searchField() { return Padding( - padding: const EdgeInsets.only(left: 12, top: 10), + padding: EdgeInsets.only( + left: 12, + top: 10, + right: !_isWebOrDesktop && model.isMobileResolution ? 12 : 0, + ), child: Row( mainAxisSize: MainAxisSize.min, children: [ - SizedBox( - width: 250, - height: 45, - child: ValueListenableBuilder( - valueListenable: searchController, - builder: (context, value, child) { - return TextField( - controller: searchController, - onSubmitted: (value) { - if (value.trim().isNotEmpty) { - _sendChatMessage(_generatePrompt()); - } - }, - decoration: InputDecoration( - contentPadding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 8, - ), - hintText: 'Search', - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(4), - ), - suffixIcon: searchController.text.isNotEmpty - ? IconButton( - icon: const Icon(Icons.clear), - onPressed: () { - searchController.clear(); - isLoadingNotifier.value = true; - - Future.delayed(const Duration(seconds: 1), () { - _patientDataSource.clearFilters(); - isLoadingNotifier.value = false; - }); - }, - ) - : null, - ), - onChanged: (value) { - if (value.trim().isEmpty) { - isLoadingNotifier.value = true; - - Future.delayed(const Duration(seconds: 1), () { - _patientDataSource.clearFilters(); - isLoadingNotifier.value = false; - }); - return; - } - }, - ); - }, - ), - ), + if (!_isWebOrDesktop && model.isMobileResolution) + Expanded(child: _buildSearchTextField()) + else + _buildSearchTextField(), const SizedBox(width: 10), SizedBox( height: 45, @@ -233,6 +248,9 @@ Now, generate a similar list for "${searchController.text}". ), ), automaticallyImplyLeading: false, + elevation: 0, + scrolledUnderElevation: 0, + backgroundColor: isMaterial3 ? Colors.transparent : null, ), ), body: Column( @@ -242,7 +260,12 @@ Now, generate a similar list for "${searchController.text}". padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row( mainAxisAlignment: MainAxisAlignment.end, - children: [searchField()], + children: [ + if (!_isWebOrDesktop && model.isMobileResolution) + Expanded(child: searchField()) + else + searchField(), + ], ), ), Expanded( @@ -251,8 +274,10 @@ Now, generate a similar list for "${searchController.text}". SfDataGrid( source: _patientDataSource, columns: _patientDataSource._columns, - columnWidthMode: ColumnWidthMode.auto, - onQueryRowHeight: (details) => 60.0, + columnWidthMode: !_isWebOrDesktop && !model.isMobileResolution + ? ColumnWidthMode.fill + : ColumnWidthMode.auto, + onQueryRowHeight: (details) => _isWebOrDesktop ? 60.0 : 80.0, placeholder: const Center( child: Text( 'No Records Found', @@ -550,9 +575,9 @@ class PatientDataSource extends DataGridSource { columnWidthMode: !isWebOrDesktop ? ColumnWidthMode.none : ColumnWidthMode.fill, - width: !isWebOrDesktop - ? 150 - : (isWebOrDesktop && model.isMobileResolution) + width: isWebOrDesktop + ? double.nan + : (!isWebOrDesktop && model.isMobileResolution) ? 150.0 : double.nan, label: Container( @@ -567,9 +592,9 @@ class PatientDataSource extends DataGridSource { columnWidthMode: !isWebOrDesktop ? ColumnWidthMode.none : ColumnWidthMode.fill, - width: !isWebOrDesktop - ? 150 - : (isWebOrDesktop && model.isMobileResolution) + width: isWebOrDesktop + ? double.nan + : (!isWebOrDesktop && model.isMobileResolution) ? 150.0 : double.nan, label: Container( @@ -584,9 +609,9 @@ class PatientDataSource extends DataGridSource { columnWidthMode: !isWebOrDesktop ? ColumnWidthMode.none : ColumnWidthMode.fill, - width: !isWebOrDesktop - ? 150 - : (isWebOrDesktop && model.isMobileResolution) + width: isWebOrDesktop + ? double.nan + : (!isWebOrDesktop && model.isMobileResolution) ? 150.0 : double.nan, label: Container( @@ -601,9 +626,9 @@ class PatientDataSource extends DataGridSource { columnWidthMode: !isWebOrDesktop ? ColumnWidthMode.none : ColumnWidthMode.fill, - width: !isWebOrDesktop - ? 150 - : (isWebOrDesktop && model.isMobileResolution) + width: isWebOrDesktop + ? double.nan + : (!isWebOrDesktop && model.isMobileResolution) ? 150.0 : double.nan, label: Container( @@ -618,9 +643,9 @@ class PatientDataSource extends DataGridSource { columnWidthMode: !isWebOrDesktop ? ColumnWidthMode.none : ColumnWidthMode.fill, - width: !isWebOrDesktop - ? 150 - : (isWebOrDesktop && model.isMobileResolution) + width: isWebOrDesktop + ? double.nan + : (!isWebOrDesktop && model.isMobileResolution) ? 150.0 : double.nan, label: Container( diff --git a/lib/samples/ai_samples/ai_maps/location_finder.dart b/lib/samples/ai_samples/ai_maps/location_finder.dart index 9a9435fd..ce6ad9c5 100644 --- a/lib/samples/ai_samples/ai_maps/location_finder.dart +++ b/lib/samples/ai_samples/ai_maps/location_finder.dart @@ -173,8 +173,7 @@ class _MapLocationFinderState extends SampleViewState ? 'Search location' : 'Hospital in New York', hintStyle: TextStyle( - color: model.themeData.colorScheme.primary - .withValues(alpha: 0.6), + color: model.themeData.colorScheme.secondary, ), filled: true, fillColor: @@ -318,7 +317,7 @@ class _MapLocationFinderState extends SampleViewState ), icon: Icon( Icons.search, - color: model.themeData.colorScheme.primary, + color: model.themeData.colorScheme.secondary, ), onPressed: () => _isButtonVisible ? _handleSearch(_textFieldController.text) diff --git a/lib/samples/barcodes/data_matrix.dart b/lib/samples/barcodes/data_matrix.dart index 905a886d..77547eaa 100644 --- a/lib/samples/barcodes/data_matrix.dart +++ b/lib/samples/barcodes/data_matrix.dart @@ -69,12 +69,12 @@ class _DataMatrixGeneratorState extends SampleViewState { shrinkWrap: true, children: [ Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 10), + padding: const EdgeInsets.fromLTRB(0, 10, 0, 10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Input value: ', + 'Input value:', overflow: TextOverflow.clip, softWrap: false, style: TextStyle( @@ -83,8 +83,7 @@ class _DataMatrixGeneratorState extends SampleViewState { color: model.textColor, ), ), - Container( - padding: const EdgeInsets.fromLTRB(0, 5, 10, 0), + SizedBox( height: 50, child: Align( alignment: Alignment.bottomLeft, diff --git a/lib/samples/barcodes/qr_code.dart b/lib/samples/barcodes/qr_code.dart index e506dccc..7a0de236 100644 --- a/lib/samples/barcodes/qr_code.dart +++ b/lib/samples/barcodes/qr_code.dart @@ -86,159 +86,128 @@ class _QRCodeGeneratorState extends SampleViewState { builder: (BuildContext context, StateSetter stateSetter) { return ListView( shrinkWrap: true, - padding: const EdgeInsets.fromLTRB(10, 10, 0, 10), + padding: const EdgeInsets.fromLTRB(0, 10, 0, 10), children: [ - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), - child: SizedBox( - height: 100, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Input value: ', - overflow: TextOverflow.clip, - softWrap: false, - style: TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.bold, - color: model.textColor, - ), + SizedBox( + height: 100, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Input value:', + overflow: TextOverflow.clip, + softWrap: false, + style: TextStyle( + fontSize: 16.0, + fontWeight: FontWeight.bold, + color: model.textColor, ), - Container( - padding: const EdgeInsets.fromLTRB(0, 5, 0, 0), - height: 50, - child: Align( - alignment: Alignment.bottomLeft, - child: Theme( - data: Theme.of( - context, - ).copyWith(canvasColor: model.drawerBackgroundColor), - child: TextField( - style: TextStyle(color: model.textColor), - decoration: InputDecoration( - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide(color: model.textColor), - ), - ), - keyboardType: TextInputType.text, - onChanged: (String text) { - setState(() { - _inputValue = text; - }); - }, - controller: _textEditingController, + ), + Align( + alignment: Alignment.bottomLeft, + child: Theme( + data: Theme.of( + context, + ).copyWith(canvasColor: model.drawerBackgroundColor), + child: TextField( + style: TextStyle(color: model.textColor), + decoration: InputDecoration( + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide(color: model.textColor), ), ), + keyboardType: TextInputType.text, + onChanged: (String text) { + setState(() { + _inputValue = text; + }); + }, + controller: _textEditingController, ), ), - ], - ), + ), + ], ), ), - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), - child: SizedBox( - height: 50, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: Text( - 'Input mode:', - overflow: TextOverflow.clip, - softWrap: false, - style: TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.bold, - color: model.textColor, - ), - ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + 'Input mode:', + overflow: TextOverflow.clip, + softWrap: false, + style: TextStyle( + fontSize: 16.0, + fontWeight: FontWeight.bold, + color: model.textColor, ), - Expanded( - child: Container( - height: 50, - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container( - color: const Color(0xFFBDBDBD), - height: 1, - ), - value: _selectedInputMode, - items: _encoding.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'binary', - child: Text( - value, - textAlign: TextAlign.center, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (String? value) { - _onInputModeChanged(value.toString()); - stateSetter(() {}); - }, - ), - ), - ), - ], + ), ), - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), - child: SizedBox( - height: 70, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( + DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container( + color: const Color(0xFFBDBDBD), + height: 1, + ), + value: _selectedInputMode, + items: _encoding.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'binary', child: Text( - 'Error level: ', - overflow: TextOverflow.clip, - softWrap: false, - style: TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.bold, - color: model.textColor, - ), + value, + textAlign: TextAlign.center, + style: TextStyle(color: model.textColor), ), + ); + }).toList(), + onChanged: (String? value) { + _onInputModeChanged(value.toString()); + stateSetter(() {}); + }, + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + 'Error level:', + overflow: TextOverflow.clip, + softWrap: false, + style: TextStyle( + fontSize: 16.0, + fontWeight: FontWeight.bold, + color: model.textColor, ), - Expanded( - child: Container( - height: 50, - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container( - color: const Color(0xFFBDBDBD), - height: 1, - ), - value: _selectedErrorCorrectionLevel, - items: _errorCorrectionLevels.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'quartile', - child: Text( - value, - textAlign: TextAlign.center, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (String? value) { - _onErrorCorrectionLevelChanged(value.toString()); - stateSetter(() {}); - }, - ), + ), + ), + DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container( + color: const Color(0xFFBDBDBD), + height: 1, + ), + value: _selectedErrorCorrectionLevel, + items: _errorCorrectionLevels.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'quartile', + child: Text( + value, + textAlign: TextAlign.center, + style: TextStyle(color: model.textColor), ), - ), - ], + ); + }).toList(), + onChanged: (String? value) { + _onErrorCorrectionLevelChanged(value.toString()); + stateSetter(() {}); + }, ), - ), + ], ), ], ); diff --git a/lib/samples/calendar/agenda_view.dart b/lib/samples/calendar/agenda_view.dart index 7af36472..b28dcc33 100644 --- a/lib/samples/calendar/agenda_view.dart +++ b/lib/samples/calendar/agenda_view.dart @@ -99,7 +99,7 @@ class _AgendaViewCalendarState extends SampleViewState { colorCollection.add(const Color(0xFF0A8043)); final List<_Meeting> meetings = <_Meeting>[]; - final Random random = Random(); + final Random random = Random.secure(); final DateTime rangeStartDate = DateTime.now().add( const Duration(days: -(365 ~/ 2)), ); diff --git a/lib/samples/calendar/airfare.dart b/lib/samples/calendar/airfare.dart index 313d6e7d..544319f0 100644 --- a/lib/samples/calendar/airfare.dart +++ b/lib/samples/calendar/airfare.dart @@ -184,7 +184,7 @@ class _AirFareCalendarCalendarState extends SampleViewState { BuildContext buildContext, MonthCellDetails details, ) { - final Random random = Random(); + final Random random = Random.secure(); final bool isToday = isSameDate(details.date, DateTime.now()); final AirFare airFare = _airFareDataCollection[random.nextInt(100)]; final Color defaultColor = diff --git a/lib/samples/calendar/appointment_editor.dart b/lib/samples/calendar/appointment_editor.dart index 6d14d26e..fd26f430 100644 --- a/lib/samples/calendar/appointment_editor.dart +++ b/lib/samples/calendar/appointment_editor.dart @@ -470,7 +470,7 @@ class _CalendarAppointmentEditorState extends SampleViewState { _timeZoneCollection.add('Yakutsk Standard Time'); final DateTime today = DateTime.now(); - final Random random = Random(); + final Random random = Random.secure(); for (int month = -1; month < 2; month++) { for (int day = -5; day < 5; day++) { for (int hour = 9; hour < 18; hour += 5) { @@ -2285,187 +2285,62 @@ class _SelectRuleDialogState extends State<_SelectRuleDialog> { Container( width: 360, padding: const EdgeInsets.only(bottom: 10), - child: Column( - children: [ - RadioListTile<_SelectRule>( - title: const Text('Does not repeat'), - value: _SelectRule.doesNotRepeat, - groupValue: _rule, - toggleable: true, - activeColor: widget.model.primaryColor, - onChanged: (_SelectRule? value) { - setState(() { - if (value != null) { - _rule = value; - widget.recurrenceProperties = null; - widget.onChanged( - PickerChangedDetails(selectedRule: _rule), - ); - } - }); - Navigator.pop(context, widget.recurrenceProperties); - }, - ), - RadioListTile<_SelectRule>( - title: const Text('Every day'), - value: _SelectRule.everyDay, - toggleable: true, - groupValue: _rule, - activeColor: widget.model.primaryColor, - onChanged: (_SelectRule? value) { - setState(() { - if (value != null) { - _rule = value; - widget.recurrenceProperties = RecurrenceProperties( - startDate: _startDate, - ); - widget.recurrenceProperties!.recurrenceType = - RecurrenceType.daily; - widget.recurrenceProperties!.interval = 1; - widget.recurrenceProperties!.recurrenceRange = - RecurrenceRange.noEndDate; - widget.onChanged( - PickerChangedDetails(selectedRule: _rule), - ); - } - }); - Navigator.pop(context, widget.recurrenceProperties); - }, - ), - RadioListTile<_SelectRule>( - title: const Text('Every week'), - value: _SelectRule.everyWeek, - toggleable: true, - groupValue: _rule, - activeColor: widget.model.primaryColor, - onChanged: (_SelectRule? value) { - setState(() { - if (value != null) { - _rule = value; - widget.recurrenceProperties = RecurrenceProperties( - startDate: _startDate, - ); - widget.recurrenceProperties!.recurrenceType = - RecurrenceType.weekly; - widget.recurrenceProperties!.interval = 1; - widget.recurrenceProperties!.recurrenceRange = - RecurrenceRange.noEndDate; - widget.recurrenceProperties!.weekDays = - _startDate.weekday == 1 - ? [WeekDays.monday] - : _startDate.weekday == 2 - ? [WeekDays.tuesday] - : _startDate.weekday == 3 - ? [WeekDays.wednesday] - : _startDate.weekday == 4 - ? [WeekDays.thursday] - : _startDate.weekday == 5 - ? [WeekDays.friday] - : _startDate.weekday == 6 - ? [WeekDays.saturday] - : [WeekDays.sunday]; - widget.onChanged( - PickerChangedDetails(selectedRule: _rule), - ); - } - }); - Navigator.pop(context, widget.recurrenceProperties); - }, - ), - RadioListTile<_SelectRule>( - title: const Text('Every month'), - value: _SelectRule.everyMonth, - toggleable: true, - groupValue: _rule, - activeColor: widget.model.primaryColor, - onChanged: (_SelectRule? value) { - setState(() { - if (value != null) { - _rule = value; - widget.recurrenceProperties = RecurrenceProperties( - startDate: _startDate, - ); - widget.recurrenceProperties!.recurrenceType = - RecurrenceType.monthly; - widget.recurrenceProperties!.interval = 1; - widget.recurrenceProperties!.recurrenceRange = - RecurrenceRange.noEndDate; - widget.recurrenceProperties!.dayOfMonth = - widget.selectedAppointment!.startTime.day; - widget.onChanged( - PickerChangedDetails(selectedRule: _rule), - ); - } - }); - Navigator.pop(context, widget.recurrenceProperties); - }, - ), - RadioListTile<_SelectRule>( - title: const Text('Every year'), - value: _SelectRule.everyYear, - toggleable: true, - groupValue: _rule, - activeColor: widget.model.primaryColor, - onChanged: (_SelectRule? value) { - setState(() { - if (value != null) { - _rule = value; - widget.recurrenceProperties = RecurrenceProperties( - startDate: _startDate, - ); - widget.recurrenceProperties!.recurrenceType = - RecurrenceType.yearly; - widget.recurrenceProperties!.interval = 1; - widget.recurrenceProperties!.recurrenceRange = - RecurrenceRange.noEndDate; - widget.recurrenceProperties!.month = - widget.selectedAppointment!.startTime.month; - widget.recurrenceProperties!.dayOfMonth = - widget.selectedAppointment!.startTime.day; - widget.onChanged( - PickerChangedDetails(selectedRule: _rule), - ); - } - }); + child: RadioGroup<_SelectRule>( + groupValue: _rule, + onChanged: (_SelectRule? value) async { + dynamic properties; + if (value != null && value == _SelectRule.custom) { + properties = await _navigateToCustomRule(context); + } + _handleSpecificRuleLogics(value, properties); + if (context.mounted && value != null) { + if (value == _SelectRule.custom) { + Navigator.pop(context, properties); + } else { Navigator.pop(context, widget.recurrenceProperties); - }, - ), - RadioListTile<_SelectRule>( - title: const Text('Custom'), - value: _SelectRule.custom, - toggleable: true, - groupValue: _rule, - activeColor: widget.model.primaryColor, - onChanged: (_SelectRule? value) async { - final dynamic properties = await Navigator.push( - context, - MaterialPageRoute( - builder: (BuildContext context) => _CustomRule( - widget.model, - widget.selectedAppointment!, - widget.appointmentColor, - widget.events, - widget.recurrenceProperties, - ), - ), - ); - if (properties != widget.recurrenceProperties) { - setState(() { - _rule = _SelectRule.custom; - widget.onChanged( - PickerChangedDetails(selectedRule: _rule), - ); - }); - } - if (!mounted) { - return; - } - if (context.mounted) { - Navigator.pop(context, properties); - } - }, - ), - ], + } + } + }, + child: Column( + children: [ + RadioListTile<_SelectRule>( + title: const Text('Does not repeat'), + value: _SelectRule.doesNotRepeat, + toggleable: true, + activeColor: widget.model.primaryColor, + ), + RadioListTile<_SelectRule>( + title: const Text('Every day'), + value: _SelectRule.everyDay, + toggleable: true, + activeColor: widget.model.primaryColor, + ), + RadioListTile<_SelectRule>( + title: const Text('Every week'), + value: _SelectRule.everyWeek, + toggleable: true, + activeColor: widget.model.primaryColor, + ), + RadioListTile<_SelectRule>( + title: const Text('Every month'), + value: _SelectRule.everyMonth, + toggleable: true, + activeColor: widget.model.primaryColor, + ), + RadioListTile<_SelectRule>( + title: const Text('Every year'), + value: _SelectRule.everyYear, + toggleable: true, + activeColor: widget.model.primaryColor, + ), + RadioListTile<_SelectRule>( + title: const Text('Custom'), + value: _SelectRule.custom, + toggleable: true, + activeColor: widget.model.primaryColor, + ), + ], + ), ), ), ], @@ -2473,6 +2348,134 @@ class _SelectRuleDialogState extends State<_SelectRuleDialog> { ), ); } + + void _handleSpecificRuleLogics(_SelectRule? value, properties) { + setState(() { + if (value != null) { + switch (value) { + case _SelectRule.doesNotRepeat: + { + _doesNotRepeatRule(value); + break; + } + case _SelectRule.everyDay: + { + _everyDayRule(value); + break; + } + case _SelectRule.everyWeek: + { + _everyWeekRule(value); + break; + } + case _SelectRule.everyMonth: + { + _everyMonthRule(value); + break; + } + case _SelectRule.everyYear: + { + _everyYearRule(value); + break; + } + case _SelectRule.custom: + { + _onChangedCustomRule(properties); + if (!mounted) { + return; + } + break; + } + } + } + }); + } + + void _onChangedCustomRule(properties) { + if (properties != widget.recurrenceProperties) { + _rule = _SelectRule.custom; + widget.onChanged(PickerChangedDetails(selectedRule: _rule)); + } + } + + Future _navigateToCustomRule(BuildContext context) async { + final dynamic properties = await Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => _CustomRule( + widget.model, + widget.selectedAppointment!, + widget.appointmentColor, + widget.events, + widget.recurrenceProperties, + ), + ), + ); + return properties; + } + + void _everyYearRule(_SelectRule value) { + _rule = value; + widget.recurrenceProperties = RecurrenceProperties(startDate: _startDate); + widget.recurrenceProperties!.recurrenceType = RecurrenceType.yearly; + widget.recurrenceProperties!.interval = 1; + widget.recurrenceProperties!.recurrenceRange = RecurrenceRange.noEndDate; + widget.recurrenceProperties!.month = + widget.selectedAppointment!.startTime.month; + widget.recurrenceProperties!.dayOfMonth = + widget.selectedAppointment!.startTime.day; + widget.onChanged(PickerChangedDetails(selectedRule: _rule)); + } + + void _everyMonthRule(_SelectRule value) { + _rule = value; + widget.recurrenceProperties = RecurrenceProperties(startDate: _startDate); + widget.recurrenceProperties!.recurrenceType = RecurrenceType.monthly; + widget.recurrenceProperties!.interval = 1; + widget.recurrenceProperties!.recurrenceRange = RecurrenceRange.noEndDate; + widget.recurrenceProperties!.dayOfMonth = + widget.selectedAppointment!.startTime.day; + widget.onChanged(PickerChangedDetails(selectedRule: _rule)); + } + + void _everyWeekRule(_SelectRule value) { + _rule = value; + widget.recurrenceProperties = RecurrenceProperties(startDate: _startDate); + widget.recurrenceProperties!.recurrenceType = RecurrenceType.weekly; + widget.recurrenceProperties!.interval = 1; + widget.recurrenceProperties!.recurrenceRange = RecurrenceRange.noEndDate; + widget.recurrenceProperties!.weekDays = _startDate.weekday == 1 + ? [WeekDays.monday] + : _startDate.weekday == 2 + ? [WeekDays.tuesday] + : _startDate.weekday == 3 + ? [WeekDays.wednesday] + : _startDate.weekday == 4 + ? [WeekDays.thursday] + : _startDate.weekday == 5 + ? [WeekDays.friday] + : _startDate.weekday == 6 + ? [WeekDays.saturday] + : [WeekDays.sunday]; + widget.onChanged(PickerChangedDetails(selectedRule: _rule)); + } + + void _everyDayRule(_SelectRule value) { + _rule = value; + widget.recurrenceProperties = RecurrenceProperties(startDate: _startDate); + widget.recurrenceProperties!.recurrenceType = RecurrenceType.daily; + widget.recurrenceProperties!.interval = 1; + widget.recurrenceProperties!.recurrenceRange = RecurrenceRange.noEndDate; + widget.onChanged(PickerChangedDetails(selectedRule: _rule)); + } + + void _doesNotRepeatRule(_SelectRule? value) { + if (value != null) { + _rule = value; + widget.recurrenceProperties = null; + widget.onChanged(PickerChangedDetails(selectedRule: _rule)); + } + } } class _DeleteDialog extends StatefulWidget { @@ -2510,108 +2513,79 @@ class _DeleteDialogState extends State<_DeleteDialog> { child: Container( width: 370, padding: const EdgeInsets.only(bottom: 10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - height: 30, - padding: const EdgeInsets.only(left: 25, top: 5), - child: Text( - 'Delete recurring event', - style: TextStyle( - color: defaultTextColor, - fontWeight: FontWeight.w500, + child: RadioGroup<_Delete>( + groupValue: _delete, + onChanged: (_Delete? value) { + setState(() { + _delete = value!; + }); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: 30, + padding: const EdgeInsets.only(left: 25, top: 5), + child: Text( + 'Delete recurring event', + style: TextStyle( + color: defaultTextColor, + fontWeight: FontWeight.w500, + ), ), ), - ), - Container(width: 20), - RadioListTile<_Delete>( - title: const Text('This event'), - value: _Delete.event, - groupValue: _delete, - activeColor: widget.model.primaryColor, - onChanged: (_Delete? value) { - setState(() { - _delete = value!; - }); - }, - ), - RadioListTile<_Delete>( - title: const Text('All events'), - value: _Delete.series, - groupValue: _delete, - activeColor: widget.model.primaryColor, - onChanged: (_Delete? value) { - setState(() { - _delete = value!; - }); - }, - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - RawMaterialButton( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(4)), - ), - onPressed: () { - Navigator.pop(context); - }, - child: Text( - 'Cancel', - style: TextStyle( - color: widget.model.primaryColor, - fontWeight: FontWeight.w500, + Container(width: 20), + RadioListTile<_Delete>( + title: const Text('This event'), + value: _Delete.event, + activeColor: widget.model.primaryColor, + ), + RadioListTile<_Delete>( + title: const Text('All events'), + value: _Delete.series, + activeColor: widget.model.primaryColor, + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + RawMaterialButton( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(4)), + ), + onPressed: () { + Navigator.pop(context); + }, + child: Text( + 'Cancel', + style: TextStyle( + color: widget.model.primaryColor, + fontWeight: FontWeight.w500, + ), ), ), - ), - RawMaterialButton( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(4)), - ), - onPressed: () { - Navigator.pop(context); - final Appointment? parentAppointment = - widget.events.getPatternAppointment( - widget.selectedAppointment, - '', - ) - as Appointment?; - if (_delete == _Delete.event) { - if (widget.selectedAppointment.recurrenceId != null) { - widget.events.appointments!.remove( - widget.selectedAppointment, - ); - widget.events.notifyListeners( - CalendarDataSourceAction.remove, - [widget.selectedAppointment], - ); - } - widget.events.appointments!.removeAt( - widget.events.appointments!.indexOf( - parentAppointment, - ), - ); - widget.events.notifyListeners( - CalendarDataSourceAction.remove, - [parentAppointment!], - ); - parentAppointment.recurrenceExceptionDates != null - ? parentAppointment.recurrenceExceptionDates!.add( - widget.selectedAppointment.startTime, - ) - : parentAppointment.recurrenceExceptionDates = - [ - widget.selectedAppointment.startTime, - ]; - widget.events.appointments!.add(parentAppointment); - widget.events.notifyListeners( - CalendarDataSourceAction.add, - [parentAppointment], - ); - } else { - if (parentAppointment!.recurrenceExceptionDates == - null) { + RawMaterialButton( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(4)), + ), + onPressed: () { + Navigator.pop(context); + final Appointment? parentAppointment = + widget.events.getPatternAppointment( + widget.selectedAppointment, + '', + ) + as Appointment?; + if (_delete == _Delete.event) { + if (widget.selectedAppointment.recurrenceId != + null) { + widget.events.appointments!.remove( + widget.selectedAppointment, + ); + widget.events.notifyListeners( + CalendarDataSourceAction.remove, + [widget.selectedAppointment], + ); + } widget.events.appointments!.removeAt( widget.events.appointments!.indexOf( parentAppointment, @@ -2619,53 +2593,78 @@ class _DeleteDialogState extends State<_DeleteDialog> { ); widget.events.notifyListeners( CalendarDataSourceAction.remove, + [parentAppointment!], + ); + parentAppointment.recurrenceExceptionDates != null + ? parentAppointment.recurrenceExceptionDates! + .add(widget.selectedAppointment.startTime) + : parentAppointment.recurrenceExceptionDates = + [ + widget.selectedAppointment.startTime, + ]; + widget.events.appointments!.add(parentAppointment); + widget.events.notifyListeners( + CalendarDataSourceAction.add, [parentAppointment], ); } else { - final List? exceptionDates = - parentAppointment.recurrenceExceptionDates; - for (int i = 0; i < exceptionDates!.length; i++) { - final Appointment? changedOccurrence = widget - .events - .getOccurrenceAppointment( - parentAppointment, - exceptionDates[i], - '', + if (parentAppointment!.recurrenceExceptionDates == + null) { + widget.events.appointments!.removeAt( + widget.events.appointments!.indexOf( + parentAppointment, + ), + ); + widget.events.notifyListeners( + CalendarDataSourceAction.remove, + [parentAppointment], + ); + } else { + final List? exceptionDates = + parentAppointment.recurrenceExceptionDates; + for (int i = 0; i < exceptionDates!.length; i++) { + final Appointment? changedOccurrence = widget + .events + .getOccurrenceAppointment( + parentAppointment, + exceptionDates[i], + '', + ); + if (changedOccurrence != null) { + widget.events.appointments!.remove( + changedOccurrence, ); - if (changedOccurrence != null) { - widget.events.appointments!.remove( - changedOccurrence, - ); - widget.events.notifyListeners( - CalendarDataSourceAction.remove, - [changedOccurrence], - ); + widget.events.notifyListeners( + CalendarDataSourceAction.remove, + [changedOccurrence], + ); + } } + widget.events.appointments!.removeAt( + widget.events.appointments!.indexOf( + parentAppointment, + ), + ); + widget.events.notifyListeners( + CalendarDataSourceAction.remove, + [parentAppointment], + ); } - widget.events.appointments!.removeAt( - widget.events.appointments!.indexOf( - parentAppointment, - ), - ); - widget.events.notifyListeners( - CalendarDataSourceAction.remove, - [parentAppointment], - ); } - } - Navigator.pop(context); - }, - child: Text( - 'Delete', - style: TextStyle( - fontWeight: FontWeight.w500, - color: widget.model.primaryColor, + Navigator.pop(context); + }, + child: Text( + 'Delete', + style: TextStyle( + fontWeight: FontWeight.w500, + color: widget.model.primaryColor, + ), ), ), - ), - ], - ), - ], + ], + ), + ], + ), ), ), ), @@ -2716,231 +2715,229 @@ class _EditDialogState extends State<_EditDialog> { child: Container( width: 370, padding: const EdgeInsets.only(bottom: 10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - height: 30, - padding: const EdgeInsets.only(left: 25, top: 5), - child: Text( - 'Save recurring event', - style: TextStyle( - color: defaultTextColor, - fontWeight: FontWeight.w500, + child: RadioGroup<_Edit>( + groupValue: _edit, + onChanged: (_Edit? value) { + setState(() { + _edit = value!; + }); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: 30, + padding: const EdgeInsets.only(left: 25, top: 5), + child: Text( + 'Save recurring event', + style: TextStyle( + color: defaultTextColor, + fontWeight: FontWeight.w500, + ), ), ), - ), - Container(width: 20), - RadioListTile<_Edit>( - title: const Text('This event'), - value: _Edit.event, - groupValue: _edit, - activeColor: widget.model.primaryColor, - onChanged: (_Edit? value) { - setState(() { - _edit = value!; - }); - }, - ), - RadioListTile<_Edit>( - title: const Text('All events'), - value: _Edit.series, - groupValue: _edit, - activeColor: widget.model.primaryColor, - onChanged: (_Edit? value) { - setState(() { - _edit = value!; - }); - }, - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - RawMaterialButton( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(4)), - ), - onPressed: () { - Navigator.pop(context); - }, - child: Text( - 'Cancel', - style: TextStyle( - color: widget.model.primaryColor, - fontWeight: FontWeight.w500, + Container(width: 20), + RadioListTile<_Edit>( + title: const Text('This event'), + value: _Edit.event, + activeColor: widget.model.primaryColor, + ), + RadioListTile<_Edit>( + title: const Text('All events'), + value: _Edit.series, + activeColor: widget.model.primaryColor, + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + RawMaterialButton( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(4)), + ), + onPressed: () { + Navigator.pop(context); + }, + child: Text( + 'Cancel', + style: TextStyle( + color: widget.model.primaryColor, + fontWeight: FontWeight.w500, + ), ), ), - ), - RawMaterialButton( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(4)), - ), - onPressed: () { - if (_edit == _Edit.event) { - final Appointment? parentAppointment = - widget.events.getPatternAppointment( - widget.selectedAppointment, - '', - ) - as Appointment?; - final Appointment newAppointment = Appointment( - startTime: widget.newAppointment.startTime, - endTime: widget.newAppointment.endTime, - color: widget.newAppointment.color, - notes: widget.newAppointment.notes, - isAllDay: widget.newAppointment.isAllDay, - location: widget.newAppointment.location, - subject: widget.newAppointment.subject, - resourceIds: widget.newAppointment.resourceIds, - id: - widget.selectedAppointment.appointmentType == - AppointmentType.changedOccurrence - ? widget.selectedAppointment.id - : null, - recurrenceId: parentAppointment!.id, - startTimeZone: widget.newAppointment.startTimeZone, - endTimeZone: widget.newAppointment.endTimeZone, - ); - parentAppointment.recurrenceExceptionDates != null - ? parentAppointment.recurrenceExceptionDates!.add( - widget.selectedAppointment.startTime, - ) - : parentAppointment.recurrenceExceptionDates = - [ - widget.selectedAppointment.startTime, - ]; - widget.events.appointments!.removeAt( - widget.events.appointments!.indexOf( - parentAppointment, - ), - ); - widget.events.notifyListeners( - CalendarDataSourceAction.remove, - [parentAppointment], - ); - widget.events.appointments!.add(parentAppointment); - widget.events.notifyListeners( - CalendarDataSourceAction.add, - [parentAppointment], - ); - if (widget.selectedAppointment.appointmentType == - AppointmentType.changedOccurrence) { + RawMaterialButton( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(4)), + ), + onPressed: () { + if (_edit == _Edit.event) { + final Appointment? parentAppointment = + widget.events.getPatternAppointment( + widget.selectedAppointment, + '', + ) + as Appointment?; + final Appointment newAppointment = Appointment( + startTime: widget.newAppointment.startTime, + endTime: widget.newAppointment.endTime, + color: widget.newAppointment.color, + notes: widget.newAppointment.notes, + isAllDay: widget.newAppointment.isAllDay, + location: widget.newAppointment.location, + subject: widget.newAppointment.subject, + resourceIds: widget.newAppointment.resourceIds, + id: + widget.selectedAppointment.appointmentType == + AppointmentType.changedOccurrence + ? widget.selectedAppointment.id + : null, + recurrenceId: parentAppointment!.id, + startTimeZone: + widget.newAppointment.startTimeZone, + endTimeZone: widget.newAppointment.endTimeZone, + ); + parentAppointment.recurrenceExceptionDates != null + ? parentAppointment.recurrenceExceptionDates! + .add(widget.selectedAppointment.startTime) + : parentAppointment.recurrenceExceptionDates = + [ + widget.selectedAppointment.startTime, + ]; widget.events.appointments!.removeAt( widget.events.appointments!.indexOf( - widget.selectedAppointment, + parentAppointment, ), ); widget.events.notifyListeners( CalendarDataSourceAction.remove, - [widget.selectedAppointment], + [parentAppointment], ); - } - widget.events.appointments!.add(newAppointment); - widget.events.notifyListeners( - CalendarDataSourceAction.add, - [newAppointment], - ); - } else { - Appointment? parentAppointment = - widget.events.getPatternAppointment( - widget.selectedAppointment, - '', - ) - as Appointment?; - final List? exceptionDates = - parentAppointment!.recurrenceExceptionDates; - if (exceptionDates != null && - exceptionDates.isNotEmpty) { - for (int i = 0; i < exceptionDates.length; i++) { - final Appointment? changedOccurrence = widget - .events - .getOccurrenceAppointment( - parentAppointment, - exceptionDates[i], - '', + widget.events.appointments!.add(parentAppointment); + widget.events.notifyListeners( + CalendarDataSourceAction.add, + [parentAppointment], + ); + if (widget.selectedAppointment.appointmentType == + AppointmentType.changedOccurrence) { + widget.events.appointments!.removeAt( + widget.events.appointments!.indexOf( + widget.selectedAppointment, + ), + ); + widget.events.notifyListeners( + CalendarDataSourceAction.remove, + [widget.selectedAppointment], + ); + } + widget.events.appointments!.add(newAppointment); + widget.events.notifyListeners( + CalendarDataSourceAction.add, + [newAppointment], + ); + } else { + Appointment? parentAppointment = + widget.events.getPatternAppointment( + widget.selectedAppointment, + '', + ) + as Appointment?; + final List? exceptionDates = + parentAppointment!.recurrenceExceptionDates; + if (exceptionDates != null && + exceptionDates.isNotEmpty) { + for (int i = 0; i < exceptionDates.length; i++) { + final Appointment? changedOccurrence = widget + .events + .getOccurrenceAppointment( + parentAppointment, + exceptionDates[i], + '', + ); + if (changedOccurrence != null) { + widget.events.appointments!.remove( + changedOccurrence, ); - if (changedOccurrence != null) { - widget.events.appointments!.remove( - changedOccurrence, - ); - widget.events.notifyListeners( - CalendarDataSourceAction.remove, - [changedOccurrence], - ); + widget.events.notifyListeners( + CalendarDataSourceAction.remove, + [changedOccurrence], + ); + } } } - } - widget.events.appointments!.removeAt( - widget.events.appointments!.indexOf( - parentAppointment, - ), - ); - widget.events.notifyListeners( - CalendarDataSourceAction.remove, - [parentAppointment], - ); - DateTime startDate, endDate; - if (widget.newAppointment.startTime.isBefore( - parentAppointment.startTime, - )) { - startDate = widget.newAppointment.startTime; - endDate = widget.newAppointment.endTime; - } else { - startDate = DateTime( - parentAppointment.startTime.year, - parentAppointment.startTime.month, - parentAppointment.startTime.day, - widget.newAppointment.startTime.hour, - widget.newAppointment.startTime.minute, + widget.events.appointments!.removeAt( + widget.events.appointments!.indexOf( + parentAppointment, + ), ); - endDate = DateTime( - parentAppointment.endTime.year, - parentAppointment.endTime.month, - parentAppointment.endTime.day, - widget.newAppointment.endTime.hour, - widget.newAppointment.endTime.minute, + widget.events.notifyListeners( + CalendarDataSourceAction.remove, + [parentAppointment], + ); + DateTime startDate, endDate; + if (widget.newAppointment.startTime.isBefore( + parentAppointment.startTime, + )) { + startDate = widget.newAppointment.startTime; + endDate = widget.newAppointment.endTime; + } else { + startDate = DateTime( + parentAppointment.startTime.year, + parentAppointment.startTime.month, + parentAppointment.startTime.day, + widget.newAppointment.startTime.hour, + widget.newAppointment.startTime.minute, + ); + endDate = DateTime( + parentAppointment.endTime.year, + parentAppointment.endTime.month, + parentAppointment.endTime.day, + widget.newAppointment.endTime.hour, + widget.newAppointment.endTime.minute, + ); + } + parentAppointment = Appointment( + startTime: startDate, + endTime: endDate, + color: widget.newAppointment.color, + notes: widget.newAppointment.notes, + isAllDay: widget.newAppointment.isAllDay, + location: widget.newAppointment.location, + subject: widget.newAppointment.subject, + resourceIds: widget.newAppointment.resourceIds, + id: parentAppointment.id, + recurrenceRule: + widget.recurrenceProperties == null + ? null + : SfCalendar.generateRRule( + widget.recurrenceProperties!, + startDate, + endDate, + ), + startTimeZone: + widget.newAppointment.startTimeZone, + endTimeZone: widget.newAppointment.endTimeZone, + ); + widget.events.appointments!.add(parentAppointment); + widget.events.notifyListeners( + CalendarDataSourceAction.add, + [parentAppointment], ); } - parentAppointment = Appointment( - startTime: startDate, - endTime: endDate, - color: widget.newAppointment.color, - notes: widget.newAppointment.notes, - isAllDay: widget.newAppointment.isAllDay, - location: widget.newAppointment.location, - subject: widget.newAppointment.subject, - resourceIds: widget.newAppointment.resourceIds, - id: parentAppointment.id, - recurrenceRule: widget.recurrenceProperties == null - ? null - : SfCalendar.generateRRule( - widget.recurrenceProperties!, - startDate, - endDate, - ), - startTimeZone: widget.newAppointment.startTimeZone, - endTimeZone: widget.newAppointment.endTimeZone, - ); - widget.events.appointments!.add(parentAppointment); - widget.events.notifyListeners( - CalendarDataSourceAction.add, - [parentAppointment], - ); - } - Navigator.pop(context); - Navigator.pop(context); - }, - child: Text( - 'Save', - style: TextStyle( - fontWeight: FontWeight.w500, - color: widget.model.primaryColor, + Navigator.pop(context); + Navigator.pop(context); + }, + child: Text( + 'Save', + style: TextStyle( + fontWeight: FontWeight.w500, + color: widget.model.primaryColor, + ), ), ), - ), - ], - ), - ], + ], + ), + ], + ), ), ), ), @@ -3708,205 +3705,224 @@ class _CustomRuleState extends State<_CustomRule> { ), ), if (_selectedRecurrenceType == 'year') const Divider(thickness: 1), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Padding( - padding: EdgeInsets.only(left: 15, top: 15), - child: Text('ENDS'), - ), - RadioListTile<_EndRule>( - contentPadding: const EdgeInsets.only(left: 7), - title: const Text('Never'), - value: _EndRule.never, - groupValue: _endRule, - activeColor: widget.model.primaryColor, - onChanged: (_EndRule? value) { - setState(() { - _endRule = _EndRule.never; - _rangeNoEndDate(); - }); - }, - ), - const Divider(indent: 50, height: 1.0, thickness: 1), - RadioListTile<_EndRule>( - contentPadding: const EdgeInsets.only(left: 7), - title: Row( - children: [ - const Text('On'), - Container( - margin: const EdgeInsets.only(left: 5), - width: 110, - height: 40, - decoration: BoxDecoration( - color: Colors.grey.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(3), - ), - child: ButtonTheme( - minWidth: 30.0, - child: MaterialButton( - elevation: 0, - focusElevation: 0, - highlightElevation: 0, - disabledElevation: 0, - hoverElevation: 0, - onPressed: () async { - final DateTime? pickedDate = await showDatePicker( - context: context, - initialDate: _selectedDate, - firstDate: _startDate.isBefore(_firstDate) - ? _startDate - : _firstDate, - currentDate: _selectedDate, - lastDate: DateTime(2050), - builder: (BuildContext context, Widget? child) { - return Theme( - data: ThemeData( - brightness: widget - .model - .themeData - .colorScheme - .brightness, - colorScheme: getColorScheme( - widget.model, - true, - ), - ), - child: child!, - ); - }, - ); - if (pickedDate == null) { - return; - } - setState(() { - _endRule = _EndRule.endDate; - _recurrenceProperties!.recurrenceRange = - RecurrenceRange.endDate; - _selectedDate = DateTime( - pickedDate.year, - pickedDate.month, - pickedDate.day, - ); - _recurrenceProperties!.endDate = _selectedDate; - }); - }, - shape: const CircleBorder(), - child: Text( - DateFormat('MM/dd/yyyy').format(_selectedDate), - style: TextStyle( - fontSize: 13, - color: defaultTextColor, - fontWeight: FontWeight.w400, - ), - ), - ), - ), - ), - ], + RadioGroup<_EndRule>( + groupValue: _endRule, + onChanged: (_EndRule? value) { + if (value != null) { + setState(() { + switch (value) { + case _EndRule.never: + { + _neverEndRule(); + break; + } + case _EndRule.endDate: + { + _endDateEndRule(value); + break; + } + case _EndRule.count: + { + _countEndRule(value); + break; + } + } + }); + } + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Padding( + padding: EdgeInsets.only(left: 15, top: 15), + child: Text('ENDS'), ), - value: _EndRule.endDate, - groupValue: _endRule, - activeColor: widget.model.primaryColor, - onChanged: (_EndRule? value) { - setState(() { - _endRule = value; - _rangeEndDate(); - }); - }, - ), - const Divider(indent: 50, height: 1.0, thickness: 1), - SizedBox( - height: 40, - child: RadioListTile<_EndRule>( + RadioListTile<_EndRule>( + contentPadding: const EdgeInsets.only(left: 7), + title: const Text('Never'), + value: _EndRule.never, + activeColor: widget.model.primaryColor, + ), + const Divider(indent: 50, height: 1.0, thickness: 1), + RadioListTile<_EndRule>( contentPadding: const EdgeInsets.only(left: 7), title: Row( children: [ - const Text('After'), + const Text('On'), Container( - height: 40, - width: 60, - padding: const EdgeInsets.only(left: 5, bottom: 10), margin: const EdgeInsets.only(left: 5), - alignment: Alignment.topCenter, + width: 110, + height: 40, decoration: BoxDecoration( color: Colors.grey.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(3), ), - child: TextField( - readOnly: _endRule != _EndRule.count, - controller: TextEditingController.fromValue( - TextEditingValue( - text: _count.toString(), - selection: TextSelection.collapsed( - offset: _count.toString().length, + child: ButtonTheme( + minWidth: 30.0, + child: MaterialButton( + elevation: 0, + focusElevation: 0, + highlightElevation: 0, + disabledElevation: 0, + hoverElevation: 0, + onPressed: () async { + final DateTime? pickedDate = await showDatePicker( + context: context, + initialDate: _selectedDate, + firstDate: _startDate.isBefore(_firstDate) + ? _startDate + : _firstDate, + currentDate: _selectedDate, + lastDate: DateTime(2050), + builder: (BuildContext context, Widget? child) { + return Theme( + data: ThemeData( + brightness: widget + .model + .themeData + .colorScheme + .brightness, + colorScheme: getColorScheme( + widget.model, + true, + ), + ), + child: child!, + ); + }, + ); + if (pickedDate == null) { + return; + } + setState(() { + _endRule = _EndRule.endDate; + _recurrenceProperties!.recurrenceRange = + RecurrenceRange.endDate; + _selectedDate = DateTime( + pickedDate.year, + pickedDate.month, + pickedDate.day, + ); + _recurrenceProperties!.endDate = _selectedDate; + }); + }, + shape: const CircleBorder(), + child: Text( + DateFormat('MM/dd/yyyy').format(_selectedDate), + style: TextStyle( + fontSize: 13, + color: defaultTextColor, + fontWeight: FontWeight.w400, ), ), ), - cursorColor: widget.model.primaryColor, - onTap: () { - setState(() { - _endRule = _EndRule.count; - }); - }, - onChanged: (String value) async { - if (value != null && value.isNotEmpty) { - _count = int.parse(value); - if (_count == 0) { - _count = 1; - } else if (_count! >= 999) { - setState(() { - _count = 999; - }); - } - } else if (value.isEmpty || value == null) { - _count = 1; - } - _endRule = _EndRule.count; - _recurrenceProperties!.recurrenceRange = - RecurrenceRange.count; - _recurrenceProperties!.recurrenceCount = _count!; - }, - keyboardType: TextInputType.number, - // ignore: always_specify_types - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - ], - style: TextStyle( - fontSize: 13, - color: defaultTextColor, - fontWeight: FontWeight.w400, - ), - textAlign: TextAlign.center, - decoration: const InputDecoration( - border: InputBorder.none, - ), ), ), - Container(width: 10), - const Text('occurrence'), ], ), - value: _EndRule.count, - groupValue: _endRule, + value: _EndRule.endDate, activeColor: widget.model.primaryColor, - onChanged: (_EndRule? value) { - setState(() { - _endRule = value; - _recurrenceProperties!.recurrenceRange = - RecurrenceRange.count; - _recurrenceProperties!.recurrenceCount = _count!; - }); - }, ), - ), - ], + const Divider(indent: 50, height: 1.0, thickness: 1), + SizedBox( + height: 40, + child: RadioListTile<_EndRule>( + contentPadding: const EdgeInsets.only(left: 7), + title: Row( + children: [ + const Text('After'), + Container( + height: 40, + width: 60, + padding: const EdgeInsets.only(left: 5, bottom: 10), + margin: const EdgeInsets.only(left: 5), + alignment: Alignment.topCenter, + decoration: BoxDecoration( + color: Colors.grey.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(3), + ), + child: TextField( + readOnly: _endRule != _EndRule.count, + controller: TextEditingController.fromValue( + TextEditingValue( + text: _count.toString(), + selection: TextSelection.collapsed( + offset: _count.toString().length, + ), + ), + ), + cursorColor: widget.model.primaryColor, + onTap: () { + setState(() { + _endRule = _EndRule.count; + }); + }, + onChanged: (String value) async { + if (value != null && value.isNotEmpty) { + _count = int.parse(value); + if (_count == 0) { + _count = 1; + } else if (_count! >= 999) { + setState(() { + _count = 999; + }); + } + } else if (value.isEmpty || value == null) { + _count = 1; + } + _endRule = _EndRule.count; + _recurrenceProperties!.recurrenceRange = + RecurrenceRange.count; + _recurrenceProperties!.recurrenceCount = _count!; + }, + keyboardType: TextInputType.number, + // ignore: always_specify_types + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + style: TextStyle( + fontSize: 13, + color: defaultTextColor, + fontWeight: FontWeight.w400, + ), + textAlign: TextAlign.center, + decoration: const InputDecoration( + border: InputBorder.none, + ), + ), + ), + Container(width: 10), + const Text('occurrence'), + ], + ), + value: _EndRule.count, + activeColor: widget.model.primaryColor, + ), + ), + ], + ), ), ], ), ); } + void _countEndRule(_EndRule value) { + _endRule = value; + _recurrenceProperties!.recurrenceRange = RecurrenceRange.count; + _recurrenceProperties!.recurrenceCount = _count!; + } + + void _endDateEndRule(_EndRule value) { + _endRule = value; + _rangeEndDate(); + } + + void _neverEndRule() { + _endRule = _EndRule.never; + _rangeNoEndDate(); + } + @override Widget build(BuildContext context) { return Theme( diff --git a/lib/samples/calendar/calendar_loadmore.dart b/lib/samples/calendar/calendar_loadmore.dart index bf6b9a15..a1f14b32 100644 --- a/lib/samples/calendar/calendar_loadmore.dart +++ b/lib/samples/calendar/calendar_loadmore.dart @@ -149,7 +149,7 @@ class _LoadMoreCalendarState extends SampleViewState { colorCollection.add(const Color(0xFF636363)); colorCollection.add(const Color(0xFF0A8043)); - final Random random = Random(); + final Random random = Random.secure(); _dataCollection = >{}; final DateTime today = DateTime.now(); final DateTime rangeStartDate = DateTime( diff --git a/lib/samples/calendar/customization.dart b/lib/samples/calendar/customization.dart index f4cd11be..64467e76 100644 --- a/lib/samples/calendar/customization.dart +++ b/lib/samples/calendar/customization.dart @@ -111,7 +111,7 @@ class _CustomizationCalendarState extends SampleViewState { void _onViewChanged(ViewChangedDetails visibleDatesChangedDetails) { final List<_Meeting> appointment = <_Meeting>[]; _events.appointments.clear(); - final Random random = Random(); + final Random random = Random.secure(); /// Remove the scroll bar on sample while change the view from /// month view or change the view to month view. diff --git a/lib/samples/calendar/drag_and_drop_calendar.dart b/lib/samples/calendar/drag_and_drop_calendar.dart index 7fc73d14..f47838dd 100644 --- a/lib/samples/calendar/drag_and_drop_calendar.dart +++ b/lib/samples/calendar/drag_and_drop_calendar.dart @@ -144,7 +144,7 @@ class _DragAndDropCalendarState extends SampleViewState { colorCollection.add(const Color(0xFF0A8043)); final List appointments = []; - final Random random = Random(); + final Random random = Random.secure(); DateTime today = DateTime.now(); final DateTime rangeStartDate = today.add( const Duration(days: -(365 ~/ 2)), diff --git a/lib/samples/calendar/getting_started.dart b/lib/samples/calendar/getting_started.dart index 6121c14a..56fdd50d 100644 --- a/lib/samples/calendar/getting_started.dart +++ b/lib/samples/calendar/getting_started.dart @@ -151,7 +151,7 @@ class GettingStartedCalendarState extends SampleViewState { void _onViewChanged(ViewChangedDetails visibleDatesChangedDetails) { final List<_Meeting> appointment = <_Meeting>[]; _events.appointments.clear(); - final Random random = Random(); + final Random random = Random.secure(); final List blockedDates = []; if (_calendarController.view == CalendarView.month || _calendarController.view == CalendarView.timelineMonth) { @@ -496,92 +496,82 @@ class GettingStartedCalendarState extends SampleViewState { ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - flex: model.isWebFullView ? 5 : 6, child: Text( - model.isWebFullView - ? 'View navigation \nmode' - : 'View navigation mode', - softWrap: false, + 'View navigation mode', style: TextStyle(fontSize: 16.0, color: model.textColor), ), ), - Expanded( - flex: model.isWebFullView ? 5 : 4, - child: Container( - padding: const EdgeInsets.only(left: 60), - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container( - color: const Color(0xFFBDBDBD), - height: 1, - ), - value: _viewNavigationModeString, - items: _viewNavigationModeList.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'Snap', - child: Text( - value, - textAlign: TextAlign.center, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - onViewNavigationModeChange(value); - stateSetter(() {}); - }, + Container( + padding: const EdgeInsets.only(left: 60), + alignment: Alignment.bottomLeft, + child: DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container( + color: const Color(0xFFBDBDBD), + height: 1, ), + value: _viewNavigationModeString, + items: _viewNavigationModeList.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'Snap', + child: Text( + value, + textAlign: TextAlign.center, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (dynamic value) { + onViewNavigationModeChange(value); + stateSetter(() {}); + }, ), ), ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: model.isWebFullView ? 5 : 6, + Flexible( child: Text( 'Number of days', - softWrap: false, style: TextStyle(fontSize: 16.0, color: model.textColor), ), ), - Expanded( - flex: model.isWebFullView ? 6 : 4, - child: Container( - padding: const EdgeInsets.only(left: 60), - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container( - color: const Color(0xFFBDBDBD), - height: 1, - ), - value: _numberOfDaysString, - items: - (_calendarController.view == CalendarView.workWeek - ? _numberOfDaysListWorkWeek - : _numberOfDaysList) - .map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'default', - child: Text( - value, - textAlign: TextAlign.center, - style: TextStyle(color: model.textColor), - ), - ); - }) - .toList(), - onChanged: (dynamic value) { - customNumberOfDaysInView(value); - stateSetter(() {}); - }, + Container( + padding: const EdgeInsets.only(left: 60), + alignment: Alignment.bottomLeft, + child: DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container( + color: const Color(0xFFBDBDBD), + height: 1, ), + value: _numberOfDaysString, + items: + (_calendarController.view == CalendarView.workWeek + ? _numberOfDaysListWorkWeek + : _numberOfDaysList) + .map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'default', + child: Text( + value, + textAlign: TextAlign.center, + style: TextStyle(color: model.textColor), + ), + ); + }) + .toList(), + onChanged: (dynamic value) { + customNumberOfDaysInView(value); + stateSetter(() {}); + }, ), ), ], diff --git a/lib/samples/calendar/recurrence.dart b/lib/samples/calendar/recurrence.dart index 0d9d7fc4..01609740 100644 --- a/lib/samples/calendar/recurrence.dart +++ b/lib/samples/calendar/recurrence.dart @@ -416,7 +416,7 @@ class RecurrenceCalendarState extends SampleViewState { _colorCollection.add(const Color(0xFF636363)); final List appointments = []; - final Random random = Random(); + final Random random = Random.secure(); final DateTime currentDate = DateTime.now(); final DateTime startTime = DateTime( diff --git a/lib/samples/calendar/resizing_calendar.dart b/lib/samples/calendar/resizing_calendar.dart index f848e905..2f9751b8 100644 --- a/lib/samples/calendar/resizing_calendar.dart +++ b/lib/samples/calendar/resizing_calendar.dart @@ -141,7 +141,7 @@ class _ResizingCalendarState extends SampleViewState { colorCollection.add(const Color(0xFF0A8043)); final List appointments = []; - final Random random = Random(); + final Random random = Random.secure(); DateTime today = DateTime.now(); final DateTime rangeStartDate = today.add( const Duration(days: -(365 ~/ 2)), diff --git a/lib/samples/calendar/schedule_view.dart b/lib/samples/calendar/schedule_view.dart index ca6393c8..44d720bb 100644 --- a/lib/samples/calendar/schedule_view.dart +++ b/lib/samples/calendar/schedule_view.dart @@ -57,7 +57,7 @@ class _ScheduleViewCalendarState extends SampleViewState { colorCollection.add(const Color(0xFF636363)); colorCollection.add(const Color(0xFF0A8043)); - final Random random = Random(); + final Random random = Random.secure(); final DateTime rangeStartDate = DateTime.now().add( const Duration(days: -(365 ~/ 2)), ); diff --git a/lib/samples/calendar/shift_scheduler.dart b/lib/samples/calendar/shift_scheduler.dart index 8d7ed713..1b1e9778 100644 --- a/lib/samples/calendar/shift_scheduler.dart +++ b/lib/samples/calendar/shift_scheduler.dart @@ -417,7 +417,7 @@ class _ShiftSchedulerState extends SampleViewState { /// Method that creates the resource collection for the Calendar, with the /// required information. void _addResources() { - final Random random = Random(); + final Random random = Random.secure(); for (int i = 0; i < _nameCollection.length; i++) { _employeeCollection.add( CalendarResource( @@ -441,7 +441,7 @@ class _ShiftSchedulerState extends SampleViewState { /// required information. void _addSpecialRegions() { final DateTime date = DateTime.now(); - final Random random = Random(); + final Random random = Random.secure(); for (int i = 0; i < _employeeCollection.length; i++) { _specialTimeRegions.add( TimeRegion( @@ -478,7 +478,7 @@ class _ShiftSchedulerState extends SampleViewState { /// Method that creates the collection the data source for Calendar, with /// required information. void _addAppointments() { - final Random random = Random(); + final Random random = Random.secure(); for (int i = 0; i < _employeeCollection.length; i++) { final List employeeIds = [_employeeCollection[i].id]; if (i == _employeeCollection.length - 1) { diff --git a/lib/samples/calendar/special_regions.dart b/lib/samples/calendar/special_regions.dart index a62dde1c..71d6b3c0 100644 --- a/lib/samples/calendar/special_regions.dart +++ b/lib/samples/calendar/special_regions.dart @@ -138,7 +138,7 @@ class _SpecialRegionsCalendarState extends SampleViewState { colorCollection.add(const Color(0xFF636363)); colorCollection.add(const Color(0xFF0A8043)); - final Random random = Random(); + final Random random = Random.secure(); final DateTime rangeStartDate = DateTime.now().add( const Duration(days: -(365 ~/ 2)), ); diff --git a/lib/samples/calendar/timeline_views.dart b/lib/samples/calendar/timeline_views.dart index 1be44965..74032588 100644 --- a/lib/samples/calendar/timeline_views.dart +++ b/lib/samples/calendar/timeline_views.dart @@ -76,7 +76,7 @@ class _TimelineViewsCalendarState extends SampleViewState { void _onViewChanged(ViewChangedDetails visibleDatesChangedDetails) { final List<_Meeting> appointment = <_Meeting>[]; _events.appointments.clear(); - final Random random = Random(); + final Random random = Random.secure(); final List blockedDates = []; if (_calendarController.view == CalendarView.timelineMonth) { for (int i = 0; i < 5; i++) { diff --git a/lib/samples/chart/cartesian_charts/axis_features/axis_animation.dart b/lib/samples/chart/cartesian_charts/axis_features/axis_animation.dart index aa5d35e7..1cf1d1d3 100644 --- a/lib/samples/chart/cartesian_charts/axis_features/axis_animation.dart +++ b/lib/samples/chart/cartesian_charts/axis_features/axis_animation.dart @@ -58,26 +58,24 @@ class _AxisAnimationDefaultState extends SampleViewState { return StatefulBuilder( builder: (BuildContext context, StateSetter stateSetter) { return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Enable axis elements\nanimation', textAlign: TextAlign.start, style: TextStyle(color: model.textColor, fontSize: 16), ), - Padding( - padding: const EdgeInsets.all(8.0), - child: SizedBox( - width: 90, - child: CheckboxListTile( - activeColor: model.primaryColor, - value: _animation, - onChanged: (bool? value) { - setState(() { - _animation = value!; - stateSetter(() {}); - }); - }, - ), + SizedBox( + width: 90, + child: CheckboxListTile( + activeColor: model.primaryColor, + value: _animation, + onChanged: (bool? value) { + setState(() { + _animation = value!; + stateSetter(() {}); + }); + }, ), ), ], @@ -146,7 +144,7 @@ class _AxisAnimationDefaultState extends SampleViewState { } int _createRandomIntData(int min, int max) { - final Random random = Random(); + final Random random = Random.secure(); return min + random.nextInt(max - min); } diff --git a/lib/samples/chart/cartesian_charts/axis_features/axis_crossing.dart b/lib/samples/chart/cartesian_charts/axis_features/axis_crossing.dart index 54f19418..a1daedd9 100644 --- a/lib/samples/chart/cartesian_charts/axis_features/axis_crossing.dart +++ b/lib/samples/chart/cartesian_charts/axis_features/axis_crossing.dart @@ -69,13 +69,14 @@ class _AxisCrossingState extends SampleViewState { shrinkWrap: true, children: [ Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Axis ', style: TextStyle(fontSize: 16.0, color: model.textColor), ), Container( - padding: const EdgeInsets.fromLTRB(138, 0, 0, 0), + padding: const EdgeInsets.fromLTRB(138, 0, 20, 0), child: DropdownButton( dropdownColor: model.drawerBackgroundColor, focusColor: Colors.transparent, @@ -102,28 +103,27 @@ class _AxisCrossingState extends SampleViewState { ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Cross at ', style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Container( - padding: const EdgeInsets.fromLTRB(85, 0, 0, 0), - child: CustomDirectionalButtons( - minValue: -8, - maxValue: 8, - initialValue: _crossAt, - onChanged: (double val) => setState(() { - _crossAt = val; - }), - step: 2, - iconColor: model.textColor, - style: TextStyle(fontSize: 20.0, color: model.textColor), - ), + CustomDirectionalButtons( + minValue: -8, + maxValue: 8, + initialValue: _crossAt, + onChanged: (double val) => setState(() { + _crossAt = val; + }), + step: 2, + iconColor: model.textColor, + style: TextStyle(fontSize: 20.0, color: model.textColor), ), ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Labels near axis line', diff --git a/lib/samples/chart/cartesian_charts/axis_features/customized_axis_label.dart b/lib/samples/chart/cartesian_charts/axis_features/customized_axis_label.dart index 61a5204b..5cf95089 100644 --- a/lib/samples/chart/cartesian_charts/axis_features/customized_axis_label.dart +++ b/lib/samples/chart/cartesian_charts/axis_features/customized_axis_label.dart @@ -71,7 +71,7 @@ class _CustomLabelsEventState extends SampleViewState { _isHours = false; _isMinutes = false; _tooltipBehavior = TooltipBehavior(enable: true); - _random = Random(); + _random = Random.secure(); _chartData = <_LabelData>[ _LabelData(DateTime(2016), 57), _LabelData(DateTime(2017), 70), diff --git a/lib/samples/chart/cartesian_charts/axis_features/interval_type.dart b/lib/samples/chart/cartesian_charts/axis_features/interval_type.dart index 8247112a..e9d255dd 100644 --- a/lib/samples/chart/cartesian_charts/axis_features/interval_type.dart +++ b/lib/samples/chart/cartesian_charts/axis_features/interval_type.dart @@ -334,6 +334,6 @@ class _IntervalTypeState extends SampleViewState { } num _buildRandomIntData(int min, int max) { - final Random random = Random(); + final Random random = Random.secure(); return min + random.nextInt(max - min); } diff --git a/lib/samples/chart/cartesian_charts/axis_features/maximum_width_for_labels.dart b/lib/samples/chart/cartesian_charts/axis_features/maximum_width_for_labels.dart index ea6da5e9..86642dd0 100644 --- a/lib/samples/chart/cartesian_charts/axis_features/maximum_width_for_labels.dart +++ b/lib/samples/chart/cartesian_charts/axis_features/maximum_width_for_labels.dart @@ -61,7 +61,7 @@ class ChartMaximumLabelWidthState extends SampleViewState { shrinkWrap: true, children: [ Row( - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( padding: const EdgeInsets.fromLTRB(0, 15, 0, 0), @@ -70,30 +70,26 @@ class ChartMaximumLabelWidthState extends SampleViewState { style: TextStyle(color: model.textColor), ), ), - Container( - padding: !model.isWebFullView - ? const EdgeInsets.fromLTRB(32, 0, 0, 0) - : const EdgeInsets.fromLTRB(42, 0, 0, 0), - child: CustomDirectionalButtons( - maxValue: 120, - minValue: 1, - initialValue: _xMaximumLabelWidth, - onChanged: (double val) { - setState(() { - _xMaximumLabelWidth = val; - }); - }, - step: 10, - loop: true, - padding: 5.0, - iconColor: model.textColor, - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + CustomDirectionalButtons( + maxValue: 120, + minValue: 1, + initialValue: _xMaximumLabelWidth, + onChanged: (double val) { + setState(() { + _xMaximumLabelWidth = val; + }); + }, + step: 10, + loop: true, + padding: 5.0, + iconColor: model.textColor, + style: TextStyle(fontSize: 16.0, color: model.textColor), ), ], ), SizedBox( child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Enable label extent', @@ -119,29 +115,25 @@ class ChartMaximumLabelWidthState extends SampleViewState { Visibility( visible: _isEnableLabelExtend, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Labels extent', style: TextStyle(color: model.textColor), ), - Container( - padding: !model.isWebFullView - ? const EdgeInsets.fromLTRB(40, 0, 0, 0) - : const EdgeInsets.fromLTRB(50, 0, 0, 0), - child: CustomDirectionalButtons( - maxValue: 200, - minValue: 1, - initialValue: _xLabelsExtent, - onChanged: (double val) { - setState(() { - _xLabelsExtent = val; - }); - }, - step: 10, - loop: true, - iconColor: model.textColor, - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + CustomDirectionalButtons( + maxValue: 200, + minValue: 1, + initialValue: _xLabelsExtent, + onChanged: (double val) { + setState(() { + _xLabelsExtent = val; + }); + }, + step: 10, + loop: true, + iconColor: model.textColor, + style: TextStyle(fontSize: 16.0, color: model.textColor), ), ], ), diff --git a/lib/samples/chart/cartesian_charts/axis_features/plot_band.dart b/lib/samples/chart/cartesian_charts/axis_features/plot_band.dart index 33308c61..748418d5 100644 --- a/lib/samples/chart/cartesian_charts/axis_features/plot_band.dart +++ b/lib/samples/chart/cartesian_charts/axis_features/plot_band.dart @@ -1,4 +1,5 @@ /// Package import. +import 'dart:math' as math; import 'package:flutter/material.dart'; /// Chart import. @@ -19,14 +20,17 @@ class PlotBandDefault extends SampleView { class _PlotBandDefaultState extends SampleViewState { _PlotBandDefaultState(); - List? _plotBandType; + late List _plotBandType; late bool isHorizontal; late bool isVertical; late String _selectedType; late bool isSegment; late bool isLine; + late bool isCustom; + late bool _shouldRenderAboveSeries; TooltipBehavior? _tooltipBehavior; List? _weatherReport; + List<_ProductSalesComparisonData>? _productSalesComparisonData; @override void initState() { @@ -35,17 +39,23 @@ class _PlotBandDefaultState extends SampleViewState { 'horizontal', 'segment', 'line', - ].toList(); + 'custom', + ]; + isHorizontal = true; isVertical = false; - _selectedType = _plotBandType!.first; + _selectedType = _plotBandType.first; isSegment = false; isLine = false; + isCustom = false; + _shouldRenderAboveSeries = false; + _tooltipBehavior = TooltipBehavior( enable: true, canShowMarker: false, header: '', ); + _weatherReport = [ ChartSampleData(xValue: 'Jan', yValue: 23), ChartSampleData(xValue: 'Feb', yValue: 24), @@ -59,6 +69,16 @@ class _PlotBandDefaultState extends SampleViewState { ChartSampleData(xValue: 'Oct', yValue: 25), ChartSampleData(xValue: 'Nov', yValue: 22), ]; + + _productSalesComparisonData = <_ProductSalesComparisonData>[ + _ProductSalesComparisonData(DateTime(2017, 12, 22), 40), + _ProductSalesComparisonData(DateTime(2017, 12, 26), 70), + _ProductSalesComparisonData(DateTime(2017, 12, 27), 75), + _ProductSalesComparisonData(DateTime(2018, 1, 2), 82), + _ProductSalesComparisonData(DateTime(2018, 1, 3), 53), + _ProductSalesComparisonData(DateTime(2018, 1, 4), 54), + ]; + super.initState(); } @@ -66,49 +86,87 @@ class _PlotBandDefaultState extends SampleViewState { Widget buildSettings(BuildContext context) { return StatefulBuilder( builder: (BuildContext context, StateSetter stateSetter) { - return Row( + return ListView( + shrinkWrap: true, children: [ - Text( - 'Plot band type', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), - Container( - padding: const EdgeInsets.fromLTRB(20, 0, 0, 0), - height: 50, - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container(color: const Color(0xFFBDBDBD), height: 1), - value: _selectedType, - items: _plotBandType!.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'horizontal', - child: Text( - value, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - _onPlotBandModeChange(value.toString()); - stateSetter(() {}); - }, - ), - ), + _buildPlotBandTypeDropdown(stateSetter), + // Show "Above series" checkbox only for custom, segment, and line types. + if (isCustom || isSegment || isLine) + _buildShouldRenderAboveSeries(stateSetter), ], ); }, ); } + Widget _buildPlotBandTypeDropdown(StateSetter stateSetter) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Plot band type', + style: TextStyle(fontSize: 16.0, color: model.textColor), + ), + DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container(color: const Color(0xFFBDBDBD), height: 1), + value: _selectedType, + items: List>.generate(_plotBandType.length, ( + int index, + ) { + final String value = _plotBandType[index]; + return DropdownMenuItem( + value: value, + child: Text(value, style: TextStyle(color: model.textColor)), + ); + }), + onChanged: (String? value) { + if (value == null) { + return; + } + _onPlotBandModeChange(value); + stateSetter(() {}); + }, + ), + ], + ); + } + + Widget _buildShouldRenderAboveSeries(StateSetter stateSetter) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Above series', + style: TextStyle(color: model.textColor, fontSize: 16), + ), + SizedBox( + width: 90, + child: CheckboxListTile( + activeColor: model.primaryColor, + value: _shouldRenderAboveSeries, + onChanged: (bool? value) { + _shouldRenderAboveSeries = value ?? false; + stateSetter(() {}); + setState(() {}); + }, + ), + ), + ], + ); + } + @override Widget build(BuildContext context) { return _buildCartesianChart(); } - /// Return the Cartesian Chart with Line series. SfCartesianChart _buildCartesianChart() { + return isCustom ? customPlotBandChart() : defaultPlotBandChart(); + } + + SfCartesianChart defaultPlotBandChart() { final Color yAxisPlotBandTextColor = ((isSegment || isLine) && model != null && @@ -121,7 +179,7 @@ class _PlotBandDefaultState extends SampleViewState { primaryXAxis: CategoryAxis( interval: 1, - /// API for Y axis plot band. + /// API for X axis plot band. /// It returns the multiple plot band to Chart. plotBands: [ PlotBand( @@ -175,6 +233,7 @@ class _PlotBandDefaultState extends SampleViewState { isVisible: isCardView ? false : isSegment, color: const Color.fromRGBO(224, 155, 0, 1), textStyle: const TextStyle(color: Colors.white, fontSize: 17), + shouldRenderAboveSeries: _shouldRenderAboveSeries, ), PlotBand( start: 7.5, @@ -187,6 +246,7 @@ class _PlotBandDefaultState extends SampleViewState { isVisible: isCardView ? false : isSegment, color: const Color.fromRGBO(224, 155, 0, 1), textStyle: const TextStyle(color: Colors.white, fontSize: 17), + shouldRenderAboveSeries: _shouldRenderAboveSeries, ), PlotBand( start: 4.5, @@ -199,6 +259,7 @@ class _PlotBandDefaultState extends SampleViewState { isVisible: isCardView ? false : isSegment, color: const Color.fromRGBO(207, 85, 7, 1), textStyle: const TextStyle(color: Colors.white, fontSize: 17), + shouldRenderAboveSeries: _shouldRenderAboveSeries, ), ], majorGridLines: const MajorGridLines(width: 0), @@ -225,7 +286,6 @@ class _PlotBandDefaultState extends SampleViewState { verticalTextAlignment: isLine ? TextAnchor.start : TextAnchor.middle, - // Padding for plotBand text. verticalTextPadding: isLine ? '-7' : '', borderWidth: isCardView ? 0 @@ -267,7 +327,6 @@ class _PlotBandDefaultState extends SampleViewState { ? const Color.fromRGBO(224, 155, 0, 1) : Colors.black, text: 'Average Temperature', - // Padding for plotBand text. verticalTextPadding: isLine ? '-7' : '', color: const Color.fromRGBO(224, 155, 0, 1), textStyle: @@ -298,7 +357,6 @@ class _PlotBandDefaultState extends SampleViewState { ? const Color.fromRGBO(237, 195, 12, 1) : Colors.black, text: 'Low Temperature', - // padding for plotBand text. verticalTextPadding: isLine ? '-7' : '', color: const Color.fromRGBO(237, 195, 12, 1), textStyle: @@ -318,12 +376,85 @@ class _PlotBandDefaultState extends SampleViewState { ); } - /// Returns the list of Cartesian Line series. + SfCartesianChart customPlotBandChart() { + return SfCartesianChart( + title: ChartTitle( + text: isCardView ? '' : 'Sales comparison with promotional periods', + ), + plotAreaBorderWidth: 0, + primaryXAxis: DateTimeCategoryAxis( + majorGridLines: const MajorGridLines(width: 0), + labelIntersectAction: isCardView + ? AxisLabelIntersectAction.multipleRows + : AxisLabelIntersectAction.rotate45, + plotBands: [ + CustomPlotBand( + start: DateTime(2017, 12, 22), + end: DateTime(2017, 12, 27), + textAngle: 0, + verticalTextPadding: '-10%', + verticalTextAlignment: TextAnchor.start, + text: 'Christmas Sale\nDec 2017', + textStyle: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + patternColor: const Color.fromRGBO(217, 112, 1, 1), + stripeOpacity: _shouldRenderAboveSeries ? 0.75 : 0.55, + stripeWidth: 2, + stripeSpacing: 8, + dashLength: 4, + stripeAngle: 135, + shouldRenderAboveSeries: _shouldRenderAboveSeries, + ), + CustomPlotBand( + textAngle: 0, + start: DateTime(2018, 1, 2), + end: DateTime(2018, 1, 5), + verticalTextPadding: '-10%', + verticalTextAlignment: TextAnchor.start, + text: 'New Year Sale\nJan 2018', + textStyle: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + ), + patternColor: const Color.fromRGBO(255, 100, 150, 1), + stripeOpacity: _shouldRenderAboveSeries ? 0.75 : 0.55, + stripeWidth: 2, + stripeSpacing: 8, + dashLength: 4, + stripeAngle: 135, + shouldRenderAboveSeries: _shouldRenderAboveSeries, + ), + ], + ), + primaryYAxis: const NumericAxis( + labelFormat: '{value}M', + interval: 20, + minimum: 0, + maximum: 100, + majorTickLines: MajorTickLines(size: 0), + axisLine: AxisLine(width: 0), + ), + series: >[ + ColumnSeries<_ProductSalesComparisonData, DateTime>( + dataSource: _productSalesComparisonData, + name: 'Sales', + xValueMapper: (_ProductSalesComparisonData x, int index) => x.year, + yValueMapper: (_ProductSalesComparisonData sales, int index) => + sales.sales, + borderRadius: const BorderRadius.all(Radius.circular(8)), + spacing: 0.15, + ), + ], + tooltipBehavior: _tooltipBehavior, + ); + } + List> _buildLineSeries() { final Color seriesColor = (isSegment || isLine) && - model != null && - model.themeData.colorScheme.brightness == Brightness.light + Theme.of(context).brightness == Brightness.light ? Colors.black54 : Colors.white; return >[ @@ -333,9 +464,10 @@ class _PlotBandDefaultState extends SampleViewState { yValueMapper: (ChartSampleData sales, int index) => sales.yValue, color: seriesColor, name: 'Weather', + width: 3, markerSettings: const MarkerSettings( - height: 5, - width: 5, + height: 7, + width: 7, isVisible: true, color: Color.fromRGBO(192, 108, 132, 1), ), @@ -343,42 +475,156 @@ class _PlotBandDefaultState extends SampleViewState { ]; } - /// Method for updating plot band type in the Chart on change. void _onPlotBandModeChange(String item) { _selectedType = item; + if (_selectedType == 'horizontal') { isVertical = true; isHorizontal = false; isSegment = false; isLine = false; - } - if (_selectedType == 'vertical') { + isCustom = false; + } else if (_selectedType == 'vertical') { isHorizontal = true; isVertical = false; isSegment = false; isLine = false; - } - if (_selectedType == 'segment') { + isCustom = false; + } else if (_selectedType == 'segment') { isHorizontal = false; isVertical = false; isSegment = true; isLine = false; - } - if (_selectedType == 'line') { + isCustom = false; + } else if (_selectedType == 'line') { isHorizontal = false; isVertical = true; isSegment = false; isLine = true; + isCustom = false; + } else if (_selectedType == 'custom') { + isHorizontal = false; + isVertical = false; + isSegment = false; + isLine = false; + isCustom = true; } - setState(() { - /// Update the plat band mode changes. - }); + + setState(() {}); } @override void dispose() { - _plotBandType!.clear(); - _weatherReport!.clear(); + _plotBandType.clear(); + _weatherReport?.clear(); + _productSalesComparisonData?.clear(); super.dispose(); } } + +class _ProductSalesComparisonData { + _ProductSalesComparisonData(this.year, this.sales); + final DateTime year; + final double sales; +} + +class CustomPlotBand extends PlotBand { + const CustomPlotBand({ + super.isVisible = true, + super.start, + super.end, + super.associatedAxisStart, + super.associatedAxisEnd, + super.color = Colors.white, + super.gradient, + super.opacity = 1.0, + super.borderColor = Colors.transparent, + super.borderWidth = 0, + super.dashArray = const [0, 0], + super.text, + super.textStyle, + super.textAngle, + super.verticalTextPadding, + super.horizontalTextPadding, + super.verticalTextAlignment = TextAnchor.middle, + super.horizontalTextAlignment = TextAnchor.middle, + super.isRepeatable = false, + super.repeatEvery = 1, + super.repeatUntil, + super.size, + super.sizeType = DateTimeIntervalType.auto, + super.shouldRenderAboveSeries = false, + this.stripeAngle = 0, + this.stripeWidth = 4.0, + this.stripeSpacing = 4.0, + this.patternColor = Colors.green, + this.stripeOpacity = 1, + this.dashLength = 8.0, + this.gapLength = 4.0, + }); + + final int stripeAngle; + final Color patternColor; + final double stripeWidth; + final double stripeSpacing; + final double stripeOpacity; + final double dashLength; + final double gapLength; + + @override + void drawRect( + Canvas canvas, + Rect rect, + Paint fillPaint, [ + Paint? strokePaint, + ]) { + canvas.save(); + canvas.clipRect(rect); + + // Transparent background for custom pattern. + fillPaint.color = Colors.transparent; + canvas.drawRect(rect, fillPaint); + + // Draw diagonal dashed pattern. + canvas.translate(rect.center.dx, rect.center.dy); + canvas.rotate(stripeAngle * math.pi / 180); + + final double diagonal = math.sqrt( + rect.width * rect.width + rect.height * rect.height, + ); + + final Paint dashPaint = Paint() + ..color = patternColor.withValues(alpha: stripeOpacity) + ..style = PaintingStyle.stroke + ..strokeWidth = stripeWidth + ..strokeCap = StrokeCap.round; + + final double step = stripeWidth + stripeSpacing; + + // Draw dashed lines with gaps. + for (double x = -diagonal; x < diagonal; x += step) { + double y = -diagonal; + while (y < diagonal) { + // Draw dash segment from (x, y) to (x, dashEnd). + final double dashEnd = y + dashLength; + if (dashEnd <= diagonal) { + canvas.drawLine(Offset(x, y), Offset(x, dashEnd), dashPaint); + y = dashEnd + gapLength; + } else { + // Draw remaining portion. + canvas.drawLine(Offset(x, y), Offset(x, diagonal), dashPaint); + break; + } + } + } + + canvas.restore(); + } +} + +/// Sample data type used in the series. +class ChartSampleData { + ChartSampleData({required this.xValue, required this.yValue}); + final String xValue; + final num yValue; +} diff --git a/lib/samples/chart/cartesian_charts/axis_features/plot_offset.dart b/lib/samples/chart/cartesian_charts/axis_features/plot_offset.dart index 3757aae1..dd2472b0 100644 --- a/lib/samples/chart/cartesian_charts/axis_features/plot_offset.dart +++ b/lib/samples/chart/cartesian_charts/axis_features/plot_offset.dart @@ -61,7 +61,6 @@ class _PlotOffsetState extends SampleViewState { @override Widget buildSettings(BuildContext context) { - const double height = 40; final TextStyle textStyle = TextStyle( fontSize: 16.0, color: model.textColor, @@ -84,86 +83,59 @@ class _PlotOffsetState extends SampleViewState { ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('PlotOffset', style: textStyle), - Container( - padding: const EdgeInsets.fromLTRB(50, 0, 0, 0), - height: height, - alignment: Alignment.bottomLeft, - child: Container( - padding: const EdgeInsets.fromLTRB(15, 0, 0, 0), - height: height, - alignment: Alignment.bottomLeft, - child: CustomDirectionalButtons( - maxValue: 50, - initialValue: _plotOffsetX ?? 0, - onChanged: (double val) { - setState(() { - _plotOffsetX = val; - if (val == 0) { - _plotOffsetX = null; - } - }); - }, - step: 5, - iconColor: model.textColor, - style: textStyle, - ), - ), + CustomDirectionalButtons( + maxValue: 50, + initialValue: _plotOffsetX ?? 0, + onChanged: (double val) { + setState(() { + _plotOffsetX = val; + if (val == 0) { + _plotOffsetX = null; + } + }); + }, + step: 5, + iconColor: model.textColor, + style: textStyle, ), ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('PlotOffsetStart', style: textStyle), - Container( - padding: const EdgeInsets.fromLTRB(18, 0, 0, 0), - height: height, - alignment: Alignment.bottomLeft, - child: Container( - padding: const EdgeInsets.fromLTRB(15, 0, 0, 0), - height: height, - alignment: Alignment.bottomLeft, - child: CustomDirectionalButtons( - maxValue: 50, - initialValue: _plotOffsetStartX ?? 0, - onChanged: (double val) { - setState(() { - _plotOffsetStartX = val; - }); - }, - step: 5, - iconColor: model.textColor, - style: textStyle, - ), - ), + CustomDirectionalButtons( + maxValue: 50, + initialValue: _plotOffsetStartX ?? 0, + onChanged: (double val) { + setState(() { + _plotOffsetStartX = val; + }); + }, + step: 5, + iconColor: model.textColor, + style: textStyle, ), ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('PlotOffsetEnd', style: textStyle), - Container( - padding: const EdgeInsets.fromLTRB(25, 0, 0, 0), - height: height, - alignment: Alignment.bottomLeft, - child: Container( - padding: const EdgeInsets.fromLTRB(15, 0, 0, 0), - height: height, - alignment: Alignment.bottomLeft, - child: CustomDirectionalButtons( - maxValue: 50, - initialValue: _plotOffsetEndX ?? 0, - onChanged: (double val) { - setState(() { - _plotOffsetEndX = val; - }); - }, - step: 5, - iconColor: model.textColor, - style: textStyle, - ), - ), + CustomDirectionalButtons( + maxValue: 50, + initialValue: _plotOffsetEndX ?? 0, + onChanged: (double val) { + setState(() { + _plotOffsetEndX = val; + }); + }, + step: 5, + iconColor: model.textColor, + style: textStyle, ), ], ), @@ -180,106 +152,84 @@ class _PlotOffsetState extends SampleViewState { ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('PlotOffset', style: textStyle), - Container( - padding: const EdgeInsets.fromLTRB(50, 0, 0, 0), - height: height, - alignment: Alignment.bottomLeft, - child: Container( - padding: const EdgeInsets.fromLTRB(15, 0, 0, 0), - height: height, - alignment: Alignment.bottomLeft, - child: CustomDirectionalButtons( - maxValue: 50, - initialValue: _plotOffsetY ?? 0, - onChanged: (double val) { - setState(() { - _plotOffsetY = val; - if (_plotOffsetY == 0) { - _plotOffsetY = null; - } - }); - }, - step: 5, - iconColor: model.textColor, - style: textStyle, - ), - ), + CustomDirectionalButtons( + maxValue: 50, + initialValue: _plotOffsetY ?? 0, + onChanged: (double val) { + setState(() { + _plotOffsetY = val; + if (_plotOffsetY == 0) { + _plotOffsetY = null; + } + }); + }, + step: 5, + iconColor: model.textColor, + style: textStyle, ), ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('PlotOffsetStart', style: textStyle), - Container( - padding: const EdgeInsets.fromLTRB(18, 0, 0, 0), - height: height, - alignment: Alignment.bottomLeft, - child: Container( - padding: const EdgeInsets.fromLTRB(15, 0, 0, 0), - height: height, - alignment: Alignment.bottomLeft, - child: CustomDirectionalButtons( - maxValue: 50, - initialValue: _plotOffsetStartY ?? 0, - onChanged: (double val) { - setState(() { - _plotOffsetStartY = val; - }); - }, - step: 5, - iconColor: model.textColor, - style: textStyle, - ), - ), + CustomDirectionalButtons( + maxValue: 50, + initialValue: _plotOffsetStartY ?? 0, + onChanged: (double val) { + setState(() { + _plotOffsetStartY = val; + }); + }, + step: 5, + iconColor: model.textColor, + style: textStyle, ), ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('PlotOffsetEnd', style: textStyle), - Container( - padding: const EdgeInsets.fromLTRB(25, 0, 0, 0), - height: height, - alignment: Alignment.bottomLeft, - child: Container( - padding: const EdgeInsets.fromLTRB(15, 0, 0, 0), - height: height, - alignment: Alignment.bottomLeft, - child: CustomDirectionalButtons( - maxValue: 50, - initialValue: _plotOffsetEndY ?? 0, - onChanged: (double val) { - setState(() { - _plotOffsetEndY = val; - }); - }, - step: 5, - iconColor: model.textColor, - style: textStyle, - ), - ), + CustomDirectionalButtons( + maxValue: 50, + initialValue: _plotOffsetEndY ?? 0, + onChanged: (double val) { + setState(() { + _plotOffsetEndY = val; + }); + }, + step: 5, + iconColor: model.textColor, + style: textStyle, ), ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - 'Inverse series', - style: TextStyle(color: model.textColor, fontSize: 16), + Flexible( + child: Text( + 'Inverse series', + style: TextStyle(color: model.textColor, fontSize: 16), + ), ), - SizedBox( - width: 127, - child: CheckboxListTile( - activeColor: model.primaryColor, - value: _isTransposed, - onChanged: (bool? value) { - setState(() { - _isTransposed = value; - stateSetter(() {}); - }); - }, + Flexible( + child: SizedBox( + width: 127, + child: CheckboxListTile( + activeColor: model.primaryColor, + value: _isTransposed, + onChanged: (bool? value) { + setState(() { + _isTransposed = value; + stateSetter(() {}); + }); + }, + ), ), ), ], diff --git a/lib/samples/chart/cartesian_charts/axis_features/positioning_axis_label.dart b/lib/samples/chart/cartesian_charts/axis_features/positioning_axis_label.dart index 4d094bac..87b706b8 100644 --- a/lib/samples/chart/cartesian_charts/axis_features/positioning_axis_label.dart +++ b/lib/samples/chart/cartesian_charts/axis_features/positioning_axis_label.dart @@ -77,6 +77,7 @@ class _AxisCrossingState extends SampleViewState { shrinkWrap: true, children: [ Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Y Axis', @@ -89,6 +90,7 @@ class _AxisCrossingState extends SampleViewState { ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Label position ', @@ -124,6 +126,7 @@ class _AxisCrossingState extends SampleViewState { ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Label alignment', @@ -159,6 +162,7 @@ class _AxisCrossingState extends SampleViewState { ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'X Axis', @@ -171,6 +175,7 @@ class _AxisCrossingState extends SampleViewState { ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Label position ', @@ -206,6 +211,7 @@ class _AxisCrossingState extends SampleViewState { ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Label alignment', diff --git a/lib/samples/chart/cartesian_charts/axis_features/range_padding.dart b/lib/samples/chart/cartesian_charts/axis_features/range_padding.dart index dfca8700..37c86c8f 100644 --- a/lib/samples/chart/cartesian_charts/axis_features/range_padding.dart +++ b/lib/samples/chart/cartesian_charts/axis_features/range_padding.dart @@ -79,28 +79,22 @@ class _RangePaddingViewState extends SampleViewState { return ListView( shrinkWrap: true, children: [ - Row( - children: [ - Text( - 'X Axis', - style: TextStyle( - fontSize: 16.0, - color: model.textColor, - fontWeight: FontWeight.bold, - ), - ), - ], + Text( + 'X Axis', + style: TextStyle( + fontSize: 16.0, + color: model.textColor, + fontWeight: FontWeight.bold, + ), ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - 'Range padding ', + 'Range padding', style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Container( - padding: const EdgeInsets.fromLTRB(20, 0, 0, 0), - height: 50, - alignment: Alignment.bottomLeft, + Flexible( child: DropdownButton( dropdownColor: model.drawerBackgroundColor, focusColor: Colors.transparent, @@ -126,28 +120,22 @@ class _RangePaddingViewState extends SampleViewState { ), ], ), - Row( - children: [ - Text( - 'Y Axis', - style: TextStyle( - fontSize: 16.0, - color: model.textColor, - fontWeight: FontWeight.bold, - ), - ), - ], + Text( + 'Y Axis', + style: TextStyle( + fontSize: 16.0, + color: model.textColor, + fontWeight: FontWeight.bold, + ), ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - 'Range padding ', + 'Range padding', style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Container( - padding: const EdgeInsets.fromLTRB(20, 0, 0, 0), - height: 50, - alignment: Alignment.bottomLeft, + Flexible( child: DropdownButton( dropdownColor: model.drawerBackgroundColor, focusColor: Colors.transparent, diff --git a/lib/samples/chart/cartesian_charts/axis_types/category/label_placement.dart b/lib/samples/chart/cartesian_charts/axis_types/category/label_placement.dart index 1c1caabf..391d4228 100644 --- a/lib/samples/chart/cartesian_charts/axis_types/category/label_placement.dart +++ b/lib/samples/chart/cartesian_charts/axis_types/category/label_placement.dart @@ -60,42 +60,33 @@ class _CategoryTicksState extends SampleViewState { shrinkWrap: true, children: [ Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - 'Label placement ', + 'Label \nplacement ', style: TextStyle(color: model.textColor, fontSize: 16), ), - Container( - padding: EdgeInsets.fromLTRB( - model.isWebFullView ? 4 : 20, - 0, - 0, - 0, - ), - height: 50, - alignment: Alignment.bottomCenter, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container( - color: const Color(0xFFBDBDBD), - height: 1, - ), - value: _selectedType, - items: _labelPosition!.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'betweenTicks', - child: Text( - value, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - _onPositionTypeChange(value.toString()); - stateSetter(() {}); - }, + DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container( + color: const Color(0xFFBDBDBD), + height: 1, ), + value: _selectedType, + items: _labelPosition!.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'betweenTicks', + child: Text( + value, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (dynamic value) { + _onPositionTypeChange(value.toString()); + stateSetter(() {}); + }, ), ], ), diff --git a/lib/samples/chart/cartesian_charts/chart_types/area/animation_area_chart.dart b/lib/samples/chart/cartesian_charts/chart_types/area/animation_area_chart.dart index 0aecfe78..6337f2d4 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/area/animation_area_chart.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/area/animation_area_chart.dart @@ -82,7 +82,7 @@ class _AnimationAreaDefaultState extends SampleViewState { /// Return the random value in Area series. int _buildRandomInt(int min, int max) { - final Random random = Random(); + final Random random = Random.secure(); return min + random.nextInt(max - min); } diff --git a/lib/samples/chart/cartesian_charts/chart_types/area/area_with_axis_base.dart b/lib/samples/chart/cartesian_charts/chart_types/area/area_with_axis_base.dart index 03a76e52..f2618678 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/area/area_with_axis_base.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/area/area_with_axis_base.dart @@ -50,34 +50,27 @@ class _AxisCrossingBaseValueState extends SampleViewState { return StatefulBuilder( builder: (BuildContext context, StateSetter stateSetter) { return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Axis base value ', style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Container( - padding: const EdgeInsets.fromLTRB(20, 0, 0, 0), - height: 50, - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container(color: const Color(0xFFBDBDBD), height: 1), - value: _selectedAxis, - items: _axis!.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : '-2 (modified)', - child: Text( - value, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - _onAxisTypeChange(value.toString()); - stateSetter(() {}); - }, - ), + DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container(color: const Color(0xFFBDBDBD), height: 1), + value: _selectedAxis, + items: _axis!.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : '-2 (modified)', + child: Text(value, style: TextStyle(color: model.textColor)), + ); + }).toList(), + onChanged: (dynamic value) { + _onAxisTypeChange(value.toString()); + stateSetter(() {}); + }, ), ], ); diff --git a/lib/samples/chart/cartesian_charts/chart_types/bar/animation_bar_chart.dart b/lib/samples/chart/cartesian_charts/chart_types/bar/animation_bar_chart.dart index 54a76c72..eb92964e 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/bar/animation_bar_chart.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/bar/animation_bar_chart.dart @@ -65,7 +65,7 @@ class _AnimationBarDefaultState extends SampleViewState { } int _buildRandomInt(int min, int max) { - final Random random = Random(); + final Random random = Random.secure(); return min + random.nextInt(max - min); } diff --git a/lib/samples/chart/cartesian_charts/chart_types/bar/bar_width_and_spacing.dart b/lib/samples/chart/cartesian_charts/chart_types/bar/bar_width_and_spacing.dart index 06326763..823dc8e5 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/bar/bar_width_and_spacing.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/bar/bar_width_and_spacing.dart @@ -50,48 +50,37 @@ class _BarSpacingState extends SampleViewState { shrinkWrap: true, children: [ Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Width ', style: TextStyle(color: model.textColor)), - Container( - padding: const EdgeInsets.fromLTRB(40, 0, 0, 0), - child: CustomDirectionalButtons( - maxValue: 1, - initialValue: _columnWidth, - onChanged: (double val) => setState(() { - _columnWidth = val; - }), - step: 0.1, - loop: true, - iconColor: model.textColor, - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + CustomDirectionalButtons( + maxValue: 1, + initialValue: _columnWidth, + onChanged: (double val) => setState(() { + _columnWidth = val; + }), + step: 0.1, + loop: true, + iconColor: model.textColor, + style: TextStyle(fontSize: 16.0, color: model.textColor), ), ], ), Row( - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Padding( - padding: const EdgeInsets.fromLTRB(0, 15, 0, 0), - child: Text( - 'Spacing ', - style: TextStyle(color: model.textColor), - ), - ), - Container( - padding: const EdgeInsets.fromLTRB(25, 0, 0, 0), - child: CustomDirectionalButtons( - maxValue: 1, - initialValue: _columnSpacing, - onChanged: (double val) => setState(() { - _columnSpacing = val; - }), - step: 0.1, - loop: true, - padding: 5.0, - iconColor: model.textColor, - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text('Spacing ', style: TextStyle(color: model.textColor)), + CustomDirectionalButtons( + maxValue: 1, + initialValue: _columnSpacing, + onChanged: (double val) => setState(() { + _columnSpacing = val; + }), + step: 0.1, + loop: true, + padding: 5.0, + iconColor: model.textColor, + style: TextStyle(fontSize: 16.0, color: model.textColor), ), ], ), diff --git a/lib/samples/chart/cartesian_charts/chart_types/bubble/animation_bubble_chart.dart b/lib/samples/chart/cartesian_charts/chart_types/bubble/animation_bubble_chart.dart index 438aaab1..b5dd578d 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/bubble/animation_bubble_chart.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/bubble/animation_bubble_chart.dart @@ -81,12 +81,12 @@ class _AnimationBubbleDefaultState extends SampleViewState { /// To get the random data and return to the chart data source. int _buildRandomInt(int min, int max) { - final Random random = Random(); + final Random random = Random.secure(); return min + random.nextInt(max - min); } void _buildChartData() { - final Random randomValue = Random(); + final Random randomValue = Random.secure(); _chartData = <_ChartData>[]; for (int i = 1; i <= 7; i++) { _chartData!.add( diff --git a/lib/samples/chart/cartesian_charts/chart_types/column/animation_column_chart.dart b/lib/samples/chart/cartesian_charts/chart_types/column/animation_column_chart.dart index 623825da..9e0e6d0a 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/column/animation_column_chart.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/column/animation_column_chart.dart @@ -66,7 +66,7 @@ class _AnimationColumnDefaultState extends SampleViewState { /// Generate random value. int _buildRandomInt(int min, int max) { - final Random random = Random(); + final Random random = Random.secure(); return min + random.nextInt(max - min); } diff --git a/lib/samples/chart/cartesian_charts/chart_types/column/column_width_and_spacing.dart b/lib/samples/chart/cartesian_charts/chart_types/column/column_width_and_spacing.dart index b240b8fb..54c66aab 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/column/column_width_and_spacing.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/column/column_width_and_spacing.dart @@ -75,6 +75,7 @@ class _ColumnSpacingState extends SampleViewState { shrinkWrap: true, children: [ Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Width ', style: TextStyle(color: model.textColor)), Container( @@ -96,31 +97,22 @@ class _ColumnSpacingState extends SampleViewState { ], ), Row( - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Padding( - padding: const EdgeInsets.fromLTRB(0, 15, 0, 0), - child: Text( - 'Spacing ', - style: TextStyle(color: model.textColor), - ), - ), - Container( - padding: const EdgeInsets.fromLTRB(25, 0, 0, 0), - child: CustomDirectionalButtons( - maxValue: 1, - initialValue: _columnSpacing, - onChanged: (double val) { - setState(() { - _columnSpacing = val; - }); - }, - step: 0.1, - loop: true, - padding: 5.0, - iconColor: model.textColor, - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text('Spacing ', style: TextStyle(color: model.textColor)), + CustomDirectionalButtons( + maxValue: 1, + initialValue: _columnSpacing, + onChanged: (double val) { + setState(() { + _columnSpacing = val; + }); + }, + step: 0.1, + loop: true, + padding: 5.0, + iconColor: model.textColor, + style: TextStyle(fontSize: 16.0, color: model.textColor), ), ], ), diff --git a/lib/samples/chart/cartesian_charts/chart_types/column/column_with_axis_base.dart b/lib/samples/chart/cartesian_charts/chart_types/column/column_with_axis_base.dart index 7f19d938..4b22b95d 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/column/column_with_axis_base.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/column/column_with_axis_base.dart @@ -88,33 +88,26 @@ class _AxisCrossingBaseValueState extends SampleViewState { return StatefulBuilder( builder: (BuildContext context, StateSetter stateSetter) { return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Axis base value ', style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Container( - padding: const EdgeInsets.fromLTRB(20, 0, 0, 0), - height: 50, - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - underline: Container(color: const Color(0xFFBDBDBD), height: 1), - value: _selectedAxis, - items: _axis!.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : '-2 (modified)', - child: Text( - value, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - _onAxisTypeChange(value.toString()); - stateSetter(() {}); - }, - ), + DropdownButton( + dropdownColor: model.drawerBackgroundColor, + underline: Container(color: const Color(0xFFBDBDBD), height: 1), + value: _selectedAxis, + items: _axis!.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : '-2 (modified)', + child: Text(value, style: TextStyle(color: model.textColor)), + ); + }).toList(), + onChanged: (dynamic value) { + _onAxisTypeChange(value.toString()); + stateSetter(() {}); + }, ), ], ); diff --git a/lib/samples/chart/cartesian_charts/chart_types/financial_charts/candle_chart.dart b/lib/samples/chart/cartesian_charts/chart_types/financial_charts/candle_chart.dart index 6d120b04..e25bc25b 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/financial_charts/candle_chart.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/financial_charts/candle_chart.dart @@ -337,6 +337,7 @@ class _CandleChartState extends SampleViewState { Widget _buildEnableSolidCandlesRow(StateSetter stateSetter) { return SizedBox( child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Enable solid candles', @@ -364,6 +365,7 @@ class _CandleChartState extends SampleViewState { Widget _buildShowIndicationRow(StateSetter stateSetter) { return SizedBox( child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Show indication for \nsame values', @@ -390,27 +392,24 @@ class _CandleChartState extends SampleViewState { // Method to build the row for setting width. Widget _buildWidthRow() { return Row( - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( padding: const EdgeInsets.fromLTRB(0, 15, 0, 0), child: Text('Width ', style: TextStyle(color: model.textColor)), ), - Container( - padding: const EdgeInsets.fromLTRB(95, 0, 0, 0), - child: CustomDirectionalButtons( - maxValue: 1, - initialValue: _width, - onChanged: (double val) { - setState(() { - _width = val; - }); - }, - step: 0.1, - loop: true, - iconColor: model.textColor, - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + CustomDirectionalButtons( + maxValue: 1, + initialValue: _width, + onChanged: (double val) { + setState(() { + _width = val; + }); + }, + step: 0.1, + loop: true, + iconColor: model.textColor, + style: TextStyle(fontSize: 16.0, color: model.textColor), ), ], ); @@ -419,28 +418,25 @@ class _CandleChartState extends SampleViewState { // Method to build the row for setting spacing. Widget _buildSpacingRow() { return Row( - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( padding: const EdgeInsets.fromLTRB(0, 15, 0, 0), child: Text('Spacing ', style: TextStyle(color: model.textColor)), ), - Container( - padding: const EdgeInsets.fromLTRB(85, 0, 0, 0), - child: CustomDirectionalButtons( - maxValue: 1, - initialValue: _space, - onChanged: (double val) { - setState(() { - _space = val; - }); - }, - step: 0.1, - loop: true, - padding: 5.0, - iconColor: model.textColor, - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + CustomDirectionalButtons( + maxValue: 1, + initialValue: _space, + onChanged: (double val) { + setState(() { + _space = val; + }); + }, + step: 0.1, + loop: true, + padding: 5.0, + iconColor: model.textColor, + style: TextStyle(fontSize: 16.0, color: model.textColor), ), ], ); @@ -449,7 +445,7 @@ class _CandleChartState extends SampleViewState { // Method to build the row for setting border radius. Widget _buildBorderRadiusRow() { return Row( - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( padding: const EdgeInsets.fromLTRB(0, 15, 0, 0), @@ -458,21 +454,18 @@ class _CandleChartState extends SampleViewState { style: TextStyle(color: model.textColor), ), ), - Container( - padding: const EdgeInsets.fromLTRB(55, 0, 0, 0), - child: CustomDirectionalButtons( - maxValue: 10, - initialValue: _borderRadius, - onChanged: (double val) { - setState(() { - _borderRadius = val; - }); - }, - loop: true, - padding: 5.0, - iconColor: model.textColor, - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + CustomDirectionalButtons( + maxValue: 10, + initialValue: _borderRadius, + onChanged: (double val) { + setState(() { + _borderRadius = val; + }); + }, + loop: true, + padding: 5.0, + iconColor: model.textColor, + style: TextStyle(fontSize: 16.0, color: model.textColor), ), ], ); diff --git a/lib/samples/chart/cartesian_charts/chart_types/histogram.dart b/lib/samples/chart/cartesian_charts/chart_types/histogram.dart index e07a6dd4..3b245af0 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/histogram.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/histogram.dart @@ -138,26 +138,21 @@ class _HistogramDefaultState extends SampleViewState { return StatefulBuilder( builder: (BuildContext context, StateSetter stateSetter) { return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - 'Show distribution line ', + 'Show distribution line', style: TextStyle(color: model.textColor, fontSize: 16), ), - Padding( - padding: const EdgeInsets.all(8.0), - child: SizedBox( - width: 90, - child: CheckboxListTile( - activeColor: model.primaryColor, - value: _showDistributionCurve, - onChanged: (bool? value) { - setState(() { - _showDistributionCurve = value!; - stateSetter(() {}); - }); - }, - ), - ), + Checkbox( + activeColor: model.primaryColor, + value: _showDistributionCurve, + onChanged: (bool? value) { + setState(() { + _showDistributionCurve = value!; + stateSetter(() {}); + }); + }, ), ], ); diff --git a/lib/samples/chart/cartesian_charts/chart_types/line/animation_line_chart.dart b/lib/samples/chart/cartesian_charts/chart_types/line/animation_line_chart.dart index 77f568a9..fbee6307 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/line/animation_line_chart.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/line/animation_line_chart.dart @@ -63,7 +63,7 @@ class _AnimationLineDefaultState extends SampleViewState { } int _createRandomInt(int min, int max) { - final Random random = Random(); + final Random random = Random.secure(); return min + random.nextInt(max - min); } diff --git a/lib/samples/chart/cartesian_charts/chart_types/line/customized_line_chart.dart b/lib/samples/chart/cartesian_charts/chart_types/line/customized_line_chart.dart index 8be0b077..3a73958e 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/line/customized_line_chart.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/line/customized_line_chart.dart @@ -133,7 +133,7 @@ class _CustomLineSeriesRenderer extends LineSeriesRenderer { _CustomLineSeriesRenderer(this.series); final LineSeries series; - static Random randomNumber = Random(); + static Random randomNumber = Random.secure(); @override LineSegment createSegment() { diff --git a/lib/samples/chart/cartesian_charts/chart_types/range_area.dart b/lib/samples/chart/cartesian_charts/chart_types/range_area.dart index c07be8d1..2118b67b 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/range_area.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/range_area.dart @@ -92,10 +92,10 @@ class _RangeAreaState extends SampleViewState { chartData = []; double value = 15; for (int i = 0; i < 100; i++) { - final Random yValue = Random(); + final Random yValue = Random.secure(); (yValue.nextDouble() > .5) - ? value += Random().nextDouble() - : value -= Random().nextDouble(); + ? value += Random.secure().nextDouble() + : value -= Random.secure().nextDouble(); chartData.add( ChartSampleData( diff --git a/lib/samples/chart/cartesian_charts/chart_types/range_column/animation_range_column_chart.dart b/lib/samples/chart/cartesian_charts/chart_types/range_column/animation_range_column_chart.dart index af7343e0..ea5943e3 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/range_column/animation_range_column_chart.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/range_column/animation_range_column_chart.dart @@ -67,7 +67,7 @@ class _AnimationRangeColumnDefaultState extends SampleViewState { } int _buildRandomInt(int min, int max) { - final Random random = Random(); + final Random random = Random.secure(); return min + random.nextInt(max - min); } diff --git a/lib/samples/chart/cartesian_charts/chart_types/scatter/animation_scatter_chart.dart b/lib/samples/chart/cartesian_charts/chart_types/scatter/animation_scatter_chart.dart index 13fc7a9f..578d94b7 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/scatter/animation_scatter_chart.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/scatter/animation_scatter_chart.dart @@ -67,7 +67,7 @@ class _AnimationScatterDefaultState extends SampleViewState { } int _buildRandomInt(int min, int max) { - final Random random = Random(); + final Random random = Random.secure(); return min + random.nextInt(max - min); } diff --git a/lib/samples/chart/cartesian_charts/chart_types/spline/animation_spline_chart.dart b/lib/samples/chart/cartesian_charts/chart_types/spline/animation_spline_chart.dart index efe0aa3b..65268662 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/spline/animation_spline_chart.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/spline/animation_spline_chart.dart @@ -65,7 +65,7 @@ class _AnimationSplineDefaultState extends SampleViewState { /// Get the random value. int _buildRandomInt(int min, int max) { - final Random random = Random(); + final Random random = Random.secure(); return min + random.nextInt(max - min); } diff --git a/lib/samples/chart/cartesian_charts/chart_types/spline/customized_spline_chart.dart b/lib/samples/chart/cartesian_charts/chart_types/spline/customized_spline_chart.dart index e6ec3c2c..340a3b25 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/spline/customized_spline_chart.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/spline/customized_spline_chart.dart @@ -94,7 +94,7 @@ class _CustomSplineSeriesRenderer extends SplineSeriesRenderer { final SplineSeries series; - static Random randomNumber = Random(); + static Random randomNumber = Random.secure(); @override SplineSegment createSegment() { diff --git a/lib/samples/chart/cartesian_charts/chart_types/spline/spline_types.dart b/lib/samples/chart/cartesian_charts/chart_types/spline/spline_types.dart index cdfde55e..5379932e 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/spline/spline_types.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/spline/spline_types.dart @@ -119,6 +119,7 @@ class _SplineTypesState extends SampleViewState { primaryXAxis: const NumericAxis( majorGridLines: MajorGridLines(width: 0), interval: 1, + edgeLabelPlacement: EdgeLabelPlacement.shift, ), primaryYAxis: const NumericAxis( labelFormat: '{value}%', diff --git a/lib/samples/chart/cartesian_charts/chart_types/step_line/animation_step_line_chart.dart b/lib/samples/chart/cartesian_charts/chart_types/step_line/animation_step_line_chart.dart index 5482144b..4a5630dc 100644 --- a/lib/samples/chart/cartesian_charts/chart_types/step_line/animation_step_line_chart.dart +++ b/lib/samples/chart/cartesian_charts/chart_types/step_line/animation_step_line_chart.dart @@ -64,7 +64,7 @@ class _AnimationStepLineDefaultState extends SampleViewState { } int _buildRandomInt(int min, int max) { - final Random random = Random(); + final Random random = Random.secure(); return min + random.nextInt(max - min); } diff --git a/lib/samples/chart/cartesian_charts/infinite_scrolling.dart b/lib/samples/chart/cartesian_charts/infinite_scrolling.dart index f8c387bf..ac3800aa 100644 --- a/lib/samples/chart/cartesian_charts/infinite_scrolling.dart +++ b/lib/samples/chart/cartesian_charts/infinite_scrolling.dart @@ -233,7 +233,7 @@ class _InfiniteScrollingState extends SampleViewState { } int _generateRandomInteger(int min, int max) { - final Random random = Random(); + final Random random = Random.secure(); final int result = min + random.nextInt(max - min); return result < 50 ? 95 : result; } diff --git a/lib/samples/chart/cartesian_charts/legend/legend_various_options.dart b/lib/samples/chart/cartesian_charts/legend/legend_various_options.dart index c39b7b82..dcc3a4d3 100644 --- a/lib/samples/chart/cartesian_charts/legend/legend_various_options.dart +++ b/lib/samples/chart/cartesian_charts/legend/legend_various_options.dart @@ -189,88 +189,80 @@ class _CartesianLegendOptionsState extends SampleViewState { children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 40, children: [ - Flexible( - child: Text( - 'Position', - softWrap: false, - style: TextStyle( - fontSize: 16, - color: model.textColor, - ), + Text( + 'Position', + softWrap: false, + style: TextStyle( + fontSize: 16, + color: model.textColor, ), ), Flexible( - child: SizedBox( - width: dropDownWidth, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - isExpanded: true, - underline: Container( - color: const Color(0xFFBDBDBD), - height: 1, - ), - value: _selectedPosition, - items: _positionList!.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'auto', - child: Text( - value, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - _onPositionTypeChange(value.toString()); - stateSetter(() {}); - }, + child: DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + isExpanded: true, + underline: Container( + color: const Color(0xFFBDBDBD), + height: 1, ), + value: _selectedPosition, + items: _positionList!.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'auto', + child: Text( + value, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (dynamic value) { + _onPositionTypeChange(value.toString()); + stateSetter(() {}); + }, ), ), ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 30, children: [ - Flexible( - child: Text( - model.isWebFullView - ? 'Overflow \nmode' - : 'Overflow mode', - softWrap: false, - style: TextStyle( - fontSize: 16, - color: model.textColor, - ), + Text( + model.isWebFullView + ? 'Overflow \nmode' + : 'Overflow mode', + softWrap: false, + style: TextStyle( + fontSize: 16, + color: model.textColor, ), ), Flexible( - child: SizedBox( - width: dropDownWidth, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - isExpanded: true, - underline: Container( - color: const Color(0xFFBDBDBD), - height: 1, - ), - value: _selectedMode, - items: _modeList!.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'wrap', - child: Text( - value, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - _onModeTypeChange(value); - stateSetter(() {}); - }, + child: DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + isExpanded: true, + underline: Container( + color: const Color(0xFFBDBDBD), + height: 1, ), + value: _selectedMode, + items: _modeList!.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'wrap', + child: Text( + value, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (dynamic value) { + _onModeTypeChange(value); + stateSetter(() {}); + }, ), ), ], diff --git a/lib/samples/chart/cartesian_charts/real_time_charts/add_remove_data/add_remove_points.dart b/lib/samples/chart/cartesian_charts/real_time_charts/add_remove_data/add_remove_points.dart index 069a56c9..f613edfc 100644 --- a/lib/samples/chart/cartesian_charts/real_time_charts/add_remove_data/add_remove_points.dart +++ b/lib/samples/chart/cartesian_charts/real_time_charts/add_remove_data/add_remove_points.dart @@ -179,7 +179,7 @@ class _LiveVerticalState extends SampleViewState { /// Generates a random integer within the specified range. int _generateRandomInt(int min, int max) { - final Random random = Random(); + final Random random = Random.secure(); return min + random.nextInt(max - min); } diff --git a/lib/samples/chart/cartesian_charts/real_time_charts/add_remove_data/add_remove_series.dart b/lib/samples/chart/cartesian_charts/real_time_charts/add_remove_data/add_remove_series.dart index 189f21b3..f17dd431 100644 --- a/lib/samples/chart/cartesian_charts/real_time_charts/add_remove_data/add_remove_series.dart +++ b/lib/samples/chart/cartesian_charts/real_time_charts/add_remove_data/add_remove_series.dart @@ -164,7 +164,7 @@ class _LiveVerticalState extends SampleViewState { /// Generates a random integer between the specified /// minimum and maximum values. int _generateRandomInteger(int min, int max) { - final Random random = Random(); + final Random random = Random.secure(); return min + random.nextInt(max - min); } diff --git a/lib/samples/chart/cartesian_charts/real_time_charts/live_update/real_time_line_chart.dart b/lib/samples/chart/cartesian_charts/real_time_charts/live_update/real_time_line_chart.dart index 0a930673..91c74a63 100644 --- a/lib/samples/chart/cartesian_charts/real_time_charts/live_update/real_time_line_chart.dart +++ b/lib/samples/chart/cartesian_charts/real_time_charts/live_update/real_time_line_chart.dart @@ -115,7 +115,7 @@ class _LiveLineChartState extends SampleViewState { /// Generates a random integer within the specified range. int _generateRandomInteger(int min, int max) { - final math.Random random = math.Random(); + final math.Random random = math.Random.secure(); return min + random.nextInt(max - min); } diff --git a/lib/samples/chart/cartesian_charts/real_time_charts/live_update/vertical_live_chart.dart b/lib/samples/chart/cartesian_charts/real_time_charts/live_update/vertical_live_chart.dart index 9e9c4cc3..2e26e268 100644 --- a/lib/samples/chart/cartesian_charts/real_time_charts/live_update/vertical_live_chart.dart +++ b/lib/samples/chart/cartesian_charts/real_time_charts/live_update/vertical_live_chart.dart @@ -103,7 +103,7 @@ class _LiveUpdateState extends SampleViewState { /// Generates a random integer between the specified range. int _generateRandomInteger(int min, int max) { - final Random random = Random(); + final Random random = Random.secure(); return min + random.nextInt(max - min); } diff --git a/lib/samples/chart/cartesian_charts/real_time_charts/update_data_source.dart b/lib/samples/chart/cartesian_charts/real_time_charts/update_data_source.dart index d2fb4d25..8cbb2ad8 100644 --- a/lib/samples/chart/cartesian_charts/real_time_charts/update_data_source.dart +++ b/lib/samples/chart/cartesian_charts/real_time_charts/update_data_source.dart @@ -35,7 +35,7 @@ class _LiveVerticalState extends SampleViewState { ChartSampleData(x: 7, y: 30), ChartSampleData(x: 9, y: 72), ]; - _random = Random(); + _random = Random.secure(); super.initState(); } diff --git a/lib/samples/chart/cartesian_charts/series_features/data_label/default_data_labels.dart b/lib/samples/chart/cartesian_charts/series_features/data_label/default_data_labels.dart index d60afabc..6b220938 100644 --- a/lib/samples/chart/cartesian_charts/series_features/data_label/default_data_labels.dart +++ b/lib/samples/chart/cartesian_charts/series_features/data_label/default_data_labels.dart @@ -81,25 +81,23 @@ class _DataLabelDefaultState extends SampleViewState { /// Builds the checkbox for using series color. Widget buildSeriesColorCheckbox(StateSetter stateSetter) { return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Use series color', style: TextStyle(color: model.textColor, fontSize: 16), ), - Padding( - padding: const EdgeInsets.fromLTRB(8, 0, 0, 0), - child: SizedBox( - width: 90, - child: CheckboxListTile( - activeColor: model.primaryColor, - value: _useSeriesColor, - onChanged: (bool? value) { - setState(() { - _useSeriesColor = value!; - stateSetter(() {}); - }); - }, - ), + SizedBox( + width: 90, + child: CheckboxListTile( + activeColor: model.primaryColor, + value: _useSeriesColor, + onChanged: (bool? value) { + setState(() { + _useSeriesColor = value!; + stateSetter(() {}); + }); + }, ), ), ], @@ -109,31 +107,27 @@ class _DataLabelDefaultState extends SampleViewState { /// Builds the dropdown for label alignment settings. Widget buildLabelAlignmentDropdown(StateSetter stateSetter) { return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Label alignment', style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Container( - padding: const EdgeInsets.fromLTRB(52, 0, 0, 0), - height: 50, - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container(color: const Color(0xFFBDBDBD), height: 1), - value: _labelAlignment, - items: _chartAlign!.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'center', - child: Text(value, style: TextStyle(color: model.textColor)), - ); - }).toList(), - onChanged: (dynamic value) { - _onAlignmentChange(value.toString()); - stateSetter(() {}); - }, - ), + DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container(color: const Color(0xFFBDBDBD), height: 1), + value: _labelAlignment, + items: _chartAlign!.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'center', + child: Text(value, style: TextStyle(color: model.textColor)), + ); + }).toList(), + onChanged: (dynamic value) { + _onAlignmentChange(value.toString()); + stateSetter(() {}); + }, ), ], ); @@ -142,31 +136,27 @@ class _DataLabelDefaultState extends SampleViewState { /// Builds the dropdown for label position settings. Widget buildLabelPositionDropdown(StateSetter stateSetter) { return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Label position', style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Container( - padding: const EdgeInsets.fromLTRB(67, 0, 0, 0), - height: 50, - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container(color: const Color(0xFFBDBDBD), height: 1), - value: _labelPosition, - items: _positionType!.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'top', - child: Text(value, style: TextStyle(color: model.textColor)), - ); - }).toList(), - onChanged: (dynamic value) { - _onPositionChange(value.toString()); - stateSetter(() {}); - }, - ), + DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container(color: const Color(0xFFBDBDBD), height: 1), + value: _labelPosition, + items: _positionType!.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'top', + child: Text(value, style: TextStyle(color: model.textColor)), + ); + }).toList(), + onChanged: (dynamic value) { + _onPositionChange(value.toString()); + stateSetter(() {}); + }, ), ], ); @@ -175,25 +165,25 @@ class _DataLabelDefaultState extends SampleViewState { /// Builds the slider for horizontal padding settings. Widget buildHorizontalPaddingSlider() { return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - 'Horizontal padding', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), - Container( - padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), - child: CustomDirectionalButtons( - minValue: -50, - maxValue: 50, - initialValue: _horizontalPadding, - onChanged: (double value) => setState(() { - _horizontalPadding = value; - }), - step: 10, - iconColor: model.textColor, - style: TextStyle(fontSize: 20.0, color: model.textColor), + Flexible( + child: Text( + 'Horizontal padding', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), ), + CustomDirectionalButtons( + minValue: -50, + maxValue: 50, + initialValue: _horizontalPadding, + onChanged: (double value) => setState(() { + _horizontalPadding = value; + }), + step: 10, + iconColor: model.textColor, + style: TextStyle(fontSize: 20.0, color: model.textColor), + ), ], ); } @@ -201,24 +191,22 @@ class _DataLabelDefaultState extends SampleViewState { /// Builds the slider for vertical padding settings. Widget buildVerticalPaddingSlider() { return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Vertical padding', style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Container( - padding: const EdgeInsets.fromLTRB(28, 0, 0, 0), - child: CustomDirectionalButtons( - minValue: -50, - maxValue: 50, - initialValue: _verticalPadding, - onChanged: (double val) => setState(() { - _verticalPadding = val; - }), - step: 10, - iconColor: model.textColor, - style: TextStyle(fontSize: 20.0, color: model.textColor), - ), + CustomDirectionalButtons( + minValue: -50, + maxValue: 50, + initialValue: _verticalPadding, + onChanged: (double val) => setState(() { + _verticalPadding = val; + }), + step: 10, + iconColor: model.textColor, + style: TextStyle(fontSize: 20.0, color: model.textColor), ), ], ); diff --git a/lib/samples/chart/cartesian_charts/series_features/sorting.dart b/lib/samples/chart/cartesian_charts/series_features/sorting.dart index b0186f63..36d183ad 100644 --- a/lib/samples/chart/cartesian_charts/series_features/sorting.dart +++ b/lib/samples/chart/cartesian_charts/series_features/sorting.dart @@ -74,31 +74,24 @@ class _SortingDefaultState extends SampleViewState { /// Builds the dropdown for sorting options. Widget _buildSortByDropdown(StateSetter stateSetter) { return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - 'Sort by ', - style: TextStyle(color: model.textColor, fontSize: 16), - ), - Container( - padding: const EdgeInsets.fromLTRB(50, 0, 0, 0), - height: 50, - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container(color: const Color(0xFFBDBDBD), height: 1), - value: _selectedType, - items: _labelList!.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'y', - child: Text(value, style: TextStyle(color: model.textColor)), - ); - }).toList(), - onChanged: (dynamic value) { - _onPositionTypeChange(value.toString()); - stateSetter(() {}); - }, - ), + Text('Sort by', style: TextStyle(color: model.textColor, fontSize: 16)), + DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container(color: const Color(0xFFBDBDBD), height: 1), + value: _selectedType, + items: _labelList!.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'y', + child: Text(value, style: TextStyle(color: model.textColor)), + ); + }).toList(), + onChanged: (dynamic value) { + _onPositionTypeChange(value.toString()); + stateSetter(() {}); + }, ), ], ); @@ -107,29 +100,27 @@ class _SortingDefaultState extends SampleViewState { /// Builds the dropdown for sorting order options. Widget _buildSortingOrderDropdown(StateSetter stateSetter) { return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - 'Sorting order ', + 'Sorting order', style: TextStyle(color: model.textColor, fontSize: 16), ), - SizedBox( - height: 50, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container(color: const Color(0xFFBDBDBD), height: 1), - value: _selectedSortType, - items: _sortList!.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'none', - child: Text(value, style: TextStyle(color: model.textColor)), - ); - }).toList(), - onChanged: (dynamic value) { - _onSortingTypeChange(value.toString()); - stateSetter(() {}); - }, - ), + DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container(color: const Color(0xFFBDBDBD), height: 1), + value: _selectedSortType, + items: _sortList!.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'none', + child: Text(value, style: TextStyle(color: model.textColor)), + ); + }).toList(), + onChanged: (dynamic value) { + _onSortingTypeChange(value.toString()); + stateSetter(() {}); + }, ), ], ); diff --git a/lib/samples/chart/cartesian_charts/trendline/default_trendline.dart b/lib/samples/chart/cartesian_charts/trendline/default_trendline.dart index 6b7351c5..ee311270 100644 --- a/lib/samples/chart/cartesian_charts/trendline/default_trendline.dart +++ b/lib/samples/chart/cartesian_charts/trendline/default_trendline.dart @@ -85,24 +85,22 @@ class _TrendLineDefaultState extends SampleViewState { ? 245 : MediaQuery.of(context).size.width; final double dropDownWidth = - (model.isWebFullView ? 0.76 : 0.57) * screenWidth; + (model.isWebFullView ? 0.72 : 0.57) * screenWidth; return StatefulBuilder( builder: (BuildContext context, StateSetter stateSetter) { return ListView( shrinkWrap: true, children: [ Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Padding( - padding: const EdgeInsets.fromLTRB(0, 15, 0, 0), - child: Text( - 'Trendline \ntype', - style: TextStyle(color: model.textColor), - ), + Text( + 'Trendline \ntype', + style: TextStyle(color: model.textColor), ), Container( padding: EdgeInsets.fromLTRB( - model.isWebFullView ? 50 : 70, + model.isWebFullView ? 30 : 70, 0, 0, 0, diff --git a/lib/samples/chart/cartesian_charts/trendline/trendline_forecast.dart b/lib/samples/chart/cartesian_charts/trendline/trendline_forecast.dart index a591e1d7..72a5a0ee 100644 --- a/lib/samples/chart/cartesian_charts/trendline/trendline_forecast.dart +++ b/lib/samples/chart/cartesian_charts/trendline/trendline_forecast.dart @@ -80,45 +80,45 @@ class _TrendLineForecastState extends SampleViewState { shrinkWrap: true, children: [ Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - 'Forward forecast', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), - Container( - padding: const EdgeInsets.fromLTRB(30, 0, 20, 0), - child: CustomDirectionalButtons( - maxValue: 50, - initialValue: _forwardForecastValue, - onChanged: (double val) => setState(() { - _forwardForecastValue = val; - }), - loop: true, - iconColor: model.textColor, - style: TextStyle(fontSize: 20.0, color: model.textColor), + Flexible( + child: Text( + 'Forward forecast', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), ), + CustomDirectionalButtons( + maxValue: 50, + initialValue: _forwardForecastValue, + onChanged: (double val) => setState(() { + _forwardForecastValue = val; + }), + loop: true, + iconColor: model.textColor, + style: TextStyle(fontSize: 20.0, color: model.textColor), + ), ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - 'Backward forecast', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), - Container( - padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), - child: CustomDirectionalButtons( - maxValue: 50, - initialValue: _backwardForecastValue, - onChanged: (double val) => setState(() { - _backwardForecastValue = val; - }), - loop: true, - iconColor: model.textColor, - style: TextStyle(fontSize: 20.0, color: model.textColor), + Flexible( + child: Text( + 'Backward forecast', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), ), + CustomDirectionalButtons( + maxValue: 50, + initialValue: _backwardForecastValue, + onChanged: (double val) => setState(() { + _backwardForecastValue = val; + }), + loop: true, + iconColor: model.textColor, + style: TextStyle(fontSize: 20.0, color: model.textColor), + ), ], ), ], diff --git a/lib/samples/chart/cartesian_charts/user_interactions/auto_scrolling.dart b/lib/samples/chart/cartesian_charts/user_interactions/auto_scrolling.dart index 80dfd2a3..ec29278d 100644 --- a/lib/samples/chart/cartesian_charts/user_interactions/auto_scrolling.dart +++ b/lib/samples/chart/cartesian_charts/user_interactions/auto_scrolling.dart @@ -173,7 +173,7 @@ class _AutoScrollingChartState extends SampleViewState { } int _generateRandomInt(int min, int max) { - final math.Random random = math.Random(); + final math.Random random = math.Random.secure(); return min + random.nextInt(max - min); } diff --git a/lib/samples/chart/cartesian_charts/user_interactions/crosshair.dart b/lib/samples/chart/cartesian_charts/user_interactions/crosshair.dart index a61acd66..509f5f1f 100644 --- a/lib/samples/chart/cartesian_charts/user_interactions/crosshair.dart +++ b/lib/samples/chart/cartesian_charts/user_interactions/crosshair.dart @@ -238,7 +238,7 @@ class _DefaultCrossHairState extends SampleViewState { /// Method to get random data points for the chart with crosshair sample. List _buildChartData() { final List randomData = []; - final Random rand = Random(); + final Random rand = Random.secure(); double value = 100; for (int i = 1; i < 2000; i++) { if (rand.nextDouble() > 0.5) { diff --git a/lib/samples/chart/circular_charts/chart_types/doughnut/semi_doughnut_chart.dart b/lib/samples/chart/circular_charts/chart_types/doughnut/semi_doughnut_chart.dart index 508bf5f0..a0f5e233 100644 --- a/lib/samples/chart/circular_charts/chart_types/doughnut/semi_doughnut_chart.dart +++ b/lib/samples/chart/circular_charts/chart_types/doughnut/semi_doughnut_chart.dart @@ -49,6 +49,7 @@ class _SemiDoughnutChartState extends SampleViewState { /// Builds the start angle adjustment setting. Widget _buildStartAngleSetting() { return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Start angle ', @@ -75,7 +76,7 @@ class _SemiDoughnutChartState extends SampleViewState { /// Builds the end angle adjustment setting. Widget _buildEndAngleSetting() { return Row( - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( padding: const EdgeInsets.fromLTRB(0, 15, 0, 0), diff --git a/lib/samples/chart/circular_charts/chart_types/pie/pie_with_image.dart b/lib/samples/chart/circular_charts/chart_types/pie/pie_with_image.dart index ca153fe5..41b5c150 100644 --- a/lib/samples/chart/circular_charts/chart_types/pie/pie_with_image.dart +++ b/lib/samples/chart/circular_charts/chart_types/pie/pie_with_image.dart @@ -113,7 +113,7 @@ class _PieImageShaderState extends SampleViewState { image1!, TileMode.repeated, TileMode.repeated, - Matrix4.identity().scaled(0.5).storage, + Matrix4.identity().scaledByDouble(0.5, 0.5, 0.5, 1.0).storage, ), ), _ChartShaderData( @@ -124,7 +124,7 @@ class _PieImageShaderState extends SampleViewState { image2!, TileMode.repeated, TileMode.repeated, - Matrix4.identity().scaled(0.6).storage, + Matrix4.identity().scaledByDouble(0.6, 0.6, 0.6, 1.0).storage, ), ), _ChartShaderData( @@ -135,7 +135,7 @@ class _PieImageShaderState extends SampleViewState { image3!, TileMode.repeated, TileMode.repeated, - Matrix4.identity().scaled(0.6).storage, + Matrix4.identity().scaledByDouble(0.6, 0.6, 0.6, 1.0).storage, ), ), _ChartShaderData( @@ -146,7 +146,7 @@ class _PieImageShaderState extends SampleViewState { image4!, TileMode.repeated, TileMode.repeated, - Matrix4.identity().scaled(0.5).storage, + Matrix4.identity().scaledByDouble(0.5, 0.5, 0.5, 1.0).storage, ), ), ], diff --git a/lib/samples/chart/circular_charts/chart_types/pie/pie_with_smart_data_label.dart b/lib/samples/chart/circular_charts/chart_types/pie/pie_with_smart_data_label.dart index 5f78de3e..7e951b18 100644 --- a/lib/samples/chart/circular_charts/chart_types/pie/pie_with_smart_data_label.dart +++ b/lib/samples/chart/circular_charts/chart_types/pie/pie_with_smart_data_label.dart @@ -65,6 +65,7 @@ class _PieSmartDataLabelsState extends SampleViewState { shrinkWrap: true, children: [ Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Label intersect \naction', diff --git a/lib/samples/chart/circular_charts/chart_types/pie/point_render_mode.dart b/lib/samples/chart/circular_charts/chart_types/pie/point_render_mode.dart index 95a33c22..82205029 100644 --- a/lib/samples/chart/circular_charts/chart_types/pie/point_render_mode.dart +++ b/lib/samples/chart/circular_charts/chart_types/pie/point_render_mode.dart @@ -72,27 +72,24 @@ class _PiePointRenderModeState extends SampleViewState { double screenWidth, StateSetter stateSetter, ) { - return SizedBox( - height: 60, - child: ListView( - shrinkWrap: true, - physics: const ClampingScrollPhysics(), - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Text( - model.isWebFullView - ? 'Point \nrendering \nmode' - : 'Point rendering mode', - softWrap: false, - style: TextStyle(fontSize: 16, color: model.textColor), - ), - _buildPointRenderModeDropdown(screenWidth, stateSetter), - ], - ), - ], - ), + return ListView( + shrinkWrap: true, + physics: const ClampingScrollPhysics(), + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Text( + model.isWebFullView + ? 'Point \nrendering \nmode' + : 'Point rendering mode', + softWrap: false, + style: TextStyle(fontSize: 16, color: model.textColor), + ), + _buildPointRenderModeDropdown(screenWidth, stateSetter), + ], + ), + ], ); } @@ -101,28 +98,23 @@ class _PiePointRenderModeState extends SampleViewState { double screenWidth, StateSetter stateSetter, ) { - return Container( - padding: EdgeInsets.only(left: 0.07 * screenWidth), - width: 0.5 * screenWidth, - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container(color: const Color(0xFFBDBDBD), height: 1), - value: _selectedMode, - items: _modeList!.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'Render mode', - child: Text(value, style: TextStyle(color: model.textColor)), - ); - }).toList(), - onChanged: (dynamic value) { - setState(() { - _onPointRenderModeChange(value); - stateSetter(() {}); - }); - }, - ), + return DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container(color: const Color(0xFFBDBDBD), height: 1), + value: _selectedMode, + items: _modeList!.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'Render mode', + child: Text(value, style: TextStyle(color: model.textColor)), + ); + }).toList(), + onChanged: (dynamic value) { + setState(() { + _onPointRenderModeChange(value); + stateSetter(() {}); + }); + }, ); } diff --git a/lib/samples/chart/circular_charts/chart_types/pie/semi_pie_chart.dart b/lib/samples/chart/circular_charts/chart_types/pie/semi_pie_chart.dart index 82b62dc6..8a86952f 100644 --- a/lib/samples/chart/circular_charts/chart_types/pie/semi_pie_chart.dart +++ b/lib/samples/chart/circular_charts/chart_types/pie/semi_pie_chart.dart @@ -55,24 +55,22 @@ class _SemiPieChartState extends SampleViewState { /// Creates a UI component for adjusting the starting angle of the chart. Widget _buildStartAngleSetting() { return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - 'Start angle ', + 'Start angle', style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Container( - padding: const EdgeInsets.fromLTRB(40, 0, 0, 0), - child: CustomDirectionalButtons( - minValue: 90, - maxValue: 270, - initialValue: _startAngle.toDouble(), - onChanged: (double val) => setState(() { - _startAngle = val.toInt(); - }), - step: 10, - iconColor: model.textColor, - style: TextStyle(fontSize: 20.0, color: model.textColor), - ), + CustomDirectionalButtons( + minValue: 90, + maxValue: 270, + initialValue: _startAngle.toDouble(), + onChanged: (double val) => setState(() { + _startAngle = val.toInt(); + }), + step: 10, + iconColor: model.textColor, + style: TextStyle(fontSize: 16.0, color: model.textColor), ), ], ); @@ -81,28 +79,22 @@ class _SemiPieChartState extends SampleViewState { /// Creates a UI component for adjusting the ending angle of the chart. Widget _buildEndAngleSetting() { return Row( - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Padding( - padding: const EdgeInsets.fromLTRB(0, 15, 0, 0), - child: Text( - 'End angle ', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'End angle', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Container( - padding: const EdgeInsets.fromLTRB(50, 0, 0, 0), - child: CustomDirectionalButtons( - minValue: 90, - maxValue: 270, - initialValue: _endAngle.toDouble(), - onChanged: (double val) => setState(() { - _endAngle = val.toInt(); - }), - step: 10, - iconColor: model.textColor, - style: TextStyle(fontSize: 20.0, color: model.textColor), - ), + CustomDirectionalButtons( + minValue: 90, + maxValue: 270, + initialValue: _endAngle.toDouble(), + onChanged: (double val) => setState(() { + _endAngle = val.toInt(); + }), + step: 10, + iconColor: model.textColor, + style: TextStyle(fontSize: 16.0, color: model.textColor), ), ], ); diff --git a/lib/samples/chart/circular_charts/legend/legend_with_various_options.dart b/lib/samples/chart/circular_charts/legend/legend_with_various_options.dart index affe0182..9847dab7 100644 --- a/lib/samples/chart/circular_charts/legend/legend_with_various_options.dart +++ b/lib/samples/chart/circular_charts/legend/legend_with_various_options.dart @@ -254,29 +254,21 @@ class _LegendOptionsState extends SampleViewState { style: TextStyle(fontSize: 16, color: model.textColor), ), ), - Flexible( - flex: 4, - child: Container( - padding: const EdgeInsets.fromLTRB(35, 0, 0, 0), - height: 50, - alignment: Alignment.bottomLeft, - child: CustomDirectionalButtons( - minValue: -100, - maxValue: 100, - initialValue: _xOffset, - onChanged: (double val) => setState(() { - _xOffset = _enableFloatingLegend ? val : 0; - }), - step: _enableFloatingLegend ? 10 : 0, - iconColor: model.textColor.withValues( - alpha: _enableFloatingLegend ? 1 : 0.5, - ), - style: TextStyle( - fontSize: 16.0, - color: model.textColor.withValues( - alpha: _enableFloatingLegend ? 1 : 0.5, - ), - ), + CustomDirectionalButtons( + minValue: -100, + maxValue: 100, + initialValue: _xOffset, + onChanged: (double val) => setState(() { + _xOffset = _enableFloatingLegend ? val : 0; + }), + step: _enableFloatingLegend ? 10 : 0, + iconColor: model.textColor.withValues( + alpha: _enableFloatingLegend ? 1 : 0.5, + ), + style: TextStyle( + fontSize: 16.0, + color: model.textColor.withValues( + alpha: _enableFloatingLegend ? 1 : 0.5, ), ), ), @@ -297,29 +289,21 @@ class _LegendOptionsState extends SampleViewState { style: TextStyle(fontSize: 16, color: model.textColor), ), ), - Flexible( - flex: 4, - child: Container( - padding: const EdgeInsets.fromLTRB(35, 0, 0, 0), - height: 50, - alignment: Alignment.bottomLeft, - child: CustomDirectionalButtons( - minValue: -100, - maxValue: 100, - initialValue: _yOffset, - onChanged: (double val) => setState(() { - _yOffset = _enableFloatingLegend ? val : 0; - }), - step: _enableFloatingLegend ? 10 : 0, - iconColor: model.textColor.withValues( - alpha: _enableFloatingLegend ? 1 : 0.5, - ), - style: TextStyle( - fontSize: 16.0, - color: model.textColor.withValues( - alpha: _enableFloatingLegend ? 1 : 0.5, - ), - ), + CustomDirectionalButtons( + minValue: -100, + maxValue: 100, + initialValue: _yOffset, + onChanged: (double val) => setState(() { + _yOffset = _enableFloatingLegend ? val : 0; + }), + step: _enableFloatingLegend ? 10 : 0, + iconColor: model.textColor.withValues( + alpha: _enableFloatingLegend ? 1 : 0.5, + ), + style: TextStyle( + fontSize: 16.0, + color: model.textColor.withValues( + alpha: _enableFloatingLegend ? 1 : 0.5, ), ), ), diff --git a/lib/samples/chart/funnel_charts/default_funnel_chart.dart b/lib/samples/chart/funnel_charts/default_funnel_chart.dart index b6feeb0c..07acbd99 100644 --- a/lib/samples/chart/funnel_charts/default_funnel_chart.dart +++ b/lib/samples/chart/funnel_charts/default_funnel_chart.dart @@ -164,8 +164,7 @@ class _FunnelDefaultState extends SampleViewState { softWrap: false, style: TextStyle(fontSize: 16, color: model.textColor), ), - Container( - padding: EdgeInsets.only(left: 0.06 * screenWidth), + SizedBox( width: 0.5 * screenWidth, child: CheckboxListTile( controlAffinity: ListTileControlAffinity.leading, diff --git a/lib/samples/chart/funnel_charts/funnel_with_smart_labels.dart b/lib/samples/chart/funnel_charts/funnel_with_smart_labels.dart index 03aa1f24..5168910b 100644 --- a/lib/samples/chart/funnel_charts/funnel_with_smart_labels.dart +++ b/lib/samples/chart/funnel_charts/funnel_with_smart_labels.dart @@ -100,35 +100,30 @@ class _FunnelSmartLabelState extends SampleViewState { /// Builds the row for selecting the label position. Widget _buildLabelPositionRow(double dropDownWidth, StateSetter stateSetter) { return Row( + spacing: 20, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Flexible( - child: Text( - 'Label position', - softWrap: false, - style: TextStyle(fontSize: 16, color: model.textColor), - ), + Text( + 'Label position', + style: TextStyle(fontSize: 16, color: model.textColor), ), Flexible( - child: SizedBox( - width: dropDownWidth, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - isExpanded: true, - underline: Container(color: const Color(0xFFBDBDBD), height: 1), - value: _selectedPosition, - items: _labelPosition!.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'outside', - child: Text(value, style: TextStyle(color: model.textColor)), - ); - }).toList(), - onChanged: (dynamic value) { - _onLabelPositionChange(value.toString()); - stateSetter(() {}); - }, - ), + child: DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + isExpanded: true, + underline: Container(color: const Color(0xFFBDBDBD), height: 1), + value: _selectedPosition, + items: _labelPosition!.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'outside', + child: Text(value, style: TextStyle(color: model.textColor)), + ); + }).toList(), + onChanged: (dynamic value) { + _onLabelPositionChange(value.toString()); + stateSetter(() {}); + }, ), ), ], @@ -138,46 +133,40 @@ class _FunnelSmartLabelState extends SampleViewState { /// Builds the row for selecting the overflow mode. Widget _buildOverflowModeRow(double dropDownWidth, StateSetter stateSetter) { return Row( + spacing: 20, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Flexible( - child: Text( - 'Overflow mode', - softWrap: false, - style: TextStyle( - fontSize: 16, - color: _selectedPosition != 'inside' - ? model.textColor.withValues(alpha: 0.3) - : model.textColor, - ), + Text( + 'Overflow mode', + style: TextStyle( + fontSize: 16, + color: _selectedPosition != 'inside' + ? model.textColor.withValues(alpha: 0.3) + : model.textColor, ), ), Flexible( - child: SizedBox( - height: 50, - width: dropDownWidth, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - isExpanded: true, - underline: Container(color: const Color(0xFFBDBDBD), height: 1), - value: _selectedOverflowMode, - items: _selectedPosition != 'inside' - ? null - : _overflowModeList!.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'none', - child: Text( - value, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - _updateOverflowMode(value.toString()); - stateSetter(() {}); - }, - ), + child: DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + isExpanded: true, + underline: Container(color: const Color(0xFFBDBDBD), height: 1), + value: _selectedOverflowMode, + items: _selectedPosition != 'inside' + ? null + : _overflowModeList!.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'none', + child: Text( + value, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (dynamic value) { + _updateOverflowMode(value.toString()); + stateSetter(() {}); + }, ), ), ], @@ -190,50 +179,44 @@ class _FunnelSmartLabelState extends SampleViewState { StateSetter stateSetter, ) { return Row( + spacing: 20, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Flexible( - child: Text( - 'Label intersect \naction', - softWrap: false, - style: TextStyle( - fontSize: 16, - color: - (_selectedOverflowMode != 'none' && - _selectedPosition != 'outside') - ? model.textColor.withValues(alpha: 0.3) - : model.textColor, - ), + Text( + 'Label intersect \naction', + style: TextStyle( + fontSize: 16, + color: + (_selectedOverflowMode != 'none' && + _selectedPosition != 'outside') + ? model.textColor.withValues(alpha: 0.3) + : model.textColor, ), ), Flexible( - child: SizedBox( - height: 50, - width: dropDownWidth, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - isExpanded: true, - underline: Container(color: const Color(0xFFBDBDBD), height: 1), - value: _labelIntersectAction, - items: - (_selectedOverflowMode != 'none' && - _selectedPosition != 'outside') - ? null - : _labelIntersectActionList!.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'shift', - child: Text( - value, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - _updateLabelIntersectAction(value.toString()); - stateSetter(() {}); - }, - ), + child: DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + isExpanded: true, + underline: Container(color: const Color(0xFFBDBDBD), height: 1), + value: _labelIntersectAction, + items: + (_selectedOverflowMode != 'none' && + _selectedPosition != 'outside') + ? null + : _labelIntersectActionList!.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'shift', + child: Text( + value, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (dynamic value) { + _updateLabelIntersectAction(value.toString()); + stateSetter(() {}); + }, ), ), ], diff --git a/lib/samples/chart/pyramid_charts/pyramid_with_smart_labels.dart b/lib/samples/chart/pyramid_charts/pyramid_with_smart_labels.dart index 21d13795..4e061956 100644 --- a/lib/samples/chart/pyramid_charts/pyramid_with_smart_labels.dart +++ b/lib/samples/chart/pyramid_charts/pyramid_with_smart_labels.dart @@ -150,33 +150,28 @@ class _PyramidSmartLabelState extends SampleViewState { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Flexible( - child: Text( - 'Label position', - softWrap: false, - style: TextStyle(fontSize: 16, color: model.textColor), - ), + Text( + 'Label position', + style: TextStyle(fontSize: 16, color: model.textColor), ), - Flexible( - child: SizedBox( - width: dropDownWidth, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - isExpanded: true, - underline: Container(color: const Color(0xFFBDBDBD), height: 1), - value: _selectedPosition, - items: _labelPosition!.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'outside', - child: Text(value, style: TextStyle(color: model.textColor)), - ); - }).toList(), - onChanged: (dynamic value) { - _updateLabelPosition(value.toString()); - stateSetter(() {}); - }, - ), + SizedBox( + width: dropDownWidth, + child: DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + isExpanded: true, + underline: Container(color: const Color(0xFFBDBDBD), height: 1), + value: _selectedPosition, + items: _labelPosition!.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'outside', + child: Text(value, style: TextStyle(color: model.textColor)), + ); + }).toList(), + onChanged: (dynamic value) { + _updateLabelPosition(value.toString()); + stateSetter(() {}); + }, ), ), ], @@ -188,44 +183,39 @@ class _PyramidSmartLabelState extends SampleViewState { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Flexible( - child: Text( - 'Overflow mode', - softWrap: false, - style: TextStyle( - fontSize: 16, - color: _selectedPosition != 'inside' - ? model.textColor.withValues(alpha: 0.3) - : model.textColor, - ), + Text( + 'Overflow mode', + style: TextStyle( + fontSize: 16, + color: _selectedPosition != 'inside' + ? model.textColor.withValues(alpha: 0.3) + : model.textColor, ), ), - Flexible( - child: SizedBox( - height: 50, - width: dropDownWidth, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - isExpanded: true, - underline: Container(color: const Color(0xFFBDBDBD), height: 1), - value: _selectedOverflowMode, - items: _selectedPosition != 'inside' - ? null - : _overflowModeList!.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'none', - child: Text( - value, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - _updateOverflowMode(value.toString()); - stateSetter(() {}); - }, - ), + SizedBox( + height: 50, + width: dropDownWidth, + child: DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + isExpanded: true, + underline: Container(color: const Color(0xFFBDBDBD), height: 1), + value: _selectedOverflowMode, + items: _selectedPosition != 'inside' + ? null + : _overflowModeList!.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'none', + child: Text( + value, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (dynamic value) { + _updateOverflowMode(value.toString()); + stateSetter(() {}); + }, ), ), ], @@ -240,48 +230,43 @@ class _PyramidSmartLabelState extends SampleViewState { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Flexible( - child: Text( - 'Label intersect \naction', - softWrap: false, - style: TextStyle( - fontSize: 16, - color: - (_selectedOverflowMode != 'none' && - _selectedPosition != 'outside') - ? model.textColor.withValues(alpha: 0.3) - : model.textColor, - ), + Text( + 'Label intersect \naction', + style: TextStyle( + fontSize: 16, + color: + (_selectedOverflowMode != 'none' && + _selectedPosition != 'outside') + ? model.textColor.withValues(alpha: 0.3) + : model.textColor, ), ), - Flexible( - child: SizedBox( - height: 50, - width: dropDownWidth, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - isExpanded: true, - underline: Container(color: const Color(0xFFBDBDBD), height: 1), - value: _selectedIntersectAction, - items: - (_selectedOverflowMode != 'none' && - _selectedPosition != 'outside') - ? null - : _labelIntersectActionList!.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'shift', - child: Text( - value, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - _updateLabelIntersectAction(value.toString()); - stateSetter(() {}); - }, - ), + SizedBox( + height: 50, + width: dropDownWidth, + child: DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + isExpanded: true, + underline: Container(color: const Color(0xFFBDBDBD), height: 1), + value: _selectedIntersectAction, + items: + (_selectedOverflowMode != 'none' && + _selectedPosition != 'outside') + ? null + : _labelIntersectActionList!.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'shift', + child: Text( + value, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (dynamic value) { + _updateLabelIntersectAction(value.toString()); + stateSetter(() {}); + }, ), ), ], diff --git a/lib/samples/datagrid/datagridsource/customer_datagridsource.dart b/lib/samples/datagrid/datagridsource/customer_datagridsource.dart index ba259610..25578eea 100644 --- a/lib/samples/datagrid/datagridsource/customer_datagridsource.dart +++ b/lib/samples/datagrid/datagridsource/customer_datagridsource.dart @@ -22,7 +22,7 @@ class CustomerDataGridSource extends DataGridSource { /// Determine to decide whether the platform is web or desktop. final bool isWebOrDesktop; - final math.Random _random = math.Random(); + final math.Random _random = math.Random.secure(); /// Instance of DataGridRow. List dataGridRows = []; diff --git a/lib/samples/datagrid/datagridsource/dealer_datagridsource.dart b/lib/samples/datagrid/datagridsource/dealer_datagridsource.dart index 9efa6a5d..80b9159e 100644 --- a/lib/samples/datagrid/datagridsource/dealer_datagridsource.dart +++ b/lib/samples/datagrid/datagridsource/dealer_datagridsource.dart @@ -48,7 +48,7 @@ class DealerDataGridSource extends DataGridSource { late TextStyle _textStyle; /// Help to generate the random number. - final Random _random = Random(); + final Random _random = Random.secure(); /// Help to control the editable text in [TextField] widget. final TextEditingController _editingController = TextEditingController(); diff --git a/lib/samples/datagrid/datagridsource/orderinfo_datagridsource.dart b/lib/samples/datagrid/datagridsource/orderinfo_datagridsource.dart index 64e3774c..0305252c 100644 --- a/lib/samples/datagrid/datagridsource/orderinfo_datagridsource.dart +++ b/lib/samples/datagrid/datagridsource/orderinfo_datagridsource.dart @@ -41,7 +41,7 @@ class OrderInfoDataGridSource extends DataGridSource { /// Get data count of an order. int? orderDataCount; - final math.Random _random = math.Random(); + final math.Random _random = math.Random.secure(); /// Instance of an order. List orders = []; diff --git a/lib/samples/datagrid/datagridsource/product_datagridsource.dart b/lib/samples/datagrid/datagridsource/product_datagridsource.dart index 523d2de2..f71dd7d0 100644 --- a/lib/samples/datagrid/datagridsource/product_datagridsource.dart +++ b/lib/samples/datagrid/datagridsource/product_datagridsource.dart @@ -25,7 +25,7 @@ class ProductDataGridSource extends DataGridSource { _buildDataGridRows(sampleType); } - final Random _random = Random(); + final Random _random = Random.secure(); /// Get data count of product. final int productDataCount; diff --git a/lib/samples/datagrid/datagridsource/realtime_datagridsource.dart b/lib/samples/datagrid/datagridsource/realtime_datagridsource.dart index 590d05f1..03662634 100644 --- a/lib/samples/datagrid/datagridsource/realtime_datagridsource.dart +++ b/lib/samples/datagrid/datagridsource/realtime_datagridsource.dart @@ -22,7 +22,7 @@ class RealTimeUpdateDataGridSource extends DataGridSource { /// Check whether the device is desktop or mobile platform. final bool isWebOrDesktop; - final math.Random _random = math.Random(); + final math.Random _random = math.Random.secure(); List _stocks = []; diff --git a/lib/samples/datagrid/datagridsource/stock_datagridsource.dart b/lib/samples/datagrid/datagridsource/stock_datagridsource.dart index 90a3d899..bfa9c634 100644 --- a/lib/samples/datagrid/datagridsource/stock_datagridsource.dart +++ b/lib/samples/datagrid/datagridsource/stock_datagridsource.dart @@ -19,7 +19,7 @@ class ConditionalStyleDataGridSource extends DataGridSource { _buildDataGridRows(); } - final math.Random _random = math.Random(); + final math.Random _random = math.Random.secure(); List _stocks = []; diff --git a/lib/samples/datagrid/datagridsource/stockinfo_datagridsource.dart b/lib/samples/datagrid/datagridsource/stockinfo_datagridsource.dart index 9977176e..b3912641 100644 --- a/lib/samples/datagrid/datagridsource/stockinfo_datagridsource.dart +++ b/lib/samples/datagrid/datagridsource/stockinfo_datagridsource.dart @@ -24,7 +24,7 @@ class StockInfoDataGridSource extends DataGridSource { /// Checks whether it's a grouping sample source or not. late bool isGroupingSample; - final math.Random _random = math.Random(); + final math.Random _random = math.Random.secure(); List _stocks = []; diff --git a/lib/samples/datagrid/datagridsource/team_datagridsource.dart b/lib/samples/datagrid/datagridsource/team_datagridsource.dart index 4b663b6b..5ac04e6a 100644 --- a/lib/samples/datagrid/datagridsource/team_datagridsource.dart +++ b/lib/samples/datagrid/datagridsource/team_datagridsource.dart @@ -229,7 +229,7 @@ class EmployeeDataGridSource extends DataGridSource { _buildDataGridRows(); } - final math.Random _random = math.Random(); + final math.Random _random = math.Random.secure(); List _dataGridRows = []; List _employees = []; diff --git a/lib/samples/date_picker/blackout_date_picker.dart b/lib/samples/date_picker/blackout_date_picker.dart index 66b2a2b1..f8fd577a 100644 --- a/lib/samples/date_picker/blackout_date_picker.dart +++ b/lib/samples/date_picker/blackout_date_picker.dart @@ -39,7 +39,7 @@ class _BlackoutDatePickerState extends SampleViewState { const Duration(days: 500), ); final DateTime endDate = DateTime.now().add(const Duration(days: 500)); - final Random random = Random(); + final Random random = Random.secure(); for ( DateTime date = startDate; date.isBefore(endDate); diff --git a/lib/samples/date_picker/customized_date_picker.dart b/lib/samples/date_picker/customized_date_picker.dart index 473f5381..b443fa3d 100644 --- a/lib/samples/date_picker/customized_date_picker.dart +++ b/lib/samples/date_picker/customized_date_picker.dart @@ -39,7 +39,7 @@ class _CustomizedDatePickerState extends SampleViewState { const Duration(days: 200), ); final DateTime endDate = DateTime.now().add(const Duration(days: 500)); - final Random random = Random(); + final Random random = Random.secure(); for ( DateTime date = startDate; date.isBefore(endDate); diff --git a/lib/samples/date_picker/date_picker_getting_started.dart b/lib/samples/date_picker/date_picker_getting_started.dart index c2fd5132..020ba863 100644 --- a/lib/samples/date_picker/date_picker_getting_started.dart +++ b/lib/samples/date_picker/date_picker_getting_started.dart @@ -309,41 +309,36 @@ class _GettingStartedDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: model.isWebFullView ? 4 : 5, - child: Text( - 'Picker view', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'Picker view', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Expanded( - flex: model.isWebFullView ? 6 : 5, - child: Container( - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container( - color: const Color(0xFFBDBDBD), - height: 1, - ), - value: _viewModeString, - items: _viewModeList.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'month', - child: Text( - value, - textAlign: TextAlign.center, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - _onPickerViewChange(value); - stateSetter(() {}); - }, + Container( + alignment: Alignment.bottomLeft, + child: DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container( + color: const Color(0xFFBDBDBD), + height: 1, ), + value: _viewModeString, + items: _viewModeList.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'month', + child: Text( + value, + textAlign: TextAlign.center, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (dynamic value) { + _onPickerViewChange(value); + stateSetter(() {}); + }, ), ), ], @@ -355,42 +350,39 @@ class _GettingStartedDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: model.isWebFullView ? 4 : 5, + Flexible( child: Text( 'Selection mode', style: TextStyle(fontSize: 16.0, color: model.textColor), ), ), - Expanded( - flex: model.isWebFullView ? 6 : 5, - child: Container( - padding: EdgeInsets.zero, - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container( - color: const Color(0xFFBDBDBD), - height: 1, - ), - value: _selectionModeString, - items: _selectionModeList.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'extendableRange', - child: Text( - value, - textAlign: TextAlign.center, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - _onSelectionModeChange(value); - stateSetter(() {}); - }, + Container( + padding: EdgeInsets.zero, + alignment: Alignment.bottomLeft, + child: DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container( + color: const Color(0xFFBDBDBD), + height: 1, ), + value: _selectionModeString, + items: _selectionModeList.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'extendableRange', + child: Text( + value, + textAlign: TextAlign.center, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (dynamic value) { + _onSelectionModeChange(value); + stateSetter(() {}); + }, ), ), ], @@ -402,44 +394,39 @@ class _GettingStartedDatePickerState extends SampleViewState { ? SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: model.isWebFullView ? 4 : 5, - child: Text( - 'Selection Direction', - style: TextStyle( - fontSize: 16.0, - color: model.textColor, - ), + Text( + 'Selection Direction', + style: TextStyle( + fontSize: 16.0, + color: model.textColor, ), ), - Expanded( - flex: model.isWebFullView ? 6 : 5, - child: Container( - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container( - color: const Color(0xFFBDBDBD), - height: 1, - ), - value: _selectionDirectionString, - items: _selectionDirectionList.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'both', - child: Text( - value, - textAlign: TextAlign.center, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - _onSelectionDirectionChanged(value); - stateSetter(() {}); - }, + Container( + alignment: Alignment.bottomLeft, + child: DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container( + color: const Color(0xFFBDBDBD), + height: 1, ), + value: _selectionDirectionString, + items: _selectionDirectionList.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'both', + child: Text( + value, + textAlign: TextAlign.center, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (dynamic value) { + _onSelectionDirectionChanged(value); + stateSetter(() {}); + }, ), ), ], @@ -451,29 +438,24 @@ class _GettingStartedDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: model.isWebFullView ? 4 : 5, - child: Text( - 'Display date', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'Display date', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Expanded( - flex: model.isWebFullView ? 6 : 5, - child: Container( - padding: EdgeInsets.zero, - alignment: Alignment.centerLeft, - child: Theme( - data: model.themeData.copyWith( - canvasColor: model.drawerBackgroundColor, - ), - child: _DateRangePickerOption( - _onDisplayDateChanged, - _controller.displayDate!, - model, - displayDate: _controller.displayDate!, - ), + Container( + padding: EdgeInsets.zero, + alignment: Alignment.centerLeft, + child: Theme( + data: model.themeData.copyWith( + canvasColor: model.drawerBackgroundColor, + ), + child: _DateRangePickerOption( + _onDisplayDateChanged, + _controller.displayDate!, + model, + displayDate: _controller.displayDate!, ), ), ), @@ -485,36 +467,31 @@ class _GettingStartedDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: 7, - child: Text( - 'Show action buttons', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'Show action buttons', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Expanded( - flex: 3, - child: Container( - padding: EdgeInsets.zero, - child: Theme( - data: Theme.of( - context, - ).copyWith(canvasColor: model.drawerBackgroundColor), - child: Container( - alignment: Alignment.centerLeft, - child: Transform.scale( - scale: 0.8, - child: CupertinoSwitch( - value: _showActionButtons, - onChanged: (dynamic value) { - setState(() { - _onBoolValueChange('ShowActionButtons', value); - stateSetter(() {}); - }); - }, - activeTrackColor: model.primaryColor, - ), + Container( + padding: EdgeInsets.zero, + child: Theme( + data: Theme.of( + context, + ).copyWith(canvasColor: model.drawerBackgroundColor), + child: Container( + alignment: Alignment.centerLeft, + child: Transform.scale( + scale: 0.8, + child: CupertinoSwitch( + value: _showActionButtons, + onChanged: (dynamic value) { + setState(() { + _onBoolValueChange('ShowActionButtons', value); + stateSetter(() {}); + }); + }, + activeTrackColor: model.primaryColor, ), ), ), @@ -528,36 +505,31 @@ class _GettingStartedDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: 7, - child: Text( - 'Show today button', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'Show today button', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Expanded( - flex: 3, - child: Container( - padding: EdgeInsets.zero, - child: Theme( - data: Theme.of( - context, - ).copyWith(canvasColor: model.drawerBackgroundColor), - child: Container( - alignment: Alignment.centerLeft, - child: Transform.scale( - scale: 0.8, - child: CupertinoSwitch( - value: _showTodayButton, - onChanged: (dynamic value) { - setState(() { - _onBoolValueChange('ShowTodayButton', value); - stateSetter(() {}); - }); - }, - activeTrackColor: model.primaryColor, - ), + Container( + padding: EdgeInsets.zero, + child: Theme( + data: Theme.of( + context, + ).copyWith(canvasColor: model.drawerBackgroundColor), + child: Container( + alignment: Alignment.centerLeft, + child: Transform.scale( + scale: 0.8, + child: CupertinoSwitch( + value: _showTodayButton, + onChanged: (dynamic value) { + setState(() { + _onBoolValueChange('ShowTodayButton', value); + stateSetter(() {}); + }); + }, + activeTrackColor: model.primaryColor, ), ), ), @@ -571,39 +543,31 @@ class _GettingStartedDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: 7, - child: Text( - 'Enable view navigation', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'Enable view navigation', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Expanded( - flex: 3, - child: Container( - padding: EdgeInsets.zero, - child: Theme( - data: Theme.of( - context, - ).copyWith(canvasColor: model.drawerBackgroundColor), - child: Container( - alignment: Alignment.centerLeft, - child: Transform.scale( - scale: 0.8, - child: CupertinoSwitch( - value: _enableViewNavigation, - onChanged: (bool value) { - setState(() { - _onBoolValueChange( - 'EnableViewNavigation', - value, - ); - stateSetter(() {}); - }); - }, - activeTrackColor: model.primaryColor, - ), + Container( + padding: EdgeInsets.zero, + child: Theme( + data: Theme.of( + context, + ).copyWith(canvasColor: model.drawerBackgroundColor), + child: Container( + alignment: Alignment.centerLeft, + child: Transform.scale( + scale: 0.8, + child: CupertinoSwitch( + value: _enableViewNavigation, + onChanged: (bool value) { + setState(() { + _onBoolValueChange('EnableViewNavigation', value); + stateSetter(() {}); + }); + }, + activeTrackColor: model.primaryColor, ), ), ), @@ -617,34 +581,29 @@ class _GettingStartedDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: 7, - child: Text( - 'Enable past dates', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'Enable past dates', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Expanded( - flex: 3, - child: Container( - padding: EdgeInsets.zero, - child: Theme( - data: Theme.of( - context, - ).copyWith(canvasColor: model.drawerBackgroundColor), - child: Container( - alignment: Alignment.centerLeft, - child: Transform.scale( - scale: 0.8, - child: CupertinoSwitch( - value: _enablePastDates, - onChanged: (dynamic value) { - _onBoolValueChange('EnablePastDates', value); - stateSetter(() {}); - }, - activeTrackColor: model.primaryColor, - ), + Container( + padding: EdgeInsets.zero, + child: Theme( + data: Theme.of( + context, + ).copyWith(canvasColor: model.drawerBackgroundColor), + child: Container( + alignment: Alignment.centerLeft, + child: Transform.scale( + scale: 0.8, + child: CupertinoSwitch( + value: _enablePastDates, + onChanged: (dynamic value) { + _onBoolValueChange('EnablePastDates', value); + stateSetter(() {}); + }, + activeTrackColor: model.primaryColor, ), ), ), @@ -658,39 +617,34 @@ class _GettingStartedDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: 7, - child: Text( - 'Enable swipe selection', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'Enable swipe selection', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Expanded( - flex: 3, - child: Container( - padding: EdgeInsets.zero, - child: Theme( - data: Theme.of( - context, - ).copyWith(canvasColor: model.drawerBackgroundColor), - child: Container( - alignment: Alignment.centerLeft, - child: Transform.scale( - scale: 0.8, - child: CupertinoSwitch( - value: _enableSwipingSelection, - onChanged: (dynamic value) { - setState(() { - _onBoolValueChange( - 'EnableSwipingSelection', - value, - ); - stateSetter(() {}); - }); - }, - activeTrackColor: model.primaryColor, - ), + Container( + padding: EdgeInsets.zero, + child: Theme( + data: Theme.of( + context, + ).copyWith(canvasColor: model.drawerBackgroundColor), + child: Container( + alignment: Alignment.centerLeft, + child: Transform.scale( + scale: 0.8, + child: CupertinoSwitch( + value: _enableSwipingSelection, + onChanged: (dynamic value) { + setState(() { + _onBoolValueChange( + 'EnableSwipingSelection', + value, + ); + stateSetter(() {}); + }); + }, + activeTrackColor: model.primaryColor, ), ), ), @@ -704,36 +658,31 @@ class _GettingStartedDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: 7, - child: Text( - 'Show week number', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'Show week number', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Expanded( - flex: 3, - child: Container( - padding: EdgeInsets.zero, - child: Theme( - data: Theme.of( - context, - ).copyWith(canvasColor: model.drawerBackgroundColor), - child: Container( - alignment: Alignment.centerLeft, - child: Transform.scale( - scale: 0.8, - child: CupertinoSwitch( - value: _showWeekNumber, - onChanged: (dynamic value) { - setState(() { - _onBoolValueChange('ShowWeekNumber', value); - stateSetter(() {}); - }); - }, - activeTrackColor: model.primaryColor, - ), + Container( + padding: EdgeInsets.zero, + child: Theme( + data: Theme.of( + context, + ).copyWith(canvasColor: model.drawerBackgroundColor), + child: Container( + alignment: Alignment.centerLeft, + child: Transform.scale( + scale: 0.8, + child: CupertinoSwitch( + value: _showWeekNumber, + onChanged: (dynamic value) { + setState(() { + _onBoolValueChange('ShowWeekNumber', value); + stateSetter(() {}); + }); + }, + activeTrackColor: model.primaryColor, ), ), ), @@ -747,37 +696,34 @@ class _GettingStartedDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: 7, + Flexible( child: Text( 'Show trailing and leading dates', style: TextStyle(fontSize: 16.0, color: model.textColor), ), ), - Expanded( - flex: 3, - child: Container( - padding: EdgeInsets.zero, - child: Theme( - data: Theme.of( - context, - ).copyWith(canvasColor: model.drawerBackgroundColor), - child: Container( - alignment: Alignment.centerLeft, - child: Transform.scale( - scale: 0.8, - child: CupertinoSwitch( - value: _showTrailingAndLeadingDates, - onChanged: (dynamic value) { - _onBoolValueChange( - 'ShowLeadingTrailingDates', - value, - ); - stateSetter(() {}); - }, - activeTrackColor: model.primaryColor, - ), + Container( + padding: EdgeInsets.zero, + child: Theme( + data: Theme.of( + context, + ).copyWith(canvasColor: model.drawerBackgroundColor), + child: Container( + alignment: Alignment.centerLeft, + child: Transform.scale( + scale: 0.8, + child: CupertinoSwitch( + value: _showTrailingAndLeadingDates, + onChanged: (dynamic value) { + _onBoolValueChange( + 'ShowLeadingTrailingDates', + value, + ); + stateSetter(() {}); + }, + activeTrackColor: model.primaryColor, ), ), ), diff --git a/lib/samples/date_picker/hijri_calendar.dart b/lib/samples/date_picker/hijri_calendar.dart index 57ef22dc..5d76a7cc 100644 --- a/lib/samples/date_picker/hijri_calendar.dart +++ b/lib/samples/date_picker/hijri_calendar.dart @@ -303,42 +303,34 @@ class _HijriDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: model.isWebFullView ? 4 : 5, - child: Text( - 'Picker view', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'Picker view', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Expanded( - flex: model.isWebFullView ? 6 : 5, - child: Container( - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container( - color: const Color(0xFFBDBDBD), - height: 1, - ), - value: _viewModeString, - items: _viewModeList.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'month', - child: Text( - value, - textAlign: TextAlign.center, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - _onPickerViewChange(value); - stateSetter(() {}); - }, - ), + DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container( + color: const Color(0xFFBDBDBD), + height: 1, ), + value: _viewModeString, + items: _viewModeList.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'month', + child: Text( + value, + textAlign: TextAlign.center, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (dynamic value) { + _onPickerViewChange(value); + stateSetter(() {}); + }, ), ], ), @@ -349,43 +341,36 @@ class _HijriDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: model.isWebFullView ? 4 : 5, + Flexible( child: Text( 'Selection mode', style: TextStyle(fontSize: 16.0, color: model.textColor), ), ), - Expanded( - flex: model.isWebFullView ? 6 : 5, - child: Container( - padding: EdgeInsets.zero, - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container( - color: const Color(0xFFBDBDBD), - height: 1, - ), - value: _selectionModeString, - items: _selectionModeList.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'extendableRange', - child: Text( - value, - textAlign: TextAlign.center, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - _onSelectionModeChange(value); - stateSetter(() {}); - }, - ), + DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container( + color: const Color(0xFFBDBDBD), + height: 1, ), + value: _selectionModeString, + items: _selectionModeList.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'extendableRange', + child: Text( + value, + textAlign: TextAlign.center, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (dynamic value) { + _onSelectionModeChange(value); + stateSetter(() {}); + }, ), ], ), @@ -396,45 +381,37 @@ class _HijriDatePickerState extends SampleViewState { ? SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: model.isWebFullView ? 4 : 5, - child: Text( - 'Selection Direction', - style: TextStyle( - fontSize: 16.0, - color: model.textColor, - ), + Text( + 'Selection Direction', + style: TextStyle( + fontSize: 16.0, + color: model.textColor, ), ), - Expanded( - flex: model.isWebFullView ? 6 : 5, - child: Container( - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container( - color: const Color(0xFFBDBDBD), - height: 1, - ), - value: _selectionDirectionString, - items: _selectionDirectionList.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'both', - child: Text( - value, - textAlign: TextAlign.center, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - _onSelectionDirectionChanged(value); - stateSetter(() {}); - }, - ), + DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container( + color: const Color(0xFFBDBDBD), + height: 1, ), + value: _selectionDirectionString, + items: _selectionDirectionList.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'both', + child: Text( + value, + textAlign: TextAlign.center, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (dynamic value) { + _onSelectionDirectionChanged(value); + stateSetter(() {}); + }, ), ], ), @@ -445,30 +422,21 @@ class _HijriDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: model.isWebFullView ? 4 : 5, - child: Text( - 'Display date', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'Display date', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Expanded( - flex: model.isWebFullView ? 6 : 5, - child: Container( - padding: EdgeInsets.zero, - alignment: Alignment.centerLeft, - child: Theme( - data: model.themeData.copyWith( - canvasColor: model.drawerBackgroundColor, - ), - child: _DateRangePickerOption( - _onDisplayDateChanged, - _controller.displayDate!, - model, - displayDate: _controller.displayDate!, - ), - ), + Theme( + data: model.themeData.copyWith( + canvasColor: model.drawerBackgroundColor, + ), + child: _DateRangePickerOption( + _onDisplayDateChanged, + _controller.displayDate!, + model, + displayDate: _controller.displayDate!, ), ), ], @@ -479,38 +447,27 @@ class _HijriDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: 7, - child: Text( - 'Show action buttons', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'Show action buttons', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Expanded( - flex: 3, - child: Container( - padding: EdgeInsets.zero, - child: Theme( - data: Theme.of( - context, - ).copyWith(canvasColor: model.drawerBackgroundColor), - child: Container( - alignment: Alignment.centerLeft, - child: Transform.scale( - scale: 0.8, - child: CupertinoSwitch( - value: _showActionButtons, - onChanged: (bool value) { - setState(() { - _onBoolValueChange('ShowActionButtons', value); - stateSetter(() {}); - }); - }, - activeTrackColor: model.primaryColor, - ), - ), - ), + Theme( + data: Theme.of( + context, + ).copyWith(canvasColor: model.drawerBackgroundColor), + child: Transform.scale( + scale: 0.8, + child: CupertinoSwitch( + value: _showActionButtons, + onChanged: (bool value) { + setState(() { + _onBoolValueChange('ShowActionButtons', value); + stateSetter(() {}); + }); + }, + activeTrackColor: model.primaryColor, ), ), ), @@ -522,38 +479,27 @@ class _HijriDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: 7, - child: Text( - 'Show today button', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'Show today button', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Expanded( - flex: 3, - child: Container( - padding: EdgeInsets.zero, - child: Theme( - data: Theme.of( - context, - ).copyWith(canvasColor: model.drawerBackgroundColor), - child: Container( - alignment: Alignment.centerLeft, - child: Transform.scale( - scale: 0.8, - child: CupertinoSwitch( - value: _showTodayButton, - onChanged: (bool value) { - setState(() { - _onBoolValueChange('ShowTodayButton', value); - stateSetter(() {}); - }); - }, - activeTrackColor: model.primaryColor, - ), - ), - ), + Theme( + data: Theme.of( + context, + ).copyWith(canvasColor: model.drawerBackgroundColor), + child: Transform.scale( + scale: 0.8, + child: CupertinoSwitch( + value: _showTodayButton, + onChanged: (bool value) { + setState(() { + _onBoolValueChange('ShowTodayButton', value); + stateSetter(() {}); + }); + }, + activeTrackColor: model.primaryColor, ), ), ), @@ -565,41 +511,27 @@ class _HijriDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: 7, - child: Text( - 'Enable view navigation', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'Enable view navigation', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Expanded( - flex: 3, - child: Container( - padding: EdgeInsets.zero, - child: Theme( - data: Theme.of( - context, - ).copyWith(canvasColor: model.drawerBackgroundColor), - child: Container( - alignment: Alignment.centerLeft, - child: Transform.scale( - scale: 0.8, - child: CupertinoSwitch( - value: _enableViewNavigation, - onChanged: (bool value) { - setState(() { - _onBoolValueChange( - 'EnableViewNavigation', - value, - ); - stateSetter(() {}); - }); - }, - activeTrackColor: model.primaryColor, - ), - ), - ), + Theme( + data: Theme.of( + context, + ).copyWith(canvasColor: model.drawerBackgroundColor), + child: Transform.scale( + scale: 0.8, + child: CupertinoSwitch( + value: _enableViewNavigation, + onChanged: (bool value) { + setState(() { + _onBoolValueChange('EnableViewNavigation', value); + stateSetter(() {}); + }); + }, + activeTrackColor: model.primaryColor, ), ), ), @@ -611,36 +543,25 @@ class _HijriDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: 7, - child: Text( - 'Enable past dates', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'Enable past dates', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Expanded( - flex: 3, - child: Container( - padding: EdgeInsets.zero, - child: Theme( - data: Theme.of( - context, - ).copyWith(canvasColor: model.drawerBackgroundColor), - child: Container( - alignment: Alignment.centerLeft, - child: Transform.scale( - scale: 0.8, - child: CupertinoSwitch( - value: _enablePastDates, - onChanged: (dynamic value) { - _onBoolValueChange('EnablePastDates', value); - stateSetter(() {}); - }, - activeTrackColor: model.primaryColor, - ), - ), - ), + Theme( + data: Theme.of( + context, + ).copyWith(canvasColor: model.drawerBackgroundColor), + child: Transform.scale( + scale: 0.8, + child: CupertinoSwitch( + value: _enablePastDates, + onChanged: (dynamic value) { + _onBoolValueChange('EnablePastDates', value); + stateSetter(() {}); + }, + activeTrackColor: model.primaryColor, ), ), ), @@ -652,41 +573,27 @@ class _HijriDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: 7, - child: Text( - 'Enable swipe selection', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'Enable swipe selection', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Expanded( - flex: 3, - child: Container( - padding: EdgeInsets.zero, - child: Theme( - data: Theme.of( - context, - ).copyWith(canvasColor: model.drawerBackgroundColor), - child: Container( - alignment: Alignment.centerLeft, - child: Transform.scale( - scale: 0.8, - child: CupertinoSwitch( - value: _enableSwipingSelection, - onChanged: (dynamic value) { - setState(() { - _onBoolValueChange( - 'EnableSwipingSelection', - value, - ); - stateSetter(() {}); - }); - }, - activeTrackColor: model.primaryColor, - ), - ), - ), + Theme( + data: Theme.of( + context, + ).copyWith(canvasColor: model.drawerBackgroundColor), + child: Transform.scale( + scale: 0.8, + child: CupertinoSwitch( + value: _enableSwipingSelection, + onChanged: (dynamic value) { + setState(() { + _onBoolValueChange('EnableSwipingSelection', value); + stateSetter(() {}); + }); + }, + activeTrackColor: model.primaryColor, ), ), ), @@ -698,38 +605,27 @@ class _HijriDatePickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: 7, - child: Text( - 'Show week number', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'Show week number', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Expanded( - flex: 3, - child: Container( - padding: EdgeInsets.zero, - child: Theme( - data: Theme.of( - context, - ).copyWith(canvasColor: model.drawerBackgroundColor), - child: Container( - alignment: Alignment.centerLeft, - child: Transform.scale( - scale: 0.8, - child: CupertinoSwitch( - value: _showWeekNumber, - onChanged: (dynamic value) { - setState(() { - _onBoolValueChange('showWeekNumber', value); - stateSetter(() {}); - }); - }, - activeTrackColor: model.primaryColor, - ), - ), - ), + Theme( + data: Theme.of( + context, + ).copyWith(canvasColor: model.drawerBackgroundColor), + child: Transform.scale( + scale: 0.8, + child: CupertinoSwitch( + value: _showWeekNumber, + onChanged: (dynamic value) { + setState(() { + _onBoolValueChange('showWeekNumber', value); + stateSetter(() {}); + }); + }, + activeTrackColor: model.primaryColor, ), ), ), diff --git a/lib/samples/date_picker/vertical_calendar.dart b/lib/samples/date_picker/vertical_calendar.dart index daba0430..d2e794d4 100644 --- a/lib/samples/date_picker/vertical_calendar.dart +++ b/lib/samples/date_picker/vertical_calendar.dart @@ -42,42 +42,37 @@ class _VerticalCalendarPickerState extends SampleViewState { SizedBox( height: 50, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - flex: 6, - child: Text( - 'Navigation mode', - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + Text( + 'Navigation mode', + style: TextStyle(fontSize: 16.0, color: model.textColor), ), - Expanded( - flex: 4, - child: Container( - padding: EdgeInsets.zero, - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container( - color: const Color(0xFFBDBDBD), - height: 1, - ), - value: _navigationModeString, - items: _navigationModeList.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'scroll', - child: Text( - value, - textAlign: TextAlign.center, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (dynamic value) { - _onNavigationModeChange(value); - stateSetter(() {}); - }, + Container( + padding: EdgeInsets.zero, + alignment: Alignment.bottomLeft, + child: DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container( + color: const Color(0xFFBDBDBD), + height: 1, ), + value: _navigationModeString, + items: _navigationModeList.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'scroll', + child: Text( + value, + textAlign: TextAlign.center, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (dynamic value) { + _onNavigationModeChange(value); + stateSetter(() {}); + }, ), ), ], diff --git a/lib/samples/gauge/pointer_interaction/radial_range_slider.dart b/lib/samples/gauge/pointer_interaction/radial_range_slider.dart index 42579528..2e323548 100644 --- a/lib/samples/gauge/pointer_interaction/radial_range_slider.dart +++ b/lib/samples/gauge/pointer_interaction/radial_range_slider.dart @@ -136,6 +136,7 @@ class _RadialRangeSliderExampleState extends SampleViewState { children: [ SizedBox( child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Enable dragging', @@ -161,28 +162,24 @@ class _RadialRangeSliderExampleState extends SampleViewState { Visibility( visible: _enableDragging, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Overlay radius', style: TextStyle(color: model.textColor), ), - Container( - padding: !model.isWebFullView - ? const EdgeInsets.fromLTRB(25, 0, 0, 0) - : const EdgeInsets.fromLTRB(50, 0, 0, 0), - child: CustomDirectionalButtons( - maxValue: 35, - minValue: 15, - initialValue: _overlayRadius, - onChanged: (double val) { - setState(() { - _overlayRadius = val; - }); - }, - step: 5, - iconColor: model.textColor, - style: TextStyle(fontSize: 16.0, color: model.textColor), - ), + CustomDirectionalButtons( + maxValue: 35, + minValue: 15, + initialValue: _overlayRadius, + onChanged: (double val) { + setState(() { + _overlayRadius = val; + }); + }, + step: 5, + iconColor: model.textColor, + style: TextStyle(fontSize: 16.0, color: model.textColor), ), ], ), diff --git a/lib/samples/gauge/pointers/marker_pointer.dart b/lib/samples/gauge/pointers/marker_pointer.dart index a9208315..441368d6 100644 --- a/lib/samples/gauge/pointers/marker_pointer.dart +++ b/lib/samples/gauge/pointers/marker_pointer.dart @@ -59,38 +59,37 @@ class _MarkerPointerExampleState extends SampleViewState { shrinkWrap: true, children: [ Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Marker type ', style: TextStyle(color: model.textColor, fontSize: 16), ), - Container( - padding: const EdgeInsets.fromLTRB(20, 0, 0, 0), - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - underline: Container( - color: const Color(0xFFBDBDBD), - height: 1, - ), - value: _selectedMarkerType, - items: _markerTypes.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'invertedTriangle', - child: Text( - value, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (String? value) { - _onMarkerTypeChange(value.toString()); - stateSetter(() {}); - }, + DropdownButton( + dropdownColor: model.drawerBackgroundColor, + underline: Container( + color: const Color(0xFFBDBDBD), + height: 1, ), + value: _selectedMarkerType, + items: _markerTypes.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'invertedTriangle', + child: Text( + value, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (String? value) { + _onMarkerTypeChange(value.toString()); + stateSetter(() {}); + }, ), ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Elevation', diff --git a/lib/samples/pdf/conformance.dart b/lib/samples/pdf/conformance.dart index e1b440f4..0ad12dd1 100644 --- a/lib/samples/pdf/conformance.dart +++ b/lib/samples/pdf/conformance.dart @@ -54,10 +54,13 @@ class _ConformancePdfState extends SampleViewState { ), ), const SizedBox(height: 10, width: 25), - if (MediaQuery.of(context).size.width > 800) - Row(children: getChildWidgets(context)) - else - Column(children: getChildWidgets(context)), + RadioGroup( + groupValue: _groupValue, + onChanged: _changed, + child: (MediaQuery.of(context).size.width > 800) + ? Row(children: getChildWidgets(context)) + : Column(children: getChildWidgets(context)), + ), const SizedBox(height: 10, width: 30), Align( child: TextButton( @@ -92,7 +95,7 @@ class _ConformancePdfState extends SampleViewState { return [ Row( children: [ - Radio(groupValue: _groupValue, onChanged: _changed, value: 0), + const Radio(value: 0), Text( 'PDF/A-1B', style: TextStyle(fontSize: 16, color: model.textColor), @@ -101,7 +104,7 @@ class _ConformancePdfState extends SampleViewState { ), Row( children: [ - Radio(groupValue: _groupValue, onChanged: _changed, value: 1), + const Radio(value: 1), Text( 'PDF/A-2B', style: TextStyle(fontSize: 16, color: model.textColor), @@ -110,7 +113,7 @@ class _ConformancePdfState extends SampleViewState { ), Row( children: [ - Radio(groupValue: _groupValue, onChanged: _changed, value: 2), + const Radio(value: 2), Text( 'PDF/A-3B', style: TextStyle(fontSize: 16, color: model.textColor), diff --git a/lib/samples/pdf/digital_signature.dart b/lib/samples/pdf/digital_signature.dart index b91a183d..0380deff 100644 --- a/lib/samples/pdf/digital_signature.dart +++ b/lib/samples/pdf/digital_signature.dart @@ -36,6 +36,11 @@ class _SignPdfState extends SampleViewState { @override Widget build(BuildContext context) { + final double width = MediaQuery.of(context).size.width; + final List digestChildWidgets = getDigestChildWidgets(context); + final List cryptographicChildWidgets = getCryptographicChildWidgets( + context, + ); return Scaffold( backgroundColor: model.sampleOutputCardColor, body: SingleChildScrollView( @@ -58,10 +63,13 @@ class _SignPdfState extends SampleViewState { ), ), const SizedBox(height: 10, width: 30), - if (MediaQuery.of(context).size.width > 800) - Row(children: getCryptographicChildWidgets(context)) - else - Column(children: getCryptographicChildWidgets(context)), + RadioGroup( + groupValue: _cryptoGroupValue, + onChanged: _cryptoChanged, + child: (width > 800) + ? Row(children: cryptographicChildWidgets) + : Column(children: cryptographicChildWidgets), + ), const SizedBox(height: 20, width: 30), Text( 'Digest Algorithm', @@ -72,10 +80,13 @@ class _SignPdfState extends SampleViewState { ), ), const SizedBox(height: 10, width: 30), - if (MediaQuery.of(context).size.width > 800) - Row(children: getDigestChildWidgets(context)) - else - Column(children: getDigestChildWidgets(context)), + RadioGroup( + groupValue: _groupValue, + onChanged: _digestChanged, + child: (width > 800) + ? Row(children: digestChildWidgets) + : Column(children: digestChildWidgets), + ), const SizedBox(height: 10, width: 30), Align( child: TextButton( @@ -110,21 +121,13 @@ class _SignPdfState extends SampleViewState { return [ Row( children: [ - Radio( - value: 0, - groupValue: _groupValue, - onChanged: _digestChanged, - ), + const Radio(value: 0), Text('SHA1', style: TextStyle(fontSize: 16, color: model.textColor)), ], ), Row( children: [ - Radio( - value: 1, - groupValue: _groupValue, - onChanged: _digestChanged, - ), + const Radio(value: 1), Text( 'SHA256', style: TextStyle(fontSize: 16, color: model.textColor), @@ -133,11 +136,7 @@ class _SignPdfState extends SampleViewState { ), Row( children: [ - Radio( - value: 2, - groupValue: _groupValue, - onChanged: _digestChanged, - ), + const Radio(value: 2), Text( 'SHA384', style: TextStyle(fontSize: 16, color: model.textColor), @@ -146,11 +145,7 @@ class _SignPdfState extends SampleViewState { ), Row( children: [ - Radio( - value: 3, - groupValue: _groupValue, - onChanged: _digestChanged, - ), + const Radio(value: 3), Text( 'SHA512', style: TextStyle(fontSize: 16, color: model.textColor), @@ -164,21 +159,13 @@ class _SignPdfState extends SampleViewState { return [ Row( children: [ - Radio( - value: 0, - groupValue: _cryptoGroupValue, - onChanged: _cryptoChanged, - ), + const Radio(value: 0), Text('CMS', style: TextStyle(fontSize: 16, color: model.textColor)), ], ), Row( children: [ - Radio( - value: 1, - groupValue: _cryptoGroupValue, - onChanged: _cryptoChanged, - ), + const Radio(value: 1), Text('CAdES', style: TextStyle(fontSize: 16, color: model.textColor)), ], ), diff --git a/lib/samples/pdf/encryption.dart b/lib/samples/pdf/encryption.dart index a76e8e99..9ea62778 100644 --- a/lib/samples/pdf/encryption.dart +++ b/lib/samples/pdf/encryption.dart @@ -51,10 +51,13 @@ class _EncryptPdfState extends SampleViewState { ), ), const SizedBox(height: 10, width: 30), - if (MediaQuery.of(context).size.width > 800) - Row(children: getChildWidgets(context)) - else - Column(children: getChildWidgets(context)), + RadioGroup( + groupValue: _groupValue, + onChanged: _changed, + child: (MediaQuery.of(context).size.width > 800) + ? Row(children: getChildWidgets(context)) + : Column(children: getChildWidgets(context)), + ), const SizedBox(height: 10, width: 30), Row( children: [ @@ -123,7 +126,7 @@ class _EncryptPdfState extends SampleViewState { return [ Row( children: [ - Radio(value: 0, groupValue: _groupValue, onChanged: _changed), + const Radio(value: 0), Text( '40-bit RC4', style: TextStyle(fontSize: 16, color: model.textColor), @@ -132,7 +135,7 @@ class _EncryptPdfState extends SampleViewState { ), Row( children: [ - Radio(value: 1, groupValue: _groupValue, onChanged: _changed), + const Radio(value: 1), Text( '128-bit RC4', style: TextStyle(fontSize: 16, color: model.textColor), @@ -141,7 +144,7 @@ class _EncryptPdfState extends SampleViewState { ), Row( children: [ - Radio(value: 2, groupValue: _groupValue, onChanged: _changed), + const Radio(value: 2), Text( '128-bit AES', style: TextStyle(fontSize: 16, color: model.textColor), @@ -150,7 +153,7 @@ class _EncryptPdfState extends SampleViewState { ), Row( children: [ - Radio(value: 3, groupValue: _groupValue, onChanged: _changed), + const Radio(value: 3), Text( '256-bit AES', style: TextStyle(fontSize: 16, color: model.textColor), @@ -159,7 +162,7 @@ class _EncryptPdfState extends SampleViewState { ), Row( children: [ - Radio(value: 4, groupValue: _groupValue, onChanged: _changed), + const Radio(value: 4), Text( '256-bit AES Revision 6', style: TextStyle(fontSize: 16, color: model.textColor), diff --git a/lib/samples/pdf/form.dart b/lib/samples/pdf/form.dart index 4711d69f..bb7c0d7a 100644 --- a/lib/samples/pdf/form.dart +++ b/lib/samples/pdf/form.dart @@ -127,7 +127,11 @@ class _FormFillingPdfState extends SampleViewState { ), child: SizedBox( height: 25, - child: Row(children: _getGenderWidgets(context)), + child: RadioGroup( + groupValue: _groupValue, + onChanged: _changed, + child: Row(children: _getGenderWidgets(context)), + ), ), ), const SizedBox(height: 20, width: 30), @@ -151,11 +155,9 @@ class _FormFillingPdfState extends SampleViewState { ), const SizedBox(height: 20, width: 30), DropdownButtonFormField( - value: _dropdownValue, + initialValue: _dropdownValue, onChanged: (String? newValue) { - setState(() { - _dropdownValue = newValue!; - }); + _dropdownValue = newValue!; }, items: [ @@ -312,10 +314,8 @@ class _FormFillingPdfState extends SampleViewState { return [ Row( children: [ - Radio( + const Radio( value: 0, - groupValue: _groupValue, - onChanged: _changed, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), Text('Male', style: TextStyle(fontSize: 16, color: model.textColor)), @@ -323,10 +323,8 @@ class _FormFillingPdfState extends SampleViewState { ), Row( children: [ - Radio( + const Radio( value: 2, - groupValue: _groupValue, - onChanged: _changed, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), Text( @@ -337,10 +335,8 @@ class _FormFillingPdfState extends SampleViewState { ), Row( children: [ - Radio( + const Radio( value: 1, - groupValue: _groupValue, - onChanged: _changed, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), Text( diff --git a/lib/samples/pdf/helper/save_file_mobile.dart b/lib/samples/pdf/helper/save_file_mobile.dart index 729c8b90..c0ea70d8 100644 --- a/lib/samples/pdf/helper/save_file_mobile.dart +++ b/lib/samples/pdf/helper/save_file_mobile.dart @@ -1,7 +1,7 @@ -///Dart import +/// Dart import import 'dart:io'; -///Package imports +/// Package imports import 'package:flutter/services.dart'; import 'package:path_provider/path_provider.dart'; // ignore: depend_on_referenced_packages @@ -12,7 +12,7 @@ import 'package:path_provider_platform_interface/path_provider_platform_interfac class FileSaveHelper { static const MethodChannel _platformCall = MethodChannel('launchFile'); - ///To save the pdf file in the device + /// To save the pdf file in the device static Future saveAndLaunchFile( List bytes, String fileName, @@ -22,7 +22,7 @@ class FileSaveHelper { final Directory directory = await getApplicationSupportDirectory(); path = directory.path; } else if (Platform.isAndroid) { - final Directory? directory = await getExternalStorageDirectory(); + final Directory? directory = await getExternalFilesDirectory(); if (directory != null) { path = directory.path; } else { @@ -61,4 +61,13 @@ class FileSaveHelper { ], runInShell: true); } } + + /// Helper for app-private external storage directory on Android + static Future getExternalFilesDirectory() async { + try { + return await getExternalStorageDirectory(); + } catch (e) { + return null; + } + } } diff --git a/lib/samples/pdf/import_and_export_annotation_data.dart b/lib/samples/pdf/import_and_export_annotation_data.dart index cd7c8b7b..c904e6a8 100644 --- a/lib/samples/pdf/import_and_export_annotation_data.dart +++ b/lib/samples/pdf/import_and_export_annotation_data.dart @@ -61,6 +61,7 @@ class _ImportAndExportAnnotationDataState extends SampleViewState { @override Widget build(BuildContext context) { + final double width = MediaQuery.of(context).size.width; return Scaffold( backgroundColor: model.sampleOutputCardColor, body: SingleChildScrollView( @@ -83,10 +84,13 @@ class _ImportAndExportAnnotationDataState extends SampleViewState { ), ), const SizedBox(height: 10, width: 30), - if (MediaQuery.of(context).size.width > 800) - Row(children: getDataTypeChildWidgets(context)) - else - Column(children: getDataTypeChildWidgets(context)), + RadioGroup( + groupValue: _groupDataTypeValue, + onChanged: _dataTypeChanged, + child: (width > 800) + ? Row(children: getDataTypeChildWidgets(context)) + : Column(children: getDataTypeChildWidgets(context)), + ), const SizedBox(height: 20, width: 30), Text( 'Select import or export:', @@ -97,10 +101,13 @@ class _ImportAndExportAnnotationDataState extends SampleViewState { ), ), const SizedBox(height: 10, width: 30), - if (MediaQuery.of(context).size.width > 800) - Row(children: getProcessChildWidgets(context)) - else - Column(children: getProcessChildWidgets(context)), + RadioGroup( + groupValue: _groupProcessValue, + onChanged: _processChanged, + child: (width > 800) + ? Row(children: getProcessChildWidgets(context)) + : Column(children: getProcessChildWidgets(context)), + ), const SizedBox(height: 20, width: 30), Row( mainAxisAlignment: MainAxisAlignment.center, @@ -160,31 +167,19 @@ class _ImportAndExportAnnotationDataState extends SampleViewState { return [ Row( children: [ - Radio( - value: 0, - groupValue: _groupDataTypeValue, - onChanged: _dataTypeChanged, - ), + const Radio(value: 0), Text('XFDF', style: TextStyle(fontSize: 16, color: model.textColor)), ], ), Row( children: [ - Radio( - value: 1, - groupValue: _groupDataTypeValue, - onChanged: _dataTypeChanged, - ), + const Radio(value: 1), Text('JSON', style: TextStyle(fontSize: 16, color: model.textColor)), ], ), Row( children: [ - Radio( - value: 2, - groupValue: _groupDataTypeValue, - onChanged: _dataTypeChanged, - ), + const Radio(value: 2), Text('FDF', style: TextStyle(fontSize: 16, color: model.textColor)), ], ), @@ -195,11 +190,7 @@ class _ImportAndExportAnnotationDataState extends SampleViewState { return [ Row( children: [ - Radio( - value: 0, - groupValue: _groupProcessValue, - onChanged: _processChanged, - ), + const Radio(value: 0), Text( 'Import', style: TextStyle(fontSize: 16, color: model.textColor), @@ -208,11 +199,7 @@ class _ImportAndExportAnnotationDataState extends SampleViewState { ), Row( children: [ - Radio( - value: 1, - groupValue: _groupProcessValue, - onChanged: _processChanged, - ), + const Radio(value: 1), Text( 'Export', style: TextStyle(fontSize: 16, color: model.textColor), diff --git a/lib/samples/pdf/import_and_export_form_data.dart b/lib/samples/pdf/import_and_export_form_data.dart index 976efcf7..b24a8fe9 100644 --- a/lib/samples/pdf/import_and_export_form_data.dart +++ b/lib/samples/pdf/import_and_export_form_data.dart @@ -63,6 +63,7 @@ class _ImportAndExportFormDataState extends SampleViewState { @override Widget build(BuildContext context) { + final double width = MediaQuery.of(context).size.width; return Scaffold( backgroundColor: model.sampleOutputCardColor, body: SingleChildScrollView( @@ -85,10 +86,14 @@ class _ImportAndExportFormDataState extends SampleViewState { ), ), const SizedBox(height: 10, width: 30), - if (MediaQuery.of(context).size.width > 800) - Row(children: getDataTypeChildWidgets(context)) - else - Column(children: getDataTypeChildWidgets(context)), + RadioGroup( + groupValue: _groupDataTypeValue, + onChanged: _dataTypeChanged, + child: (width > 800) + ? Row(children: getDataTypeChildWidgets(context)) + : Column(children: getDataTypeChildWidgets(context)), + ), + const SizedBox(height: 20, width: 30), Text( 'Select import or export:', @@ -99,10 +104,13 @@ class _ImportAndExportFormDataState extends SampleViewState { ), ), const SizedBox(height: 10, width: 30), - if (MediaQuery.of(context).size.width > 800) - Row(children: getProcessChildWidgets(context)) - else - Column(children: getProcessChildWidgets(context)), + RadioGroup( + groupValue: _groupProcessValue, + onChanged: _processChanged, + child: (width > 800) + ? Row(children: getProcessChildWidgets(context)) + : Column(children: getProcessChildWidgets(context)), + ), const SizedBox(height: 20, width: 30), Row( mainAxisAlignment: MainAxisAlignment.center, @@ -162,31 +170,19 @@ class _ImportAndExportFormDataState extends SampleViewState { return [ Row( children: [ - Radio( - value: 0, - groupValue: _groupDataTypeValue, - onChanged: _dataTypeChanged, - ), + const Radio(value: 0), Text('XFDF', style: TextStyle(fontSize: 16, color: model.textColor)), ], ), Row( children: [ - Radio( - value: 1, - groupValue: _groupDataTypeValue, - onChanged: _dataTypeChanged, - ), + const Radio(value: 1), Text('JSON', style: TextStyle(fontSize: 16, color: model.textColor)), ], ), Row( children: [ - Radio( - value: 2, - groupValue: _groupDataTypeValue, - onChanged: _dataTypeChanged, - ), + const Radio(value: 2), Text('XML', style: TextStyle(fontSize: 16, color: model.textColor)), ], ), @@ -197,11 +193,7 @@ class _ImportAndExportFormDataState extends SampleViewState { return [ Row( children: [ - Radio( - value: 0, - groupValue: _groupProcessValue, - onChanged: _processChanged, - ), + const Radio(value: 0), Text( 'Import', style: TextStyle(fontSize: 16, color: model.textColor), @@ -210,11 +202,7 @@ class _ImportAndExportFormDataState extends SampleViewState { ), Row( children: [ - Radio( - value: 1, - groupValue: _groupProcessValue, - onChanged: _processChanged, - ), + const Radio(value: 1), Text( 'Export', style: TextStyle(fontSize: 16, color: model.textColor), diff --git a/lib/samples/pdf_viewer/pdf_viewer_custom_toolbar.dart b/lib/samples/pdf_viewer/pdf_viewer_custom_toolbar.dart index 04ed739c..3fd183ab 100644 --- a/lib/samples/pdf_viewer/pdf_viewer_custom_toolbar.dart +++ b/lib/samples/pdf_viewer/pdf_viewer_custom_toolbar.dart @@ -1962,9 +1962,6 @@ class _CustomToolbarPdfViewerState extends SampleViewState { }); } if (toolbarItem.toString() == 'Bookmarks') { - setState(() { - _canShowToolbar = false; - }); _pdfViewerKey.currentState?.openBookmarkView(); } else if (toolbarItem.toString() == 'Search') { setState(() { diff --git a/lib/samples/pdf_viewer/shared/toolbar_widgets.dart b/lib/samples/pdf_viewer/shared/toolbar_widgets.dart index 602f1a3c..bdf07a7c 100644 --- a/lib/samples/pdf_viewer/shared/toolbar_widgets.dart +++ b/lib/samples/pdf_viewer/shared/toolbar_widgets.dart @@ -530,7 +530,7 @@ class SearchToolbarState extends State { /// Toolbar item widget class ToolbarItem extends StatelessWidget { ///Creates a toolbar item - const ToolbarItem({Key? key, this.height, this.width, @required this.child}) + const ToolbarItem({Key? key, this.height, this.width, required this.child}) : super(key: key); /// Height of the toolbar item diff --git a/lib/samples/radial_range_slider/basic_features/state.dart b/lib/samples/radial_range_slider/basic_features/state.dart index ea5ece87..79746e62 100644 --- a/lib/samples/radial_range_slider/basic_features/state.dart +++ b/lib/samples/radial_range_slider/basic_features/state.dart @@ -120,6 +120,7 @@ class _RadialRangeSliderStateTypesState extends SampleViewState { return StatefulBuilder( builder: (BuildContext context, StateSetter stateSetter) { return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Enable drag', style: TextStyle(color: model.textColor)), Padding( diff --git a/lib/samples/radial_slider/basic_features/state.dart b/lib/samples/radial_slider/basic_features/state.dart index cdd4f04f..8d7ad0d3 100644 --- a/lib/samples/radial_slider/basic_features/state.dart +++ b/lib/samples/radial_slider/basic_features/state.dart @@ -113,6 +113,7 @@ class _RadialSliderStateTypesState extends SampleViewState { return StatefulBuilder( builder: (BuildContext context, StateSetter stateSetter) { return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Enable drag', style: TextStyle(color: model.textColor)), Padding( diff --git a/lib/samples/sliders/range_selector/range_selector_label_customization.dart b/lib/samples/sliders/range_selector/range_selector_label_customization.dart index 67675ff8..0d3e9435 100644 --- a/lib/samples/sliders/range_selector/range_selector_label_customization.dart +++ b/lib/samples/sliders/range_selector/range_selector_label_customization.dart @@ -69,7 +69,7 @@ class _RangeSelectorLabelCustomizationState extends SampleViewState chartData.add( ChartSampleData( x: DateTime(2000).add(Duration(days: i)), - y: Random().nextInt(190) + 50, + y: Random.secure().nextInt(190) + 50, ), ); } diff --git a/lib/samples/sliders/range_selector/range_selector_with_zooming.dart b/lib/samples/sliders/range_selector/range_selector_with_zooming.dart index 088ff937..88ef1c35 100644 --- a/lib/samples/sliders/range_selector/range_selector_with_zooming.dart +++ b/lib/samples/sliders/range_selector/range_selector_with_zooming.dart @@ -54,7 +54,7 @@ class _RangeSelectorZoomingPageState extends SampleViewState chartData.add( ChartSampleData( x: DateTime(2000).add(Duration(days: i)), - y: Random().nextInt(190) + 50, + y: Random.secure().nextInt(190) + 50, ), ); } diff --git a/lib/samples/sliders/vertical_range_slider/default_appearance/vertical_range_slider_tooltip_position.dart b/lib/samples/sliders/vertical_range_slider/default_appearance/vertical_range_slider_tooltip_position.dart index 05f70adc..7b483266 100644 --- a/lib/samples/sliders/vertical_range_slider/default_appearance/vertical_range_slider_tooltip_position.dart +++ b/lib/samples/sliders/vertical_range_slider/default_appearance/vertical_range_slider_tooltip_position.dart @@ -100,7 +100,7 @@ class _VerticalTooltipRangeSliderPageState extends SampleViewState { } Widget _buildMobileLayout() { - final double padding = MediaQuery.of(context).size.height / 10.0; + final double padding = MediaQuery.of(context).size.height / 12.0; return Padding( padding: EdgeInsets.all(padding), child: Row( @@ -144,6 +144,7 @@ class _VerticalTooltipRangeSliderPageState extends SampleViewState { return StatefulBuilder( builder: (BuildContext context, StateSetter stateSetter) { return Column( + mainAxisSize: MainAxisSize.min, children: [ CheckboxListTile( value: _isInversed, diff --git a/lib/samples/sliders/vertical_slider/basic_features/vertical_slider_tooltip_position.dart b/lib/samples/sliders/vertical_slider/basic_features/vertical_slider_tooltip_position.dart index aa5aa43f..e6cafd99 100644 --- a/lib/samples/sliders/vertical_slider/basic_features/vertical_slider_tooltip_position.dart +++ b/lib/samples/sliders/vertical_slider/basic_features/vertical_slider_tooltip_position.dart @@ -99,7 +99,7 @@ class _VerticalSliderTooltipPageState extends SampleViewState { } Widget _buildMobileLayout() { - final double padding = MediaQuery.of(context).size.height / 10.0; + final double padding = MediaQuery.of(context).size.height / 12.0; return Padding( padding: EdgeInsets.all(padding), child: Row( @@ -143,6 +143,7 @@ class _VerticalSliderTooltipPageState extends SampleViewState { return StatefulBuilder( builder: (BuildContext context, StateSetter stateSetter) { return Column( + mainAxisSize: MainAxisSize.min, children: [ CheckboxListTile( value: _isInversed, @@ -158,7 +159,7 @@ class _VerticalSliderTooltipPageState extends SampleViewState { ), CheckboxListTile( value: _shouldAlwaysShowTooltip, - title: const Text('Show tooltip always', softWrap: false), + title: const Text('Show tooltip always'), activeColor: model.primaryColor, contentPadding: EdgeInsets.zero, onChanged: (bool? value) { diff --git a/lib/samples/sparkline/customization.dart b/lib/samples/sparkline/customization.dart index 46c3f44a..4bd5a571 100644 --- a/lib/samples/sparkline/customization.dart +++ b/lib/samples/sparkline/customization.dart @@ -129,159 +129,151 @@ class _SparklineCustomizationState extends SampleViewState { return StatefulBuilder( builder: (BuildContext context, StateSetter stateSetter) { return ListView( + padding: const EdgeInsets.only(right: 10), shrinkWrap: true, children: [ Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Marker', style: TextStyle(color: model.textColor, fontSize: 16), ), - const Padding(padding: EdgeInsets.fromLTRB(75, 0, 0, 0)), - Container( - height: 50, - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container( - color: const Color(0xFFBDBDBD), - height: 1, - ), - value: _selectedMarkerDisplayMode, - items: _markerDisplayModeList.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'none', - child: Text( - value, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (String? value) { - _onMarkerDisplayModeChange(value.toString()); - stateSetter(() {}); - }, + DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container( + color: const Color(0xFFBDBDBD), + height: 1, ), + value: _selectedMarkerDisplayMode, + items: _markerDisplayModeList.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'none', + child: Text( + value, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (String? value) { + _onMarkerDisplayModeChange(value.toString()); + stateSetter(() {}); + }, ), ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Data label', style: TextStyle(color: model.textColor, fontSize: 16), ), - const Padding(padding: EdgeInsets.fromLTRB(55, 0, 0, 0)), - Container( - height: 50, - alignment: Alignment.bottomLeft, - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - focusColor: Colors.transparent, - underline: Container( - color: const Color(0xFFBDBDBD), - height: 1, - ), - value: _selectedDatalabelDisplayMode, - items: _datalabelDisplayModeList.map((String value) { - return DropdownMenuItem( - value: (value != null) ? value : 'none', - child: Text( - value, - style: TextStyle(color: model.textColor), - ), - ); - }).toList(), - onChanged: (String? value) { - _onDatalabelDisplayModeChange(value.toString()); - stateSetter(() {}); - }, + DropdownButton( + dropdownColor: model.drawerBackgroundColor, + focusColor: Colors.transparent, + underline: Container( + color: const Color(0xFFBDBDBD), + height: 1, ), + value: _selectedDatalabelDisplayMode, + items: _datalabelDisplayModeList.map((String value) { + return DropdownMenuItem( + value: (value != null) ? value : 'none', + child: Text( + value, + style: TextStyle(color: model.textColor), + ), + ); + }).toList(), + onChanged: (String? value) { + _onDatalabelDisplayModeChange(value.toString()); + stateSetter(() {}); + }, ), ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Trackball', style: TextStyle(color: model.textColor, fontSize: 16), ), - const Padding(padding: EdgeInsets.fromLTRB(16, 0, 0, 0)), - SizedBox( - width: 90, - child: CheckboxListTile( - activeColor: model.primaryColor, - value: _enableTrackLine, - onChanged: (bool? value) { - setState(() { - _enableTrackLine = value!; - stateSetter(() {}); - }); - }, - ), + Checkbox( + activeColor: model.primaryColor, + value: _enableTrackLine, + onChanged: (bool? value) { + setState(() { + _enableTrackLine = value!; + stateSetter(() {}); + }); + }, ), ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Axis value', style: TextStyle(color: model.textColor, fontSize: 16), ), - const Padding(padding: EdgeInsets.fromLTRB(30, 0, 0, 0)), - SizedBox( - width: 150, - child: SliderTheme( - data: SliderThemeData( - tickMarkShape: SliderTickMarkShape.noTickMark, + Row( + spacing: 2, + children: [ + SizedBox( + width: 130, + child: SliderTheme( + data: SliderThemeData( + tickMarkShape: SliderTickMarkShape.noTickMark, + ), + child: Slider( + value: _axisCrossingValue, + min: -10, + max: 13, + divisions: 24, + onChanged: (double value) { + setState(() { + _axisCrossingValue = value; + stateSetter(() {}); + }); + }, + ), + ), ), - child: Slider( - value: _axisCrossingValue, - min: -10, - max: 13, - divisions: 24, - onChanged: (double value) { - setState(() { - _axisCrossingValue = value; - stateSetter(() {}); - }); - }, + Text( + '${_axisCrossingValue.floor()}', + style: TextStyle(color: model.textColor, fontSize: 16), ), - ), - ), - const Padding(padding: EdgeInsets.fromLTRB(2, 0, 0, 0)), - Text( - '${_axisCrossingValue.floor()}', - style: TextStyle(color: model.textColor, fontSize: 16), + ], ), ], ), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Plot band', style: TextStyle(color: model.textColor, fontSize: 16), ), - const Padding(padding: EdgeInsets.fromLTRB(12, 0, 0, 0)), - SizedBox( - width: 90, - child: CheckboxListTile( - activeColor: model.primaryColor, - value: _enablePlotband, - onChanged: (bool? value) { - setState(() { - _enablePlotband = value!; - stateSetter(() {}); - }); - }, - ), + Checkbox( + activeColor: model.primaryColor, + value: _enablePlotband, + onChanged: (bool? value) { + setState(() { + _enablePlotband = value!; + stateSetter(() {}); + }); + }, ), ], ), Visibility( visible: _enablePlotband, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( mainAxisAlignment: MainAxisAlignment.center, @@ -295,7 +287,6 @@ class _SparklineCustomizationState extends SampleViewState { ), ], ), - const Padding(padding: EdgeInsets.fromLTRB(20, 0, 0, 0)), Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -324,6 +315,7 @@ class _SparklineCustomizationState extends SampleViewState { Visibility( visible: _enablePlotband, child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( mainAxisAlignment: MainAxisAlignment.center, @@ -337,7 +329,6 @@ class _SparklineCustomizationState extends SampleViewState { ), ], ), - const Padding(padding: EdgeInsets.fromLTRB(25, 0, 0, 0)), Column( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/lib/samples/sparkline/live_update.dart b/lib/samples/sparkline/live_update.dart index 9f384dd5..befd6980 100644 --- a/lib/samples/sparkline/live_update.dart +++ b/lib/samples/sparkline/live_update.dart @@ -364,7 +364,7 @@ class _SparklineLiveUpdateState extends SampleViewState { ///Get random value double _getRandomInt(int min, int max) { - final Random random = Random(); + final Random random = Random.secure(); return min + random.nextInt(max - min).toDouble(); } diff --git a/lib/samples/treemap/hierarchical.dart b/lib/samples/treemap/hierarchical.dart index 0fdabdcc..1921a877 100644 --- a/lib/samples/treemap/hierarchical.dart +++ b/lib/samples/treemap/hierarchical.dart @@ -316,35 +316,30 @@ class _HierarchicalTreemapSampleState extends SampleViewState { 'Layout direction', style: TextStyle(color: model.textColor, fontSize: 16), ), - Padding( - padding: const EdgeInsets.only(right: 15.0), - child: DropdownButton( - dropdownColor: model.drawerBackgroundColor, - value: _layoutDirection, - items: _dropDownMenuItems, - onChanged: (TreemapLayoutDirection? value) { - setState(() { - _layoutDirection = value!; - switch (_layoutDirection) { - case TreemapLayoutDirection.topLeft: - _layoutDirection = TreemapLayoutDirection.topLeft; - break; - case TreemapLayoutDirection.topRight: - _layoutDirection = TreemapLayoutDirection.topRight; - break; - case TreemapLayoutDirection.bottomLeft: - _layoutDirection = - TreemapLayoutDirection.bottomLeft; - break; - case TreemapLayoutDirection.bottomRight: - _layoutDirection = - TreemapLayoutDirection.bottomRight; - break; - } - stateSetter(() {}); - }); - }, - ), + DropdownButton( + dropdownColor: model.drawerBackgroundColor, + value: _layoutDirection, + items: _dropDownMenuItems, + onChanged: (TreemapLayoutDirection? value) { + setState(() { + _layoutDirection = value!; + switch (_layoutDirection) { + case TreemapLayoutDirection.topLeft: + _layoutDirection = TreemapLayoutDirection.topLeft; + break; + case TreemapLayoutDirection.topRight: + _layoutDirection = TreemapLayoutDirection.topRight; + break; + case TreemapLayoutDirection.bottomLeft: + _layoutDirection = TreemapLayoutDirection.bottomLeft; + break; + case TreemapLayoutDirection.bottomRight: + _layoutDirection = TreemapLayoutDirection.bottomRight; + break; + } + stateSetter(() {}); + }); + }, ), ], ), diff --git a/lib/showcase_samples/expense_tracker/helper/common_helper.dart b/lib/showcase_samples/expense_tracker/helper/common_helper.dart index f0507745..eaf1da51 100644 --- a/lib/showcase_samples/expense_tracker/helper/common_helper.dart +++ b/lib/showcase_samples/expense_tracker/helper/common_helper.dart @@ -49,7 +49,7 @@ List randomColors(BuildContext context) { context, listen: false, ); - final Random random = Random(); + final Random random = Random.secure(); final List cardAvatarColors = _cardAvatarColors(themeNotifier); return List.generate(10, (int index) { diff --git a/lib/showcase_samples/expense_tracker/pages/base_home.dart b/lib/showcase_samples/expense_tracker/pages/base_home.dart index f40612f0..0d0e5608 100644 --- a/lib/showcase_samples/expense_tracker/pages/base_home.dart +++ b/lib/showcase_samples/expense_tracker/pages/base_home.dart @@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../../../meta_tag/meta_tag.dart'; import '../constants.dart'; // import '../data_processing/budget_handler.dart' // if (dart.library.html) '../data_processing/budget_web_handler.dart'; @@ -70,11 +71,29 @@ class _ExpenseAnalysisState extends State { final GlobalKey> _popupMenuKey = GlobalKey(); final GlobalKey> _profilePopupMenuKey = GlobalKey(); + final WebMetaTagUpdate metaTagUpdate = WebMetaTagUpdate(); + + void _onPageNavigatorChanged() { + final String pageTitle = _buildPageTitle( + context, + pageNavigatorNotifier.value, + ); + metaTagUpdate.update(pageTitle, 'Expense Tracker'); + } @override void initState() { currentUserDetails = widget.currentUserDetails; super.initState(); + + // Updates meta tag details when navigating from the import page + // to the dashboard page in Expense Tracker. + metaTagUpdate.update( + _buildPageTitle(context, pageNavigatorNotifier.value), + 'Expense Tracker', + ); + + pageNavigatorNotifier.addListener(_onPageNavigatorChanged); } @override @@ -89,6 +108,7 @@ class _ExpenseAnalysisState extends State { _isAccountPageActive.dispose(); _isGoalsPageActive.dispose(); _appBarTitle.dispose(); + pageNavigatorNotifier.removeListener(_onPageNavigatorChanged); super.dispose(); } @@ -799,6 +819,9 @@ class _ExpenseAnalysisState extends State { mouseCursor: SystemMouseCursors.click, onTap: () { Navigator.of(context, rootNavigator: true).pop(context); + // Sets default meta tag details when navigating back from the + // Expense Tracker to the home page. + metaTagUpdate.setDefault(); }, child: Padding( padding: const EdgeInsets.only(left: 10), diff --git a/lib/showcase_samples/expense_tracker/pages/settings/sections/appearance.dart b/lib/showcase_samples/expense_tracker/pages/settings/sections/appearance.dart index e8135781..a1d72a0a 100644 --- a/lib/showcase_samples/expense_tracker/pages/settings/sections/appearance.dart +++ b/lib/showcase_samples/expense_tracker/pages/settings/sections/appearance.dart @@ -186,30 +186,28 @@ class _AppearancePageState extends State { ) { return InkWell( onTap: () => _updateTheme(title), - child: Row( - children: [ - Center( - child: Radio( - value: title, - groupValue: themeNotifier.selectedTheme, - onChanged: (value) => _updateTheme(value!), - ), - ), - horizontalSpacer14, - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - title, - style: textTheme.bodyLarge?.copyWith( - color: colorScheme.onSurface, + child: RadioGroup( + groupValue: themeNotifier.selectedTheme, + onChanged: (value) => _updateTheme(value!), + child: Row( + children: [ + Center(child: Radio(value: title)), + horizontalSpacer14, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: textTheme.bodyLarge?.copyWith( + color: colorScheme.onSurface, + ), ), - ), - ], + ], + ), ), - ), - ], + ], + ), ), ); } diff --git a/lib/showcase_samples/expense_tracker/pages/settings/settings.dart b/lib/showcase_samples/expense_tracker/pages/settings/settings.dart index 6f079384..b55f8396 100644 --- a/lib/showcase_samples/expense_tracker/pages/settings/settings.dart +++ b/lib/showcase_samples/expense_tracker/pages/settings/settings.dart @@ -289,8 +289,7 @@ class _SettingsPageState extends State { await excelFile.delete(); } } - } catch (e, stackTrace) { - debugPrint('Error deleting app data: $e\n$stackTrace'); + } catch (e) { throw Exception('Failed to delete app data: $e'); } } diff --git a/lib/showcase_samples/expense_tracker/pages/welcome_screens/import_page.dart b/lib/showcase_samples/expense_tracker/pages/welcome_screens/import_page.dart index 0cae8bbc..fec94a8d 100644 --- a/lib/showcase_samples/expense_tracker/pages/welcome_screens/import_page.dart +++ b/lib/showcase_samples/expense_tracker/pages/welcome_screens/import_page.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../../../../meta_tag/meta_tag.dart'; import '../../base.dart'; import '../../constants.dart'; // import '../../data_processing/utils.dart'; @@ -34,6 +35,7 @@ class ImportPageState extends State { VerifyUserNotifier? _homeScreenNotifier; ImportNotifier? _importNotifier; + final WebMetaTagUpdate metaTagUpdate = WebMetaTagUpdate(); Widget _buildMobileLayout(BuildContext context) { return Padding( @@ -353,6 +355,11 @@ class ImportPageState extends State { listen: false, ); _importNotifier = Provider.of(context, listen: false); + + // Updates meta tag details when navigating from the setup page to the + // import page in Expense Tracker. + metaTagUpdate.update('Import', 'Expense Tracker'); + super.didChangeDependencies(); } diff --git a/lib/showcase_samples/expense_tracker/pages/welcome_screens/setup_page.dart b/lib/showcase_samples/expense_tracker/pages/welcome_screens/setup_page.dart index 3255006b..985e11d3 100644 --- a/lib/showcase_samples/expense_tracker/pages/welcome_screens/setup_page.dart +++ b/lib/showcase_samples/expense_tracker/pages/welcome_screens/setup_page.dart @@ -4,6 +4,7 @@ import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; +import '../../../../meta_tag/meta_tag.dart'; import '../../constants.dart'; import '../../custom_widgets/custom_drop_down_menu.dart'; import '../../custom_widgets/single_selection_date_picker.dart'; @@ -54,6 +55,7 @@ class SetupProfilePageState extends State { late WelcomeScreenNotifier _pageNotifier; late ImportNotifier _importNotifier; DateTime? _selectedDate; + final WebMetaTagUpdate metaTagUpdate = WebMetaTagUpdate(); /// Initializes all controllers, notifiers, and focus nodes. void _initializeFields() { @@ -165,6 +167,10 @@ class SetupProfilePageState extends State { ), onPressed: () { Navigator.of(context, rootNavigator: true).pop(context); + + // Sets default meta tag details when navigating from the + // sign-up page to the home page. + metaTagUpdate.setDefault(); }, child: Row( mainAxisSize: MainAxisSize.min, @@ -576,6 +582,13 @@ class SetupProfilePageState extends State { _setupNotifier = Provider.of(context, listen: false); _pageNotifier = Provider.of(context, listen: false); _importNotifier = Provider.of(context, listen: false); + + if (_pageNotifier.currentPage == WelcomeScreens.setupPage) { + // Updates meta tag details when navigating from the home page to the + // expense tracker setup (sign-up) page. + metaTagUpdate.update('Setup', 'Expense Tracker'); + } + super.didChangeDependencies(); } diff --git a/lib/showcase_samples/stock_analysis/dialogs/settings.dart b/lib/showcase_samples/stock_analysis/dialogs/settings.dart index 22f04ccf..acf0c39a 100644 --- a/lib/showcase_samples/stock_analysis/dialogs/settings.dart +++ b/lib/showcase_samples/stock_analysis/dialogs/settings.dart @@ -218,49 +218,45 @@ class SettingsDialog extends StatelessWidget { selector: (BuildContext context, StockChartProvider provider) => provider.tempSettings.logarithmicYAxis, builder: (BuildContext context, bool isLogarithmic, Widget? child) => - Row( - children: [ - Expanded( - child: RadioListTile( - title: Text( - 'Numeric Axis', - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Theme.of(context).colorScheme.onSurface, - fontWeight: fontWeight400(), + RadioGroup( + groupValue: isLogarithmic, + onChanged: (bool? value) { + if (value != null) { + context.read().updateTempSetting( + logarithmicYAxis: value, + ); + } + }, + child: Row( + children: [ + Expanded( + child: RadioListTile( + title: Text( + 'Numeric Axis', + style: Theme.of(context).textTheme.bodyMedium + ?.copyWith( + color: Theme.of(context).colorScheme.onSurface, + fontWeight: fontWeight400(), + ), ), + value: false, ), - value: false, - groupValue: isLogarithmic, - onChanged: (bool? value) { - if (value != null) { - context.read().updateTempSetting( - logarithmicYAxis: value, - ); - } - }, ), - ), - Expanded( - child: RadioListTile( - title: Text( - 'Log Axis', - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Theme.of(context).colorScheme.onSurface, - fontWeight: fontWeight400(), + Expanded( + child: RadioListTile( + title: Text( + 'Log Axis', + style: Theme.of(context).textTheme.bodyMedium + ?.copyWith( + color: Theme.of(context).colorScheme.onSurface, + fontWeight: fontWeight400(), + ), ), + value: true, ), - value: true, - groupValue: isLogarithmic, - onChanged: (bool? value) { - if (value != null) { - context.read().updateTempSetting( - logarithmicYAxis: value, - ); - } - }, ), - ), - ], + ], + ), ), ), ], diff --git a/lib/showcase_samples/stock_analysis/screens/setup.dart b/lib/showcase_samples/stock_analysis/screens/setup.dart index c2db5fda..5501edf7 100644 --- a/lib/showcase_samples/stock_analysis/screens/setup.dart +++ b/lib/showcase_samples/stock_analysis/screens/setup.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; +import '../../../meta_tag/meta_tag.dart'; import '../enum.dart'; import '../helper/helper.dart'; import '../helper/responsive_layout.dart'; @@ -55,6 +56,7 @@ class SetupProfilePage extends StatefulWidget { class SetupProfilePageState extends State { late final TextEditingController _firstNameController; late final TextEditingController _lastNameController; + final WebMetaTagUpdate metaTagUpdate = WebMetaTagUpdate(); @override void initState() { @@ -396,6 +398,10 @@ class SetupProfilePageState extends State { context.read().isLoading = false; } } + + // Updates meta tag details when navigating from the setup page + // to the Stock Chart page in Stock Analysis. + metaTagUpdate.update('Stock Chart', 'Stock Analysis'); }, child: const Text('Skip', textAlign: TextAlign.left), ), @@ -421,6 +427,10 @@ class SetupProfilePageState extends State { context.read().isLoading = false; } } + + // Updates meta tag details when navigating from the + // setup page to the Stock Chart page in Stock Analysis. + metaTagUpdate.update('Stock Chart', 'Stock Analysis'); } : null, child: Text( diff --git a/lib/showcase_samples/stock_analysis/screens/splash.dart b/lib/showcase_samples/stock_analysis/screens/splash.dart index ccaf16a1..aeb20efd 100644 --- a/lib/showcase_samples/stock_analysis/screens/splash.dart +++ b/lib/showcase_samples/stock_analysis/screens/splash.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import '../../../meta_tag/meta_tag.dart'; + /// A widget that displays a splash screen with animation. class SplashScreen extends StatefulWidget { const SplashScreen({required this.nextScreen, super.key}); @@ -19,6 +21,7 @@ class _SplashScreenState extends State /// Animation for fading effect. late final Animation _fadeAnimation; + final WebMetaTagUpdate metaTagUpdate = WebMetaTagUpdate(); /// Initializes animations for the splash screen. void _initializeAnimations() { @@ -95,6 +98,10 @@ class _SplashScreenState extends State super.initState(); _initializeAnimations(); _startNavigationTimer(); + + // Updates meta tag details when navigating from the home page to the + // setup page in Stock Analysis. + metaTagUpdate.update('Setup page', 'Stock Analysis'); } @override diff --git a/lib/showcase_samples/stock_analysis/stock_home/stock_chart_view/profile_menu_popup.dart b/lib/showcase_samples/stock_analysis/stock_home/stock_chart_view/profile_menu_popup.dart index 208b2a39..92802581 100644 --- a/lib/showcase_samples/stock_analysis/stock_home/stock_chart_view/profile_menu_popup.dart +++ b/lib/showcase_samples/stock_analysis/stock_home/stock_chart_view/profile_menu_popup.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; +import '../../../../meta_tag/meta_tag.dart'; import '../../helper/helper.dart'; import '../settings_page.dart'; +final WebMetaTagUpdate metaTagUpdate = WebMetaTagUpdate(); + class ProfileMenuPopup extends StatelessWidget { const ProfileMenuPopup( this.firstNameFirstLetter, @@ -85,6 +88,10 @@ class ProfileMenuPopup extends StatelessWidget { mouseCursor: SystemMouseCursors.click, onTap: () { Navigator.of(context, rootNavigator: true).pop(context); + + // Reset meta tags to default when navigating from Stock Analyst + // page to Home page. + metaTagUpdate.setDefault(); }, child: Padding( padding: const EdgeInsets.only(left: 10), diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/CHANGELOG.md b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/CHANGELOG.md new file mode 100644 index 00000000..c02c0c58 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/CHANGELOG.md @@ -0,0 +1,30 @@ +## 0.4.2 - 2024/10/29 +* windows: fix crash issue + + +## 0.4.1 - 2024/10/04 +* macos: Use first window when mainWindow is nil + + +## 0.4.0 - 2021/03/09 +* null safety + + +## 0.3.0 - 2020/10/08 +* add Windows support + + +## 0.2.0 - 2020/09/04 +* add Linux support +* add getFullScreen and setFullScreen functions + + +## 0.1.0 - 2020/05/25 + +* add setMaxWindowSize, setMinWindowSize +* add toggleFullScreen + + +## 0.0.1 - 2020/05/25 + +* initial release diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/lib/main.dart b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/lib/main.dart new file mode 100644 index 00000000..efb7322d --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/lib/main.dart @@ -0,0 +1,180 @@ +import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'package:desktop_window/desktop_window.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatefulWidget { + @override + _MyAppState createState() => _MyAppState(); +} + +class _MyAppState extends State { + String _windowSize = 'Unknown'; + + @override + void initState() { + super.initState(); + } + + Future _getWindowSize() async { + var size = await DesktopWindow.getWindowSize(); + setState(() { + _windowSize = '${size.width} x ${size.height}'; + }); + } + + @override + Widget build(BuildContext context) { + // MediaQuery.of(context).size; + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('desktop_window example app'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('$_windowSize\n'), + ElevatedButton( + child: Text("getWindowSize"), + onPressed: _getWindowSize, + ), + ElevatedButton( + child: Text("setMinWindowSize(300,400)"), + onPressed: () async { + await DesktopWindow.setMinWindowSize(Size(300, 400)); + }, + ), + ElevatedButton( + child: Text("setMaxWindowSize(800,800)"), + onPressed: () async { + await DesktopWindow.setMaxWindowSize(Size(800, 800)); + }, + ), + Wrap( + children: [ + ElevatedButton( + child: Text("Smaller"), + onPressed: () async { + var size = await DesktopWindow.getWindowSize(); + await DesktopWindow.setWindowSize( + Size(size.width - 50, size.height - 50)); + await _getWindowSize(); + }, + ), + ElevatedButton( + child: Text("Larger"), + onPressed: () async { + var size = await DesktopWindow.getWindowSize(); + await DesktopWindow.setWindowSize( + Size(size.width + 50, size.height + 50)); + await _getWindowSize(); + }, + ), + ], + ), + Wrap( + children: [ + ElevatedButton( + child: Text("toggleFullScreen"), + onPressed: () async { + await DesktopWindow.resetMaxWindowSize(); + await DesktopWindow.toggleFullScreen(); + }, + ), + Builder(builder: (ctx) { + return ElevatedButton( + child: Text("getFullScreen"), + onPressed: () async { + final isFullScreen = + await DesktopWindow.getFullScreen(); + ScaffoldMessenger.of(ctx).showSnackBar(SnackBar( + content: Text('getFullScreen = $isFullScreen'), + duration: Duration(seconds: 1))); + }, + ); + }), + ElevatedButton( + child: Text("setFullScreen(true)"), + onPressed: () async { + await DesktopWindow.setFullScreen(true); + }, + ), + ElevatedButton( + child: Text("setFullScreen(false)"), + onPressed: () async { + await DesktopWindow.setFullScreen(false); + }, + ), + ], + ), + Wrap( + children: [ + ElevatedButton( + child: Text("toggleBorders"), + onPressed: () async { + await DesktopWindow.toggleBorders(); + }, + ), + Builder(builder: (ctx) { + return ElevatedButton( + child: Text("setBorders(true)"), + onPressed: () async { + await DesktopWindow.setBorders(true); + }, + ); + }), + ElevatedButton( + child: Text("setBorders(false)"), + onPressed: () async { + await DesktopWindow.setBorders(false); + }, + ), + ElevatedButton( + child: Text("hasBorders"), + onPressed: () async { + print('hasBorders: ' + + (await DesktopWindow.hasBorders ? 'true' : 'false')); + }, + ), + ], + ), + Wrap( + children: [ + ElevatedButton( + child: Text("focus"), + onPressed: () { + Timer(Duration(seconds: 3), () async { + print('focus!!!'); + await DesktopWindow.focus(); + }); + }, + ), + ElevatedButton( + child: Text("stayOnTop(true)"), + onPressed: () async { + print('stayOnTop(true)'); + await DesktopWindow.stayOnTop(true); + }, + ), + ElevatedButton( + child: Text("stayOnTop(false)"), + onPressed: () async { + print('stayOnTop(false)'); + await DesktopWindow.stayOnTop(false); + }, + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/CMakeLists.txt new file mode 100644 index 00000000..77594227 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/CMakeLists.txt @@ -0,0 +1,98 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "desktop_window_example") +set(APPLICATION_ID "com.example.desktop_window") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/flutter/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/flutter/CMakeLists.txt new file mode 100644 index 00000000..94f43ff7 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,86 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + linux-x64 ${CMAKE_BUILD_TYPE} +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..321a26bf --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) desktop_window_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWindowPlugin"); + desktop_window_plugin_register_with_registrar(desktop_window_registrar); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/flutter/generated_plugin_registrant.h b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..e0f0a47b --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/flutter/generated_plugins.cmake b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 00000000..28296477 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,16 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + desktop_window +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/main.cc b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/main.cc new file mode 100644 index 00000000..058e6178 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/main.cc @@ -0,0 +1,10 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + // Only X11 is currently supported. + // Wayland support is being developed: https://github.com/flutter/flutter/issues/57932. + gdk_set_allowed_backends("x11"); + + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/my_application.cc b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/my_application.cc new file mode 100644 index 00000000..e6dd5c04 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/my_application.cc @@ -0,0 +1,46 @@ +#include "my_application.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "desktop_window_example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + nullptr)); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/my_application.h b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/my_application.h new file mode 100644 index 00000000..72271d5e --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Flutter/Flutter-Debug.xcconfig b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 00000000..785633d3 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Flutter/Flutter-Release.xcconfig b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 00000000..5fba960c --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Flutter/GeneratedPluginRegistrant.swift b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 00000000..070083d2 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,12 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import desktop_window + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + DesktopWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWindowPlugin")) +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Podfile b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Podfile new file mode 100644 index 00000000..d60ec710 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Podfile @@ -0,0 +1,82 @@ +platform :osx, '10.11' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def parse_KV_file(file, separator='=') + file_abs_path = File.expand_path(file) + if !File.exists? file_abs_path + return []; + end + pods_ary = [] + skip_line_start_symbols = ["#", "/"] + File.foreach(file_abs_path) { |line| + next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } + plugin = line.split(pattern=separator) + if plugin.length == 2 + podname = plugin[0].strip() + path = plugin[1].strip() + podpath = File.expand_path("#{path}", file_abs_path) + pods_ary.push({:name => podname, :path => podpath}); + else + puts "Invalid plugin specification: #{line}" + end + } + return pods_ary +end + +def pubspec_supports_macos(file) + file_abs_path = File.expand_path(file) + if !File.exists? file_abs_path + return false; + end + File.foreach(file_abs_path) { |line| + return true if line =~ /^\s*macos:/ + } + return false +end + +target 'Runner' do + use_frameworks! + use_modular_headers! + + # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock + # referring to absolute paths on developers' machines. + ephemeral_dir = File.join('Flutter', 'ephemeral') + symlink_dir = File.join(ephemeral_dir, '.symlinks') + symlink_plugins_dir = File.join(symlink_dir, 'plugins') + system("rm -rf #{symlink_dir}") + system("mkdir -p #{symlink_plugins_dir}") + + # Flutter Pods + generated_xcconfig = parse_KV_file(File.join(ephemeral_dir, 'Flutter-Generated.xcconfig')) + if generated_xcconfig.empty? + puts "Flutter-Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." + end + generated_xcconfig.map { |p| + if p[:name] == 'FLUTTER_FRAMEWORK_DIR' + symlink = File.join(symlink_dir, 'flutter') + File.symlink(File.dirname(p[:path]), symlink) + pod 'FlutterMacOS', :path => File.join(symlink, File.basename(p[:path])) + end + } + + # Plugin Pods + plugin_pods = parse_KV_file('../.flutter-plugins') + plugin_pods.map { |p| + symlink = File.join(symlink_plugins_dir, p[:name]) + File.symlink(p[:path], symlink) + if pubspec_supports_macos(File.join(symlink, 'pubspec.yaml')) + pod p[:name], :path => File.join(symlink, 'macos') + end + } +end + +# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. +install! 'cocoapods', :disable_input_output_paths => true diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Podfile.lock b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Podfile.lock new file mode 100644 index 00000000..69f85e56 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Podfile.lock @@ -0,0 +1,23 @@ +PODS: + - desktop_window (0.0.1): + - FlutterMacOS + - FlutterMacOS (2.0.0) + +DEPENDENCIES: + - desktop_window (from `Flutter/ephemeral/.symlinks/plugins/desktop_window/macos`) + +SPEC REPOS: + trunk: + - FlutterMacOS + +EXTERNAL SOURCES: + desktop_window: + :path: Flutter/ephemeral/.symlinks/plugins/desktop_window/macos + +SPEC CHECKSUMS: + desktop_window: fb7c4f12c1129f947ac482296b6f14059d57a3c3 + FlutterMacOS: 2f1b456c4d9436c4d4d13919bd3be8ef03ba6322 + +PODFILE CHECKSUM: d8ba9b3e9e93c62c74a660b46c6fcb09f03991a7 + +COCOAPODS: 1.10.1 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcodeproj/project.pbxproj b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..446bde31 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,642 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 4BA0982C3D6F097DE4C67291 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C82DFC54C86BB48A26A30862 /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 104809D3C7D2B95436B80D2A /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* desktop_window_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = desktop_window_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 799E46E8BBA7DCBEFA0E92BD /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + C82DFC54C86BB48A26A30862 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D7B139BEA3CC563DC273EDEA /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4BA0982C3D6F097DE4C67291 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + AFED8D8B5B9C7862813EA647 /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* desktop_window_example.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + AFED8D8B5B9C7862813EA647 /* Pods */ = { + isa = PBXGroup; + children = ( + D7B139BEA3CC563DC273EDEA /* Pods-Runner.debug.xcconfig */, + 104809D3C7D2B95436B80D2A /* Pods-Runner.release.xcconfig */, + 799E46E8BBA7DCBEFA0E92BD /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + C82DFC54C86BB48A26A30862 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + E91A418CE0F03238BC861E04 /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + 6DE760BC69C42E267332AFAC /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* desktop_window_example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0930; + ORGANIZATIONNAME = "The Flutter Authors"; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh\ntouch Flutter/ephemeral/tripwire\n"; + }; + 6DE760BC69C42E267332AFAC /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + E91A418CE0F03238BC861E04 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter/ephemeral", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter/ephemeral", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter/ephemeral", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..93470232 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..21a3cc14 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/AppDelegate.swift b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/AppDelegate.swift new file mode 100644 index 00000000..d53ef643 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..a2ec33f1 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..3c4935a7ca84f0976aca34b7f2895d65fb94d1ea GIT binary patch literal 46993 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 literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..ed4cc16421680a50164ba74381b4b35ceaa0ccfc GIT binary patch literal 3276 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 N;OXk;vd$@?2>?>Ex^Vyi literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000000000000000000000000000000000000..bcbf36df2f2aaaa0a63c7dabc94e600184229d0d GIT binary patch literal 5933 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 f$KZHO*s&vt|9^w-6P?|#0pRK8NSwWJ?9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!ItFh?!xdN1Q+aGJ{c&& zS>O>_%)r1c48n{Iv*t(u1=&kHeO=ifbFy+6aSK)V_AxLppYn8Z42d|rc6w}vOsL55 z`t&mC&y2@JTEyg!eDiFX^k#CC!jq%>erB=yHqUP0XcDOTw6ko}L zX;EmMrq(fKk*eygEuA616;0)>@A{TK|55PV@70 z$OfzS*(VJxQev3J?yY?O=ul(v`fp}?u9z`JK3ugibK>)DyCwImZOF4d{xK%%Ks1*} zv$oa)9anR%lXIBUqYnhLmT>VOzHfNP?ZwJNZ!5$s9M08RynIvaXw>@G^T9@r9^KH1 zVy??F&uuk)bH9Y4pQY!hP58i_H6 znl-NcuCpLV6ZWU;4C zu@9exF&OZi`Bovq_m%T+WhU2kvkz@^_LpycBvqm3bMpLw8X-Or5sL>0AKE1$(k_L=_Zc=CUq#=x1-QZf)G7nHu@fmsQ1eN_N3+nTEz`4HI4Z6uVlE zJH+X&det8JU?tO?upcM4Z=cV!JV;yF>FfL5Q$M|W_2Z!P`S=}Wzp|_1^#d%e?_H`> zV@%vA$+bFVqhw9`U;TfP|5|PD{||OiYdor8P*i??|NJcb%kzT_73*7WE?Ua5hAnR2 z=7WE=PhTlJ#ZeRznjTUb;`E(wkMZrj4e|Hilz-mK>9cZHQY**5TUPw~u}k;u73KI}xAx!0m-)GVia|x^d3p~s_9gh83jA&Ra<8rM%`>U3x69t&NzbwWY}7Ar?)FK#IZ0z|d0H0EkRO w3{9;}4Xg|ebq&m|3=9_N6z8I7$jwj5OsmAL;bP(Gi$Dzwp00i_>zopr02+f8CIA2c literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000000000000000000000000000000000000..e71a726136a47ed24125c7efc79d68a4a01961b4 GIT binary patch literal 14800 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Configs/AppInfo.xcconfig b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 00000000..274b2c2b --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = desktop_window_example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.mix1009.desktopWindow + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2020 com.mix1009. All rights reserved. diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Configs/Debug.xcconfig b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 00000000..36b0fd94 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Configs/Release.xcconfig b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 00000000..dff4f495 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Configs/Warnings.xcconfig b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 00000000..42bcbf47 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/DebugProfile.entitlements b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 00000000..dddb8a30 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Info.plist b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Info.plist new file mode 100644 index 00000000..4789daa6 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/MainFlutterWindow.swift b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 00000000..2722837e --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Release.entitlements b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Release.entitlements new file mode 100644 index 00000000..852fa1a4 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/pubspec.yaml b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/pubspec.yaml new file mode 100644 index 00000000..eb3dd583 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/pubspec.yaml @@ -0,0 +1,71 @@ +name: desktop_window_example +description: Demonstrates how to use the desktop_window plugin. + +# The following line prevents the package from being accidentally published to +# pub.dev using `pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: '>=2.12.0 <3.0.0' + +dependencies: + flutter: + sdk: flutter + + desktop_window: + # When depending on this package from a real application you should use: + # desktop_window: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/CMakeLists.txt new file mode 100644 index 00000000..b60fb4d5 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.15) +project(desktop_window_example LANGUAGES CXX) + +set(BINARY_NAME "desktop_window_example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/flutter/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..c7a8c760 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,101 @@ +cmake_minimum_required(VERSION 3.15) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/flutter/generated_plugin_registrant.cc b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..1923f294 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,14 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + DesktopWindowPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("DesktopWindowPlugin")); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/flutter/generated_plugin_registrant.h b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..dc139d85 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/flutter/generated_plugins.cmake b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 00000000..c2629e86 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,16 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + desktop_window +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..977e38b5 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.15) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "run_loop.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/Runner.rc b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/Runner.rc new file mode 100644 index 00000000..80da6bcd --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "Demonstrates how to use the desktop_window plugin." "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "desktop_window_example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2020 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "desktop_window_example.exe" "\0" + VALUE "ProductName", "desktop_window_example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/flutter_window.cpp b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..c4227230 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/flutter_window.cpp @@ -0,0 +1,64 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(RunLoop* run_loop, + const flutter::DartProject& project) + : run_loop_(run_loop), project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + run_loop_->RegisterFlutterInstance(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + run_loop_->UnregisterFlutterInstance(flutter_controller_->engine()); + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opporutunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/flutter_window.h b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/flutter_window.h new file mode 100644 index 00000000..b663ddd5 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/flutter_window.h @@ -0,0 +1,39 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "run_loop.h" +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow driven by the |run_loop|, hosting a + // Flutter view running |project|. + explicit FlutterWindow(RunLoop* run_loop, + const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The run loop driving events for this window. + RunLoop* run_loop_; + + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/main.cpp b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/main.cpp new file mode 100644 index 00000000..9f713b7a --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/main.cpp @@ -0,0 +1,36 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "run_loop.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + RunLoop run_loop; + + flutter::DartProject project(L"data"); + FlutterWindow window(&run_loop, project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"desktop_window_example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + run_loop.Run(); + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/resource.h b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/resource.h new file mode 100644 index 00000000..66a65d1e --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/resources/app_icon.ico b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/run_loop.cpp b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/run_loop.cpp new file mode 100644 index 00000000..2d6636ab --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/run_loop.cpp @@ -0,0 +1,66 @@ +#include "run_loop.h" + +#include + +#include + +RunLoop::RunLoop() {} + +RunLoop::~RunLoop() {} + +void RunLoop::Run() { + bool keep_running = true; + TimePoint next_flutter_event_time = TimePoint::clock::now(); + while (keep_running) { + std::chrono::nanoseconds wait_duration = + std::max(std::chrono::nanoseconds(0), + next_flutter_event_time - TimePoint::clock::now()); + ::MsgWaitForMultipleObjects( + 0, nullptr, FALSE, static_cast(wait_duration.count() / 1000), + QS_ALLINPUT); + bool processed_events = false; + MSG message; + // All pending Windows messages must be processed; MsgWaitForMultipleObjects + // won't return again for items left in the queue after PeekMessage. + while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) { + processed_events = true; + if (message.message == WM_QUIT) { + keep_running = false; + break; + } + ::TranslateMessage(&message); + ::DispatchMessage(&message); + // Allow Flutter to process messages each time a Windows message is + // processed, to prevent starvation. + next_flutter_event_time = + std::min(next_flutter_event_time, ProcessFlutterMessages()); + } + // If the PeekMessage loop didn't run, process Flutter messages. + if (!processed_events) { + next_flutter_event_time = + std::min(next_flutter_event_time, ProcessFlutterMessages()); + } + } +} + +void RunLoop::RegisterFlutterInstance( + flutter::FlutterEngine* flutter_instance) { + flutter_instances_.insert(flutter_instance); +} + +void RunLoop::UnregisterFlutterInstance( + flutter::FlutterEngine* flutter_instance) { + flutter_instances_.erase(flutter_instance); +} + +RunLoop::TimePoint RunLoop::ProcessFlutterMessages() { + TimePoint next_event_time = TimePoint::max(); + for (auto instance : flutter_instances_) { + std::chrono::nanoseconds wait_duration = instance->ProcessMessages(); + if (wait_duration != std::chrono::nanoseconds::max()) { + next_event_time = + std::min(next_event_time, TimePoint::clock::now() + wait_duration); + } + } + return next_event_time; +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/run_loop.h b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/run_loop.h new file mode 100644 index 00000000..000d3624 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/run_loop.h @@ -0,0 +1,40 @@ +#ifndef RUNNER_RUN_LOOP_H_ +#define RUNNER_RUN_LOOP_H_ + +#include + +#include +#include + +// A runloop that will service events for Flutter instances as well +// as native messages. +class RunLoop { + public: + RunLoop(); + ~RunLoop(); + + // Prevent copying + RunLoop(RunLoop const&) = delete; + RunLoop& operator=(RunLoop const&) = delete; + + // Runs the run loop until the application quits. + void Run(); + + // Registers the given Flutter instance for event servicing. + void RegisterFlutterInstance( + flutter::FlutterEngine* flutter_instance); + + // Unregisters the given Flutter instance from event servicing. + void UnregisterFlutterInstance( + flutter::FlutterEngine* flutter_instance); + + private: + using TimePoint = std::chrono::steady_clock::time_point; + + // Processes all currently pending messages for registered Flutter instances. + TimePoint ProcessFlutterMessages(); + + std::set flutter_instances_; +}; + +#endif // RUNNER_RUN_LOOP_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/runner.exe.manifest b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..c977c4a4 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/utils.cpp b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/utils.cpp new file mode 100644 index 00000000..37501e5d --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/utils.cpp @@ -0,0 +1,22 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/utils.h b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/utils.h new file mode 100644 index 00000000..d792603b --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/utils.h @@ -0,0 +1,8 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +#endif // RUNNER_UTILS_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/win32_window.cpp b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/win32_window.cpp new file mode 100644 index 00000000..efc3eb9f --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/win32_window.cpp @@ -0,0 +1,244 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/win32_window.h b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/win32_window.h new file mode 100644 index 00000000..17ba4311 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/example/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/lib/desktop_window.dart b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/lib/desktop_window.dart new file mode 100644 index 00000000..416555da --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/lib/desktop_window.dart @@ -0,0 +1,80 @@ +import 'dart:async'; + +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/material.dart'; + +class DesktopWindow { + static const MethodChannel _channel = const MethodChannel('desktop_window'); + + static Future getWindowSize() async { + final arr = await _channel.invokeMethod('getWindowSize'); + if (arr is List && arr.length == 2) { + return Size(arr[0], arr[1]); + } + throw arr; + } + + static Future setWindowSize(Size size, {bool animate = false}) async { + return await _channel.invokeMethod('setWindowSize', + {'width': size.width, 'height': size.height, 'animate': animate}); + } + + static Future setMinWindowSize(Size size) async { + return await _channel.invokeMethod( + 'setMinWindowSize', {'width': size.width, 'height': size.height}); + } + + static Future setMaxWindowSize(Size size) async { + return await _channel.invokeMethod( + 'setMaxWindowSize', {'width': size.width, 'height': size.height}); + } + + static Future resetMaxWindowSize() async { + return await _channel.invokeMethod('resetMaxWindowSize'); + } + + static Future toggleFullScreen() async { + return await _channel.invokeMethod('toggleFullScreen'); + } + + static Future setFullScreen(bool fullscreen) async { + return await _channel + .invokeMethod('setFullScreen', {'fullscreen': fullscreen}); + } + + static Future getFullScreen() async { + final fullscreen = await _channel.invokeMethod('getFullScreen'); + if (fullscreen is bool) return fullscreen; + throw fullscreen; + } + + static Future get hasBorders async { + final hasBorders = await _channel.invokeMethod('hasBorders'); + if (hasBorders is bool) return hasBorders; + throw hasBorders; + } + + static Future toggleBorders() async { + return await _channel.invokeMethod('toggleBorders'); + } + + static Future setBorders(bool border) async { + return await _channel.invokeMethod('setBorders', {'border': border}); + } + + static Future stayOnTop( + [bool stayOnTop = true, bool throwOnUnsupportedPlatform = false]) async { + if (!kIsWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS)) + return await _channel.invokeMethod('stayOnTop', {'stayOnTop': stayOnTop}); + else if (throwOnUnsupportedPlatform) + throw UnsupportedError( + "only Linux and Windows support windows staying focused"); + } + + static Future focus() async { + return await _channel.invokeMethod('focus'); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/linux/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/linux/CMakeLists.txt new file mode 100644 index 00000000..812b3d13 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/linux/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.10) +set(PROJECT_NAME "desktop_window") +project(${PROJECT_NAME} LANGUAGES CXX) + +set(PLUGIN_NAME "${PROJECT_NAME}_plugin") + +add_library(${PLUGIN_NAME} SHARED + "${PLUGIN_NAME}.cc" +) +apply_standard_settings(${PLUGIN_NAME}) +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter) +target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK) + +# List of absolute paths to libraries that should be bundled with the plugin +set(desktop_window_bundled_libraries + "" + PARENT_SCOPE +) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/linux/desktop_window_plugin.cc b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/linux/desktop_window_plugin.cc new file mode 100644 index 00000000..b8dfdc65 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/linux/desktop_window_plugin.cc @@ -0,0 +1,187 @@ +#include "include/desktop_window/desktop_window_plugin.h" + +#include +#include +#include + +#define DESKTOP_WINDOW_PLUGIN(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), desktop_window_plugin_get_type(), \ + DesktopWindowPlugin)) + +struct _DesktopWindowPlugin +{ + GObject parent_instance; + GtkWidget *widget; +}; + +G_DEFINE_TYPE(DesktopWindowPlugin, desktop_window_plugin, g_object_get_type()) + +// Called when a method call is received from Flutter. +static void desktop_window_plugin_handle_method_call( + DesktopWindowPlugin *self, + FlMethodCall *method_call) +{ + g_autoptr(FlMethodResponse) response = nullptr; + + const gchar *method = fl_method_call_get_name(method_call); + + if (!gtk_widget_is_toplevel(self->widget)) + { + response = FL_METHOD_RESPONSE(fl_method_error_response_new("MAINWINDOW_NOT_FOUND", "GtkWindow not found", fl_value_new_null())); + } + else if (strcmp(method, "getWindowSize") == 0) + { + gint width; + gint height; + + gtk_window_get_size((GtkWindow *)self->widget, &width, &height); + + g_autoptr(FlValue) list = fl_value_new_list(); + fl_value_append_take(list, fl_value_new_float((float)width)); + fl_value_append_take(list, fl_value_new_float((float)height)); + + response = FL_METHOD_RESPONSE(fl_method_success_response_new(list)); + } + else if (strcmp(method, "setWindowSize") == 0) + { + const float width = fl_value_get_float(fl_value_lookup(fl_method_call_get_args(method_call), fl_value_new_string("width"))); + const float height = fl_value_get_float(fl_value_lookup(fl_method_call_get_args(method_call), fl_value_new_string("height"))); + + gtk_window_resize((GtkWindow *)self->widget, (int)width, (int)height); + + response = FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_bool(true))); + } + else if (strcmp(method, "setMinWindowSize") == 0) + { + const float width = fl_value_get_float(fl_value_lookup(fl_method_call_get_args(method_call), fl_value_new_string("width"))); + const float height = fl_value_get_float(fl_value_lookup(fl_method_call_get_args(method_call), fl_value_new_string("height"))); + + GdkGeometry geometry; + geometry.min_height = (int)height; + geometry.min_width = (int)width; + gdk_window_set_geometry_hints(gtk_widget_get_window(self->widget), &geometry, GDK_HINT_MIN_SIZE); + + response = FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_bool(true))); + } + else if (strcmp(method, "setMaxWindowSize") == 0) + { + const float width = fl_value_get_float(fl_value_lookup(fl_method_call_get_args(method_call), fl_value_new_string("width"))); + const float height = fl_value_get_float(fl_value_lookup(fl_method_call_get_args(method_call), fl_value_new_string("height"))); + + GdkGeometry geometry; + geometry.max_height = ((int)height) == 0 ? INT_MAX : ((int)height); + geometry.max_width = ((int)width) == 0 ? INT_MAX : ((int)width); + gdk_window_set_geometry_hints(gtk_widget_get_window(self->widget), &geometry, GDK_HINT_MAX_SIZE); + + response = FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_bool(true))); + } + else if (strcmp(method, "resetMaxWindowSize") == 0) + { + GdkGeometry geometry; + geometry.max_height = INT_MAX; + geometry.max_width = INT_MAX; + gdk_window_set_geometry_hints(gtk_widget_get_window(self->widget), &geometry, GDK_HINT_MAX_SIZE); + + response = FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_bool(true))); + } + else if (strcmp(method, "toggleFullScreen") == 0) + { + bool isFullscreen = (bool)(gdk_window_get_state(gtk_widget_get_window(self->widget)) & GDK_WINDOW_STATE_FULLSCREEN); + if (!isFullscreen) + gtk_window_fullscreen((GtkWindow *)self->widget); + else + gtk_window_unfullscreen((GtkWindow *)self->widget); + + response = FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_bool(true))); + } + else if (strcmp(method, "setFullScreen") == 0) + { + bool fullscreen = fl_value_get_bool(fl_value_lookup(fl_method_call_get_args(method_call), fl_value_new_string("fullscreen"))); + + if (fullscreen) + gtk_window_fullscreen((GtkWindow *)self->widget); + else + gtk_window_unfullscreen((GtkWindow *)self->widget); + + response = FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_bool(true))); + } + else if (strcmp(method, "getFullScreen") == 0) + { + bool isFullscreen = (bool)(gdk_window_get_state(gtk_widget_get_window(self->widget)) & GDK_WINDOW_STATE_FULLSCREEN); + response = FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_bool(isFullscreen))); + } + else if (strcmp(method, "hasBorders") == 0) + { + bool isDecorated = (bool)gtk_window_get_decorated((GtkWindow *)self->widget); + response = FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_bool(isDecorated))); + } + else if (strcmp(method, "setBorders") == 0) + { + bool decorated = fl_value_get_bool(fl_value_lookup(fl_method_call_get_args(method_call), fl_value_new_string("border"))); + gtk_window_set_decorated((GtkWindow *)self->widget, decorated); + + response = FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_bool(true))); + } + else if (strcmp(method, "toggleBorders") == 0) + { + bool isDecorated = (bool)gtk_window_get_decorated((GtkWindow *)self->widget); + gtk_window_set_decorated((GtkWindow *)self->widget, !isDecorated); + + response = FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_bool(true))); + } + else if (strcmp(method, "focus") == 0) + { + gtk_window_present((GtkWindow *) self->widget); + response = FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_bool(true))); + } + else if(strcmp(method, "stayOnTop") == 0){ + bool stayOnTop = !fl_value_get_bool(fl_value_lookup(fl_method_call_get_args(method_call), fl_value_new_string("stayOnTop"))); + gdk_window_set_keep_above(gtk_widget_get_window(self->widget), stayOnTop); + response = FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_bool(true))); + } + + + if (response == nullptr) + response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new()); + + fl_method_call_respond(method_call, response, nullptr); +} + +static void desktop_window_plugin_dispose(GObject *object) +{ + G_OBJECT_CLASS(desktop_window_plugin_parent_class)->dispose(object); +} + +static void desktop_window_plugin_class_init(DesktopWindowPluginClass *klass) +{ + G_OBJECT_CLASS(klass)->dispose = desktop_window_plugin_dispose; +} + +static void desktop_window_plugin_init(DesktopWindowPlugin *self) {} + +static void method_call_cb(FlMethodChannel *channel, FlMethodCall *method_call, + gpointer user_data) +{ + DesktopWindowPlugin *plugin = DESKTOP_WINDOW_PLUGIN(user_data); + desktop_window_plugin_handle_method_call(plugin, method_call); +} + +void desktop_window_plugin_register_with_registrar(FlPluginRegistrar *registrar) +{ + + GtkWidget *toplevel = gtk_widget_get_toplevel((GtkWidget *)fl_plugin_registrar_get_view(registrar)); + DesktopWindowPlugin *plugin = DESKTOP_WINDOW_PLUGIN( + g_object_new(desktop_window_plugin_get_type(), nullptr)); + plugin->widget = toplevel; + + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); + g_autoptr(FlMethodChannel) channel = + fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar), + "desktop_window", + FL_METHOD_CODEC(codec)); + fl_method_channel_set_method_call_handler(channel, method_call_cb, + g_object_ref(plugin), + g_object_unref); + + g_object_unref(plugin); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/linux/include/desktop_window/desktop_window_plugin.h b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/linux/include/desktop_window/desktop_window_plugin.h new file mode 100644 index 00000000..8315b3b8 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/linux/include/desktop_window/desktop_window_plugin.h @@ -0,0 +1,26 @@ +#ifndef FLUTTER_PLUGIN_DESKTOP_WINDOW_PLUGIN_H_ +#define FLUTTER_PLUGIN_DESKTOP_WINDOW_PLUGIN_H_ + +#include + +G_BEGIN_DECLS + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default"))) +#else +#define FLUTTER_PLUGIN_EXPORT +#endif + +typedef struct _DesktopWindowPlugin DesktopWindowPlugin; +typedef struct { + GObjectClass parent_class; +} DesktopWindowPluginClass; + +FLUTTER_PLUGIN_EXPORT GType desktop_window_plugin_get_type(); + +FLUTTER_PLUGIN_EXPORT void desktop_window_plugin_register_with_registrar( + FlPluginRegistrar* registrar); + +G_END_DECLS + +#endif // FLUTTER_PLUGIN_DESKTOP_WINDOW_PLUGIN_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/macos/Classes/DesktopWindowPlugin.swift b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/macos/Classes/DesktopWindowPlugin.swift new file mode 100644 index 00000000..36e1b30f --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/macos/Classes/DesktopWindowPlugin.swift @@ -0,0 +1,135 @@ +import Cocoa +import FlutterMacOS + +public class DesktopWindowPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "desktop_window", binaryMessenger: registrar.messenger) + let instance = DesktopWindowPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + let firstWindow = NSApplication.shared.windows.isEmpty ? nil : NSApplication.shared.windows[0] + if let window = NSApplication.shared.mainWindow ?? firstWindow { + switch call.method { + + case "getWindowSize": + let width = window.frame.size.width + let height = window.frame.size.height + result([width, height]) + + case "setWindowSize": + if let width: Float = (call.arguments as? [String: Any])?["width"] as? Float, + let height: Float = (call.arguments as? [String: Any])?["height"] as? Float, + let animate: Bool = (call.arguments as? [String: Any])?["animate"] as? Bool + { + var rect = window.frame + rect.origin.y += (rect.size.height - CGFloat(height)) + rect.size.width = CGFloat(width) + rect.size.height = CGFloat(height) + + window.animator().setFrame(rect, display: true, animate: animate) + + } + + result(true) + + case "setMinWindowSize": + if let width: Float = (call.arguments as? [String: Any])?["width"] as? Float, + let height: Float = (call.arguments as? [String: Any])?["height"] as? Float + { + window.minSize = CGSize(width: CGFloat(width), height: CGFloat(height)) + + } + + result(true) + + case "setMaxWindowSize": + if let width: Float = (call.arguments as? [String: Any])?["width"] as? Float, + let height: Float = (call.arguments as? [String: Any])?["height"] as? Float + { + if width == 0 || height == 0 { + window.maxSize = CGSize( + width: CGFloat(Float.greatestFiniteMagnitude), + height: CGFloat(Float.greatestFiniteMagnitude)) + } else { + window.maxSize = CGSize(width: CGFloat(width), height: CGFloat(height)) + } + + } + result(true) + + case "resetMaxWindowSize": + window.maxSize = CGSize( + width: CGFloat(Float.greatestFiniteMagnitude), + height: CGFloat(Float.greatestFiniteMagnitude)) + + result(true) + + case "toggleFullScreen": + window.toggleFullScreen(nil) + result(true) + + case "setFullScreen": + if let bFullScreen: Bool = (call.arguments as? [String: Any])?["fullscreen"] as? Bool { + + if bFullScreen { + if !window.styleMask.contains(.fullScreen) { + window.toggleFullScreen(nil) + } + } else { + if window.styleMask.contains(.fullScreen) { + window.toggleFullScreen(nil) + } + } + result(true) + } + + case "getFullScreen": + result(window.styleMask.contains(.fullScreen)) + + case "toggleBorders": + if window.styleMask.contains(.borderless) { + window.styleMask.remove(.borderless) + } else { + window.styleMask.insert(.borderless) + } + result(true) + + case "setBorders": + if let bBorders: Bool = (call.arguments as? [String: Any])?["borders"] as? Bool { + if window.styleMask.contains(.borderless) == bBorders { + if bBorders { + window.styleMask.remove(.borderless) + } else { + window.styleMask.insert(.borderless) + } + } + result(true) + } + + case "hasBorders": + result(!window.styleMask.contains(.borderless)) + + case "focus": + NSApplication.shared.activate(ignoringOtherApps: true) + result(true) + + case "stayOnTop": + if let bstayOnTop: Bool = (call.arguments as? [String: Any])?["stayOnTop"] as? Bool { + if bstayOnTop { + window.level = .floating; + } else { + window.level = .normal; + } + } + result(true) + + default: + result(FlutterMethodNotImplemented) + } + } else { + result("mainWindow not found") // should return error or throw exception here. + } + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/macos/desktop_window.podspec b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/macos/desktop_window.podspec new file mode 100644 index 00000000..ffdf7621 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/macos/desktop_window.podspec @@ -0,0 +1,22 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint desktop_window.podspec' to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'desktop_window' + s.version = '0.0.1' + s.summary = 'A new flutter plugin project.' + s.description = <<-DESC +A new flutter plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'FlutterMacOS' + + s.platform = :osx, '10.11' + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } + s.swift_version = '5.0' +end diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/pubspec.yaml b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/pubspec.yaml new file mode 100644 index 00000000..0a83e250 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/pubspec.yaml @@ -0,0 +1,34 @@ +name: desktop_window +description: Flutter desktop plugin(macOS/Linux/Windows) to get and change window size. +version: 0.4.2 +#author: ChunKoo Park +homepage: https://github.com/mix1009/desktop_window + + +environment: + sdk: '>=2.12.0 <4.0.0' + flutter: ">=1.20.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' and Android 'package' identifiers should not ordinarily + # be modified. They are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + macos: + pluginClass: DesktopWindowPlugin + linux: + pluginClass: DesktopWindowPlugin + windows: + pluginClass: DesktopWindowPlugin + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/CMakeLists.txt new file mode 100644 index 00000000..ab52811a --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.15) +set(PROJECT_NAME "desktop_window") +project(${PROJECT_NAME} LANGUAGES CXX) + +set(PLUGIN_NAME "${PROJECT_NAME}_plugin") + +add_library(${PLUGIN_NAME} SHARED + "${PLUGIN_NAME}.cpp" + "include/borders.cpp" + "include/method_call.cpp" +) +apply_standard_settings(${PLUGIN_NAME}) +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) + +# List of absolute paths to libraries that should be bundled with the plugin +set(desktop_window_bundled_libraries + "" + PARENT_SCOPE +) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/desktop_window_plugin.cpp b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/desktop_window_plugin.cpp new file mode 100644 index 00000000..40c8381e --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/desktop_window_plugin.cpp @@ -0,0 +1,148 @@ +#include "include/desktop_window/desktop_window_plugin.h" + +// This must be included before many other Windows headers. +#include + +// For getPlatformVersion; remove unless needed for your plugin implementation. +#include + +#include +#include +#include + +#include "include/method_call.h" + +#include +#include +#include + +namespace +{ + + LRESULT CALLBACK MyWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam); + WNDPROC oldProc; + + int maxWidth = 0; + int maxHeight = 0; + int minWidth = 0; + int minHeight = 0; + + class DesktopWindowPlugin : public flutter::Plugin + { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar); + + DesktopWindowPlugin(); + + virtual ~DesktopWindowPlugin(); + + private: + // Called when a method is called on this plugin's channel from Dart. + void HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result); + }; + + // static + void DesktopWindowPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows *registrar) + { + auto channel = + std::make_unique>( + registrar->messenger(), "desktop_window", + &flutter::StandardMethodCodec::GetInstance()); + + auto plugin = std::make_unique(); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto &call, auto result) + { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); + + HWND handle = GetActiveWindow(); + oldProc = reinterpret_cast(GetWindowLongPtr(handle, GWLP_WNDPROC)); + SetWindowLongPtr(handle, GWLP_WNDPROC, (LONG_PTR)MyWndProc); + } + + DesktopWindowPlugin::DesktopWindowPlugin() {} + + DesktopWindowPlugin::~DesktopWindowPlugin() {} + + + void DesktopWindowPlugin::HandleMethodCall(const flutter::MethodCall &method_call, + std::unique_ptr> result) + { + DesktopWindowMethodCall::MethodCall methodCall(method_call, std::move(result)); + + const std::string method_name = method_call.method_name(); + if (method_name == "getWindowSize") + methodCall.getWindowSize(); + else if (method_name == "setWindowSize") + methodCall.setWindowSize(); + else if (method_name == "setFullScreen") + methodCall.setFullscreen(); + else if (method_name == "getFullScreen") + methodCall.getFullscreen(); + else if (method_name == "toggleFullScreen") + methodCall.toggleFullscreen(); + else if (method_name == "setBorders") + methodCall.setBorders(); + else if (method_name == "hasBorders") + methodCall.hasBorders(); + else if (method_name == "toggleBorders") + methodCall.toggleBorders(); + else if (method_name == "resetMaxWindowSize") + methodCall.resetMaxWindowSize(maxWidth, maxHeight); + else if (method_name == "setMinWindowSize") + methodCall.setMinWindowSize(minWidth, minHeight); + else if (method_name == "setMaxWindowSize") + methodCall.setMaxWindowSize(maxWidth, maxHeight); + else if (method_name == "stayOnTop") + methodCall.stayOnTop(); + else if (method_name == "focus") + methodCall.focus(); + else + result->NotImplemented(); + } + + LRESULT CALLBACK MyWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) + { + if (iMessage == WM_GETMINMAXINFO) + { + // OutputDebugString(L"WM_GETMINMAXINFO called"); + + bool changed = false; + + if (maxWidth != 0 && maxHeight != 0) + { + ((MINMAXINFO *)lParam)->ptMaxTrackSize.x = maxWidth; + ((MINMAXINFO *)lParam)->ptMaxTrackSize.y = maxHeight; + changed = true; + } + if (minWidth != 0 && minHeight != 0) + { + ((MINMAXINFO *)lParam)->ptMinTrackSize.x = minWidth; + ((MINMAXINFO *)lParam)->ptMinTrackSize.y = minHeight; + changed = true; + } + if (changed) + { + return FALSE; + } + } + + return oldProc(hWnd, iMessage, wParam, lParam); + } + +} // namespace + +void DesktopWindowPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) +{ + DesktopWindowPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} \ No newline at end of file diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/borders.cpp b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/borders.cpp new file mode 100644 index 00000000..b3c37798 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/borders.cpp @@ -0,0 +1,66 @@ +#include "borders.h" + +namespace Borders +{ + long borderStyle = NULL; + + const long mapWS_SM(const long *changes, long WS, long SM) + { + return ((*changes & WS) > 0) * GetSystemMetrics(SM); + } + + bool hasBorders(HWND *hWnd) + { + return (GetWindowLong(*hWnd, GWL_STYLE) & WINDOW_BORDERS) != 0; + } + + void setBorders(HWND *hWnd, bool borders, bool changeThickFrame) + { + if (hasBorders(hWnd) == borders) + return; + + RECT rect; + GetWindowRect(*hWnd, &rect); + + long currentStyle = GetWindowLong(*hWnd, GWL_STYLE); + if (borderStyle == NULL) + borderStyle = currentStyle & (WINDOW_BORDERS | WS_THICKFRAME); + + const long lastStyle = currentStyle; + + if (borders) + currentStyle |= borderStyle & (WINDOW_BORDERS | (changeThickFrame * WS_THICKFRAME)); + else + currentStyle &= ~(WINDOW_BORDERS | (changeThickFrame * WS_THICKFRAME)); + + SetWindowLong(*hWnd, GWL_STYLE, currentStyle); + + const long diff = lastStyle ^ currentStyle; + + const short op = 2 * borders - 1; // gets borders = 1, looses borders = -1 + + const long xBorder = + mapWS_SM(&diff, WS_THICKFRAME, SM_CXSIZEFRAME) + + mapWS_SM(&diff, WS_BORDER, SM_CXBORDER) + + mapWS_SM(&diff, WS_DLGFRAME, SM_CXDLGFRAME); + const long yBorder = + mapWS_SM(&diff, WS_THICKFRAME, SM_CYSIZEFRAME) + + mapWS_SM(&diff, WS_BORDER, SM_CYBORDER) + + mapWS_SM(&diff, WS_DLGFRAME, SM_CYDLGFRAME); + + const long caption = mapWS_SM(&diff, WS_CAPTION & ~WS_BORDER, SM_CYCAPTION); + + const long xPos = rect.left - (op * xBorder); + const long yPos = rect.top - (op * yBorder) - (op * caption); + const long width = rect.right - rect.left + (2 * op * xBorder); + const long height = rect.bottom - rect.top + (2 * op * yBorder) + (op * caption); + + SetWindowPos(*hWnd, HWND_TOP, xPos, yPos, width, height, SWP_SHOWWINDOW); + } + + void toggleBorders(HWND *hWnd, bool changeThickframe) + { + setBorders(hWnd, !hasBorders(hWnd), changeThickframe); + } + +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/borders.h b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/borders.h new file mode 100644 index 00000000..c68912ef --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/borders.h @@ -0,0 +1,20 @@ +#ifndef FLUTTER_PLUGIN_DESKTOP_WINDOW_PLUGIN_WINDOW_BORDERS_ +#define FLUTTER_PLUGIN_DESKTOP_WINDOW_PLUGIN_WINDOW_BORDERS_ + +#include + +#define WINDOW_BORDERS (WS_BORDER | WS_CAPTION | WS_DLGFRAME) + +namespace Borders +{ + + extern long borderStyle; + + const long mapWS_SM(const long *changes, long WS, long SM); + + bool hasBorders(HWND *hWnd); + void setBorders(HWND *hWnd, bool borders, bool changeThickFrame = false); + void toggleBorders(HWND *hWnd, bool changeThickframe = false); +} + +#endif // FLUTTER_PLUGIN_DESKTOP_WINDOW_PLUGIN_WINDOW_BORDERS_ \ No newline at end of file diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/desktop_window/desktop_window_plugin.h b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/desktop_window/desktop_window_plugin.h new file mode 100644 index 00000000..3f84e799 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/desktop_window/desktop_window_plugin.h @@ -0,0 +1,24 @@ +#ifndef FLUTTER_PLUGIN_DESKTOP_WINDOW_PLUGIN_H_ +#define FLUTTER_PLUGIN_DESKTOP_WINDOW_PLUGIN_H_ + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) +#endif + +#if defined(__cplusplus) +extern "C" +{ +#endif + + FLUTTER_PLUGIN_EXPORT void DesktopWindowPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_DESKTOP_WINDOW_PLUGIN_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/method_call.cpp b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/method_call.cpp new file mode 100644 index 00000000..cf7c24a0 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/method_call.cpp @@ -0,0 +1,260 @@ +#include "method_call.h" +#include "borders.h" + +namespace DesktopWindowMethodCall +{ + + MethodCall::MethodCall(const flutter::MethodCall &Cmethod_call, + std::unique_ptr> Cresult): method_call(Cmethod_call) + { + result = std::move(Cresult); + } + + void MethodCall::setFullscreen() + { + const auto *arguments = std::get_if(method_call.arguments()); + bool fullscreen = false; + if (arguments) + { + auto fs_it = arguments->find(flutter::EncodableValue("fullscreen")); + if (fs_it != arguments->end()) + { + fullscreen = std::get(fs_it->second); + } + } + HWND handle = GetActiveWindow(); + + WINDOWPLACEMENT placement; + + GetWindowPlacement(handle, &placement); + + if (fullscreen) + { + placement.showCmd = SW_MAXIMIZE; + SetWindowPlacement(handle, &placement); + } + else + { + placement.showCmd = SW_NORMAL; + SetWindowPlacement(handle, &placement); + } + + result->Success(flutter::EncodableValue(true)); + } + + void MethodCall::getFullscreen() + { + HWND handle = GetActiveWindow(); + + WINDOWPLACEMENT placement; + GetWindowPlacement(handle, &placement); + + result->Success(flutter::EncodableValue(placement.showCmd == SW_MAXIMIZE)); + } + + void MethodCall::toggleFullscreen() + { + HWND handle = GetActiveWindow(); + + WINDOWPLACEMENT placement; + GetWindowPlacement(handle, &placement); + + if (placement.showCmd == SW_MAXIMIZE) + { + placement.showCmd = SW_NORMAL; + SetWindowPlacement(handle, &placement); + } + else + { + placement.showCmd = SW_MAXIMIZE; + SetWindowPlacement(handle, &placement); + } + result->Success(flutter::EncodableValue(true)); + } + + void MethodCall::setBorders() + { + const auto *arguments = std::get_if(method_call.arguments()); + bool border = false; + if (arguments) + { + auto fs_it = arguments->find(flutter::EncodableValue("border")); + if (fs_it != arguments->end()) + { + border = std::get(fs_it->second); + } + } + + HWND hWnd = GetActiveWindow(); + + Borders::setBorders(&hWnd, border, true); + + result->Success(flutter::EncodableValue(true)); + } + + void MethodCall::hasBorders() + { + HWND hWnd = GetActiveWindow(); + result->Success(flutter::EncodableValue(Borders::hasBorders(&hWnd))); + } + + void MethodCall::toggleBorders() + { + HWND hWnd = GetActiveWindow(); + Borders::toggleBorders(&hWnd, true); + + result->Success(flutter::EncodableValue(true)); + } + + void MethodCall::stayOnTop() + { + const auto *arguments = std::get_if(method_call.arguments()); + bool stayOnTop = false; + if (arguments) + { + auto fs_it = arguments->find(flutter::EncodableValue("stayOnTop")); + if (fs_it != arguments->end()) + { + stayOnTop = std::get(fs_it->second); + } + } + + HWND hWnd = GetActiveWindow(); + + RECT rect; + GetWindowRect(hWnd, &rect); + SetWindowPos(hWnd, stayOnTop? HWND_TOPMOST: HWND_NOTOPMOST, rect.left, rect.top, rect.right-rect.left, rect.bottom -rect.top, SWP_SHOWWINDOW); + + result->Success(flutter::EncodableValue(true)); + } + + + void MethodCall::focus() + { + HWND hWnd = GetActiveWindow(); + SetFocus(hWnd); + + result->Success(flutter::EncodableValue(true)); + } + + void MethodCall::getWindowSize() { + HWND hwnd = GetActiveWindow(); + if (!hwnd) { + result->Error("WINDOW_ERROR", "Unable to get active window."); + return; + } + + RECT rect; + GetWindowRect(hwnd, &rect); + + LONG lWidth = rect.right - rect.left; + LONG lHeight = rect.bottom - rect.top; + + double width = lWidth * 1.0f; + double height = lHeight * 1.0f; + + result->Success(flutter::EncodableValue(flutter::EncodableList{flutter::EncodableValue(width), flutter::EncodableValue(height)})); + } + + void MethodCall::setWindowSize() { + double width = 0; + double height = 0; + const auto *arguments = std::get_if(method_call.arguments()); + if (arguments) + { + auto width_it = arguments->find(flutter::EncodableValue("width")); + if (width_it != arguments->end()) + { + width = std::get(width_it->second); + } + auto height_it = arguments->find(flutter::EncodableValue("height")); + if (height_it != arguments->end()) + { + height = std::get(height_it->second); + } + } + if (width == 0 || height == 0) + { + result->Error("argument_error", "width or height not provided"); + return; + } + + HWND handle = GetActiveWindow(); + + int iWidth = int(width + 0.5); + int iHeight = int(height + 0.5); + + SetWindowPos(handle, HWND_TOP, 0, 0, iWidth, iHeight, SWP_NOMOVE); + + result->Success(flutter::EncodableValue(true)); + } + + void MethodCall::resetMaxWindowSize(int &maxWidth, int &maxHeight) + { + maxWidth = 0; + maxHeight = 0; + + result->Success(flutter::EncodableValue(true)); + } + + void MethodCall::setMinWindowSize(int &minWidth, int &minHeight) + { + double width = 0; + double height = 0; + const auto *arguments = std::get_if(method_call.arguments()); + if (arguments) + { + auto width_it = arguments->find(flutter::EncodableValue("width")); + if (width_it != arguments->end()) + { + width = std::get(width_it->second); + } + auto height_it = arguments->find(flutter::EncodableValue("height")); + if (height_it != arguments->end()) + { + height = std::get(height_it->second); + } + } + if (width == 0 || height == 0) + { + result->Error("argument_error", "width or height not provided"); + return; + } + + minWidth = int(width + 0.5); + minHeight = int(height + 0.5); + + result->Success(flutter::EncodableValue(true)); + } + + void MethodCall::setMaxWindowSize(int &maxWidth, int &maxHeight) + { + double width = 0; + double height = 0; + const auto *arguments = std::get_if(method_call.arguments()); + if (arguments) + { + auto width_it = arguments->find(flutter::EncodableValue("width")); + if (width_it != arguments->end()) + { + width = std::get(width_it->second); + } + auto height_it = arguments->find(flutter::EncodableValue("height")); + if (height_it != arguments->end()) + { + height = std::get(height_it->second); + } + } + if (width == 0 || height == 0) + { + result->Error("argument_error", "width or height not provided"); + return; + } + + maxWidth = int(width + 0.5); + maxHeight = int(height + 0.5); + + result->Success(flutter::EncodableValue(true)); + } + +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/method_call.h b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/method_call.h new file mode 100644 index 00000000..5c2f3943 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/desktop_window/windows/include/method_call.h @@ -0,0 +1,37 @@ +#ifndef FLUTTER_PLUGIN_DESKTOP_WINDOW_PLUGIN_WINDOW_METHOD_CALL_ +#define FLUTTER_PLUGIN_DESKTOP_WINDOW_PLUGIN_WINDOW_METHOD_CALL_ + +#include +#include +#include +#include + +namespace DesktopWindowMethodCall +{ + class MethodCall + { + public: + MethodCall(const flutter::MethodCall &Cmethod_call, + std::unique_ptr> Cresult); + + void setFullscreen(); + void getFullscreen(); + void toggleFullscreen(); + void setBorders(); + void hasBorders(); + void toggleBorders(); + void stayOnTop(); + void focus(); + void getWindowSize(); + void setWindowSize(); + void resetMaxWindowSize(int &maxWidth, int &maxHeight); + void setMinWindowSize(int &minWidth, int &minHeight); + void setMaxWindowSize(int &maxWidth, int &maxHeight); + + private: + const flutter::MethodCall &method_call; + std::unique_ptr> result; + }; +} + +#endif // FLUTTER_PLUGIN_DESKTOP_WINDOW_PLUGIN_WINDOW_METHOD_CALL_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/CHANGELOG.md b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/CHANGELOG.md new file mode 100644 index 00000000..2e2d918a --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/CHANGELOG.md @@ -0,0 +1,524 @@ +## 12.3.0 + + - **FEAT**(device_info_plus): Add identifiers for iPad Pro 11-Inch (M5) models ([#3708](https://github.com/fluttercommunity/plus_plugins/issues/3708)). ([61786a1d](https://github.com/fluttercommunity/plus_plugins/commit/61786a1d680d8c3886b4fceb34d689ec94e10d8a)) + +## 12.2.0 + + - **FIX**(device_info_plus): correct Mac model name mappings ([#3690](https://github.com/fluttercommunity/plus_plugins/issues/3690)). ([5040a78e](https://github.com/fluttercommunity/plus_plugins/commit/5040a78e26e2abbc9b9e1e8cbb23b1e6e38ad32a)) + - **FEAT**(device_info_plus): Add missing device identifiers for Macs on M4 ([#3700](https://github.com/fluttercommunity/plus_plugins/issues/3700)). ([b4dbd144](https://github.com/fluttercommunity/plus_plugins/commit/b4dbd1444c3104bc1f10af941907905d3070edcf)) + - **FEAT**(device_info_plus): Add identifiers for new Apple devices on M5 chip ([#3698](https://github.com/fluttercommunity/plus_plugins/issues/3698)). ([4cc3f07f](https://github.com/fluttercommunity/plus_plugins/commit/4cc3f07fd4836e5417089630439fcb8a9cdb6bf5)) + +## 12.1.0 + + - **FEAT**(device_info_plus): add mapping for new iPhone 17 series models ([#3676](https://github.com/fluttercommunity/plus_plugins/issues/3676)). ([80eb9815](https://github.com/fluttercommunity/plus_plugins/commit/80eb98156aed96fbc214736964724604e9c80b4f)) + +## 12.0.0 + +> Note: This release has breaking changes. +> +> Plugin now requires the following: +> - Android Gradle Plugin >=8.12.1 +> - Gradle wrapper >=8.13 +> - Kotlin 2.2.0 + + - **DOCS**(all): replace MacOS by macOS in package READMEs ([#3658](https://github.com/fluttercommunity/plus_plugins/issues/3658)). ([72b6234c](https://github.com/fluttercommunity/plus_plugins/commit/72b6234c25315c30d8efc9f15a9258b0bb7273a8)) + - **BREAKING** **REFACTOR**(device_info_plus): remove `serialNumber` property from AndroidDeviceInfo ([#3663](https://github.com/fluttercommunity/plus_plugins/issues/3663)). ([d0fdc582](https://github.com/fluttercommunity/plus_plugins/commit/d0fdc582e8187f71522678e19b6329221b5f535d)) + - **BREAKING** **FEAT**(device_info_plus): Change Android compile SDK, update Android build config ([#3668](https://github.com/fluttercommunity/plus_plugins/issues/3668)). ([32528e7f](https://github.com/fluttercommunity/plus_plugins/commit/32528e7f852893dd4448c448096e459e1950c7e4)) + +## 11.5.0 + + - **FIX**(device_info_plus): Specify correct Dart and Flutter version requirements ([#3597](https://github.com/fluttercommunity/plus_plugins/issues/3597)). ([6ebc0ead](https://github.com/fluttercommunity/plus_plugins/commit/6ebc0ead68df477f06c6d4738a69a9e56223cf47)) + - **FEAT**(device_info_plus): Add identifier for iPhone 16e ([#3596](https://github.com/fluttercommunity/plus_plugins/issues/3596)). ([e1152bb8](https://github.com/fluttercommunity/plus_plugins/commit/e1152bb87d702ff86aaf29e9b7362d8dfd3247e0)) + - **FEAT**(device_info_plus): Add storage information ([#3536](https://github.com/fluttercommunity/plus_plugins/issues/3536)). ([fefe6ce7](https://github.com/fluttercommunity/plus_plugins/commit/fefe6ce7a04c01c39f0cf0ecd0855bce1d701883)) + +## 11.4.0 + + - **FEAT**(device_info_plus): add ram information ([#3535](https://github.com/fluttercommunity/plus_plugins/issues/3535)). ([160a0182](https://github.com/fluttercommunity/plus_plugins/commit/160a01824cb8a245aec52eb3f5a1fa804ef791eb)) + - **FEAT**(device_info_plus): allow to mock device info ([#3525](https://github.com/fluttercommunity/plus_plugins/issues/3525)). ([f78f32c4](https://github.com/fluttercommunity/plus_plugins/commit/f78f32c47fef8045f34944899f6082303182b615)) + +## 11.3.3 + + - **FIX**(device_info_plus): handle nullability on getString(DEVICE_NAME) ([#3507](https://github.com/fluttercommunity/plus_plugins/issues/3507)). ([3201e056](https://github.com/fluttercommunity/plus_plugins/commit/3201e056b2a44ce74a3a9218fba59d71d9795379)) + +## 11.3.2 + +**Note:** This release bumps dependency `win32_registry` from `1.1.5` to `2.0.1`. It will not compile if you have Dependency Overrides for that package. + + - **FIX**(device_info_plus): tighten dependency constraints ([#3497](https://github.com/fluttercommunity/plus_plugins/issues/3497)). ([c7e2428a](https://github.com/fluttercommunity/plus_plugins/commit/c7e2428a6075df4e37da9ef4934861c7cb0c3bee)) + - **FIX**(device_info_plus): fix memory leak when calling DeviceInfoPlugin().macOsInfo ([#3474](https://github.com/fluttercommunity/plus_plugins/issues/3474)). ([1cbf2b56](https://github.com/fluttercommunity/plus_plugins/commit/1cbf2b5621465456221b50ade7ac6c2f3266788d)) + +## 11.3.1 + +- Retracted release due to [#3496](https://github.com/fluttercommunity/plus_plugins/issues/3496) + +## 11.3.0 + + - **FEAT**(device_info_plus): Add User Device Name in Android (PR [#3437](https://github.com/fluttercommunity/plus_plugins/issues/3437)) ([#3456](https://github.com/fluttercommunity/plus_plugins/issues/3456)). ([8c38a31d](https://github.com/fluttercommunity/plus_plugins/commit/8c38a31d7c1073d7011ec3e3193f6b99b3851ef1)) + +## 11.2.2 + + - **FIX**(device_info_plus): Replace throwing exception with returning default values on Windows ([#3445](https://github.com/fluttercommunity/plus_plugins/issues/3445)). ([084730f8](https://github.com/fluttercommunity/plus_plugins/commit/084730f82436b474b31b16f6dc2d7b90585e899f)) + - **DOCS**(device_info_plus): Update the documentation URL for property descriptions. ([#3441](https://github.com/fluttercommunity/plus_plugins/issues/3441)). ([743bec62](https://github.com/fluttercommunity/plus_plugins/commit/743bec626c909fdc8ba6d087006568cca60563d8)) + +## 11.2.1 + + - **FIX**(device_info_plus): Resolve compilation issues with SPM enabled ([#3405](https://github.com/fluttercommunity/plus_plugins/issues/3405)). ([3f098c30](https://github.com/fluttercommunity/plus_plugins/commit/3f098c30320e1595c06b093e8eb9827a44435c5d)) + - **FIX**(device_info_plus): device memory null error on Safari and Firefox ([#3401](https://github.com/fluttercommunity/plus_plugins/issues/3401)). ([2b7cb088](https://github.com/fluttercommunity/plus_plugins/commit/2b7cb0888cd725dc69e409590861fe8118058c4d)) + - **FIX**(device_info_plus): add @Suppress(deprecate) to Build.SERIAL ([#3402](https://github.com/fluttercommunity/plus_plugins/issues/3402)). ([8e70d3f3](https://github.com/fluttercommunity/plus_plugins/commit/8e70d3f33d5f1c005dbb1aef733a8a8578989bac)) + +## 11.2.0 + + - **REFACTOR**(all): Use range of flutter_lints for broader compatibility ([#3371](https://github.com/fluttercommunity/plus_plugins/issues/3371)). ([8a303add](https://github.com/fluttercommunity/plus_plugins/commit/8a303add3dee1acb8bac5838246490ed8a0fe408)) + - **FIX**(device_info_plus): fix the error in the e2e test. ([#3382](https://github.com/fluttercommunity/plus_plugins/issues/3382)). ([3d06bf0e](https://github.com/fluttercommunity/plus_plugins/commit/3d06bf0ed8f1029df1230e4be6e75537abfcb19f)) + - **FIX**(device_info_plus): Set correct Flutter and Dart versions requirements ([#3362](https://github.com/fluttercommunity/plus_plugins/issues/3362)). ([77861523](https://github.com/fluttercommunity/plus_plugins/commit/778615231c376c829d6241e7988f15a77bcaeb55)) + - **FEAT**(device_info_plus): Return model name for iOS and MacOS devices ([#3358](https://github.com/fluttercommunity/plus_plugins/issues/3358)). ([63ca4cd8](https://github.com/fluttercommunity/plus_plugins/commit/63ca4cd8127e010650468a79532dd3a6047d2b31)) + - **FEAT**(device_info_plus): Add the isiOSAppOnMac property for the iOS platform. ([#3383](https://github.com/fluttercommunity/plus_plugins/issues/3383)). ([e9077845](https://github.com/fluttercommunity/plus_plugins/commit/e9077845342023d325280985234b6a09d245ac02)) + +## 11.1.1 + + - **FIX**(device_info_plus): Update privacy manifest paths ([#3347](https://github.com/fluttercommunity/plus_plugins/issues/3347)). ([46df2302](https://github.com/fluttercommunity/plus_plugins/commit/46df23023a5ba6c98edd31d5fd06bec5df40bd3b)) + +## 11.1.0 + + - **FIX**(device_info_plus): Ignore `MissingPermission` lint error on Android ([#3317](https://github.com/fluttercommunity/plus_plugins/issues/3317)). ([6469523f](https://github.com/fluttercommunity/plus_plugins/commit/6469523fb14f32f7aa23892183693a8f502992d3)) + - **FEAT**(device_info_plus): Add Swift Package Manager support ([#3167](https://github.com/fluttercommunity/plus_plugins/issues/3167)). ([6a347cb1](https://github.com/fluttercommunity/plus_plugins/commit/6a347cb106182d68329cd32827938e26bc7e7b00)) + +## 11.0.0 + +> Note: This release has breaking changes. + + - **FIX**(all): Clean up macOS Privacy Manifests ([#3268](https://github.com/fluttercommunity/plus_plugins/issues/3268)). ([d7b98ebd](https://github.com/fluttercommunity/plus_plugins/commit/d7b98ebd7d39b0143931f5cc6e627187576223dc)) + - **FIX**(all): Add macOS Privacy Manifests ([#3251](https://github.com/fluttercommunity/plus_plugins/issues/3251)). ([bf5dad2a](https://github.com/fluttercommunity/plus_plugins/commit/bf5dad2ad249605055bcbd5f663e42569df12d64)) + - **FIX**(device_info_plus): Fix type cast of digitalProductId on windows ([#3188](https://github.com/fluttercommunity/plus_plugins/issues/3188)). ([91f48a6b](https://github.com/fluttercommunity/plus_plugins/commit/91f48a6bc7d11c4238c9539ca06e6fa768995580)) + - **BREAKING** **FIX**(device_info_plus): fixed webasm compliance ([#3254](https://github.com/fluttercommunity/plus_plugins/issues/3254)). ([e35e2123](https://github.com/fluttercommunity/plus_plugins/commit/e35e2123451fc103bbb6f6d94f71ebced2ae8af5)) + +## 10.1.2 + + - **DOCS**(device_info_plus): Update plugin requirements in README ([#3162](https://github.com/fluttercommunity/plus_plugins/issues/3162)). ([6cfa950f](https://github.com/fluttercommunity/plus_plugins/commit/6cfa950f66fec649093b6c44755dc06a3a23319e)) + +## 10.1.1 + + - **CHORE**(device_info_plus): Use `>=0.5.0 < 2.0.0` version range for package:web. + - **FIX**(device_info_plus): fix integration_test iOS ([#2958](https://github.com/fluttercommunity/plus_plugins/issues/2958)). ([93ab854e](https://github.com/fluttercommunity/plus_plugins/commit/93ab854ee76a3de48387b6c54ddaeccb01cf49a9)) + - **REFACTOR**(all): Remove website files, configs, mentions ([#3018](https://github.com/fluttercommunity/plus_plugins/issues/3018)). ([ecc57146](https://github.com/fluttercommunity/plus_plugins/commit/ecc57146aa8c6b1c9c332169d3cc2205bc4a700f)) + - **FIX**(all): changed homepage url in pubspec.yaml ([#3099](https://github.com/fluttercommunity/plus_plugins/issues/3099)). ([66613656](https://github.com/fluttercommunity/plus_plugins/commit/66613656a85c176ba2ad337e4d4943d1f4171129)) + +## 10.1.0 + + - **REFACTOR**(device_info_plus): Migrate Android example to use the new plugins declaration ([#2769](https://github.com/fluttercommunity/plus_plugins/issues/2769)). ([6103b155](https://github.com/fluttercommunity/plus_plugins/commit/6103b1559d6f9383bd66460bf7717afeeeb51d86)) + - **FIX**(device_info_plus): WASM-compatible conditional imports ([#2826](https://github.com/fluttercommunity/plus_plugins/issues/2826)). ([11200cf4](https://github.com/fluttercommunity/plus_plugins/commit/11200cf4eb38bfa6bc83e955a3ceff7b8fc72493)) + - **FEAT**(device_info_plus): Add isLowRamDevice property to AndroidDeviceInfo ([#2765](https://github.com/fluttercommunity/plus_plugins/issues/2765)). ([1376b035](https://github.com/fluttercommunity/plus_plugins/commit/1376b0359fd39172cfb54595178313c73f5d1942)) + - **DOCS**(device_info_plus): Add iOS name property entitlements info ([#2756](https://github.com/fluttercommunity/plus_plugins/issues/2756)). ([d21f285a](https://github.com/fluttercommunity/plus_plugins/commit/d21f285a1d26e7a512c4a9aea579de9680a6ca48)) + +## 10.0.1 + +> Note: This release has breaking changes. + +In this release plugin migrated to package:web, meaning that it now supports WASM! + +Plugin now requires the following: +- Flutter >=3.19.0 +- Dart >=3.3.0 +- compileSDK 34 for Android part +- Java 17 for Android part +- Gradle 8.4 for Android part +- + - **BREAKING** **REFACTOR**(device_info_plus): bump MACOSX_DEPLOYMENT_TARGET from 10.11 to 10.14 ([#2589](https://github.com/fluttercommunity/plus_plugins/issues/2589)). ([1c586abf](https://github.com/fluttercommunity/plus_plugins/commit/1c586abf7ee351927242a70cb88e2e36140cec9e)) + - **BREAKING** **FIX**(device_info_plus): Remove Display Metrics from Android Device Info ([#2731](https://github.com/fluttercommunity/plus_plugins/issues/2731)). ([c5af3322](https://github.com/fluttercommunity/plus_plugins/commit/c5af332207e44902ac92765da72d2acb213fae91)) + - **BREAKING** **FEAT**(device_info_plus): migrate to package:web ([#2624](https://github.com/fluttercommunity/plus_plugins/issues/2624)). ([154e76ca](https://github.com/fluttercommunity/plus_plugins/commit/154e76ca2f9e8c1ccdaa6e2076426002c9d372a3)) + - **BREAKING** **BUILD**(device_info_plus): Target Java 17 on Android ([#2725](https://github.com/fluttercommunity/plus_plugins/issues/2725)). ([aa826dea](https://github.com/fluttercommunity/plus_plugins/commit/aa826deac5ef8136ce922f5823be2e7f90f828e9)) + - **BREAKING** **BUILD**(device_info_plus): Update to target and compile SDK 34 ([#2704](https://github.com/fluttercommunity/plus_plugins/pull/2704)). ([a3cd72f](https://github.com/fluttercommunity/plus_plugins/commit/a3cd72f86ba47f43c507f8b83f89aac7519404de)) + - **FIX**(device_info_plus): remove unnecessary print ([#2607](https://github.com/fluttercommunity/plus_plugins/issues/2607)). ([5d515816](https://github.com/fluttercommunity/plus_plugins/commit/5d5158169f75c50f15588c10e07af2e25f950c23)) + - **FIX**(device_info_plus): return type of isPhysicalDevice as boolean for ios ([#2508](https://github.com/fluttercommunity/plus_plugins/issues/2508)). ([e3a983bb](https://github.com/fluttercommunity/plus_plugins/commit/e3a983bbf0b0bb70c7c50835ddb7f3c4a46b7122)) + - **FIX**(device_info_plus): Add iOS Privacy Info ([#2582](https://github.com/fluttercommunity/plus_plugins/issues/2582)). ([34fe31eb](https://github.com/fluttercommunity/plus_plugins/commit/34fe31eb29e21fa9ea336e61d8df6858eb441a00)) + - **FEAT**(device_info_plus): Update min iOS target to 12 ([#2658](https://github.com/fluttercommunity/plus_plugins/issues/2658)). ([a3436100](https://github.com/fluttercommunity/plus_plugins/commit/a3436100fabd04a4d4db7ac09128b5b5962579d3)) + - **FEAT**(device_info_plus): LinuxDeviceInfo toString method ([#2652](https://github.com/fluttercommunity/plus_plugins/issues/2652)). ([f2fbcdb8](https://github.com/fluttercommunity/plus_plugins/commit/f2fbcdb813b62dcb76c18b00e51383e6643a93ed)) + +## 10.0.0 + +> Note: This release was retracted due to ([#2251](https://github.com/fluttercommunity/plus_plugins/issues/2251)). + +## 9.1.2 + + - **FIX**(device_info_plus): fix crash on non-standard Digital Product IDs ([#2537](https://github.com/fluttercommunity/plus_plugins/issues/2537)). ([7b318b5c](https://github.com/fluttercommunity/plus_plugins/commit/7b318b5cd8496cf7d31c62314eb9bae17f9ef8d6)) + +## 9.1.1 + + - **FIX**(device_info_plus): Fix deprecation warning on MacOS ([#2377](https://github.com/fluttercommunity/plus_plugins/issues/2377)). ([56a6d0ff](https://github.com/fluttercommunity/plus_plugins/commit/56a6d0ff3752570de89f00876eb7181d662a0465)) + +## 9.1.0 + +> Info: This release is a replacement for release 10.0.0, which was retracted due to issue ([#2251](https://github.com/fluttercommunity/plus_plugins/issues/2251)). As breaking change was reverted the major release was also reverted in favor of this one. + + - **FIX**(device_info_plus): Change Kotlin version from 1.9.10 to 1.7.22 ([#2256](https://github.com/fluttercommunity/plus_plugins/issues/2256)). ([313ec2c3](https://github.com/fluttercommunity/plus_plugins/commit/313ec2c328f34b278f197ee1f2d896f8820ac789)) + - **FIX**(device_info_plus): Revert bump compileSDK to 34 ([#2230](https://github.com/fluttercommunity/plus_plugins/issues/2230)). ([2ba5b054](https://github.com/fluttercommunity/plus_plugins/commit/2ba5b054948f48a9aae72c8a63b39f6536ab678d)) + - **FIX**(device_info_plus): Update exports to avoid web compatibility issues ([#2028](https://github.com/fluttercommunity/plus_plugins/issues/2028)). ([6c216053](https://github.com/fluttercommunity/plus_plugins/commit/6c2160537dc51493adc5bf22cd480a52582845b0)) + - **FIX**(device_info_plus): Regenerate iOS and MacOS example apps ([#1868](https://github.com/fluttercommunity/plus_plugins/issues/1868)). ([6e1111ac](https://github.com/fluttercommunity/plus_plugins/commit/6e1111acff40fef6f77fe2561810d679bafe938c)) + - **FEAT**(device_info_plus): Remove deprecated VALID_ARCHS iOS property ([#2022](https://github.com/fluttercommunity/plus_plugins/issues/2022)). ([13053295](https://github.com/fluttercommunity/plus_plugins/commit/13053295137201b34a6bf52e494ccf77e0321b18)) + - **DOCS**(device_info_plus): Add note about arch returned value on MacOS ([#2220](https://github.com/fluttercommunity/plus_plugins/issues/2220)). ([80409e2a](https://github.com/fluttercommunity/plus_plugins/commit/80409e2ab13a6379b9101034ad453517151a719a)) + - **DOCS**(all): Fix example links on pub.dev ([#1863](https://github.com/fluttercommunity/plus_plugins/issues/1863)). ([d726035a](https://github.com/fluttercommunity/plus_plugins/commit/d726035ad7631d5a1397d0a2e5df23dc7e30a4f7)) + +## 9.0.3 + + - **FIX**(device_info_plus): Regenerate iOS and MacOS example apps ([#1868](https://github.com/fluttercommunity/plus_plugins/issues/1868)). ([6e1111ac](https://github.com/fluttercommunity/plus_plugins/commit/6e1111acff40fef6f77fe2561810d679bafe938c)) + - **DOCS**(all): Fix example links on pub.dev ([#1863](https://github.com/fluttercommunity/plus_plugins/issues/1863)). ([d726035a](https://github.com/fluttercommunity/plus_plugins/commit/d726035ad7631d5a1397d0a2e5df23dc7e30a4f7)) + +## 9.0.2 + + - **DOCS**(device_info_plus): Add links to Android and iOS docs to every field ([#1857](https://github.com/fluttercommunity/plus_plugins/issues/1857)). ([89eb5217](https://github.com/fluttercommunity/plus_plugins/commit/89eb52177c90d5453ba512e73472536fc8a03c9a)) + +## 9.0.1 + + - **FIX**: Add jvm target compatibility to Kotlin plugins ([#1798](https://github.com/fluttercommunity/plus_plugins/issues/1798)). ([1b7dc432](https://github.com/fluttercommunity/plus_plugins/commit/1b7dc432ffb8d0474c9be6339d20b5a2cbcbab3f)) + - **DOCS**(all): Update READMEs ([#1828](https://github.com/fluttercommunity/plus_plugins/issues/1828)). ([57d9c884](https://github.com/fluttercommunity/plus_plugins/commit/57d9c8845edfc81fdbabcef9eb1d1ca450e62e7d)) + - **CHORE**(device_info_plus): Win32 dependency upgrade ([#1805](https://github.com/fluttercommunity/plus_plugins/pull/1805)). ([3f68800](https://github.com/fluttercommunity/plus_plugins/commit/c8f7b6342a7c51eafafae95792775505d2b52ce9)) + +## 9.0.0 + +> Note: This release has breaking changes. + + - **CHORE**(device_info_plus): Update Flutter dependencies, set Flutter >=3.3.0 and Dart to >=2.18.0 <4.0.0 + - **BREAKING** **FIX**(all): Add support of namespace property to support Android Gradle Plugin (AGP) 8 (#1727). Projects with AGP < 4.2 are not supported anymore. It is highly recommended to update at least to AGP 7.0 or newer. + - **BREAKING** **CHORE**(device_info_plus): Bump min Android to 4.4 (API 19) and iOS to 11, update podspec file (#1781). + - **REFACTOR**(device_info_plus): Refactor Windows implementation (#1772). + - **REFACTOR**(device_info_plus): Remove redundant checks for PRODUCT strings with sdk (#1745). + - **REFACTOR**(device_info_plus): Declare proper nullability for iOS properties (#1728). + +## 8.2.2 + + - **FIX**(all): Revert addition of namespace to avoid build fails on old AGPs (#1725). + +## 8.2.1 + + - **FIX**(device_info_plus): Add compatibility with AGP 8 (Android Gradle Plugin) (#1702). + +## 8.2.0 + + - **REFACTOR**(all): Remove all manual dependency_overrides (#1628). + - **FEAT**(device_info_plus): add major, minor and patch versions to macos (#1649). + +## 8.1.0 + + - **FEAT**: Add serialNumber property to AndroidDeviceInfo (#1349). + - **DOCS**: Updates for READMEs and website pages (#1389). + - **DOCS**: Explain how to get serial number on Android (#1390). + - **DOCS**: Add info about iOS 16 changes to device name (#1356). + +## 8.0.0 + +> Note: This release has breaking changes. + + - **DOCS**: Document toMap deprecation (#1292). + - **BREAKING** **FEAT**: refactor of device_info_plus platform implementation (#1293). + +## 7.0.1 + + - **FIX**: Increase min Flutter version to fix dartPluginClass registration (#1275). + +## 7.0.0 + +> Note: This release has breaking changes. + + - **REFACTOR**: Migrate Android part to Kotlin, update Android dependencies (#1245). + - **FIX**: add `@Deprecated` annotation to `toMap` method (#1142). + - **DOCS**: Add info about Android properties availability, update API docs links (#1243). + - **BREAKING** **REFACTOR**: two-package federated architecture (#1228). + +## 6.0.0 + +> Note: This release has breaking changes. + + - **BREAKING** **FEAT**: Add support of Android display metrics (#829). + +## 5.0.5 + + - Update a dependency to the latest release. + +## 5.0.4 + + - **FIX**: fixed wrong dependency version #1175. + +## 5.0.3 + + - **FIX**: fix version dependency. + +## 5.0.2 + + - **CHORE**: Version tagging using melos. + +## 5.0.1 + +- Fixing federated plugin architecture versions. + +## 5.0.0 + +- Re-introduce: Added more information to `WindowsDeviceInfo`. +- device_info_plus_platform_interface to 4.0.0 + +## 4.1.3 + +- Detects iOS simulator device id instead of simulator's underlying architecture. + +## 4.1.2 + +- Redo changes in 4.1.0 +- device_info_plus_platform_interface to 3.0.0 + +## 4.1.1 + +- Revert changes in 4.1.0 + +## 4.1.0 + +- Remove `androidId` (that already got removed from the method channel in 4.0.0, thus always returned null) +- There is a **new, separate [pub.dev package](https://pub.dev/packages/android_id) for getting the correct `androidId`** + +## 4.0.3 + +- Reverted changes in 4.0.2 + +## 4.0.2 + +- Added more information to `WindowsDeviceInfo`. + +## 4.0.1 + +- Update dependencies + +## 4.0.0 + +- **Breaking change** Remove `AndroidId` getter to avoid Google Play policies violations +- Update flutter_lints to 2.0.1 +- Remove explicit `test` dependency to use `flutter_test` from Flutter SDK + +## 3.2.4 + +- Update the description of getAndroidId method + +## 3.2.3 + +- Fix crash on macOS running on Apple M1 + +## 3.2.2 + +- Fix embedding issue in example +- Update Android dependencies in example + +## 3.2.1 + +- iOS: fix `identifierForVendor` (can be `null` in rare circumstances) +- Use automatic plugin registration on Linux and Windows +- Fix warnings when building for macOS + +## 3.2.0 + +- add `deviceInfo` + +## 3.1.1 + +- add toMap to WebBrowserInfo + +## 3.1.0 + +- add System GUID to MacOS + +## 3.0.1 + +- Upgrade Android compile SDK version +- Several code improvements + +## 3.0.0 + +- Remove deprecated method `registerWith` (of Android v1 embedding) + +## 2.2.0 + +- migrate integration_test to flutter sdk + +## 2.1.0 + +- add toMap to models + +## 2.0.1 + +- Android: migrate to mavenCentral + +## 2.0.0 + +- WebBrowserInfo properties are now nullable + +## 1.0.1 + +- Improve documentation + +## 1.0.0 + +- Migrated to null safety +- Update dependencies. + +## 0.7.2 + +- Update dependencies. + +## 0.7.1 + +- Fix macOS support. + +## 0.7.0 + +- Add macOS support via `device_info_plus_macos`. + +## 0.6.0 + +- Rename method channel to avoid conflicts. + +## 0.5.0 + +- Transfer to plus-plugins monorepo + +## 0.4.2+8 + +- Transfer package to Flutter Community under new name `device_info_plus`. + +## 0.4.2+7 + +- Port device_info plugin to use platform interface. + +## 0.4.2+6 + +- Moved everything from device_info to device_info/device_info + +## 0.4.2+5 + +- Update package:e2e reference to use the local version in the flutter/plugins + repository. + +## 0.4.2+4 + +Update lower bound of dart dependency to 2.1.0. + +## 0.4.2+3 + +- Declare API stability and compatibility with `1.0.0` (more details at: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0). + +## 0.4.2+2 + +- Fix CocoaPods podspec lint warnings. + +## 0.4.2+1 + +- Bump the minimum Flutter version to 1.12.13+hotfix.5. +- Remove deprecated API usage warning in AndroidIntentPlugin.java. +- Migrates the Android example to V2 embedding. +- Bumps AGP to 3.6.1. + +## 0.4.2 + +- Add systemFeatures to AndroidDeviceInfo. + +## 0.4.1+5 + +- Make the pedantic dev_dependency explicit. + +## 0.4.1+4 + +- Remove the deprecated `author:` field from pubspec.yaml +- Migrate the plugin to the pubspec platforms manifest. +- Require Flutter SDK 1.10.0 or greater. + +## 0.4.1+3 + +- Fix pedantic errors. Adds some missing documentation and fixes unawaited + futures in the tests. + +## 0.4.1+2 + +- Remove AndroidX warning. + +## 0.4.1+1 + +- Include lifecycle dependency as a compileOnly one on Android to resolve + potential version conflicts with other transitive libraries. + +## 0.4.1 + +- Support the v2 Android embedding. +- Update to AndroidX. +- Migrate to using the new e2e test binding. +- Add a e2e test. + +## 0.4.0+4 + +- Define clang module for iOS. + +## 0.4.0+3 + +- Update and migrate iOS example project. + +## 0.4.0+2 + +- Bump minimum Flutter version to 1.5.0. +- Add missing template type parameter to `invokeMethod` calls. +- Replace invokeMethod with invokeMapMethod wherever necessary. + +## 0.4.0+1 + +- Log a more detailed warning at build time about the previous AndroidX + migration. + +## 0.4.0 + +- **Breaking change**. Migrate from the deprecated original Android Support + Library to AndroidX. This shouldn't result in any functional changes, but it + requires any Android apps using this plugin to [also + migrate](https://developer.android.com/jetpack/androidx/migrate) if they're + using the original support library. + +## 0.3.0 + +- Added ability to get Android ID for Android devices + +## 0.2.1 + +- Updated Gradle tooling to match Android Studio 3.1.2. + +## 0.2.0 + +- **Breaking change**. Set SDK constraints to match the Flutter beta release. + +## 0.1.2 + +- Fixed Dart 2 type errors. + +## 0.1.1 + +- Simplified and upgraded Android project template to Android SDK 27. +- Updated package description. + +## 0.1.0 + +- **Breaking change**. Upgraded to Gradle 4.1 and Android Studio Gradle plugin + 3.0.1. Older Flutter projects need to upgrade their Gradle setup as well in + order to use this version of the plugin. Instructions can be found + [here](https://github.com/flutter/flutter/wiki/Updating-Flutter-projects-to-Gradle-4.1-and-Android-Studio-Gradle-plugin-3.0.1). + +## 0.0.5 + +- Added FLT prefix to iOS types + +## 0.0.4 + +- Fixed Java/Dart communication error with empty lists + +## 0.0.3 + +- Added support for utsname + +## 0.0.2 + +- Fixed broken type comparison +- Added "isPhysicalDevice" field, detecting emulators/simulators + +## 0.0.1 + +- Implements platform-specific device/OS properties diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/build.gradle b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/build.gradle new file mode 100644 index 00000000..79a345a4 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/build.gradle @@ -0,0 +1,52 @@ +group 'dev.fluttercommunity.plus.device_info' +version '1.0-SNAPSHOT' + +buildscript { + ext.kotlin_version = '2.2.0' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:8.12.1' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + namespace 'dev.fluttercommunity.plus.device_info' + compileSdk = flutter.compileSdkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = 17 + } + + defaultConfig { + minSdk 19 + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + lintOptions { + disable 'InvalidPackage', 'MissingPermission' + } + + dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/gradle.properties b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/gradle.properties new file mode 100644 index 00000000..8bd86f68 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx1536M diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/settings.gradle b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/settings.gradle new file mode 100644 index 00000000..0e75718c --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'device_info' diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/src/main/AndroidManifest.xml b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/src/main/AndroidManifest.xml new file mode 100644 index 00000000..412ea27f --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/src/main/kotlin/dev/fluttercommunity/plus/device_info/DeviceInfoPlusPlugin.kt b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/src/main/kotlin/dev/fluttercommunity/plus/device_info/DeviceInfoPlusPlugin.kt new file mode 100644 index 00000000..3ba8c921 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/src/main/kotlin/dev/fluttercommunity/plus/device_info/DeviceInfoPlusPlugin.kt @@ -0,0 +1,32 @@ +package dev.fluttercommunity.plus.device_info + +import android.app.ActivityManager +import android.content.Context +import android.content.pm.PackageManager +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.MethodChannel + +/** DeviceInfoPlusPlugin */ +class DeviceInfoPlusPlugin : FlutterPlugin { + + private lateinit var methodChannel: MethodChannel + + override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + setupMethodChannel(binding.binaryMessenger, binding.applicationContext) + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + methodChannel.setMethodCallHandler(null) + } + + private fun setupMethodChannel(messenger: BinaryMessenger, context: Context) { + methodChannel = MethodChannel(messenger, "dev.fluttercommunity.plus/device_info") + val packageManager: PackageManager = context.packageManager + val activityManager: ActivityManager = + context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + val contentResolver = context.contentResolver + val handler = MethodCallHandlerImpl(packageManager, activityManager, contentResolver) + methodChannel.setMethodCallHandler(handler) + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/src/main/kotlin/dev/fluttercommunity/plus/device_info/MethodCallHandlerImpl.kt b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/src/main/kotlin/dev/fluttercommunity/plus/device_info/MethodCallHandlerImpl.kt new file mode 100644 index 00000000..2d82b027 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/android/src/main/kotlin/dev/fluttercommunity/plus/device_info/MethodCallHandlerImpl.kt @@ -0,0 +1,114 @@ +package dev.fluttercommunity.plus.device_info + +import android.app.ActivityManager +import android.content.ContentResolver +import android.content.pm.FeatureInfo +import android.content.pm.PackageManager +import android.os.Build +import android.provider.Settings +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.MethodChannel.MethodCallHandler +import kotlin.collections.HashMap +import android.os.StatFs +import android.os.Environment + +/** + * The implementation of [MethodChannel.MethodCallHandler] for the plugin. Responsible for + * receiving method calls from method channel. + */ +internal class MethodCallHandlerImpl( + private val packageManager: PackageManager, + private val activityManager: ActivityManager, + private val contentResolver: ContentResolver, +) : MethodCallHandler { + + override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { + if (call.method.equals("getDeviceInfo")) { + val build: MutableMap = HashMap() + + build["board"] = Build.BOARD + build["bootloader"] = Build.BOOTLOADER + build["brand"] = Build.BRAND + build["device"] = Build.DEVICE + build["display"] = Build.DISPLAY + build["fingerprint"] = Build.FINGERPRINT + build["hardware"] = Build.HARDWARE + build["host"] = Build.HOST + build["id"] = Build.ID + build["manufacturer"] = Build.MANUFACTURER + build["model"] = Build.MODEL + build["product"] = Build.PRODUCT + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { + build["name"] = Settings.Global.getString(contentResolver, Settings.Global.DEVICE_NAME) ?: "" + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + build["supported32BitAbis"] = listOf(*Build.SUPPORTED_32_BIT_ABIS) + build["supported64BitAbis"] = listOf(*Build.SUPPORTED_64_BIT_ABIS) + build["supportedAbis"] = listOf(*Build.SUPPORTED_ABIS) + } else { + build["supported32BitAbis"] = emptyList() + build["supported64BitAbis"] = emptyList() + build["supportedAbis"] = emptyList() + } + + build["tags"] = Build.TAGS + build["type"] = Build.TYPE + build["isPhysicalDevice"] = !isEmulator + build["systemFeatures"] = getSystemFeatures() + + val statFs = StatFs(Environment.getDataDirectory().getPath()) + build["freeDiskSize"] = statFs.getFreeBytes() + build["totalDiskSize"] = statFs.getTotalBytes() + + val version: MutableMap = HashMap() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + version["baseOS"] = Build.VERSION.BASE_OS + version["previewSdkInt"] = Build.VERSION.PREVIEW_SDK_INT + version["securityPatch"] = Build.VERSION.SECURITY_PATCH + } + version["codename"] = Build.VERSION.CODENAME + version["incremental"] = Build.VERSION.INCREMENTAL + version["release"] = Build.VERSION.RELEASE + version["sdkInt"] = Build.VERSION.SDK_INT + build["version"] = version + + val memoryInfo: ActivityManager.MemoryInfo = ActivityManager.MemoryInfo() + activityManager.getMemoryInfo(memoryInfo) + build["isLowRamDevice"] = memoryInfo.lowMemory + build["physicalRamSize"] = memoryInfo.totalMem / 1048576L // Mb + build["availableRamSize"] = memoryInfo.availMem / 1048576L // Mb + result.success(build) + } else { + result.notImplemented() + } + } + + private fun getSystemFeatures(): List { + val featureInfos: Array = packageManager.systemAvailableFeatures + return featureInfos + .filterNot { featureInfo -> featureInfo.name == null } + .map { featureInfo -> featureInfo.name } + } + + /** + * A simple emulator-detection based on the flutter tools detection logic and a couple of legacy + * detection systems + */ + private val isEmulator: Boolean + get() = ((Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")) + || Build.FINGERPRINT.startsWith("generic") + || Build.FINGERPRINT.startsWith("unknown") + || Build.HARDWARE.contains("goldfish") + || Build.HARDWARE.contains("ranchu") + || Build.MODEL.contains("google_sdk") + || Build.MODEL.contains("Emulator") + || Build.MODEL.contains("Android SDK built for x86") + || Build.MANUFACTURER.contains("Genymotion") + || Build.PRODUCT.contains("sdk") + || Build.PRODUCT.contains("vbox86p") + || Build.PRODUCT.contains("emulator") + || Build.PRODUCT.contains("simulator")) +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/build.gradle b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/build.gradle new file mode 100644 index 00000000..c6e51e66 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/build.gradle @@ -0,0 +1,65 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +android { + namespace 'io.flutter.plugins.deviceinfoexample.example' + compileSdk = flutter.compileSdkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = 17 + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + applicationId "io.flutter.plugins.deviceinfoexample.example" + minSdk flutter.minSdkVersion + targetSdk flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/debug/AndroidManifest.xml b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 00000000..175e81dc --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/AndroidManifest.xml b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..d75557c5 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/kotlin/io/flutter/plugins/deviceinfoexample/example/MainActivity.kt b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/kotlin/io/flutter/plugins/deviceinfoexample/example/MainActivity.kt new file mode 100644 index 00000000..e2bd9925 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/kotlin/io/flutter/plugins/deviceinfoexample/example/MainActivity.kt @@ -0,0 +1,21 @@ +package io.flutter.plugins.deviceinfoexample.example + +import android.os.Build +import android.os.Bundle +import android.os.StrictMode +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + // Ensures correct use of Activity Context to obtain the WindowManager + StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder() + .detectIncorrectContextUse() + .penaltyLog() + .penaltyDeath() + .build()) + } + super.onCreate(savedInstanceState) + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/drawable/launch_background.xml b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 00000000..304732f8 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/values/styles.xml b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..1f83a33f --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/profile/AndroidManifest.xml b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 00000000..175e81dc --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/build.gradle b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/build.gradle new file mode 100644 index 00000000..bc157bd1 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/build.gradle @@ -0,0 +1,18 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/gradle.properties b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/gradle.properties new file mode 100644 index 00000000..d9cf55df --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/gradle/wrapper/gradle-wrapper.properties b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..f3dd2fa1 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Oct 05 15:15:38 CEST 2021 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/settings.gradle b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/settings.gradle new file mode 100644 index 00000000..f1c2904d --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/android/settings.gradle @@ -0,0 +1,25 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.12.1" apply false + id "org.jetbrains.kotlin.android" version "2.2.0" apply false +} + +include ":app" diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/integration_test/device_info_plus_test.dart b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/integration_test/device_info_plus_test.dart new file mode 100644 index 00000000..be6d7531 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/integration_test/device_info_plus_test.dart @@ -0,0 +1,186 @@ +// Copyright 2019, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; + +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + late IosDeviceInfo iosInfo; + late AndroidDeviceInfo androidInfo; + late WebBrowserInfo webBrowserInfo; + late WindowsDeviceInfo windowsInfo; + late LinuxDeviceInfo linuxInfo; + late MacOsDeviceInfo macosInfo; + late BaseDeviceInfo deviceInfo; + + setUpAll(() async { + final deviceInfoPlugin = DeviceInfoPlugin(); + if (kIsWeb) { + webBrowserInfo = await deviceInfoPlugin.webBrowserInfo; + } else { + if (Platform.isIOS) { + iosInfo = await deviceInfoPlugin.iosInfo; + } else if (Platform.isAndroid) { + androidInfo = await deviceInfoPlugin.androidInfo; + } else if (Platform.isWindows) { + windowsInfo = await deviceInfoPlugin.windowsInfo; + } else if (Platform.isLinux) { + linuxInfo = await deviceInfoPlugin.linuxInfo; + } else if (Platform.isMacOS) { + macosInfo = await deviceInfoPlugin.macOsInfo; + } + } + + deviceInfo = await deviceInfoPlugin.deviceInfo; + }); + + testWidgets('Can get non-null device model', (WidgetTester tester) async { + if (kIsWeb) { + expect(webBrowserInfo.userAgent, isNotNull); + expect(deviceInfo, same(webBrowserInfo)); + } else { + if (Platform.isIOS) { + expect(iosInfo.model, isNotNull); + expect(deviceInfo, same(iosInfo)); + } else if (Platform.isAndroid) { + expect(androidInfo.model, isNotNull); + expect(deviceInfo, same(androidInfo)); + } else if (Platform.isWindows) { + expect(windowsInfo.computerName, isNotNull); + expect(deviceInfo, same(windowsInfo)); + } else if (Platform.isLinux) { + expect(linuxInfo.name, isNotNull); + expect(deviceInfo, same(linuxInfo)); + } else if (Platform.isMacOS) { + expect(macosInfo.computerName, isNotNull); + expect(deviceInfo, same(macosInfo)); + } + } + }); + + testWidgets('Can get non-null iOS utsname fields', ( + WidgetTester tester, + ) async { + expect(iosInfo.utsname.machine, isNotNull); + expect(iosInfo.utsname.nodename, isNotNull); + expect(iosInfo.utsname.release, isNotNull); + expect(iosInfo.utsname.sysname, isNotNull); + expect(iosInfo.utsname.version, isNotNull); + }, skip: !Platform.isIOS); + + testWidgets('Check all android info values are available', ( + WidgetTester tester, + ) async { + if (androidInfo.version.sdkInt >= 23) { + expect(androidInfo.version.baseOS, isNotNull); + expect(androidInfo.version.previewSdkInt, isNotNull); + expect(androidInfo.version.securityPatch, isNotNull); + } + + expect(androidInfo.version.codename, isNotNull); + expect(androidInfo.version.incremental, isNotNull); + expect(androidInfo.version.release, isNotNull); + expect(androidInfo.version.sdkInt, isNotNull); + expect(androidInfo.board, isNotNull); + expect(androidInfo.bootloader, isNotNull); + expect(androidInfo.brand, isNotNull); + expect(androidInfo.device, isNotNull); + expect(androidInfo.display, isNotNull); + expect(androidInfo.fingerprint, isNotNull); + expect(androidInfo.hardware, isNotNull); + + expect(androidInfo.host, isNotNull); + expect(androidInfo.id, isNotNull); + expect(androidInfo.manufacturer, isNotNull); + expect(androidInfo.model, isNotNull); + expect(androidInfo.product, isNotNull); + expect(androidInfo.name, isNotNull); + + expect(androidInfo.supported32BitAbis, isNotNull); + expect(androidInfo.supported64BitAbis, isNotNull); + expect(androidInfo.supportedAbis, isNotNull); + + expect(androidInfo.tags, isNotNull); + expect(androidInfo.type, isNotNull); + expect(androidInfo.isPhysicalDevice, isNotNull); + expect(androidInfo.systemFeatures, isNotNull); + }, skip: !Platform.isAndroid); + + testWidgets('Check all macos info values are available', (( + WidgetTester tester, + ) async { + expect(macosInfo.computerName, isNotNull); + expect(macosInfo.hostName, isNotNull); + expect(macosInfo.arch, isNotNull); + expect(macosInfo.model, isNotNull); + expect(macosInfo.modelName, isNotNull); + expect(macosInfo.kernelVersion, isNotNull); + expect(macosInfo.osRelease, isNotNull); + expect(macosInfo.activeCPUs, isNotNull); + expect(macosInfo.memorySize, isNotNull); + expect(macosInfo.cpuFrequency, isNotNull); + expect(macosInfo.systemGUID, isNotNull); + }), skip: !Platform.isMacOS); + + testWidgets('Check all Linux info values are available', (( + WidgetTester tester, + ) async { + expect(linuxInfo.name, isNotNull); + expect(linuxInfo.version, isNotNull); + expect(linuxInfo.id, isNotNull); + expect(linuxInfo.idLike, isNotNull); + expect(linuxInfo.versionCodename, isNotNull); + expect(linuxInfo.versionId, isNotNull); + expect(linuxInfo.prettyName, isNotNull); + expect(linuxInfo.buildId, isNull); + expect(linuxInfo.variant, isNull); + expect(linuxInfo.variantId, isNull); + }), skip: !Platform.isLinux); + + testWidgets('Check all Windows info values are available', (( + WidgetTester tester, + ) async { + expect(windowsInfo.numberOfCores, isPositive); + expect(windowsInfo.computerName, isNotEmpty); + expect(windowsInfo.systemMemoryInMegabytes, isPositive); + expect(windowsInfo.userName, isNotEmpty); + expect(windowsInfo.majorVersion, equals(10)); + expect(windowsInfo.minorVersion, equals(0)); + expect(windowsInfo.buildNumber, greaterThan(10240)); + expect(windowsInfo.platformId, equals(2)); + expect(windowsInfo.reserved, isZero); + expect(windowsInfo.buildLab, isNotEmpty); + expect( + windowsInfo.buildLab, + startsWith(windowsInfo.buildNumber.toString()), + ); + expect(windowsInfo.buildLabEx, isNotEmpty); + expect( + windowsInfo.buildLab, + startsWith(windowsInfo.buildNumber.toString()), + ); + expect(windowsInfo.digitalProductId, isNotEmpty); + expect(windowsInfo.editionId, isNotEmpty); + expect(windowsInfo.productId, isNotEmpty); + expect( + RegExp( + r'^([A-Z0-9]{5}-){4}[A-Z0-9]{5}$', + ).hasMatch(windowsInfo.productId) || + RegExp( + r'^([A-Z0-9]{5}-){3}[A-Z0-9]{5}$', + ).hasMatch(windowsInfo.productId), + isTrue, + ); + expect(windowsInfo.productName, isNotEmpty); + expect(windowsInfo.productName, startsWith('Windows')); + expect(windowsInfo.releaseId, isNotEmpty); + expect(windowsInfo.deviceId, isNotEmpty); + }), skip: !Platform.isWindows); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Flutter/AppFrameworkInfo.plist b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 00000000..8c6e5614 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Flutter/Debug.xcconfig b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 00000000..ec97fc6f --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Flutter/Release.xcconfig b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Flutter/Release.xcconfig new file mode 100644 index 00000000..c4855bfe --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/project.pbxproj b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..075d8eec --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,723 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 429829504B428B91CD34394A /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E34A4006F430DB1C5A018A69 /* Pods_Runner.framework */; }; + 5DA2260ADE5F76F3FFB815A0 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0CDBB1B1CAFCC61F3647887B /* Pods_RunnerTests.framework */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0CDBB1B1CAFCC61F3647887B /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 6FDE560C1C66BB7D507C271F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 70B46233CBC827D9357CAED6 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B3E39AFFADC3FF84A14CEAD9 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + B5226E28C7B6DFA3F85A5F86 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + CC560B6DE1FF1FA91F20AF89 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + E34A4006F430DB1C5A018A69 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F7BF1F60B00C1E3390715BB3 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 479820A9DCB23F89315408D4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5DA2260ADE5F76F3FFB815A0 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 429829504B428B91CD34394A /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 2A76172809353E74F0878C8F /* Frameworks */ = { + isa = PBXGroup; + children = ( + E34A4006F430DB1C5A018A69 /* Pods_Runner.framework */, + 0CDBB1B1CAFCC61F3647887B /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + EE25C9049E007CD6D24A97DF /* Pods */, + 2A76172809353E74F0878C8F /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + EE25C9049E007CD6D24A97DF /* Pods */ = { + isa = PBXGroup; + children = ( + 6FDE560C1C66BB7D507C271F /* Pods-Runner.debug.xcconfig */, + 70B46233CBC827D9357CAED6 /* Pods-Runner.release.xcconfig */, + B5226E28C7B6DFA3F85A5F86 /* Pods-Runner.profile.xcconfig */, + B3E39AFFADC3FF84A14CEAD9 /* Pods-RunnerTests.debug.xcconfig */, + CC560B6DE1FF1FA91F20AF89 /* Pods-RunnerTests.release.xcconfig */, + F7BF1F60B00C1E3390715BB3 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + AC296610EEC611E8563F8C04 /* [CP] Check Pods Manifest.lock */, + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + 479820A9DCB23F89315408D4 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + F83E525764739090E301A316 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 4FD9CA2815573411DDA641D3 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 4FD9CA2815573411DDA641D3 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + AC296610EEC611E8563F8C04 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + F83E525764739090E301A316 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.exampleDeviceInfo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B3E39AFFADC3FF84A14CEAD9 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.exampleDeviceInfo.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = CC560B6DE1FF1FA91F20AF89 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.exampleDeviceInfo.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F7BF1F60B00C1E3390715BB3 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.exampleDeviceInfo.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.exampleDeviceInfo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.exampleDeviceInfo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..8e3ca5df --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..21a3cc14 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/AppDelegate.swift b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/AppDelegate.swift new file mode 100644 index 00000000..b6363034 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@main +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d36b1fab --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..797d452e458972bab9d994556c8305db4c827017 GIT binary patch literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed2d933e1120817fe9182483a228007b18ab6ae GIT binary patch literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd7b0099ca80c806f8fe495613e8d6c69460d76 GIT binary patch literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe730945a01f64a61e2235dbe3f45b08f7729182 GIT binary patch literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..502f463a9bc882b461c96aadf492d1729e49e725 GIT binary patch literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec303439225b78712f49115768196d8d76f6790 GIT binary patch literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f5fea27c705180eb716271f41b582e76dcbd90 GIT binary patch literal 1674 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 literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8 GIT binary patch literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 00000000..0bedcf2f --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..f2e259c7 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Base.lproj/Main.storyboard b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f3c28516 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Info.plist b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Info.plist new file mode 100644 index 00000000..c30f9332 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Info.plist @@ -0,0 +1,51 @@ + + + + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Device Info Plus + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + device_info_plus_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Runner-Bridging-Header.h b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 00000000..308a2a56 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/RunnerTests/RunnerTests.swift b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..86a7c3b1 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/lib/main.dart b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/lib/main.dart new file mode 100644 index 00000000..5a70d40d --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/lib/main.dart @@ -0,0 +1,279 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'dart:developer' as developer; + +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +void main() { + runZonedGuarded( + () { + runApp(const MyApp()); + }, + (dynamic error, dynamic stack) { + developer.log("Something went wrong!", error: error, stackTrace: stack); + }, + ); +} + +class MyApp extends StatefulWidget { + const MyApp({super.key}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + static final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); + Map _deviceData = {}; + + @override + void initState() { + super.initState(); + initPlatformState(); + } + + Future initPlatformState() async { + var deviceData = {}; + + try { + if (kIsWeb) { + deviceData = _readWebBrowserInfo(await deviceInfoPlugin.webBrowserInfo); + } else { + deviceData = switch (defaultTargetPlatform) { + TargetPlatform.android => _readAndroidBuildData( + await deviceInfoPlugin.androidInfo, + ), + TargetPlatform.iOS => _readIosDeviceInfo( + await deviceInfoPlugin.iosInfo, + ), + TargetPlatform.linux => _readLinuxDeviceInfo( + await deviceInfoPlugin.linuxInfo, + ), + TargetPlatform.windows => _readWindowsDeviceInfo( + await deviceInfoPlugin.windowsInfo, + ), + TargetPlatform.macOS => _readMacOsDeviceInfo( + await deviceInfoPlugin.macOsInfo, + ), + TargetPlatform.fuchsia => { + 'Error:': 'Fuchsia platform isn\'t supported', + }, + }; + } + } on PlatformException { + deviceData = { + 'Error:': 'Failed to get platform version.', + }; + } + + if (!mounted) return; + + setState(() { + _deviceData = deviceData; + }); + } + + Map _readAndroidBuildData(AndroidDeviceInfo build) { + return { + 'version.securityPatch': build.version.securityPatch, + 'version.sdkInt': build.version.sdkInt, + 'version.release': build.version.release, + 'version.previewSdkInt': build.version.previewSdkInt, + 'version.incremental': build.version.incremental, + 'version.codename': build.version.codename, + 'version.baseOS': build.version.baseOS, + 'board': build.board, + 'bootloader': build.bootloader, + 'brand': build.brand, + 'device': build.device, + 'display': build.display, + 'fingerprint': build.fingerprint, + 'hardware': build.hardware, + 'host': build.host, + 'id': build.id, + 'manufacturer': build.manufacturer, + 'model': build.model, + 'product': build.product, + 'name': build.name, + 'supported32BitAbis': build.supported32BitAbis, + 'supported64BitAbis': build.supported64BitAbis, + 'supportedAbis': build.supportedAbis, + 'tags': build.tags, + 'type': build.type, + 'isPhysicalDevice': build.isPhysicalDevice, + 'freeDiskSize': build.freeDiskSize, + 'totalDiskSize': build.totalDiskSize, + 'systemFeatures': build.systemFeatures, + 'isLowRamDevice': build.isLowRamDevice, + 'physicalRamSize': build.physicalRamSize, + 'availableRamSize': build.availableRamSize, + }; + } + + Map _readIosDeviceInfo(IosDeviceInfo data) { + return { + 'name': data.name, + 'systemName': data.systemName, + 'systemVersion': data.systemVersion, + 'model': data.model, + 'modelName': data.modelName, + 'localizedModel': data.localizedModel, + 'identifierForVendor': data.identifierForVendor, + 'isPhysicalDevice': data.isPhysicalDevice, + 'isiOSAppOnMac': data.isiOSAppOnMac, + 'freeDiskSize': data.freeDiskSize, + 'totalDiskSize': data.totalDiskSize, + 'physicalRamSize': data.physicalRamSize, + 'availableRamSize': data.availableRamSize, + 'utsname.sysname:': data.utsname.sysname, + 'utsname.nodename:': data.utsname.nodename, + 'utsname.release:': data.utsname.release, + 'utsname.version:': data.utsname.version, + 'utsname.machine:': data.utsname.machine, + }; + } + + Map _readLinuxDeviceInfo(LinuxDeviceInfo data) { + return { + 'name': data.name, + 'version': data.version, + 'id': data.id, + 'idLike': data.idLike, + 'versionCodename': data.versionCodename, + 'versionId': data.versionId, + 'prettyName': data.prettyName, + 'buildId': data.buildId, + 'variant': data.variant, + 'variantId': data.variantId, + 'machineId': data.machineId, + }; + } + + Map _readWebBrowserInfo(WebBrowserInfo data) { + return { + 'browserName': data.browserName.name, + 'appCodeName': data.appCodeName, + 'appName': data.appName, + 'appVersion': data.appVersion, + 'deviceMemory': data.deviceMemory, + 'language': data.language, + 'languages': data.languages, + 'platform': data.platform, + 'product': data.product, + 'productSub': data.productSub, + 'userAgent': data.userAgent, + 'vendor': data.vendor, + 'vendorSub': data.vendorSub, + 'hardwareConcurrency': data.hardwareConcurrency, + 'maxTouchPoints': data.maxTouchPoints, + }; + } + + Map _readMacOsDeviceInfo(MacOsDeviceInfo data) { + return { + 'computerName': data.computerName, + 'hostName': data.hostName, + 'arch': data.arch, + 'model': data.model, + 'modelName': data.modelName, + 'kernelVersion': data.kernelVersion, + 'majorVersion': data.majorVersion, + 'minorVersion': data.minorVersion, + 'patchVersion': data.patchVersion, + 'osRelease': data.osRelease, + 'activeCPUs': data.activeCPUs, + 'memorySize': data.memorySize, + 'cpuFrequency': data.cpuFrequency, + 'systemGUID': data.systemGUID, + }; + } + + Map _readWindowsDeviceInfo(WindowsDeviceInfo data) { + return { + 'numberOfCores': data.numberOfCores, + 'computerName': data.computerName, + 'systemMemoryInMegabytes': data.systemMemoryInMegabytes, + 'userName': data.userName, + 'majorVersion': data.majorVersion, + 'minorVersion': data.minorVersion, + 'buildNumber': data.buildNumber, + 'platformId': data.platformId, + 'csdVersion': data.csdVersion, + 'servicePackMajor': data.servicePackMajor, + 'servicePackMinor': data.servicePackMinor, + 'suitMask': data.suitMask, + 'productType': data.productType, + 'reserved': data.reserved, + 'buildLab': data.buildLab, + 'buildLabEx': data.buildLabEx, + 'digitalProductId': data.digitalProductId, + 'displayVersion': data.displayVersion, + 'editionId': data.editionId, + 'installDate': data.installDate, + 'productId': data.productId, + 'productName': data.productName, + 'registeredOwner': data.registeredOwner, + 'releaseId': data.releaseId, + 'deviceId': data.deviceId, + }; + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + theme: ThemeData( + useMaterial3: true, + colorSchemeSeed: const Color(0x9f4376f8), + ), + home: Scaffold( + appBar: AppBar(title: Text(_getAppBarTitle()), elevation: 4), + body: ListView( + children: + _deviceData.keys.map((String property) { + return Row( + children: [ + Container( + padding: const EdgeInsets.all(10), + child: Text( + property, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ), + Expanded( + child: Container( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Text( + '${_deviceData[property]}', + maxLines: 10, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ], + ); + }).toList(), + ), + ), + ); + } + + String _getAppBarTitle() => + kIsWeb + ? 'Web Browser info' + : switch (defaultTargetPlatform) { + TargetPlatform.android => 'Android Device Info', + TargetPlatform.iOS => 'iOS Device Info', + TargetPlatform.linux => 'Linux Device Info', + TargetPlatform.windows => 'Windows Device Info', + TargetPlatform.macOS => 'MacOS Device Info', + TargetPlatform.fuchsia => 'Fuchsia Device Info', + }; +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/CMakeLists.txt new file mode 100644 index 00000000..fe30a3e6 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/CMakeLists.txt @@ -0,0 +1,106 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "example") +set(APPLICATION_ID "io.flutter.plugins.deviceinfoexample.example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/flutter/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/flutter/CMakeLists.txt new file mode 100644 index 00000000..5b465c7e --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,89 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) +pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO + PkgConfig::BLKID +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/main.cc b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/main.cc new file mode 100644 index 00000000..8bc15615 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/main.cc @@ -0,0 +1,11 @@ +#include "my_application.h" + +int main(int argc, char **argv) { + // Only X11 is currently supported. + // Wayland support is being developed: + // https://github.com/flutter/flutter/issues/57932. + gdk_set_allowed_backends("x11"); + + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/my_application.cc b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/my_application.cc new file mode 100644 index 00000000..cef2f771 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/my_application.cc @@ -0,0 +1,45 @@ +#include "my_application.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication *application) { + GtkWindow *window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + + FlView *view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +static void my_application_class_init(MyApplicationClass *klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; +} + +static void my_application_init(MyApplication *self) {} + +MyApplication *my_application_new() { + return MY_APPLICATION(g_object_new( + my_application_get_type(), "application-id", APPLICATION_ID, nullptr)); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/my_application.h b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/my_application.h new file mode 100644 index 00000000..3258a73c --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication *my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Flutter/Flutter-Debug.xcconfig b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 00000000..4b81f9b2 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Flutter/Flutter-Release.xcconfig b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 00000000..5caa9d15 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcodeproj/project.pbxproj b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..17738f85 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,791 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 1330411FFFFCBD68C52506B9 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F374CDA27EFCEFF13CF00643 /* Pods_Runner.framework */; }; + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + B32C194861AFDC5B38D41D89 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F061CD8A37C585748E94D972 /* Pods_RunnerTests.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 279B31026D7637D54DF99D75 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* example_device_info.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example_device_info.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 7D5FF3AEF35298EDF0C1201D /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + D768A5BD5BCBEE43226A64A7 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + D94DF7FF96C592E0D5C3E009 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + E07B0AE1E178661F8653244B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + E92B0E6DF286F1A21B388CA1 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + F061CD8A37C585748E94D972 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F374CDA27EFCEFF13CF00643 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B32C194861AFDC5B38D41D89 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1330411FFFFCBD68C52506B9 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + C28D557964D09D46EC4A0164 /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* example_device_info.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + C28D557964D09D46EC4A0164 /* Pods */ = { + isa = PBXGroup; + children = ( + E07B0AE1E178661F8653244B /* Pods-Runner.debug.xcconfig */, + 7D5FF3AEF35298EDF0C1201D /* Pods-Runner.release.xcconfig */, + D768A5BD5BCBEE43226A64A7 /* Pods-Runner.profile.xcconfig */, + D94DF7FF96C592E0D5C3E009 /* Pods-RunnerTests.debug.xcconfig */, + E92B0E6DF286F1A21B388CA1 /* Pods-RunnerTests.release.xcconfig */, + 279B31026D7637D54DF99D75 /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + F374CDA27EFCEFF13CF00643 /* Pods_Runner.framework */, + F061CD8A37C585748E94D972 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 55412E34F2958028872E9D98 /* [CP] Check Pods Manifest.lock */, + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 88B27E32653C92A6BA8EB23C /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + 5BEC5C74096CA740BAE5D11D /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* example_device_info.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; + 55412E34F2958028872E9D98 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 5BEC5C74096CA740BAE5D11D /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 88B27E32653C92A6BA8EB23C /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D94DF7FF96C592E0D5C3E009 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.exampleDeviceInfo.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example_device_info.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example_device_info"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E92B0E6DF286F1A21B388CA1 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.exampleDeviceInfo.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example_device_info.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example_device_info"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 279B31026D7637D54DF99D75 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.exampleDeviceInfo.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example_device_info.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example_device_info"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..7bb1c7df --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..21a3cc14 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/AppDelegate.swift b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/AppDelegate.swift new file mode 100644 index 00000000..8e02df28 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..a2ec33f1 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..82b6f9d9a33e198f5747104729e1fcef999772a5 GIT binary patch literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? 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 z 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 Pe7HEdAEg|=c=LY&YVNkY literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..13b35eba55c6dabc3aac36f33d859266c18fa0d0 GIT binary patch literal 5680 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 literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3f5fa40fb3d1e0710331a48de5d256da3f275d GIT binary patch literal 520 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 literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000000000000000000000000000000000000..2f1632cfddf3d9dade342351e627a0a75609fb46 GIT binary patch literal 2218 zcmV;b2vzrqP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuE6iGxuRCodHTWf3-RTMruyW6Fu zQYeUM04eX6D5c0FCjKKPrco1(K`<0SL=crI{PC3-^hZU0kQie$gh-5!7z6SH6Q0J% zqot*`H1q{R5fHFYS}dje@;kG=v$L0(yY0?wY2%*c?A&{2?!D*x?m71{of2gv!$5|C z3>qG_BW}7K_yUcT3A5C6QD<+{aq?x;MAUyAiJn#Jv8_zZtQ{P zTRzbL3U9!qVuZzS$xKU10KiW~Bgdcv1-!uAhQxf3a7q+dU6lj?yoO4Lq4TUN4}h{N z*fIM=SS8|C2$(T>w$`t@3Tka!(r!7W`x z-isCVgQD^mG-MJ;XtJuK3V{Vy72GQ83KRWsHU?e*wrhKk=ApIYeDqLi;JI1e zuvv}5^Dc=k7F7?nm3nIw$NVmU-+R>> zyqOR$-2SDpJ}Pt;^RkJytDVXNTsu|mI1`~G7yw`EJR?VkGfNdqK9^^8P`JdtTV&tX4CNcV4 z&N06nZa??Fw1AgQOUSE2AmPE@WO(Fvo`%m`cDgiv(fAeRA%3AGXUbsGw{7Q`cY;1BI#ac3iN$$Hw z0LT0;xc%=q)me?Y*$xI@GRAw?+}>=9D+KTk??-HJ4=A>`V&vKFS75@MKdSF1JTq{S zc1!^8?YA|t+uKigaq!sT;Z!&0F2=k7F0PIU;F$leJLaw2UI6FL^w}OG&!;+b%ya1c z1n+6-inU<0VM-Y_s5iTElq)ThyF?StVcebpGI znw#+zLx2@ah{$_2jn+@}(zJZ{+}_N9BM;z)0yr|gF-4=Iyu@hI*Lk=-A8f#bAzc9f z`Kd6K--x@t04swJVC3JK1cHY-Hq+=|PN-VO;?^_C#;coU6TDP7Bt`;{JTG;!+jj(` zw5cLQ-(Cz-Tlb`A^w7|R56Ce;Wmr0)$KWOUZ6ai0PhzPeHwdl0H(etP zUV`va_i0s-4#DkNM8lUlqI7>YQLf)(lz9Q3Uw`)nc(z3{m5ZE77Ul$V%m)E}3&8L0 z-XaU|eB~Is08eORPk;=<>!1w)Kf}FOVS2l&9~A+@R#koFJ$Czd%Y(ENTV&A~U(IPI z;UY+gf+&6ioZ=roly<0Yst8ck>(M=S?B-ys3mLdM&)ex!hbt+ol|T6CTS+Sc0jv(& z7ijdvFwBq;0a{%3GGwkDKTeG`b+lyj0jjS1OMkYnepCdoosNY`*zmBIo*981BU%%U z@~$z0V`OVtIbEx5pa|Tct|Lg#ZQf5OYMUMRD>Wdxm5SAqV2}3!ceE-M2 z@O~lQ0OiKQp}o9I;?uxCgYVV?FH|?Riri*U$Zi_`V2eiA>l zdSm6;SEm6#T+SpcE8Ro_f2AwxzI z44hfe^WE3!h@W3RDyA_H440cpmYkv*)6m1XazTqw%=E5Xv7^@^^T7Q2wxr+Z2kVYr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Configs/AppInfo.xcconfig b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 00000000..af912343 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = device_info_plus_example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.deviceinfoexample.example + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2023 io.flutter.plugins.deviceinfoexample. All rights reserved. diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Configs/Debug.xcconfig b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 00000000..36b0fd94 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Configs/Release.xcconfig b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 00000000..dff4f495 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Configs/Warnings.xcconfig b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 00000000..42bcbf47 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/DebugProfile.entitlements b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 00000000..dddb8a30 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Info.plist b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Info.plist new file mode 100644 index 00000000..4789daa6 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/MainFlutterWindow.swift b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 00000000..3cc05eb2 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Release.entitlements b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Release.entitlements new file mode 100644 index 00000000..852fa1a4 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/RunnerTests/RunnerTests.swift b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..5418c9f5 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import FlutterMacOS +import Cocoa +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/pubspec.yaml b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/pubspec.yaml new file mode 100644 index 00000000..a3d73752 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/pubspec.yaml @@ -0,0 +1,25 @@ +name: device_info_plus_example +description: Demonstrates how to use the device_info_plus plugin. + +dependencies: + flutter: + sdk: flutter + device_info_plus: ^12.3.0 + +dev_dependencies: + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + flutter_lints: ">=4.0.0 <6.0.0" + +flutter: + uses-material-design: true + +environment: + sdk: '>=3.7.0 <4.0.0' + flutter: '>=3.29.0' + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/web/favicon.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/web/icons/Icon-192.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/web/icons/Icon-512.png b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/web/index.html b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/web/index.html new file mode 100644 index 00000000..99eacb28 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/web/index.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + example + + + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/web/manifest.json b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/web/manifest.json new file mode 100644 index 00000000..8c012917 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/web/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "example", + "short_name": "example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/CMakeLists.txt new file mode 100644 index 00000000..abf90408 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.15) +project(example LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/flutter/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..b02c5485 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.15) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..977e38b5 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.15) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "run_loop.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/Runner.rc b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/Runner.rc new file mode 100644 index 00000000..13f98c0a --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "io.flutter.plugins.deviceinfoexample" "\0" + VALUE "FileDescription", "A new Flutter project." "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2021 io.flutter.plugins.deviceinfoexample. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/flutter_window.cpp b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..0f0105d9 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/flutter_window.cpp @@ -0,0 +1,64 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(RunLoop *run_loop, + const flutter::DartProject &project) + : run_loop_(run_loop), project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + run_loop_->RegisterFlutterInstance(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + run_loop_->UnregisterFlutterInstance(flutter_controller_->engine()); + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opporutunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/flutter_window.h b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/flutter_window.h new file mode 100644 index 00000000..69234d4f --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/flutter_window.h @@ -0,0 +1,39 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "run_loop.h" +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { +public: + // Creates a new FlutterWindow driven by the |run_loop|, hosting a + // Flutter view running |project|. + explicit FlutterWindow(RunLoop *run_loop, + const flutter::DartProject &project); + virtual ~FlutterWindow(); + +protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + +private: + // The run loop driving events for this window. + RunLoop *run_loop_; + + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/main.cpp b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/main.cpp new file mode 100644 index 00000000..09895686 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/main.cpp @@ -0,0 +1,41 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "run_loop.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + RunLoop run_loop; + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(&run_loop, project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + run_loop.Run(); + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/resource.h b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/resource.h new file mode 100644 index 00000000..d5d958dc --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/resources/app_icon.ico b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/run_loop.cpp b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/run_loop.cpp new file mode 100644 index 00000000..31b89f62 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/run_loop.cpp @@ -0,0 +1,66 @@ +#include "run_loop.h" + +#include + +#include + +RunLoop::RunLoop() {} + +RunLoop::~RunLoop() {} + +void RunLoop::Run() { + bool keep_running = true; + TimePoint next_flutter_event_time = TimePoint::clock::now(); + while (keep_running) { + std::chrono::nanoseconds wait_duration = + std::max(std::chrono::nanoseconds(0), + next_flutter_event_time - TimePoint::clock::now()); + ::MsgWaitForMultipleObjects( + 0, nullptr, FALSE, static_cast(wait_duration.count() / 1000), + QS_ALLINPUT); + bool processed_events = false; + MSG message; + // All pending Windows messages must be processed; MsgWaitForMultipleObjects + // won't return again for items left in the queue after PeekMessage. + while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) { + processed_events = true; + if (message.message == WM_QUIT) { + keep_running = false; + break; + } + ::TranslateMessage(&message); + ::DispatchMessage(&message); + // Allow Flutter to process messages each time a Windows message is + // processed, to prevent starvation. + next_flutter_event_time = + std::min(next_flutter_event_time, ProcessFlutterMessages()); + } + // If the PeekMessage loop didn't run, process Flutter messages. + if (!processed_events) { + next_flutter_event_time = + std::min(next_flutter_event_time, ProcessFlutterMessages()); + } + } +} + +void RunLoop::RegisterFlutterInstance( + flutter::FlutterEngine *flutter_instance) { + flutter_instances_.insert(flutter_instance); +} + +void RunLoop::UnregisterFlutterInstance( + flutter::FlutterEngine *flutter_instance) { + flutter_instances_.erase(flutter_instance); +} + +RunLoop::TimePoint RunLoop::ProcessFlutterMessages() { + TimePoint next_event_time = TimePoint::max(); + for (auto instance : flutter_instances_) { + std::chrono::nanoseconds wait_duration = instance->ProcessMessages(); + if (wait_duration != std::chrono::nanoseconds::max()) { + next_event_time = + std::min(next_event_time, TimePoint::clock::now() + wait_duration); + } + } + return next_event_time; +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/run_loop.h b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/run_loop.h new file mode 100644 index 00000000..7fe1aacc --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/run_loop.h @@ -0,0 +1,38 @@ +#ifndef RUNNER_RUN_LOOP_H_ +#define RUNNER_RUN_LOOP_H_ + +#include + +#include +#include + +// A runloop that will service events for Flutter instances as well +// as native messages. +class RunLoop { +public: + RunLoop(); + ~RunLoop(); + + // Prevent copying + RunLoop(RunLoop const &) = delete; + RunLoop &operator=(RunLoop const &) = delete; + + // Runs the run loop until the application quits. + void Run(); + + // Registers the given Flutter instance for event servicing. + void RegisterFlutterInstance(flutter::FlutterEngine *flutter_instance); + + // Unregisters the given Flutter instance from event servicing. + void UnregisterFlutterInstance(flutter::FlutterEngine *flutter_instance); + +private: + using TimePoint = std::chrono::steady_clock::time_point; + + // Processes all currently pending messages for registered Flutter instances. + TimePoint ProcessFlutterMessages(); + + std::set flutter_instances_; +}; + +#endif // RUNNER_RUN_LOOP_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/runner.exe.manifest b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..c977c4a4 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/utils.cpp b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/utils.cpp new file mode 100644 index 00000000..7758aabf --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/utils.cpp @@ -0,0 +1,63 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t **argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t *utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = + ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, + nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/utils.h b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/utils.h new file mode 100644 index 00000000..ff43ce2c --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t *utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/win32_window.cpp b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/win32_window.cpp new file mode 100644 index 00000000..90ff01e5 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/win32_window.cpp @@ -0,0 +1,237 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { +public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar *GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t *GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + +private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar *instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar *WindowClassRegistrar::instance_ = nullptr; + +const wchar_t *WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { ++g_active_window_count; } + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring &title, const Point &origin, + const Size &size) { + Destroy(); + + const wchar_t *window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window *that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window *Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { return window_handle_; } + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/win32_window.h b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/win32_window.h new file mode 100644 index 00000000..7b518125 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/example/windows/runner/win32_window.h @@ -0,0 +1,95 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { +public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring &title, const Point &origin, + const Size &size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + +protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + +private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window *GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus.podspec b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus.podspec new file mode 100644 index 00000000..6038dd29 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus.podspec @@ -0,0 +1,24 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html +# +Pod::Spec.new do |s| + s.name = 'device_info_plus' + s.version = '0.0.1' + s.summary = 'Flutter Device Info Plus' + s.description = <<-DESC +Get current device information from within the Flutter application. +Downloaded by pub (not CocoaPods). + DESC + s.homepage = 'https://github.com/fluttercommunity/plus_plugins' + s.license = { :type => 'BSD', :file => '../LICENSE' } + s.author = { 'Flutter Community Team' => 'authors@fluttercommunity.dev' } + s.source = { :http => 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/device_info_plus' } + s.documentation_url = 'https://pub.dev/packages/device_info_plus' + s.source_files = 'device_info_plus/Sources/device_info_plus/**/*.{h,m}' + s.public_header_files = 'device_info_plus/Sources/device_info_plus/include/**/*.h' + s.dependency 'Flutter' + s.platform = :ios, '12.0' + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } + s.resource_bundles = {'device_info_plus_privacy' => ['device_info_plus/Sources/device_info_plus/PrivacyInfo.xcprivacy']} +end + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Package.swift b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Package.swift new file mode 100644 index 00000000..a4defdd7 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Package.swift @@ -0,0 +1,27 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "device_info_plus", + platforms: [ + .iOS("12.0"), + ], + products: [ + .library(name: "device-info-plus", targets: ["device_info_plus"]) + ], + dependencies: [], + targets: [ + .target( + name: "device_info_plus", + dependencies: [], + resources: [ + .process("PrivacyInfo.xcprivacy"), + ], + cSettings: [ + .headerSearchPath("include/device_info_plus") + ] + ) + ] +) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/DeviceIdentifiers.m b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/DeviceIdentifiers.m new file mode 100644 index 00000000..fe877557 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/DeviceIdentifiers.m @@ -0,0 +1,230 @@ +// +// DeviceIdentifiers.m +// device_info_plus +// +// Created by Volodymyr on 06.11.2024. +// +#import "./include/device_info_plus/DeviceIdentifiers.h" + +@implementation DeviceIdentifiers + ++ (NSString *)userKnownDeviceModel:(NSString *)identifier { + if ([identifier isEqualToString:@"iPhone6,1"]) { + return @"iPhone 5s"; + } else if ([identifier isEqualToString:@"iPhone6,2"]) { + return @"iPhone 5s"; + } else if ([identifier isEqualToString:@"iPhone7,2"]) { + return @"iPhone 6"; + } else if ([identifier isEqualToString:@"iPhone7,1"]) { + return @"iPhone 6 Plus"; + } else if ([identifier isEqualToString:@"iPhone8,1"]) { + return @"iPhone 6s"; + } else if ([identifier isEqualToString:@"iPhone8,2"]) { + return @"iPhone 6s Plus"; + } else if ([identifier isEqualToString:@"iPhone9,1"] || + [identifier isEqualToString:@"iPhone9,3"]) { + return @"iPhone 7"; + } else if ([identifier isEqualToString:@"iPhone9,2"] || + [identifier isEqualToString:@"iPhone9,4"]) { + return @"iPhone 7 Plus"; + } else if ([identifier isEqualToString:@"iPhone8,4"]) { + return @"iPhone SE"; + } else if ([identifier isEqualToString:@"iPhone10,1"] || + [identifier isEqualToString:@"iPhone10,4"]) { + return @"iPhone 8"; + } else if ([identifier isEqualToString:@"iPhone10,2"] || + [identifier isEqualToString:@"iPhone10,5"]) { + return @"iPhone 8 Plus"; + } else if ([identifier isEqualToString:@"iPhone10,3"] || + [identifier isEqualToString:@"iPhone10,6"]) { + return @"iPhone X"; + } else if ([identifier isEqualToString:@"iPhone11,2"]) { + return @"iPhone XS"; + } else if ([identifier isEqualToString:@"iPhone11,4"] || + [identifier isEqualToString:@"iPhone11,6"]) { + return @"iPhone XS Max"; + } else if ([identifier isEqualToString:@"iPhone11,8"]) { + return @"iPhone XR"; + } else if ([identifier isEqualToString:@"iPhone12,1"]) { + return @"iPhone 11"; + } else if ([identifier isEqualToString:@"iPhone12,3"]) { + return @"iPhone 11 Pro"; + } else if ([identifier isEqualToString:@"iPhone12,5"]) { + return @"iPhone 11 Pro Max"; + } else if ([identifier isEqualToString:@"iPhone12,8"]) { + return @"iPhone SE 2"; + } else if ([identifier isEqualToString:@"iPhone13,2"]) { + return @"iPhone 12"; + } else if ([identifier isEqualToString:@"iPhone13,1"]) { + return @"iPhone 12 Mini"; + } else if ([identifier isEqualToString:@"iPhone13,3"]) { + return @"iPhone 12 Pro"; + } else if ([identifier isEqualToString:@"iPhone13,4"]) { + return @"iPhone 12 Pro Max"; + } else if ([identifier isEqualToString:@"iPhone14,5"]) { + return @"iPhone 13"; + } else if ([identifier isEqualToString:@"iPhone14,4"]) { + return @"iPhone 13 Mini"; + } else if ([identifier isEqualToString:@"iPhone14,2"]) { + return @"iPhone 13 Pro"; + } else if ([identifier isEqualToString:@"iPhone14,3"]) { + return @"iPhone 13 Pro Max"; + } else if ([identifier isEqualToString:@"iPhone14,6"]) { + return @"iPhone SE 3"; + } else if ([identifier isEqualToString:@"iPhone14,7"]) { + return @"iPhone 14"; + } else if ([identifier isEqualToString:@"iPhone14,8"]) { + return @"iPhone 14 Plus"; + } else if ([identifier isEqualToString:@"iPhone15,2"]) { + return @"iPhone 14 Pro"; + } else if ([identifier isEqualToString:@"iPhone15,3"]) { + return @"iPhone 14 Pro Max"; + } else if ([identifier isEqualToString:@"iPhone15,4"]) { + return @"iPhone 15"; + } else if ([identifier isEqualToString:@"iPhone15,5"]) { + return @"iPhone 15 Plus"; + } else if ([identifier isEqualToString:@"iPhone16,1"]) { + return @"iPhone 15 Pro"; + } else if ([identifier isEqualToString:@"iPhone16,2"]) { + return @"iPhone 15 Pro Max"; + } else if ([identifier isEqualToString:@"iPhone17,3"]) { + return @"iPhone 16"; + } else if ([identifier isEqualToString:@"iPhone17,4"]) { + return @"iPhone 16 Plus"; + } else if ([identifier isEqualToString:@"iPhone17,1"]) { + return @"iPhone 16 Pro"; + } else if ([identifier isEqualToString:@"iPhone17,2"]) { + return @"iPhone 16 Pro Max"; + } else if ([identifier isEqualToString:@"iPhone17,5"]) { + return @"iPhone 16e"; + } else if ([identifier isEqualToString:@"iPhone18,3"]) { + return @"iPhone 17"; + } else if ([identifier isEqualToString:@"iPhone18,1"]) { + return @"iPhone 17 Pro"; + } else if ([identifier isEqualToString:@"iPhone18,2"]) { + return @"iPhone 17 Pro Max"; + } else if ([identifier isEqualToString:@"iPhone18,4"]) { + return @"iPhone Air"; + // iPads + } else if ([identifier isEqualToString:@"iPad4,1"] || + [identifier isEqualToString:@"iPad4,2"] || + [identifier isEqualToString:@"iPad4,3"]) { + return @"iPad Air"; + } else if ([identifier isEqualToString:@"iPad5,3"] || + [identifier isEqualToString:@"iPad5,4"]) { + return @"iPad Air 2"; + } else if ([identifier isEqualToString:@"iPad6,11"] || + [identifier isEqualToString:@"iPad6,12"]) { + return @"iPad 5"; + } else if ([identifier isEqualToString:@"iPad7,5"] || + [identifier isEqualToString:@"iPad7,6"]) { + return @"iPad 6"; + } else if ([identifier isEqualToString:@"iPad11,3"] || + [identifier isEqualToString:@"iPad11,4"]) { + return @"iPad Air 3"; + } else if ([identifier isEqualToString:@"iPad7,11"] || + [identifier isEqualToString:@"iPad7,12"]) { + return @"iPad 7"; + } else if ([identifier isEqualToString:@"iPad11,6"] || + [identifier isEqualToString:@"iPad11,7"]) { + return @"iPad 8"; + } else if ([identifier isEqualToString:@"iPad12,1"] || + [identifier isEqualToString:@"iPad12,2"]) { + return @"iPad 9"; + } else if ([identifier isEqualToString:@"iPad13,18"] || + [identifier isEqualToString:@"iPad13,19"]) { + return @"iPad 10"; + } else if ([identifier isEqualToString:@"iPad13,1"] || + [identifier isEqualToString:@"iPad13,2"]) { + return @"iPad Air 4"; + } else if ([identifier isEqualToString:@"iPad13,16"] || + [identifier isEqualToString:@"iPad13,17"]) { + return @"iPad Air 5"; + } else if ([identifier isEqualToString:@"iPad14,8"] || + [identifier isEqualToString:@"iPad14,9"]) { + return @"iPad Air 11-Inch M2"; + } else if ([identifier isEqualToString:@"iPad14,10"] || + [identifier isEqualToString:@"iPad14,11"]) { + return @"iPad Air 13-Inch M2"; + } else if ([identifier isEqualToString:@"iPad2,5"] || + [identifier isEqualToString:@"iPad2,6"] || + [identifier isEqualToString:@"iPad2,7"]) { + return @"iPad Mini"; + } else if ([identifier isEqualToString:@"iPad4,4"] || + [identifier isEqualToString:@"iPad4,5"] || + [identifier isEqualToString:@"iPad4,6"]) { + return @"iPad Mini 2"; + } else if ([identifier isEqualToString:@"iPad4,7"] || + [identifier isEqualToString:@"iPad4,8"] || + [identifier isEqualToString:@"iPad4,9"]) { + return @"iPad Mini 3"; + } else if ([identifier isEqualToString:@"iPad5,1"] || + [identifier isEqualToString:@"iPad5,2"]) { + return @"iPad Mini 4"; + } else if ([identifier isEqualToString:@"iPad11,1"] || + [identifier isEqualToString:@"iPad11,2"]) { + return @"iPad Mini 5"; + } else if ([identifier isEqualToString:@"iPad14,1"] || + [identifier isEqualToString:@"iPad14,2"]) { + return @"iPad Mini 6"; + } else if ([identifier isEqualToString:@"iPad6,3"] || + [identifier isEqualToString:@"iPad6,4"]) { + return @"iPad Pro 9-Inch"; + } else if ([identifier isEqualToString:@"iPad6,7"] || + [identifier isEqualToString:@"iPad6,8"]) { + return @"iPad Pro 12-Inch"; + } else if ([identifier isEqualToString:@"iPad7,1"] || + [identifier isEqualToString:@"iPad7,2"]) { + return @"iPad Pro 12-Inch 2"; + } else if ([identifier isEqualToString:@"iPad7,3"] || + [identifier isEqualToString:@"iPad7,4"]) { + return @"iPad Pro 10-Inch"; + } else if ([identifier isEqualToString:@"iPad8,1"] || + [identifier isEqualToString:@"iPad8,2"] || + [identifier isEqualToString:@"iPad8,3"] || + [identifier isEqualToString:@"iPad8,4"]) { + return @"iPad Pro 11-Inch"; + } else if ([identifier isEqualToString:@"iPad8,5"] || + [identifier isEqualToString:@"iPad8,6"] || + [identifier isEqualToString:@"iPad8,7"] || + [identifier isEqualToString:@"iPad8,8"]) { + return @"iPad Pro 12-Inch 3"; + } else if ([identifier isEqualToString:@"iPad8,9"] || + [identifier isEqualToString:@"iPad8,10"]) { + return @"iPad Pro 11-Inch 2"; + } else if ([identifier isEqualToString:@"iPad8,11"] || + [identifier isEqualToString:@"iPad8,12"]) { + return @"iPad Pro 12-Inch 4"; + } else if ([identifier isEqualToString:@"iPad13,4"] || + [identifier isEqualToString:@"iPad13,5"] || + [identifier isEqualToString:@"iPad13,6"] || + [identifier isEqualToString:@"iPad13,7"]) { + return @"iPad Pro 11-Inch 3"; + } else if ([identifier isEqualToString:@"iPad13,8"] || + [identifier isEqualToString:@"iPad13,9"] || + [identifier isEqualToString:@"iPad13,10"] || + [identifier isEqualToString:@"iPad13,11"]) { + return @"iPad Pro 12-Inch 5"; + } else if ([identifier isEqualToString:@"iPad14,3"] || + [identifier isEqualToString:@"iPad14,4"]) { + return @"iPad Pro 11-Inch 4"; + } else if ([identifier isEqualToString:@"iPad14,5"] || + [identifier isEqualToString:@"iPad14,6"]) { + return @"iPad Pro 12-Inch 6"; + } else if ([identifier isEqualToString:@"iPad16,3"] || + [identifier isEqualToString:@"iPad16,4"]) { + return @"iPad Pro 11-Inch (M4)"; + } else if ([identifier isEqualToString:@"iPad16,5"] || + [identifier isEqualToString:@"iPad16,6"]) { + return @"iPad Pro 13-Inch (M4)"; + } else if ([identifier isEqualToString:@"iPad17,1"] || + [identifier isEqualToString:@"iPad17,2"]) { + return @"iPad Pro 11-Inch (M5)"; + } else if ([identifier isEqualToString:@"iPad17,3"] || + [identifier isEqualToString:@"iPad17,4"]) { + return @"iPad Pro 13-Inch (M5)"; + } else { + return @"Unknown device"; + } +} +@end diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/FPPDeviceInfoPlusPlugin.m b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/FPPDeviceInfoPlusPlugin.m new file mode 100644 index 00000000..9e3ee569 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/FPPDeviceInfoPlusPlugin.m @@ -0,0 +1,112 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "./include/device_info_plus/FPPDeviceInfoPlusPlugin.h" +#import "./include/device_info_plus/DeviceIdentifiers.h" +#import +#import + +@implementation FPPDeviceInfoPlusPlugin ++ (void)registerWithRegistrar:(NSObject *)registrar { + FlutterMethodChannel *channel = [FlutterMethodChannel + methodChannelWithName:@"dev.fluttercommunity.plus/device_info" + binaryMessenger:[registrar messenger]]; + FPPDeviceInfoPlusPlugin *instance = [[FPPDeviceInfoPlusPlugin alloc] init]; + [registrar addMethodCallDelegate:instance channel:channel]; +} + +- (void)handleMethodCall:(FlutterMethodCall *)call + result:(FlutterResult)result { + if ([@"getDeviceInfo" isEqualToString:call.method]) { + UIDevice *device = [UIDevice currentDevice]; + struct utsname un; + uname(&un); + + NSNumber *isPhysicalNumber = + [NSNumber numberWithBool:[self isDevicePhysical]]; + NSProcessInfo *info = [NSProcessInfo processInfo]; + NSNumber *isiOSAppOnMac = [NSNumber numberWithBool:NO]; + if (@available(iOS 14.0, *)) { + isiOSAppOnMac = [NSNumber numberWithBool:[info isiOSAppOnMac]]; + } + NSError *error = nil; + NSDictionary *fsAttributes = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error]; + NSNumber *freeSize = [NSNumber numberWithInt:-1]; + NSNumber *totalSize = [NSNumber numberWithInt:-1]; + if(fsAttributes) { + freeSize = fsAttributes[NSFileSystemFreeSize]; + totalSize = fsAttributes[NSFileSystemSize]; + } + + NSString *machine; + NSString *deviceName; + if ([self isDevicePhysical]) { + machine = @(un.machine); + } else { + machine = [info environment][@"SIMULATOR_MODEL_IDENTIFIER"]; + } + deviceName = [DeviceIdentifiers userKnownDeviceModel:machine]; + + NSNumber *physicalRamSize = @([NSProcessInfo processInfo].physicalMemory / 1048576); // Mb + NSNumber *availableRamSize = @([self availableMemoryInbMB]); + + result(@{ + @"name" : [device name], + @"systemName" : [device systemName], + @"systemVersion" : [device systemVersion], + @"model" : [device model], + @"localizedModel" : [device localizedModel], + @"modelName" : deviceName, + @"identifierForVendor" : [[device identifierForVendor] UUIDString] + ?: [NSNull null], + @"freeDiskSize" : freeSize, + @"totalDiskSize" : totalSize, + @"isPhysicalDevice" : isPhysicalNumber, + @"isiOSAppOnMac" : isiOSAppOnMac, + @"physicalRamSize" : physicalRamSize, + @"availableRamSize" : availableRamSize, + @"utsname" : @{ + @"sysname" : @(un.sysname), + @"nodename" : @(un.nodename), + @"release" : @(un.release), + @"version" : @(un.version), + @"machine" : machine, + } + }); + } else { + result(FlutterMethodNotImplemented); + } +} + +// Return available memory in megabytes +- (int)availableMemoryInbMB { + mach_port_t host_port = mach_host_self(); + mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t); + + vm_size_t page_size; + host_page_size(host_port, &page_size); + + vm_statistics_data_t vm_stat; + if (host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size) != KERN_SUCCESS) { + // Failed to fetch vm statistics + return -1; + } + + natural_t mem_free = vm_stat.free_count * page_size; + return mem_free / 1048576; +} + +// Return value is false if code is run on a simulator +- (BOOL)isDevicePhysical { + BOOL isPhysicalDevice = NO; +#if TARGET_OS_SIMULATOR + isPhysicalDevice = NO; +#else + isPhysicalDevice = YES; +#endif + + return isPhysicalDevice; +} + +@end diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/PrivacyInfo.xcprivacy b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..a34b7e2e --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/PrivacyInfo.xcprivacy @@ -0,0 +1,14 @@ + + + + + NSPrivacyTrackingDomains + + NSPrivacyAccessedAPITypes + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/include/device_info_plus/DeviceIdentifiers.h b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/include/device_info_plus/DeviceIdentifiers.h new file mode 100644 index 00000000..0f5c8d70 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/include/device_info_plus/DeviceIdentifiers.h @@ -0,0 +1,13 @@ +// +// DeviceIdentifiers.h +// device_info_plus +// +// Created by Volodymyr on 06.11.2024. +// +#import + +@interface DeviceIdentifiers : NSObject + ++ (NSString *)userKnownDeviceModel:(NSString *)identifier; + +@end diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/include/device_info_plus/FPPDeviceInfoPlusPlugin.h b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/include/device_info_plus/FPPDeviceInfoPlusPlugin.h new file mode 100644 index 00000000..430e81ce --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/ios/device_info_plus/Sources/device_info_plus/include/device_info_plus/FPPDeviceInfoPlusPlugin.h @@ -0,0 +1,8 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +@interface FPPDeviceInfoPlusPlugin : NSObject +@end diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/device_info_plus.dart b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/device_info_plus.dart new file mode 100644 index 00000000..f5acdb5c --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/device_info_plus.dart @@ -0,0 +1,141 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:io'; + +import 'package:device_info_plus_platform_interface/device_info_plus_platform_interface.dart'; +import 'package:flutter/foundation.dart'; + +import 'src/model/android_device_info.dart'; +import 'src/model/ios_device_info.dart'; +import 'src/model/linux_device_info.dart'; +import 'src/model/macos_device_info.dart'; +import 'src/model/web_browser_info.dart'; +import 'src/model/windows_device_info.dart'; + +export 'package:device_info_plus_platform_interface/device_info_plus_platform_interface.dart' + show BaseDeviceInfo; + +export 'src/model/android_device_info.dart'; +export 'src/model/ios_device_info.dart'; +export 'src/model/linux_device_info.dart'; +export 'src/model/macos_device_info.dart'; +export 'src/model/web_browser_info.dart'; +export 'src/model/windows_device_info.dart'; + +export 'src/device_info_plus_linux.dart' + if (dart.library.js_interop) 'src/device_info_plus_web.dart'; +export 'src/device_info_plus_windows.dart' + if (dart.library.js_interop) 'src/device_info_plus_web.dart'; + +/// Provides device and operating system information. +class DeviceInfoPlugin { + /// No work is done when instantiating the plugin. It's safe to call this + /// repeatedly or in performance-sensitive blocks. + DeviceInfoPlugin(); + + // This is to manually endorse the Linux plugin until automatic registration + // of dart plugins is implemented. + // See https://github.com/flutter/flutter/issues/52267 for more details. + static DeviceInfoPlatform get _platform { + return DeviceInfoPlatform.instance; + } + + /// This information does not change from call to call. Cache it. + AndroidDeviceInfo? _cachedAndroidDeviceInfo; + + /// Information derived from `android.os.Build`. + /// + /// See: https://developer.android.com/reference/android/os/Build.html + Future get androidInfo async => + _cachedAndroidDeviceInfo ??= AndroidDeviceInfo.fromMap( + (await _platform.deviceInfo()).data, + ); + + /// This information does not change from call to call. Cache it. + IosDeviceInfo? _cachedIosDeviceInfo; + + /// Information derived from `UIDevice`. + /// + /// See: https://developer.apple.com/documentation/uikit/uidevice + Future get iosInfo async => + _cachedIosDeviceInfo ??= IosDeviceInfo.fromMap( + (await _platform.deviceInfo()).data, + ); + + /// This information does not change from call to call. Cache it. + LinuxDeviceInfo? _cachedLinuxDeviceInfo; + + /// Information derived from `/etc/os-release`. + /// + /// See: https://www.freedesktop.org/software/systemd/man/os-release.html + Future get linuxInfo async => + _cachedLinuxDeviceInfo ??= + await _platform.deviceInfo() as LinuxDeviceInfo; + + /// This information does not change from call to call. Cache it. + WebBrowserInfo? _cachedWebBrowserInfo; + + /// Information derived from `Navigator`. + Future get webBrowserInfo async => + _cachedWebBrowserInfo ??= await _platform.deviceInfo() as WebBrowserInfo; + + /// This information does not change from call to call. Cache it. + MacOsDeviceInfo? _cachedMacosDeviceInfo; + + /// Returns device information for macos. Information sourced from Sysctl. + Future get macOsInfo async => + _cachedMacosDeviceInfo ??= MacOsDeviceInfo.fromMap( + (await _platform.deviceInfo()).data, + ); + + WindowsDeviceInfo? _cachedWindowsDeviceInfo; + + /// Returns device information for Windows. + Future get windowsInfo async => + _cachedWindowsDeviceInfo ??= + await _platform.deviceInfo() as WindowsDeviceInfo; + + /// Returns device information for the current platform. + Future get deviceInfo async { + if (kIsWeb) { + return webBrowserInfo; + } else { + if (Platform.isAndroid) { + return androidInfo; + } else if (Platform.isIOS) { + return iosInfo; + } else if (Platform.isLinux) { + return linuxInfo; + } else if (Platform.isMacOS) { + return macOsInfo; + } else if (Platform.isWindows) { + return windowsInfo; + } + } + // allow for extension of the plugin + return _platform.deviceInfo(); + } + + /// Initializes the application metadata with mock values for testing. + @visibleForTesting + static DeviceInfoPlugin setMockInitialValues({ + AndroidDeviceInfo? androidDeviceInfo, + IosDeviceInfo? iosDeviceInfo, + LinuxDeviceInfo? linuxDeviceInfo, + WebBrowserInfo? webBrowserInfo, + MacOsDeviceInfo? macOsDeviceInfo, + WindowsDeviceInfo? windowsDeviceInfo, + }) { + final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); + deviceInfoPlugin._cachedAndroidDeviceInfo = androidDeviceInfo; + deviceInfoPlugin._cachedIosDeviceInfo = iosDeviceInfo; + deviceInfoPlugin._cachedLinuxDeviceInfo = linuxDeviceInfo; + deviceInfoPlugin._cachedWebBrowserInfo = webBrowserInfo; + deviceInfoPlugin._cachedMacosDeviceInfo = macOsDeviceInfo; + deviceInfoPlugin._cachedWindowsDeviceInfo = windowsDeviceInfo; + return deviceInfoPlugin; + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/device_info_plus_linux.dart b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/device_info_plus_linux.dart new file mode 100644 index 00000000..bc4b42d3 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/device_info_plus_linux.dart @@ -0,0 +1,107 @@ +import 'dart:async'; + +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:device_info_plus_platform_interface/device_info_plus_platform_interface.dart'; +import 'package:file/file.dart'; +import 'package:file/local.dart'; +import 'package:meta/meta.dart'; + +/// See [DeviceInfoPlatform] +class DeviceInfoPlusLinuxPlugin extends DeviceInfoPlatform { + /// Register this dart class as the platform implementation for linux + static void registerWith() { + DeviceInfoPlatform.instance = DeviceInfoPlusLinuxPlugin(); + } + + LinuxDeviceInfo? _cache; + final FileSystem _fileSystem; + + /// + DeviceInfoPlusLinuxPlugin({@visibleForTesting FileSystem? fileSystem}) + : _fileSystem = fileSystem ?? const LocalFileSystem(); + + @override + Future deviceInfo() async { + return _cache ??= await _getInfo(); + } + + Future linuxInfo() async { + return (await deviceInfo()) as LinuxDeviceInfo; + } + + Future _getInfo() async { + final os = await _getOsRelease() ?? {}; + final lsb = await _getLsbRelease() ?? {}; + final machineId = await _getMachineId(); + + return LinuxDeviceInfo( + name: os['NAME'] ?? 'Linux', + version: os['VERSION'] ?? lsb['LSB_VERSION'], + id: os['ID'] ?? lsb['DISTRIB_ID'] ?? 'linux', + idLike: os['ID_LIKE']?.split(' '), + versionCodename: os['VERSION_CODENAME'] ?? lsb['DISTRIB_CODENAME'], + versionId: os['VERSION_ID'] ?? lsb['DISTRIB_RELEASE'], + prettyName: os['PRETTY_NAME'] ?? lsb['DISTRIB_DESCRIPTION'] ?? 'Linux', + buildId: os['BUILD_ID'], + variant: os['VARIANT'], + variantId: os['VARIANT_ID'], + machineId: machineId, + ); + } + + Future?> _getOsRelease() { + return _tryReadKeyValues('/etc/os-release').then( + (value) async => value ?? await _tryReadKeyValues('/usr/lib/os-release'), + ); + } + + Future?> _getLsbRelease() { + return _tryReadKeyValues('/etc/lsb-release'); + } + + Future _getMachineId() { + return _tryReadValue('/etc/machine-id'); + } + + Future _tryReadValue(String path) { + return _fileSystem + .file(path) + .readAsString() + .then((str) => str.trim(), onError: (_) => null); + } + + Future?> _tryReadKeyValues(String path) { + return _fileSystem + .file(path) + .readAsLines() + .then((lines) => lines.toKeyValues(), onError: (_) => null); + } +} + +extension _Unquote on String { + String removePrefix(String prefix) { + if (!startsWith(prefix)) return this; + return substring(prefix.length); + } + + String removeSuffix(String suffix) { + if (!endsWith(suffix)) return this; + return substring(0, length - suffix.length); + } + + String unquote() { + return removePrefix('"').removeSuffix('"'); + } +} + +extension _KeyValues on List { + Map toKeyValues() { + return Map.fromEntries( + map((line) { + final parts = line.split('='); + if (parts.length != 2) return MapEntry(line, null); + return MapEntry(parts.first, parts.last.unquote()); + }), + ); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/device_info_plus_macos.dart b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/device_info_plus_macos.dart new file mode 100644 index 00000000..39e7bb81 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/device_info_plus_macos.dart @@ -0,0 +1,2 @@ +// ignore: dangling_library_doc_comments +/// The platform interface handles the method channel calls. This file exists to silence errors on pub. diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/device_info_plus_web.dart b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/device_info_plus_web.dart new file mode 100644 index 00000000..c9f153a1 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/device_info_plus_web.dart @@ -0,0 +1,59 @@ +import 'dart:async'; +import 'dart:js_interop'; +import 'package:web/web.dart' as html show window, Navigator; + +import 'package:device_info_plus_platform_interface/device_info_plus_platform_interface.dart'; +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; + +import 'model/web_browser_info.dart'; + +/// The web implementation of the BatteryPlusPlatform of the BatteryPlus plugin. +class DeviceInfoPlusWebPlugin extends DeviceInfoPlatform { + /// Constructs a DeviceInfoPlusPlugin. + DeviceInfoPlusWebPlugin(navigator) : _navigator = navigator; + + final html.Navigator _navigator; + + /// Factory method that initializes the DeviceInfoPlus plugin platform + /// with an instance of the plugin for the web. + static void registerWith(Registrar registrar) { + DeviceInfoPlatform.instance = DeviceInfoPlusWebPlugin( + html.window.navigator, + ); + } + + @override + Future deviceInfo() { + return Future.value( + WebBrowserInfo.fromMap({ + 'appCodeName': _navigator.appCodeName, + 'appName': _navigator.appName, + 'appVersion': _navigator.appVersion, + 'deviceMemory': _navigator.safeDeviceMemory, + 'language': _navigator.language, + 'languages': _navigator.languages.toDart, + 'platform': _navigator.platform, + 'product': _navigator.product, + 'productSub': _navigator.productSub, + 'userAgent': _navigator.userAgent, + 'vendor': _navigator.vendor, + 'vendorSub': _navigator.vendorSub, + 'hardwareConcurrency': _navigator.hardwareConcurrency, + 'maxTouchPoints': _navigator.maxTouchPoints, + }), + ); + } +} + +/// Some Navigator properties are not fully supported in all browsers. +/// However, package:web does not provide a safe way to access these properties, +/// and assumes they are always not null. +/// +/// This extension provides a safe way to access these properties. +/// +/// See: https://github.com/dart-lang/web/issues/326 +/// https://github.com/fluttercommunity/plus_plugins/issues/3391 +extension SafeNavigationGetterExtensions on html.Navigator { + @JS('deviceMemory') + external double? get safeDeviceMemory; +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/device_info_plus_windows.dart b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/device_info_plus_windows.dart new file mode 100644 index 00000000..d4385abb --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/device_info_plus_windows.dart @@ -0,0 +1,187 @@ +/// The Windows implementation of `device_info_plus`. +library; + +import 'dart:ffi'; +import 'dart:typed_data'; +import 'dart:developer' as developer; + +import 'package:device_info_plus_platform_interface/device_info_plus_platform_interface.dart'; +import 'package:ffi/ffi.dart'; +import 'package:meta/meta.dart'; +import 'package:win32/win32.dart'; +import 'package:win32_registry/win32_registry.dart'; + +import 'model/windows_device_info.dart'; + +/// The Windows implementation of [DeviceInfoPlatform]. +class DeviceInfoPlusWindowsPlugin extends DeviceInfoPlatform { + /// Register this dart class as the platform implementation for windows + static void registerWith() { + DeviceInfoPlatform.instance = DeviceInfoPlusWindowsPlugin(); + } + + WindowsDeviceInfo? _cache; + + // In a well-meaning but somewhat misguided attempt to reduce apps from using + // version numbers for feature detection, most version number APIs don't give + // expected results on Windows 8 and above. RtlGetVersion is reliable and + // stable, but as a kernel API is documented in the Windows DDK (Driver + // Development Kit), rather than the SDK. As such, it's not included in + // package:win32, so we have to manually define it here. + // + // ignore: non_constant_identifier_names + void Function(Pointer) RtlGetVersion = DynamicLibrary.open( + 'ntdll.dll', + ).lookupFunction< + Void Function(Pointer), + void Function(Pointer) + >('RtlGetVersion'); + + /// Returns a [WindowsDeviceInfo] with information about the device. + @override + Future deviceInfo() { + return Future.value(_cache ??= getInfo()); + } + + @visibleForTesting + WindowsDeviceInfo getInfo() { + final systemInfo = calloc(); + final osVersionInfo = + calloc() + ..ref.dwOSVersionInfoSize = sizeOf(); + + try { + final currentVersionKey = Registry.openPath( + RegistryHive.localMachine, + path: r'SOFTWARE\Microsoft\Windows NT\CurrentVersion', + ); + final buildLab = currentVersionKey.getStringValue('BuildLab') ?? ''; + final buildLabEx = currentVersionKey.getStringValue('BuildLabEx') ?? ''; + final digitalProductId = + currentVersionKey.getBinaryValue('DigitalProductId') ?? + Uint8List.fromList([]); + final displayVersion = + currentVersionKey.getStringValue('DisplayVersion') ?? ''; + final editionId = currentVersionKey.getStringValue('EditionID') ?? ''; + final installDate = DateTime.fromMillisecondsSinceEpoch( + 1000 * (currentVersionKey.getIntValue('InstallDate') ?? 0), + ); + final productId = currentVersionKey.getStringValue('ProductID') ?? ''; + var productName = currentVersionKey.getStringValue('ProductName') ?? ''; + final registeredOwner = + currentVersionKey.getStringValue('RegisteredOwner') ?? ''; + final releaseId = currentVersionKey.getStringValue('ReleaseId') ?? ''; + + final sqmClientKey = Registry.openPath( + RegistryHive.localMachine, + path: r'SOFTWARE\Microsoft\SQMClient', + ); + final machineId = sqmClientKey.getStringValue('MachineId') ?? ''; + + GetSystemInfo(systemInfo); + + // Use `RtlGetVersion` from `ntdll.dll` to get the Windows version. + RtlGetVersion(osVersionInfo); + + // Handle [productName] for Windows 11 separately (as per Raymond Chen's comment). + // https://stackoverflow.com/questions/69460588/how-can-i-find-the-windows-product-name-in-windows-11 + if (osVersionInfo.ref.dwBuildNumber >= 22000) { + productName = productName.replaceAll('10', '11'); + } + final data = WindowsDeviceInfo( + numberOfCores: systemInfo.ref.dwNumberOfProcessors, + computerName: getComputerName(), + systemMemoryInMegabytes: getSystemMemoryInMegabytes(), + userName: getUserName(), + majorVersion: osVersionInfo.ref.dwMajorVersion, + minorVersion: osVersionInfo.ref.dwMinorVersion, + buildNumber: osVersionInfo.ref.dwBuildNumber, + platformId: osVersionInfo.ref.dwPlatformId, + csdVersion: osVersionInfo.ref.szCSDVersion, + servicePackMajor: osVersionInfo.ref.wServicePackMajor, + servicePackMinor: osVersionInfo.ref.wServicePackMinor, + suitMask: osVersionInfo.ref.wSuiteMask, + productType: osVersionInfo.ref.wProductType, + reserved: osVersionInfo.ref.wReserved, + buildLab: buildLab, + buildLabEx: buildLabEx, + digitalProductId: digitalProductId, + displayVersion: displayVersion, + editionId: editionId, + installDate: installDate, + productId: productId, + productName: productName, + registeredOwner: registeredOwner, + releaseId: releaseId, + deviceId: machineId, + ); + return data; + } finally { + free(systemInfo); + free(osVersionInfo); + } + } + + @visibleForTesting + int getSystemMemoryInMegabytes() { + final memoryInKilobytes = calloc(); + try { + final result = GetPhysicallyInstalledSystemMemory(memoryInKilobytes); + if (result != 0) { + return memoryInKilobytes.value ~/ 1024; + } else { + developer.log('Failed to get system memory', error: GetLastError()); + return 0; + } + } finally { + free(memoryInKilobytes); + } + } + + @visibleForTesting + String getComputerName() { + // We call this a first time to get the length of the string in characters, + // so we can allocate sufficient memory. + final nSize = calloc(); + GetComputerNameEx(ComputerNameDnsFullyQualified, nullptr, nSize); + + // Now allocate memory for a native string and call this a second time. + final lpBuffer = wsalloc(nSize.value); + try { + final result = GetComputerNameEx( + ComputerNameDnsFullyQualified, + lpBuffer, + nSize, + ); + + if (result != 0) { + return lpBuffer.toDartString(); + } else { + developer.log('Failed to get computer name', error: GetLastError()); + return ""; + } + } finally { + free(lpBuffer); + free(nSize); + } + } + + @visibleForTesting + String getUserName() { + const maxLength = 256; // defined as UNLEN in Lmcons.h + final lpBuffer = wsalloc(maxLength + 1); // allow for terminating null + final pcbBuffer = calloc()..value = maxLength + 1; + try { + final result = GetUserName(lpBuffer, pcbBuffer); + if (result != 0) { + return lpBuffer.toDartString(); + } else { + developer.log('Failed to get user name', error: GetLastError()); + return ""; + } + } finally { + free(pcbBuffer); + free(lpBuffer); + } + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/android_device_info.dart b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/android_device_info.dart new file mode 100644 index 00000000..5fae3696 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/android_device_info.dart @@ -0,0 +1,395 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:device_info_plus_platform_interface/model/base_device_info.dart'; +import 'package:meta/meta.dart'; + +/// Information derived from `android.os.Build`. +/// +/// See: https://developer.android.com/reference/android/os/Build.html +class AndroidDeviceInfo extends BaseDeviceInfo { + AndroidDeviceInfo._({ + required Map data, + required this.version, + required this.board, + required this.bootloader, + required this.brand, + required this.device, + required this.display, + required this.fingerprint, + required this.hardware, + required this.host, + required this.id, + required this.manufacturer, + required this.model, + required this.product, + required this.name, + required List supported32BitAbis, + required List supported64BitAbis, + required List supportedAbis, + required this.tags, + required this.type, + required this.isPhysicalDevice, + required this.freeDiskSize, + required this.totalDiskSize, + required List systemFeatures, + required this.isLowRamDevice, + required this.physicalRamSize, + required this.availableRamSize, + }) : supported32BitAbis = List.unmodifiable(supported32BitAbis), + supported64BitAbis = List.unmodifiable(supported64BitAbis), + supportedAbis = List.unmodifiable(supportedAbis), + systemFeatures = List.unmodifiable(systemFeatures), + super(data); + + /// Android operating system version values derived from `android.os.Build.VERSION`. + final AndroidBuildVersion version; + + /// The name of the underlying board, like "goldfish". + /// https://developer.android.com/reference/android/os/Build#BOARD + final String board; + + /// The system bootloader version number. + /// https://developer.android.com/reference/android/os/Build#BOOTLOADER + final String bootloader; + + /// The consumer-visible brand with which the product/hardware will be associated, if any. + /// https://developer.android.com/reference/android/os/Build#BRAND + final String brand; + + /// The name of the industrial design. + /// https://developer.android.com/reference/android/os/Build#DEVICE + final String device; + + /// A build ID string meant for displaying to the user. + /// https://developer.android.com/reference/android/os/Build#DISPLAY + final String display; + + /// A string that uniquely identifies this build. + /// https://developer.android.com/reference/android/os/Build#FINGERPRINT + final String fingerprint; + + /// The name of the hardware (from the kernel command line or /proc). + /// https://developer.android.com/reference/android/os/Build#HARDWARE + final String hardware; + + /// Hostname. + /// https://developer.android.com/reference/android/os/Build#HOST + final String host; + + /// Either a changelist number, or a label like "M4-rc20". + /// https://developer.android.com/reference/android/os/Build#ID + final String id; + + /// The manufacturer of the product/hardware. + /// https://developer.android.com/reference/android/os/Build#MANUFACTURER + final String manufacturer; + + /// The end-user-visible name for the end product. + /// https://developer.android.com/reference/android/os/Build#MODEL + final String model; + + /// The name of the overall product. + /// https://developer.android.com/reference/android/os/Build#PRODUCT + final String product; + + /// The name of the device. + /// https://developer.android.com/reference/android/provider/Settings.Global#DEVICE_NAME + final String name; + + /// An ordered list of 32 bit ABIs supported by this device. + /// Available only on Android L (API 21) and newer + /// https://developer.android.com/reference/android/os/Build#SUPPORTED_32_BIT_ABIS + final List supported32BitAbis; + + /// An ordered list of 64 bit ABIs supported by this device. + /// Available only on Android L (API 21) and newer + /// https://developer.android.com/reference/android/os/Build#SUPPORTED_64_BIT_ABIS + final List supported64BitAbis; + + /// An ordered list of ABIs supported by this device. + /// Available only on Android L (API 21) and newer + /// https://developer.android.com/reference/android/os/Build#SUPPORTED_ABIS + final List supportedAbis; + + /// Comma-separated tags describing the build, like "unsigned,debug". + /// https://developer.android.com/reference/android/os/Build#TAGS + final String tags; + + /// The type of build, like "user" or "eng". + /// https://developer.android.com/reference/android/os/Build#TYPE + final String type; + + /// `false` if the application is running in an emulator, `true` otherwise. + final bool isPhysicalDevice; + + /// Available disk size in bytes + /// + /// https://developer.android.com/reference/android/os/StatFs#getFreeBytes() + final int freeDiskSize; + + /// Total disk size in bytes + /// + /// https://developer.android.com/reference/android/os/StatFs#getTotalBytes() + final int totalDiskSize; + + /// Describes what features are available on the current device. + /// + /// This can be used to check if the device has, for example, a front-facing + /// camera, or a touchscreen. However, in many cases this is not the best + /// API to use. For example, if you are interested in bluetooth, this API + /// can tell you if the device has a bluetooth radio, but it cannot tell you + /// if bluetooth is currently enabled, or if you have been granted the + /// necessary permissions to use it. Please *only* use this if there is no + /// other way to determine if a feature is supported. + /// + /// This data comes from Android's PackageManager.getSystemAvailableFeatures, + /// and many of the common feature strings to look for are available in + /// PackageManager's public documentation: + /// https://developer.android.com/reference/android/content/pm/PackageManager + final List systemFeatures; + + /// `true` if the application is running on a low-RAM device, `false` otherwise. + final bool isLowRamDevice; + + /// Total physical RAM size of the device in megabytes + /// + /// https://developer.android.com/reference/android/app/ActivityManager.MemoryInfo#totalMem + final int physicalRamSize; + + /// Current unallocated RAM size of the device in megabytes + /// + /// https://developer.android.com/reference/android/app/ActivityManager.MemoryInfo#availMem + final int availableRamSize; + + /// Deserializes from the message received from [_kChannel]. + static AndroidDeviceInfo fromMap(Map map) { + return AndroidDeviceInfo._( + data: map, + version: AndroidBuildVersion._fromMap( + map['version']?.cast() ?? {}, + ), + board: map['board'], + bootloader: map['bootloader'], + brand: map['brand'], + device: map['device'], + display: map['display'], + fingerprint: map['fingerprint'], + hardware: map['hardware'], + host: map['host'], + id: map['id'], + manufacturer: map['manufacturer'], + model: map['model'], + product: map['product'], + name: map['name'] ?? '', + supported32BitAbis: _fromList(map['supported32BitAbis'] ?? []), + supported64BitAbis: _fromList(map['supported64BitAbis'] ?? []), + supportedAbis: _fromList(map['supportedAbis'] ?? []), + tags: map['tags'], + type: map['type'], + isPhysicalDevice: map['isPhysicalDevice'], + freeDiskSize: map['freeDiskSize'], + totalDiskSize: map['totalDiskSize'], + systemFeatures: _fromList(map['systemFeatures'] ?? []), + isLowRamDevice: map['isLowRamDevice'], + physicalRamSize: map['physicalRamSize'], + availableRamSize: map['availableRamSize'], + ); + } + + /// Initializes the application metadata with mock values for testing. + @visibleForTesting + static AndroidDeviceInfo setMockInitialValues({ + required AndroidBuildVersion version, + required String board, + required String bootloader, + required String brand, + required String device, + required String display, + required String fingerprint, + required String hardware, + required String host, + required String id, + required String manufacturer, + required String model, + required String product, + required String name, + required List supported32BitAbis, + required List supported64BitAbis, + required List supportedAbis, + required String tags, + required String type, + required bool isPhysicalDevice, + required int freeDiskSize, + required int totalDiskSize, + required List systemFeatures, + required bool isLowRamDevice, + required int physicalRamSize, + required int availableRamSize, + }) { + final Map data = { + 'version': { + 'baseOS': version.baseOS, + 'sdkInt': version.sdkInt, + 'release': version.release, + 'codename': version.codename, + 'incremental': version.incremental, + 'previewSdkInt': version.previewSdkInt, + 'securityPatch': version.securityPatch, + }, + 'board': board, + 'bootloader': bootloader, + 'brand': brand, + 'device': device, + 'display': display, + 'fingerprint': fingerprint, + 'hardware': hardware, + 'host': host, + 'id': id, + 'manufacturer': manufacturer, + 'model': model, + 'product': product, + 'name': name, + 'supported32BitAbis': supported32BitAbis, + 'supported64BitAbis': supported64BitAbis, + 'supportedAbis': supportedAbis, + 'tags': tags, + 'type': type, + 'isPhysicalDevice': isPhysicalDevice, + 'freeDiskSize': freeDiskSize, + 'totalDiskSize': totalDiskSize, + 'systemFeatures': systemFeatures, + 'isLowRamDevice': isLowRamDevice, + 'physicalRamSize': physicalRamSize, + 'availableRamSize': availableRamSize, + }; + + return AndroidDeviceInfo._( + data: data, + version: version, + board: board, + bootloader: bootloader, + brand: brand, + device: device, + display: display, + fingerprint: fingerprint, + hardware: hardware, + host: host, + id: id, + manufacturer: manufacturer, + model: model, + product: product, + name: name, + supported32BitAbis: _fromList(supported32BitAbis), + supported64BitAbis: _fromList(supported64BitAbis), + supportedAbis: _fromList(supportedAbis), + tags: tags, + type: type, + isPhysicalDevice: isPhysicalDevice, + freeDiskSize: freeDiskSize, + totalDiskSize: totalDiskSize, + systemFeatures: _fromList(systemFeatures), + isLowRamDevice: isLowRamDevice, + physicalRamSize: physicalRamSize, + availableRamSize: availableRamSize, + ); + } + + /// Deserializes message as `List` + static List _fromList(List message) { + final list = message.takeWhile((item) => item != null).toList(); + return List.from(list); + } +} + +/// Version values of the current Android operating system build derived from +/// `android.os.Build.VERSION`. +/// +/// See: https://developer.android.com/reference/android/os/Build.VERSION.html +class AndroidBuildVersion { + const AndroidBuildVersion._({ + this.baseOS, + required this.codename, + required this.incremental, + required this.previewSdkInt, + required this.release, + required this.sdkInt, + this.securityPatch, + }); + + /// The base OS build the product is based on. + /// Available only on Android M (API 23) and newer + final String? baseOS; + + /// The current development codename, or the string "REL" if this is a release build. + final String codename; + + /// The internal value used by the underlying source control to represent this build. + /// Available only on Android M (API 23) and newer + final String incremental; + + /// The developer preview revision of a pre-release SDK. + final int? previewSdkInt; + + /// The user-visible version string. + final String release; + + /// The user-visible SDK version of the framework. + /// + /// Possible values are defined in: https://developer.android.com/reference/android/os/Build.VERSION_CODES.html + final int sdkInt; + + /// The user-visible security patch level. + /// Available only on Android M (API 23) and newer + final String? securityPatch; + + /// Serializes [ AndroidBuildVersion ] to map. + @Deprecated('[toMap] method will be discontinued') + Map toMap() { + return { + 'baseOS': baseOS, + 'sdkInt': sdkInt, + 'release': release, + 'codename': codename, + 'incremental': incremental, + 'previewSdkInt': previewSdkInt, + 'securityPatch': securityPatch, + }; + } + + /// Deserializes from the map message received from [_kChannel]. + static AndroidBuildVersion _fromMap(Map map) { + return AndroidBuildVersion._( + baseOS: map['baseOS'], + codename: map['codename'], + incremental: map['incremental'], + previewSdkInt: map['previewSdkInt'], + release: map['release'], + sdkInt: map['sdkInt'], + securityPatch: map['securityPatch'], + ); + } + + /// Initializes the application metadata with mock values for testing. + @visibleForTesting + static AndroidBuildVersion setMockInitialValues({ + String? baseOS, + required String codename, + required String incremental, + required int previewSdkInt, + required String release, + required int sdkInt, + String? securityPatch, + }) { + return AndroidBuildVersion._( + baseOS: baseOS, + codename: codename, + incremental: incremental, + previewSdkInt: previewSdkInt, + release: release, + sdkInt: sdkInt, + securityPatch: securityPatch, + ); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/ios_device_info.dart b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/ios_device_info.dart new file mode 100644 index 00000000..def717e9 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/ios_device_info.dart @@ -0,0 +1,222 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:device_info_plus_platform_interface/model/base_device_info.dart'; +import 'package:meta/meta.dart'; + +/// Information derived from `UIDevice`. +/// +/// See: https://developer.apple.com/documentation/uikit/uidevice +class IosDeviceInfo extends BaseDeviceInfo { + /// IOS device info class. + IosDeviceInfo._({ + required Map data, + required this.name, + required this.systemName, + required this.systemVersion, + required this.model, + required this.modelName, + required this.localizedModel, + required this.freeDiskSize, + required this.totalDiskSize, + this.identifierForVendor, + required this.isPhysicalDevice, + required this.physicalRamSize, + required this.availableRamSize, + required this.isiOSAppOnMac, + required this.utsname, + }) : super(data); + + /// Device name. + /// + /// On iOS < 16 returns user-assigned device name + /// On iOS >= 16 returns a generic device name if project has + /// no entitlement to get user-assigned device name. + /// https://developer.apple.com/documentation/uikit/uidevice/1620015-name + final String name; + + /// The name of the current operating system. + /// https://developer.apple.com/documentation/uikit/uidevice/1620054-systemname + final String systemName; + + /// The current operating system version. + /// https://developer.apple.com/documentation/uikit/uidevice/1620043-systemversion + final String systemVersion; + + /// Device model according to OS + /// https://developer.apple.com/documentation/uikit/uidevice/1620044-model + final String model; + + /// Commercial or user-known model name + /// Examples: `iPhone 16 Pro`, `iPad Pro 11-Inch 3` + final String modelName; + + /// Localized name of the device model. + /// https://developer.apple.com/documentation/uikit/uidevice/1620029-localizedmodel + final String localizedModel; + + /// Unique UUID value identifying the current device. + /// https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor + final String? identifierForVendor; + + /// `false` if the application is running in a simulator, `true` otherwise. + final bool isPhysicalDevice; + + /// Total physical RAM size of the device in megabytes + final int physicalRamSize; + + /// Current unallocated RAM size of the device in megabytes + final int availableRamSize; + + /// that indicates whether the process is an iPhone or iPad app running on a Mac. + /// https://developer.apple.com/documentation/foundation/nsprocessinfo/3608556-iosapponmac + final bool isiOSAppOnMac; + + /// Operating system information derived from `sys/utsname.h`. + final IosUtsname utsname; + + /// Free disk size in bytes + final int freeDiskSize; + + /// Total disk size in bytes + final int totalDiskSize; + + /// Deserializes from the map message received from [_kChannel]. + static IosDeviceInfo fromMap(Map map) { + return IosDeviceInfo._( + data: map, + name: map['name'], + systemName: map['systemName'], + systemVersion: map['systemVersion'], + model: map['model'], + modelName: map['modelName'], + localizedModel: map['localizedModel'], + identifierForVendor: map['identifierForVendor'], + freeDiskSize: map['freeDiskSize'], + totalDiskSize: map['totalDiskSize'], + isPhysicalDevice: map['isPhysicalDevice'], + physicalRamSize: map['physicalRamSize'], + availableRamSize: map['availableRamSize'], + isiOSAppOnMac: map['isiOSAppOnMac'], + utsname: IosUtsname._fromMap( + map['utsname']?.cast() ?? {}, + ), + ); + } + + /// Initializes the application metadata with mock values for testing. + @visibleForTesting + static IosDeviceInfo setMockInitialValues({ + required String name, + required String systemName, + required String systemVersion, + required String model, + required String modelName, + required String localizedModel, + required int freeDiskSize, + required int totalDiskSize, + String? identifierForVendor, + required bool isPhysicalDevice, + required bool isiOSAppOnMac, + required int physicalRamSize, + required int availableRamSize, + required IosUtsname utsname, + }) { + final Map data = { + 'name': name, + 'systemName': systemName, + 'systemVersion': systemVersion, + 'model': model, + 'modelName': modelName, + 'localizedModel': localizedModel, + 'identifierForVendor': identifierForVendor, + 'freeDiskSize': freeDiskSize, + 'totalDiskSize': totalDiskSize, + 'isPhysicalDevice': isPhysicalDevice, + 'isiOSAppOnMac': isiOSAppOnMac, + 'physicalRamSize': physicalRamSize, + 'availableRamSize': availableRamSize, + 'utsname': { + 'sysname': utsname.sysname, + 'nodename': utsname.nodename, + 'release': utsname.release, + 'version': utsname.version, + 'machine': utsname.machine, + }, + }; + return IosDeviceInfo._( + data: data, + name: name, + systemName: systemName, + systemVersion: systemVersion, + model: model, + modelName: modelName, + localizedModel: localizedModel, + identifierForVendor: identifierForVendor, + freeDiskSize: freeDiskSize, + totalDiskSize: totalDiskSize, + isPhysicalDevice: isPhysicalDevice, + isiOSAppOnMac: isiOSAppOnMac, + physicalRamSize: physicalRamSize, + availableRamSize: availableRamSize, + utsname: utsname, + ); + } +} + +/// Information derived from `utsname`. +/// See http://pubs.opengroup.org/onlinepubs/7908799/xsh/sysutsname.h.html for details. +class IosUtsname { + const IosUtsname._({ + required this.sysname, + required this.nodename, + required this.release, + required this.version, + required this.machine, + }); + + /// Operating system name. + final String sysname; + + /// Network node name. + final String nodename; + + /// Release level. + final String release; + + /// Version level. + final String version; + + /// Hardware type (e.g. 'iPhone7,1' for iPhone 6 Plus). + final String machine; + + /// Deserializes from the map message received from [_kChannel]. + static IosUtsname _fromMap(Map map) { + return IosUtsname._( + sysname: map['sysname'], + nodename: map['nodename'], + release: map['release'], + version: map['version'], + machine: map['machine'], + ); + } + + /// Initializes the application metadata with mock values for testing. + @visibleForTesting + static IosUtsname setMockInitialValues({ + required String sysname, + required String nodename, + required String release, + required String version, + required String machine, + }) { + return IosUtsname._( + sysname: sysname, + nodename: nodename, + release: release, + version: version, + machine: machine, + ); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/linux_device_info.dart b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/linux_device_info.dart new file mode 100644 index 00000000..f7aa1088 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/linux_device_info.dart @@ -0,0 +1,168 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:device_info_plus_platform_interface/model/base_device_info.dart'; + +/// Device information for a Linux system. +/// +/// See: +/// - https://www.freedesktop.org/software/systemd/man/os-release.html +/// - https://www.freedesktop.org/software/systemd/man/machine-id.html +class LinuxDeviceInfo implements BaseDeviceInfo { + /// Constructs a LinuxDeviceInfo. + LinuxDeviceInfo({ + required this.name, + this.version, + required this.id, + this.idLike, + this.versionCodename, + this.versionId, + required this.prettyName, + this.buildId, + this.variant, + this.variantId, + required this.machineId, + }); + + /// A string identifying the operating system, without a version component, + /// and suitable for presentation to the user. + /// + /// Examples: 'Fedora', 'Debian GNU/Linux'. + /// + /// If not set, defaults to 'Linux'. + final String name; + + /// A string identifying the operating system version, excluding any OS name + /// information, possibly including a release code name, and suitable for + /// presentation to the user. + /// + /// Examples: '17', '17 (Beefy Miracle)'. + /// + /// This field is optional and may be null on some systems. + final String? version; + + /// A lower-case string identifying the operating system, excluding any + /// version information and suitable for processing by scripts or usage in + /// generated filenames. + /// + /// The ID contains no spaces or other characters outside of 0–9, a–z, '.', + /// '_' and '-'. + /// + /// Examples: 'fedora', 'debian'. + /// + /// If not set, defaults to 'linux'. + final String id; + + /// A space-separated list of operating system identifiers in the same syntax + /// as the [id] value. It lists identifiers of operating systems that are + /// closely related to the local operating system in regards to packaging + /// and programming interfaces, for example listing one or more OS identifiers + /// the local OS is a derivative from. + /// + /// Examples: an operating system with [id] 'centos', would list 'rhel' and + /// 'fedora', and an operating system with [id] 'ubuntu' would list 'debian'. + /// + /// This field is optional and may be null on some systems. + final List? idLike; + + /// A lower-case string identifying the operating system release code name, + /// excluding any OS name information or release version, and suitable for + /// processing by scripts or usage in generated filenames. + /// + /// The codename contains no spaces or other characters outside of 0–9, a–z, + /// '.', '_' and '-'. + /// + /// Examples: 'buster', 'xenial'. + /// + /// This field is optional and may be null on some systems. + final String? versionCodename; + + /// A lower-case string identifying the operating system version, excluding + /// any OS name information or release code name, and suitable for processing + /// by scripts or usage in generated filenames. + /// + /// The version is mostly numeric, and contains no spaces or other characters + /// outside of 0–9, a–z, '.', '_' and '-'. + /// + /// Examples: '17', '11.04'. + /// + /// This field is optional and may be null on some systems. + final String? versionId; + + /// A pretty operating system name in a format suitable for presentation to + /// the user. May or may not contain a release code name or OS version of some + /// kind, as suitable. + /// + /// Examples: 'Fedora 17 (Beefy Miracle)'. + /// + /// If not set, defaults to 'Linux'. + final String prettyName; + + /// A string uniquely identifying the system image used as the origin for a + /// distribution (it is not updated with system updates). The field can be + /// identical between different [versionId]s as `buildId` is an only a unique + /// identifier to a specific version. + /// + /// Examples: '2013-03-20.3', '201303203'. + /// + /// This field is optional and may be null on some systems. + final String? buildId; + + /// A string identifying a specific variant or edition of the operating system + /// suitable for presentation to the user. This field may be used to inform + /// the user that the configuration of this system is subject to a specific + /// divergent set of rules or default configuration settings. + /// + /// Examples: 'Server Edition', 'Smart Refrigerator Edition'. + /// + /// Note: this field is for display purposes only. The [variantId] field + /// should be used for making programmatic decisions. + /// + /// This field is optional and may be null on some systems. + final String? variant; + + /// A lower-case string identifying a specific variant or edition of the + /// operating system. This may be interpreted in order to determine a + /// divergent default configuration. + /// + /// The variant ID contains no spaces or other characters outside of 0–9, a–z, + /// '.', '_' and '-'. + /// + /// Examples: 'server', 'embedded'. + /// + /// This field is optional and may be null on some systems. + final String? variantId; + + /// A unique machine ID of the local system that is set during installation or + /// boot. The machine ID is hexadecimal, 32-character, lowercase ID. When + /// decoded from hexadecimal, this corresponds to a 16-byte/128-bit value. + final String? machineId; + + @override + // ignore: deprecated_member_use_from_same_package + Map get data => toMap(); + + @Deprecated('Use [data] getter instead') + @override + Map toMap() { + return { + 'name': name, + 'version': version, + 'id': id, + 'idLike': idLike, + 'versionCodename': versionCodename, + 'versionId': versionId, + 'prettyName': prettyName, + 'buildId': buildId, + 'variant': variant, + 'variantId': variantId, + 'machineId': machineId, + }; + } + + @override + String toString() { + return 'LinuxDeviceInfo(name: $name, version: $version, id: $id, idLike: $idLike, versionCodename: $versionCodename, versionId: $versionId, prettyName: $prettyName, buildId: $buildId, variant: $variant, variantId: $variantId, machineId: $machineId)'; + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/macos_device_info.dart b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/macos_device_info.dart new file mode 100644 index 00000000..e1812cbc --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/macos_device_info.dart @@ -0,0 +1,151 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:device_info_plus_platform_interface/model/base_device_info.dart'; +import 'package:meta/meta.dart'; + +/// Object encapsulating MACOS device information. +class MacOsDeviceInfo extends BaseDeviceInfo { + /// Constructs a MacOsDeviceInfo. + MacOsDeviceInfo._({ + required Map data, + required this.computerName, + required this.hostName, + required this.arch, + required this.model, + required this.modelName, + required this.kernelVersion, + required this.osRelease, + required this.majorVersion, + required this.minorVersion, + required this.patchVersion, + required this.activeCPUs, + required this.memorySize, + required this.cpuFrequency, + required this.systemGUID, + }) : super(data); + + /// Name given to the local machine. + final String computerName; + + /// Operating system type + final String hostName; + + /// Machine cpu architecture + /// Note, that on Apple Silicon Macs can return `x86_64` if app runs via Rosetta + final String arch; + + /// Device model identifier + /// Examples: `MacBookPro18,3`, `Mac16,2`. + final String model; + + /// Device model name + /// Examples: `MacBook Pro (16-inch, 2021)`, `iMac (24-inch, 2024)`. + final String modelName; + + /// Machine Kernel version. + /// Examples: + /// `Darwin Kernel Version 15.3.0: Thu Dec 10 18:40:58 PST 2015; root:xnu-3248.30.4~1/RELEASE_X86_64` + /// or + /// `Darwin Kernel Version 15.0.0: Wed Dec 9 22:19:38 PST 2015; root:xnu-3248.31.3~2/RELEASE_ARM64_S8000` + final String kernelVersion; + + /// Operating system release number + final String osRelease; + + /// The major release number, such as 10 in version 10.9.3. + final int majorVersion; + + /// The minor release number, such as 9 in version 10.9.3. + final int minorVersion; + + /// The update release number, such as 3 in version 10.9.3. + final int patchVersion; + + /// Number of active CPUs + final int activeCPUs; + + /// Machine's memory size + final int memorySize; + + /// Device CPU Frequency + final int cpuFrequency; + + /// Device GUID + final String? systemGUID; + + /// Constructs a [MacOsDeviceInfo] from a Map of dynamic. + static MacOsDeviceInfo fromMap(Map map) { + return MacOsDeviceInfo._( + data: map, + computerName: map['computerName'], + hostName: map['hostName'], + arch: map['arch'], + model: map['model'], + modelName: map['modelName'], + kernelVersion: map['kernelVersion'], + osRelease: map['osRelease'], + majorVersion: map['majorVersion'], + minorVersion: map['minorVersion'], + patchVersion: map['patchVersion'], + activeCPUs: map['activeCPUs'], + memorySize: map['memorySize'], + cpuFrequency: map['cpuFrequency'], + systemGUID: map['systemGUID'], + ); + } + + /// Initializes the application metadata with mock values for testing. + @visibleForTesting + static MacOsDeviceInfo setMockInitialValues({ + required String computerName, + required String hostName, + required String arch, + required String model, + required String modelName, + required String kernelVersion, + required String osRelease, + required int majorVersion, + required int minorVersion, + required int patchVersion, + required int activeCPUs, + required int memorySize, + required int cpuFrequency, + required String systemGUID, + }) { + final Map data = { + 'computerName': computerName, + 'hostName': hostName, + 'arch': arch, + 'model': model, + 'modelName': modelName, + 'kernelVersion': kernelVersion, + 'osRelease': osRelease, + 'majorVersion': majorVersion, + 'minorVersion': minorVersion, + 'patchVersion': patchVersion, + 'activeCPUs': activeCPUs, + 'memorySize': memorySize, + 'cpuFrequency': cpuFrequency, + 'systemGUID': systemGUID, + }; + return MacOsDeviceInfo._( + data: data, + computerName: computerName, + hostName: hostName, + arch: arch, + model: model, + modelName: modelName, + kernelVersion: kernelVersion, + osRelease: osRelease, + majorVersion: majorVersion, + minorVersion: minorVersion, + patchVersion: patchVersion, + activeCPUs: activeCPUs, + memorySize: memorySize, + cpuFrequency: cpuFrequency, + systemGUID: systemGUID, + ); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/web_browser_info.dart b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/web_browser_info.dart new file mode 100644 index 00000000..03fa7f83 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/web_browser_info.dart @@ -0,0 +1,188 @@ +// Copyright 2020 The Flutter Community Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:device_info_plus_platform_interface/model/base_device_info.dart'; + +/// List of supported browsers +enum BrowserName { + /// Mozilla Firefox + firefox, + + /// Samsumg Internet Browser + samsungInternet, + + /// Opera Web Browser + opera, + + /// Microsoft Internet Explorer + msie, + + /// Microsoft Edge + edge, + + /// Google Chrome + chrome, + + /// Apple Safari + safari, + + /// Unknown web browser + unknown, +} + +/// Information derived from `navigator`. +/// +/// See: https://developer.mozilla.org/en-US/docs/Web/API/Window/navigator +class WebBrowserInfo implements BaseDeviceInfo { + /// Web Browser info class. + WebBrowserInfo({ + required this.appCodeName, + required this.appName, + required this.appVersion, + required this.deviceMemory, + required this.language, + required this.languages, + required this.platform, + required this.product, + required this.productSub, + required this.userAgent, + required this.vendor, + required this.vendorSub, + required this.maxTouchPoints, + required this.hardwareConcurrency, + }); + + /// the name of the current browser. + BrowserName get browserName { + return _parseUserAgentToBrowserName(); + } + + /// the internal "code" name of the current browser. + /// Note: Do not rely on this property to return the correct value. + final String? appCodeName; + + /// a DOMString with the official name of the browser. + /// Note: Do not rely on this property to return the correct value. + final String? appName; + + /// the version of the browser as a DOMString. + /// Note: Do not rely on this property to return the correct value. + final String? appVersion; + + /// the amount of device memory in gigabytes. This value is an approximation given by rounding to the nearest power of 2 and dividing that number by 1024. + final double? deviceMemory; + + /// a DOMString representing the preferred language of the user, usually the language of the browser UI. The null value is returned when this is unknown. + final String? language; + + /// an array of DOMString representing the languages known to the user, by order of preference. + final List? languages; + + /// the version of the browser as a DOMString. + /// Note: Do not rely on this property to return the correct value. + final String? platform; + + /// Always returns 'Gecko', on any browser. + /// Note: Do not rely on this property to return the correct value. + /// This property is kept only for compatibility purpose. + final String? product; + + /// the build number of the current browser + /// Note: Do not rely on this property to return the correct value. + final String? productSub; + + /// the build number of the current browser (e.g., "20060909") + final String? userAgent; + + /// the vendor name of the current browser + final String? vendor; + + /// Returns the vendor version number (e.g. "6.1") + /// Note: Do not rely on this property to return the correct value. + final String? vendorSub; + + /// the number of logical processor cores available. + final int? hardwareConcurrency; + + /// the maximum number of simultaneous touch contact points are supported by the current device. + final int? maxTouchPoints; + + /// Deserializes from the map message received from [Navigator]. + static WebBrowserInfo fromMap(Map map) { + return WebBrowserInfo( + appCodeName: map['appCodeName'], + appName: map['appName'], + appVersion: map['appVersion'], + deviceMemory: map['deviceMemory'], + language: map['language'], + languages: map['languages'], + platform: map['platform'], + product: map['product'], + productSub: map['productSub'], + userAgent: map['userAgent'], + vendor: map['vendor'], + vendorSub: map['vendorSub'], + hardwareConcurrency: map['hardwareConcurrency'], + maxTouchPoints: map['maxTouchPoints'], + ); + } + + @Deprecated('use [data] instead') + @override + Map toMap() { + return { + 'browserName': browserName, + 'appCodeName': appCodeName, + 'appName': appName, + 'appVersion': appVersion, + 'deviceMemory': deviceMemory, + 'language': language, + 'languages': languages, + 'platform': platform, + 'product': product, + 'productSub': productSub, + 'userAgent': userAgent, + 'vendor': vendor, + 'vendorSub': vendorSub, + 'hardwareConcurrency': hardwareConcurrency, + 'maxTouchPoints': maxTouchPoints, + }; + } + + BrowserName _parseUserAgentToBrowserName() { + final userAgent = this.userAgent; + if (userAgent == null) { + return BrowserName.unknown; + } else if (userAgent.contains('Firefox')) { + return BrowserName.firefox; + // "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0" + } else if (userAgent.contains('SamsungBrowser')) { + return BrowserName.samsungInternet; + // "Mozilla/5.0 (Linux; Android 9; SAMSUNG SM-G955F Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/9.4 Chrome/67.0.3396.87 Mobile Safari/537.36 + } else if (userAgent.contains('Opera') || userAgent.contains('OPR')) { + return BrowserName.opera; + // "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 OPR/57.0.3098.106" + } else if (userAgent.contains('Trident')) { + return BrowserName.msie; + // "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; Zoom 3.6.0; wbx 1.0.0; rv:11.0) like Gecko" + } else if (userAgent.contains('Edg')) { + return BrowserName.edge; + // https://docs.microsoft.com/en-us/microsoft-edge/web-platform/user-agent-string + // "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.74 Safari/537.36 Edg/79.0.309.43" + // "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299" + } else if (userAgent.contains('Chrome')) { + return BrowserName.chrome; + // "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/66.0.3359.181 Chrome/66.0.3359.181 Safari/537.36" + } else if (userAgent.contains('Safari')) { + return BrowserName.safari; + // "Mozilla/5.0 (iPhone; CPU iPhone OS 11_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.0 Mobile/15E148 Safari/604.1 980x1306" + } else { + return BrowserName.unknown; + } + } + + @override + // ignore: deprecated_member_use_from_same_package + Map get data => toMap(); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/windows_device_info.dart b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/windows_device_info.dart new file mode 100644 index 00000000..d18037b7 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/lib/src/model/windows_device_info.dart @@ -0,0 +1,184 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:typed_data'; + +import 'package:device_info_plus_platform_interface/model/base_device_info.dart'; + +/// Object encapsulating WINDOWS device information. +class WindowsDeviceInfo implements BaseDeviceInfo { + /// Constructs a [WindowsDeviceInfo]. + const WindowsDeviceInfo({ + required this.computerName, + required this.numberOfCores, + required this.systemMemoryInMegabytes, + required this.userName, + required this.majorVersion, + required this.minorVersion, + required this.buildNumber, + required this.platformId, + required this.csdVersion, + required this.servicePackMajor, + required this.servicePackMinor, + required this.suitMask, + required this.productType, + required this.reserved, + required this.buildLab, + required this.buildLabEx, + required this.digitalProductId, + required this.displayVersion, + required this.editionId, + required this.installDate, + required this.productId, + required this.productName, + required this.registeredOwner, + required this.releaseId, + required this.deviceId, + }); + + /// The computer's fully-qualified DNS name, where available. + final String computerName; + + /// Number of CPU cores on the local machine + final int numberOfCores; + + /// The physically installed memory in the computer. + /// This may not be the same as available memory. + final int systemMemoryInMegabytes; + + final String userName; + + /// The major version number of the operating system. + /// For example, for Windows 2000, the major version number is five. + /// For more information, see the table in Remarks. + /// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw#remarks + final int majorVersion; + + /// The minor version number of the operating system. + /// For example, for Windows 2000, the minor version number is zero. + /// For more information, see the table in Remarks. + /// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw#remarks + final int minorVersion; + + /// The build number of the operating system. + /// For example: + /// - `22000` or greater for Windows 11. + /// - `10240` or greator for Windows 10. + final int buildNumber; + + /// The operating system platform. For Win32 on NT-based operating systems, + /// RtlGetVersion returns the value `VER_PLATFORM_WIN32_NT`. + final int platformId; + + /// The service-pack version string. + /// + /// This member contains a string, such as "Service Pack 3", which indicates + /// the latest service pack installed on the system. + final String csdVersion; + + /// The major version number of the latest service pack installed on the system. + /// For example, for Service Pack 3, the major version number is three. If no + /// service pack has been installed, the value is zero. + final int servicePackMajor; + + /// The minor version number of the latest service pack installed on the + /// system. For example, for Service Pack 3, the minor version number is zero. + final int servicePackMinor; + + /// The product suites available on the system. + final int suitMask; + + /// The product type. This member contains additional information about the + /// system. + final int productType; + + /// Reserved for future use. + final int reserved; + + /// Value of `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows + /// NT\CurrentVersion\BuildLab` registry key. For example: + /// `22000.co_release.210604-1628`. + final String buildLab; + + /// Value of `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows + /// NT\CurrentVersion\BuildLabEx` registry key. For example: + /// `22000.1.amd64fre.co_release.210604-1628`. + final String buildLabEx; + + /// Value of `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows + /// NT\CurrentVersion\DigitalProductId` registry key. + final Uint8List digitalProductId; + + /// Value of `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows + /// NT\CurrentVersion\DisplayVersion` registry key. For example: `21H2`. + final String displayVersion; + + /// Value of `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows + /// NT\CurrentVersion\EditionID` registry key. + final String editionId; + + /// Value of `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows + /// NT\CurrentVersion\InstallDate` registry key. + final DateTime installDate; + + /// Displayed as "Product ID" in Windows Settings. Value of the + /// `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows + /// NT\CurrentVersion\ProductId` registry key. For example: + /// `00000-00000-0000-AAAAA`. + final String productId; + + /// Value of `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows + /// NT\CurrentVersion\ProductName` registry key. For example: `Windows 10 Home + /// Single Language`. + final String productName; + + /// Value of the `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows + /// NT\CurrentVersion\RegisteredOwner` registry key. For example: `Microsoft + /// Corporation`. + final String registeredOwner; + + /// Value of the `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows + /// NT\CurrentVersion\ReleaseId` registry key. For example: `1903`. + final String releaseId; + + /// Displayed as "Device ID" in Windows Settings. Value of + /// `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SQMClient\MachineId` registry key. + final String deviceId; + + @Deprecated('use [data] instead') + @override + Map toMap() { + return { + 'computerName': computerName, + 'numberOfCores': numberOfCores, + 'systemMemoryInMegabytes': systemMemoryInMegabytes, + 'userName': userName, + 'majorVersion': majorVersion, + 'minorVersion': minorVersion, + 'buildNumber': buildNumber, + 'platformId': platformId, + 'csdVersion': csdVersion, + 'servicePackMajor': servicePackMajor, + 'servicePackMinor': servicePackMinor, + 'suitMask': suitMask, + 'productType': productType, + 'reserved': reserved, + 'buildLab': buildLab, + 'buildLabEx': buildLabEx, + 'digitalProductId': digitalProductId, + 'displayVersion': displayVersion, + 'editionId': editionId, + 'installDate': installDate, + 'productId': productId, + 'productName': productName, + 'registeredOwner': registeredOwner, + 'releaseId': releaseId, + 'deviceId': deviceId, + }; + } + + @override + // ignore: deprecated_member_use_from_same_package + Map get data => toMap(); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus.podspec b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus.podspec new file mode 100644 index 00000000..a5031bea --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus.podspec @@ -0,0 +1,23 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html +# +Pod::Spec.new do |s| + s.name = 'device_info_plus' + s.version = '0.0.1' + s.summary = 'No-op implementation of the macos device_info_plus to avoid build issues on macos' + s.description = <<-DESC + No-op implementation of the device_info_plus plugin to avoid build issues on macos. +https://github.com/flutter/flutter/issues/46618 + DESC + s.homepage = 'https://github.com/fluttercommunity/plus_plugins/tree/master/packages/device_info_plus' + s.license = { :file => '../LICENSE' } + s.author = { 'Flutter Community' => 'authors@fluttercommunity.dev' } + s.source = { :path => '.' } + s.source_files = 'device_info_plus/Sources/device_info_plus/**/*.swift' + s.public_header_files = 'device_info_plus/Sources/device_info_plus/**/*.h' + s.dependency 'FlutterMacOS' + + s.platform = :osx + s.osx.deployment_target = '10.14' + s.resource_bundles = {'device_info_plus_privacy' => ['device_info_plus/Sources/device_info_plus/PrivacyInfo.xcprivacy']} +end diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Package.swift b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Package.swift new file mode 100644 index 00000000..9cd83e6e --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Package.swift @@ -0,0 +1,24 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "device_info_plus", + platforms: [ + .macOS("10.14") + ], + products: [ + .library(name: "device-info-plus", targets: ["device_info_plus"]) + ], + dependencies: [], + targets: [ + .target( + name: "device_info_plus", + dependencies: [], + resources: [ + .process("PrivacyInfo.xcprivacy"), + ] + ) + ] +) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/CwlSysctl.swift b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/CwlSysctl.swift new file mode 100644 index 00000000..3f29e31c --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/CwlSysctl.swift @@ -0,0 +1,152 @@ +// +// CwlSysctl.swift +// CwlUtils +// +// Created by Matt Gallagher on 2016/02/03. +// Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +// IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +import Foundation + +/// A "static"-only namespace around a series of functions that operate on buffers returned from the `Darwin.sysctl` function +public struct Sysctl { + /// Possible errors. + public enum Error: Swift.Error { + case unknown + case malformedUTF8 + case invalidSize + case posixError(POSIXErrorCode) + } + + /// Access the raw data for an array of sysctl identifiers. + public static func data(for keys: [Int32]) throws -> [Int8] { + return try keys.withUnsafeBufferPointer() { keysPointer throws -> [Int8] in + // Preflight the request to get the required data size + var requiredSize = 0 + let preFlightResult = Darwin.sysctl(UnsafeMutablePointer(mutating: keysPointer.baseAddress), UInt32(keys.count), nil, &requiredSize, nil, 0) + if preFlightResult != 0 { + throw POSIXErrorCode(rawValue: errno).map { + return Error.posixError($0) + } ?? Error.unknown + } + + // Run the actual request with an appropriately sized array buffer + let data = Array(repeating: 0, count: requiredSize) + let result = data.withUnsafeBufferPointer() { dataBuffer -> Int32 in + return Darwin.sysctl(UnsafeMutablePointer(mutating: keysPointer.baseAddress), UInt32(keys.count), UnsafeMutableRawPointer(mutating: dataBuffer.baseAddress), &requiredSize, nil, 0) + } + if result != 0 { + throw POSIXErrorCode(rawValue: errno).map { Error.posixError($0) } ?? Error.unknown + } + + return data + } + } + + /// Convert a sysctl name string like "hw.memsize" to the array of `sysctl` identifiers (e.g. [CTL_HW, HW_MEMSIZE]) + public static func keys(for name: String) throws -> [Int32] { + var keysBufferSize = Int(CTL_MAXNAME) + var keysBuffer = Array(repeating: 0, count: keysBufferSize) + try keysBuffer.withUnsafeMutableBufferPointer { (lbp: inout UnsafeMutableBufferPointer) throws in + try name.withCString { (nbp: UnsafePointer) throws in + guard sysctlnametomib(nbp, lbp.baseAddress, &keysBufferSize) == 0 else { + throw POSIXErrorCode(rawValue: errno).map { Error.posixError($0) } ?? Error.unknown + } + } + } + if keysBuffer.count > keysBufferSize { + keysBuffer.removeSubrange(keysBufferSize..(ofType: T.Type, forKeys keys: [Int32]) throws -> T { + let buffer = try data(for: keys) + if buffer.count != MemoryLayout.size { + throw Error.invalidSize + } + return try buffer.withUnsafeBufferPointer() { bufferPtr throws -> T in + guard let baseAddress = bufferPtr.baseAddress else { throw Error.unknown } + return baseAddress.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee } + } + } + + /// Invoke `sysctl` with an array of identifers, interpreting the returned buffer as the specified type. This function will throw `Error.invalidSize` if the size of buffer returned from `sysctl` fails to match the size of `T`. + public static func value(ofType type: T.Type, forKeys keys: Int32...) throws -> T { + return try value(ofType: type, forKeys: keys) + } + + /// Invoke `sysctl` with the specified name, interpreting the returned buffer as the specified type. This function will throw `Error.invalidSize` if the size of buffer returned from `sysctl` fails to match the size of `T`. + public static func value(ofType type: T.Type, forName name: String) throws -> T { + return try value(ofType: type, forKeys: keys(for: name)) + } + + /// Invoke `sysctl` with an array of identifers, interpreting the returned buffer as a `String`. This function will throw `Error.malformedUTF8` if the buffer returned from `sysctl` cannot be interpreted as a UTF8 buffer. + public static func string(for keys: [Int32]) throws -> String { + let optionalString = try data(for: keys).withUnsafeBufferPointer() { dataPointer -> String? in + dataPointer.baseAddress.flatMap { String(validatingUTF8: $0) } + } + guard let s = optionalString else { + throw Error.malformedUTF8 + } + return s + } + + /// Invoke `sysctl` with an array of identifers, interpreting the returned buffer as a `String`. This function will throw `Error.malformedUTF8` if the buffer returned from `sysctl` cannot be interpreted as a UTF8 buffer. + public static func string(for keys: Int32...) throws -> String { + return try string(for: keys) + } + + /// Invoke `sysctl` with the specified name, interpreting the returned buffer as a `String`. This function will throw `Error.malformedUTF8` if the buffer returned from `sysctl` cannot be interpreted as a UTF8 buffer. + public static func string(for name: String) throws -> String { + return try string(for: keys(for: name)) + } + + /// e.g. "MyComputer.local" (from System Preferences -> Sharing -> Computer Name) + public static var hostName: String { return (try? Sysctl.string(for: [CTL_KERN, KERN_HOSTNAME])) ?? "" } + + /// e.g. "x86_64" or "arm64" + public static var machine: String { return (try? Sysctl.string(for: [CTL_HW, HW_MACHINE])) ?? "" } + + /// e.g. "MacBookPro18,2" + public static var model: String { return (try? Sysctl.string(for: [CTL_HW, HW_MODEL])) ?? "" } + + /// e.g. "8" or "2" + public static var activeCPUs: Int32 { return (try? Sysctl.value(ofType: Int32.self, forKeys: [CTL_HW, HW_AVAILCPU])) ?? 0 } + + /// e.g. "15.3.0" or "15.0.0" + public static var osRelease: String { return (try? Sysctl.string(for: [CTL_KERN, KERN_OSRELEASE])) ?? "" } + + /// e.g. "Darwin" or "Darwin" + public static var osType: String { return (try? Sysctl.string(for: [CTL_KERN, KERN_OSTYPE])) ?? "" } + + /// e.g. "15D21" or "13D20" + public static var osVersion: String { return (try? Sysctl.string(for: [CTL_KERN, KERN_OSVERSION])) ?? "" } + + /// e.g. "Darwin Kernel Version 15.3.0: Thu Dec 10 18:40:58 PST 2015; root:xnu-3248.30.4~1/RELEASE_X86_64" or + /// "Darwin Kernel Version 15.0.0: Wed Dec 9 22:19:38 PST 2015; root:xnu-3248.31.3~2/RELEASE_ARM64_S8000" + public static var version: String { return (try? Sysctl.string(for: [CTL_KERN, KERN_VERSION])) ?? "" } + + #if os(macOS) + /// e.g. 199506 (not available on iOS) + public static var osRev: Int32 { return (try? Sysctl.value(ofType: Int32.self, forKeys: [CTL_KERN, KERN_OSREV])) ?? 0 } + + /// e.g. 2659000000 (not available on iOS) + public static var cpuFreq: Int64 { return (try? Sysctl.value(ofType: Int64.self, forName: "hw.cpufrequency")) ?? 0 } + + /// e.g. 25769803776 (not available on iOS) + public static var memSize: UInt64 { return (try? Sysctl.value(ofType: UInt64.self, forKeys: [CTL_HW, HW_MEMSIZE])) ?? 0 } + #endif +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/DeviceIdentifiers.swift b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/DeviceIdentifiers.swift new file mode 100644 index 00000000..750c9b7f --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/DeviceIdentifiers.swift @@ -0,0 +1,105 @@ +import Foundation + +// Models list is taken from support.apple.com and https://theapplewiki.com/wiki/Models +// Example of the list with models https://support.apple.com/en-us/102869 + +func getMacModelName(modelNumber: String) -> String { + switch modelNumber { + // MacBook models (2015 and later) + case "MacBook8,1": return "MacBook (12-inch, 2015)" + case "MacBook9,1": return "MacBook (12-inch, 2016)" + case "MacBook10,1": return "MacBook (12-inch, 2017)" + + // MacBook Air models (2013 and later) + case "MacBookAir6,1": return "MacBook Air (11-inch, 2013)" + case "MacBookAir6,2": return "MacBook Air (13-inch, 2013)" + case "MacBookAir7,1": return "MacBook Air (11-inch, 2015)" + case "MacBookAir7,2": return "MacBook Air (13-inch, 2015-2017)" + case "MacBookAir8,1": return "MacBook Air (13-inch, 2018)" + case "MacBookAir8,2": return "MacBook Air (13-inch, 2019)" + case "MacBookAir9,1": return "MacBook Air (13-inch, 2020)" + case "MacBookAir10,1": return "MacBook Air (13-inch, 2020)" + case "Mac14,2": return "MacBook Air (13-inch, 2022)" + case "Mac14,15": return "MacBook Air (15-inch, 2023)" + case "Mac15,12": return "MacBook Air (13-inch, 2024)" + case "Mac15,13": return "MacBook Air (15-inch, 2024)" + case "Mac16,12": return "MacBook Air (13-inch, 2025)" + case "Mac16,13": return "MacBook Air (15-inch, 2025)" + + // MacBook Pro models (2012 and later) + case "MacBookPro10,1": return "MacBook Pro (15-inch, 2012-2013)" + case "MacBookPro10,2": return "MacBook Pro (13-inch, 2012-2013)" + case "MacBookPro11,1": return "MacBook Pro (13-inch, 2013-2014)" + case "MacBookPro11,2", "MacBookPro11,3": return "MacBook Pro (15-inch, 2013-2014)" + case "MacBookPro11,4", "MacBookPro11,5": return "MacBook Pro (15-inch, 2015)" + case "MacBookPro12,1": return "MacBook Pro (13-inch, 2015)" + case "MacBookPro13,1": return "MacBook Pro (13-inch, 2016)" + case "MacBookPro13,2": return "MacBook Pro (13-inch, 2016)" + case "MacBookPro13,3": return "MacBook Pro (15-inch, 2016)" + case "MacBookPro14,1": return "MacBook Pro (13-inch, 2017)" + case "MacBookPro14,2": return "MacBook Pro (13-inch, 2017)" + case "MacBookPro14,3": return "MacBook Pro (15-inch, 2017)" + case "MacBookPro15,1", "MacBookPro15,3": return "MacBook Pro (15-inch, 2018-2019)" + case "MacBookPro15,2": return "MacBook Pro (13-inch, 2018-2019)" + case "MacBookPro15,4": return "MacBook Pro (13-inch, 2019)" + case "MacBookPro16,1", "MacBookPro16,4": return "MacBook Pro (16-inch, 2019)" + case "MacBookPro16,2": return "MacBook Pro (13-inch, 2019)" + case "MacBookPro16,3": return "MacBook Pro (13-inch, 2020)" + case "MacBookPro17,1": return "MacBook Pro (13-inch, 2020)" + case "MacBookPro18,1": return "MacBook Pro (14-inch, 2021)" + case "MacBookPro18,2": return "MacBook Pro (16-inch, 2021)" + case "MacBookPro18,3": return "MacBook Pro (16-inch, 2021)" + case "Mac14,5", "Mac14,9": return "MacBook Pro (14-inch, 2023)" + case "Mac14,6", "Mac14,10": return "MacBook Pro (14-inch, 2023)" + case "Mac14,7": return "MacBook Pro (13-inch, 2022)" + case "Mac15,3": return "MacBook Pro (14-inch, 2023)" + case "Mac15,6", "Mac15,8", "Mac15,10": return "MacBook Pro (14-inch, 2023)" + case "Mac15,7", "Mac15,9", "Mac15,11": return "MacBook Pro (16-inch, 2023)" + case "Mac16,1", "Mac16,6", "Mac16,8": return "MacBook Pro (14-inch, 2024)" + case "Mac16,5", "Mac16,7": return "MacBook Pro (16-inch, 2024)" + case "Mac17,2": return "MacBook Pro (14-inch, 2025)" + + // iMac models (2013 and later) + case "iMac13,1": return "iMac (21.5-inch, 2013)" + case "iMac13,2": return "iMac (27-inch, 2013)" + case "iMac14,1": return "iMac (21.5-inch, 2014)" + case "iMac14,2": return "iMac (27-inch, 2014)" + case "iMac14,4": return "iMac (21.5-inch, 2014)" + case "iMac15,1": return "iMac (27-inch, 2014-2015)" + case "iMac16,1","iMac16,2": return "iMac (21.5-inch, 2015)" + case "iMac17,1": return "iMac (27-inch, 2015)" + case "iMac18,1": return "iMac (21.5-inch, 2017)" + case "iMac18,2": return "iMac (21.5-inch, 2017)" + case "iMac18,3": return "iMac (27-inch, 2017)" + case "iMac19,1": return "iMac (27-inch, 2019)" + case "iMac19,2": return "iMac (21.5-inch, 2019)" + case "iMac20,1", "iMac20,2": return "iMac (27-inch, 2020)" + case "iMac21,1", "iMac21,2": return "iMac (24-inch, 2021)" + case "Mac15,4", "Mac15,5": return "iMac (24-inch, 2023)" + case "Mac16,2", "Mac16,3": return "iMac (24-inch, 2024)" + + // Mac mini models (2012 and later) + case "MacMini6,1", "MacMini6,2": return "Mac mini (2012)" + case "MacMini7,1": return "Mac mini (2014)" + case "MacMini8,1": return "Mac mini (2018)" + case "MacMini9,1": return "Mac mini (2020)" + case "Mac14,12": return "Mac mini (2023)" + case "Mac14,3": return "Mac mini (2023)" + case "Mac16,11", "Mac16,10": return "Mac mini (2024)" + + // Mac Pro models (2013 and later) + case "MacPro6,1": return "Mac Pro (Late 2013)" + case "MacPro7,1": return "Mac Pro (2019)" + case "Mac14,8": return "Mac Pro (2023)" + + // iMac Pro + case "iMacPro1,1": return "iMac Pro (2017)" + + // Mac Studio (2022 and newer) + case "Mac13,1", "Mac13,2": return "Mac Studio (2022)" + case "Mac14,13", "Mac14,14": return "Mac Studio (2023)" + case "Mac15,14", "Mac16,9": return "Mac Studio (2025)" + + default: return "Unknown Model" + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/DeviceInfoPlusMacosPlugin.swift b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/DeviceInfoPlusMacosPlugin.swift new file mode 100644 index 00000000..a1f68236 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/DeviceInfoPlusMacosPlugin.swift @@ -0,0 +1,54 @@ +import Cocoa +import FlutterMacOS + +public class DeviceInfoPlusMacosPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "dev.fluttercommunity.plus/device_info", binaryMessenger: registrar.messenger) + let instance = DeviceInfoPlusMacosPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "getDeviceInfo": + handleDeviceInfo(result: result) + default: + result(FlutterMethodNotImplemented) + } + } + + private func handleDeviceInfo(result: @escaping FlutterResult)-> Void{ + let computerName = Host.current().localizedName ?? Sysctl.hostName + let hostName = Sysctl.osType + let arch = Sysctl.machine + let model = Sysctl.model + let modelName = getMacModelName(modelNumber: Sysctl.model) + let kernelVersion = Sysctl.version + let osRelease = ProcessInfo.processInfo.operatingSystemVersionString + let osVersion = ProcessInfo.processInfo.operatingSystemVersion; + let majorVersion = osVersion.majorVersion + let minorVersion = osVersion.minorVersion + let patchVersion = osVersion.patchVersion + let activeCPUs = Sysctl.activeCPUs + let memorySize = Sysctl.memSize + let cpuFrequency = Sysctl.cpuFreq + let guid = SystemUUID.getSystemUUID() + + result([ + "computerName": computerName, + "hostName": hostName, + "arch": arch, + "model": model, + "modelName": modelName, + "kernelVersion": kernelVersion, + "osRelease": osRelease, + "majorVersion": majorVersion, + "minorVersion": minorVersion, + "patchVersion": patchVersion, + "activeCPUs": activeCPUs, + "memorySize": memorySize, + "cpuFrequency": cpuFrequency, + "systemGUID": guid + ] as [String: Any?]) + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/PrivacyInfo.xcprivacy b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..918d80be --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/PrivacyInfo.xcprivacy @@ -0,0 +1,12 @@ + + + + + NSPrivacyTrackingDomains + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + + diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/SystemUUID.swift b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/SystemUUID.swift new file mode 100644 index 00000000..a651c5f4 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/macos/device_info_plus/Sources/device_info_plus/SystemUUID.swift @@ -0,0 +1,16 @@ +import Foundation + +public enum SystemUUID { + public static func getSystemUUID() -> String? { + let mainPort: mach_port_t + if #available(macOS 12.0, *) { + mainPort = kIOMainPortDefault + } else { + mainPort = kIOMasterPortDefault + } + let service = IOServiceGetMatchingService(mainPort, IOServiceMatching("IOPlatformExpertDevice")) + let uuid = IORegistryEntryCreateCFProperty(service, kIOPlatformUUIDKey as CFString, kCFAllocatorDefault, 0).takeRetainedValue() as? String + IOObjectRelease(service) + return uuid + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/pubspec.yaml b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/pubspec.yaml new file mode 100644 index 00000000..ad90671e --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/device_info_plus/pubspec.yaml @@ -0,0 +1,53 @@ +name: device_info_plus +description: Flutter plugin providing detailed information about the device + (make, model, etc.), and Android or iOS version the app is running on. +version: 12.3.0 +homepage: https://github.com/fluttercommunity/plus_plugins +repository: https://github.com/fluttercommunity/plus_plugins/tree/main/packages/device_info_plus/device_info_plus +issue_tracker: https://github.com/fluttercommunity/plus_plugins/labels/device_info_plus +topics: + - device + - information + - utils + +flutter: + plugin: + platforms: + android: + package: dev.fluttercommunity.plus.device_info + pluginClass: DeviceInfoPlusPlugin + ios: + pluginClass: FPPDeviceInfoPlusPlugin + linux: + dartPluginClass: DeviceInfoPlusLinuxPlugin + web: + pluginClass: DeviceInfoPlusWebPlugin + fileName: src/device_info_plus_web.dart + macos: + pluginClass: DeviceInfoPlusMacosPlugin + windows: + dartPluginClass: DeviceInfoPlusWindowsPlugin + +dependencies: + device_info_plus_platform_interface: ^7.0.3 + ffi: ^2.1.4 + file: ^7.0.1 + flutter: + sdk: flutter + flutter_web_plugins: + sdk: flutter + meta: ^1.16.0 + web: ^1.1.0 + win32: ^5.11.0 + win32_registry: ^2.1.0 + +dev_dependencies: + flutter_lints: ^5.0.0 + flutter_test: + sdk: flutter + mockito: ^5.4.5 + test: ^1.25.15 + +environment: + sdk: ">=3.7.0 <4.0.0" + flutter: ">=3.29.0" diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/AUTHORS b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/AUTHORS new file mode 100644 index 00000000..557dff97 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/AUTHORS @@ -0,0 +1,6 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/CHANGELOG.md b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/CHANGELOG.md new file mode 100644 index 00000000..54d3a9e6 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/CHANGELOG.md @@ -0,0 +1,76 @@ +## 0.9.4 + +* Adds `getDirectoryPathWithOptions` and `getDirectoryPathsWithOptions` implementations. + +## 0.9.3+3 + +* Updates to Pigeon 26. +* Updates minimum supported SDK version to Flutter 3.32/Dart 3.8. + +## 0.9.3+2 + +* Updates Pigeon to resolve a compilation failure with some versions of glib. + +## 0.9.3+1 + +* Fixes a regression in 0.9.3 with handling of canceled dialogs. + +## 0.9.3 + +* Updates method channel implementation to use Pigeon. +* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. + +## 0.9.2+1 + +* Adds pub topics to package metadata. +* Updates minimum supported SDK version to Flutter 3.7/Dart 2.19. +* Migrates `styleFrom` usage in examples off of deprecated `primary` and `onPrimary` parameters. + +## 0.9.2 + +* Adds `getSaveLocation` and deprecates `getSavePath`. +* Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. + +## 0.9.1+3 + +* Sets a cmake_policy compatibility version to fix build warnings. + +## 0.9.1+2 + +* Clarifies explanation of endorsement in README. +* Aligns Dart and Flutter SDK constraints. + +## 0.9.1+1 + +* Updates links for the merge of flutter/plugins into flutter/packages. +* Updates example code for `use_build_context_synchronously` lint. +* Updates minimum Flutter version to 3.0. + +## 0.9.1 + +* Adds `getDirectoryPaths` implementation. + +## 0.9.0+1 + +* Changes XTypeGroup initialization from final to const. +* Updates minimum Flutter version to 2.10. + +## 0.9.0 + +* Moves source to flutter/plugins. + +## 0.0.3 + +* Adds Dart implementation for in-package method channel. + +## 0.0.2+1 + +* Updates README + +## 0.0.2 + +* Updates SDK constraint to signal compatibility with null safety. + +## 0.0.1 + +* Initial Linux implementation of `file_selector`. diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/get_directory_page.dart b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/get_directory_page.dart new file mode 100644 index 00000000..e483b8c6 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/get_directory_page.dart @@ -0,0 +1,76 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select a directory using `getDirectoryPath`, +/// then displays the selected directory in a dialog. +class GetDirectoryPage extends StatelessWidget { + /// Default Constructor + const GetDirectoryPage({super.key}); + + Future _getDirectoryPath(BuildContext context) async { + const String confirmButtonText = 'Choose'; + final String? directoryPath = await FileSelectorPlatform.instance + .getDirectoryPath(confirmButtonText: confirmButtonText); + if (directoryPath == null) { + // Operation was canceled by the user. + return; + } + if (context.mounted) { + await showDialog( + context: context, + builder: (BuildContext context) => TextDisplay(directoryPath), + ); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Open a text file')), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + ), + child: const Text('Press to ask user to choose a directory'), + onPressed: () => _getDirectoryPath(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class TextDisplay extends StatelessWidget { + /// Creates a `TextDisplay`. + const TextDisplay(this.directoryPath, {super.key}); + + /// The path selected in the dialog. + final String directoryPath; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Selected Directory'), + content: Scrollbar( + child: SingleChildScrollView(child: Text(directoryPath)), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () => Navigator.pop(context), + ), + ], + ); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/get_multiple_directories_page.dart b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/get_multiple_directories_page.dart new file mode 100644 index 00000000..fe7fd3d2 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/get_multiple_directories_page.dart @@ -0,0 +1,79 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select one or more directories using `getDirectoryPaths`, +/// then displays the selected directories in a dialog. +class GetMultipleDirectoriesPage extends StatelessWidget { + /// Default Constructor + const GetMultipleDirectoriesPage({super.key}); + + Future _getDirectoryPaths(BuildContext context) async { + const String confirmButtonText = 'Choose'; + final List directoryPaths = await FileSelectorPlatform.instance + .getDirectoryPaths(confirmButtonText: confirmButtonText); + if (directoryPaths.isEmpty) { + // Operation was canceled by the user. + return; + } + if (context.mounted) { + await showDialog( + context: context, + builder: (BuildContext context) => + TextDisplay(directoryPaths.join('\n')), + ); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Select multiple directories')), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + ), + child: const Text( + 'Press to ask user to choose multiple directories', + ), + onPressed: () => _getDirectoryPaths(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class TextDisplay extends StatelessWidget { + /// Creates a `TextDisplay`. + const TextDisplay(this.directoriesPaths, {super.key}); + + /// The path selected in the dialog. + final String directoriesPaths; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Selected Directories'), + content: Scrollbar( + child: SingleChildScrollView(child: Text(directoriesPaths)), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () => Navigator.pop(context), + ), + ], + ); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/home_page.dart b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/home_page.dart new file mode 100644 index 00000000..b4988485 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/home_page.dart @@ -0,0 +1,65 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +/// Home Page of the application. +class HomePage extends StatelessWidget { + /// Default Constructor + const HomePage({super.key}); + + @override + Widget build(BuildContext context) { + final ButtonStyle style = ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + ); + return Scaffold( + appBar: AppBar(title: const Text('File Selector Demo Home Page')), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: style, + child: const Text('Open a text file'), + onPressed: () => Navigator.pushNamed(context, '/open/text'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open an image'), + onPressed: () => Navigator.pushNamed(context, '/open/image'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open multiple images'), + onPressed: () => Navigator.pushNamed(context, '/open/images'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Save a file'), + onPressed: () => Navigator.pushNamed(context, '/save/text'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open a get directory dialog'), + onPressed: () => Navigator.pushNamed(context, '/directory'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open a get directories dialog'), + onPressed: () => + Navigator.pushNamed(context, '/multi-directories'), + ), + ], + ), + ), + ); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/main.dart b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/main.dart new file mode 100644 index 00000000..28a34200 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/main.dart @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +import 'get_directory_page.dart'; +import 'get_multiple_directories_page.dart'; +import 'home_page.dart'; +import 'open_image_page.dart'; +import 'open_multiple_images_page.dart'; +import 'open_text_page.dart'; +import 'save_text_page.dart'; + +void main() { + runApp(const MyApp()); +} + +/// MyApp is the Main Application. +class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'File Selector Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: const HomePage(), + routes: { + '/open/image': (BuildContext context) => const OpenImagePage(), + '/open/images': (BuildContext context) => + const OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => const OpenTextPage(), + '/save/text': (BuildContext context) => SaveTextPage(), + '/directory': (BuildContext context) => const GetDirectoryPage(), + '/multi-directories': (BuildContext context) => + const GetMultipleDirectoriesPage(), + }, + ); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/open_image_page.dart b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/open_image_page.dart new file mode 100644 index 00000000..f5705d24 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/open_image_page.dart @@ -0,0 +1,91 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select an image file using +/// `openFiles`, then displays the selected images in a gallery dialog. +class OpenImagePage extends StatelessWidget { + /// Default Constructor + const OpenImagePage({super.key}); + + Future _openImageFile(BuildContext context) async { + const XTypeGroup typeGroup = XTypeGroup( + label: 'images', + extensions: ['jpg', 'png'], + ); + final XFile? file = await FileSelectorPlatform.instance.openFile( + acceptedTypeGroups: [typeGroup], + ); + if (file == null) { + // Operation was canceled by the user. + return; + } + final String fileName = file.name; + final String filePath = file.path; + + if (context.mounted) { + await showDialog( + context: context, + builder: (BuildContext context) => ImageDisplay(fileName, filePath), + ); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Open an image')), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + ), + child: const Text('Press to open an image file(png, jpg)'), + onPressed: () => _openImageFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays an image in a dialog. +class ImageDisplay extends StatelessWidget { + /// Default Constructor. + const ImageDisplay(this.fileName, this.filePath, {super.key}); + + /// The name of the selected file. + final String fileName; + + /// The path to the selected file. + final String filePath; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(fileName), + // On web the filePath is a blob url + // while on other platforms it is a system path. + content: kIsWeb ? Image.network(filePath) : Image.file(File(filePath)), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/open_multiple_images_page.dart b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/open_multiple_images_page.dart new file mode 100644 index 00000000..0b9fe352 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/open_multiple_images_page.dart @@ -0,0 +1,101 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select multiple image files using +/// `openFiles`, then displays the selected images in a gallery dialog. +class OpenMultipleImagesPage extends StatelessWidget { + /// Default Constructor + const OpenMultipleImagesPage({super.key}); + + Future _openImageFile(BuildContext context) async { + const XTypeGroup jpgsTypeGroup = XTypeGroup( + label: 'JPEGs', + extensions: ['jpg', 'jpeg'], + ); + const XTypeGroup pngTypeGroup = XTypeGroup( + label: 'PNGs', + extensions: ['png'], + ); + final List files = await FileSelectorPlatform.instance.openFiles( + acceptedTypeGroups: [jpgsTypeGroup, pngTypeGroup], + ); + if (files.isEmpty) { + // Operation was canceled by the user. + return; + } + if (context.mounted) { + await showDialog( + context: context, + builder: (BuildContext context) => MultipleImagesDisplay(files), + ); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Open multiple images')), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + ), + child: const Text('Press to open multiple images (png, jpg)'), + onPressed: () => _openImageFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class MultipleImagesDisplay extends StatelessWidget { + /// Default Constructor. + const MultipleImagesDisplay(this.files, {super.key}); + + /// The files containing the images. + final List files; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Gallery'), + // On web the filePath is a blob url + // while on other platforms it is a system path. + content: Center( + child: Row( + children: [ + ...files.map( + (XFile file) => Flexible( + child: kIsWeb + ? Image.network(file.path) + : Image.file(File(file.path)), + ), + ), + ], + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/open_text_page.dart b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/open_text_page.dart new file mode 100644 index 00000000..0a50be30 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/open_text_page.dart @@ -0,0 +1,86 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select a text file using `openFile`, then +/// displays its contents in a dialog. +class OpenTextPage extends StatelessWidget { + /// Default Constructor + const OpenTextPage({super.key}); + + Future _openTextFile(BuildContext context) async { + const XTypeGroup typeGroup = XTypeGroup( + label: 'text', + extensions: ['txt', 'json'], + ); + final XFile? file = await FileSelectorPlatform.instance.openFile( + acceptedTypeGroups: [typeGroup], + ); + if (file == null) { + // Operation was canceled by the user. + return; + } + final String fileName = file.name; + final String fileContent = await file.readAsString(); + + if (context.mounted) { + await showDialog( + context: context, + builder: (BuildContext context) => TextDisplay(fileName, fileContent), + ); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Open a text file')), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + ), + child: const Text('Press to open a text file (json, txt)'), + onPressed: () => _openTextFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class TextDisplay extends StatelessWidget { + /// Default Constructor. + const TextDisplay(this.fileName, this.fileContent, {super.key}); + + /// The name of the selected file. + final String fileName; + + /// The contents of the text file. + final String fileContent; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(fileName), + content: Scrollbar( + child: SingleChildScrollView(child: Text(fileContent)), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () => Navigator.pop(context), + ), + ], + ); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/save_text_page.dart b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/save_text_page.dart new file mode 100644 index 00000000..bcc26efe --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/lib/save_text_page.dart @@ -0,0 +1,81 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:typed_data'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select a save location using `getSavePath`, +/// then writes text to a file at that location. +class SaveTextPage extends StatelessWidget { + /// Default Constructor + SaveTextPage({super.key}); + + final TextEditingController _nameController = TextEditingController(); + final TextEditingController _contentController = TextEditingController(); + + Future _saveFile() async { + final String fileName = _nameController.text; + final FileSaveLocation? result = await FileSelectorPlatform.instance + .getSaveLocation(options: SaveDialogOptions(suggestedName: fileName)); + // Operation was canceled by the user. + if (result == null) { + return; + } + final String text = _contentController.text; + final Uint8List fileData = Uint8List.fromList(text.codeUnits); + const String fileMimeType = 'text/plain'; + final XFile textFile = XFile.fromData( + fileData, + mimeType: fileMimeType, + name: fileName, + ); + await textFile.saveTo(result.path); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Save text into a file')), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _nameController, + decoration: const InputDecoration( + hintText: '(Optional) Suggest File Name', + ), + ), + ), + SizedBox( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _contentController, + decoration: const InputDecoration( + hintText: 'Enter File Contents', + ), + ), + ), + const SizedBox(height: 10), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + ), + onPressed: _saveFile, + child: const Text('Press to save a text file'), + ), + ], + ), + ), + ); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/CMakeLists.txt new file mode 100644 index 00000000..9d7224cc --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/CMakeLists.txt @@ -0,0 +1,111 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "example") +set(APPLICATION_ID "com.example.example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Enable the test target. +set(include_file_selector_linux_tests TRUE) +# Provide an alias for the test target using the name expected by repo tooling. +add_custom_target(unit_tests DEPENDS file_selector_linux_test) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/flutter/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/flutter/CMakeLists.txt new file mode 100644 index 00000000..33fd5801 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,87 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/flutter/generated_plugins.cmake b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 00000000..2db3c22a --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + file_selector_linux +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/main.cc b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/main.cc new file mode 100644 index 00000000..3b03bbf6 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/main.cc @@ -0,0 +1,10 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/my_application.cc b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/my_application.cc new file mode 100644 index 00000000..19727b67 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/my_application.cc @@ -0,0 +1,110 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "example"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments( + project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, + gchar*** arguments, + int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = + my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new( + my_application_get_type(), "application-id", APPLICATION_ID, nullptr)); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/my_application.h b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/my_application.h new file mode 100644 index 00000000..9ae704a9 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/linux/my_application.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/pubspec.yaml b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/pubspec.yaml new file mode 100644 index 00000000..73248a27 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/example/pubspec.yaml @@ -0,0 +1,22 @@ +name: file_selector_linux_example +description: Local testbed for Linux file_selector implementation. +publish_to: 'none' +version: 1.0.0+1 + +environment: + sdk: ^3.8.0 + flutter: ">=3.32.0" + +dependencies: + file_selector_linux: + path: ../ + file_selector_platform_interface: ^2.7.0 + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/lib/file_selector_linux.dart b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/lib/file_selector_linux.dart new file mode 100644 index 00000000..f80c9f60 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/lib/file_selector_linux.dart @@ -0,0 +1,187 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; + +import 'src/messages.g.dart'; + +/// An implementation of [FileSelectorPlatform] for Linux. +class FileSelectorLinux extends FileSelectorPlatform { + /// Creates a new plugin implementation instance. + FileSelectorLinux({@visibleForTesting FileSelectorApi? api}) + : _hostApi = api ?? FileSelectorApi(); + + final FileSelectorApi _hostApi; + + /// Registers the Linux implementation. + static void registerWith() { + FileSelectorPlatform.instance = FileSelectorLinux(); + } + + @override + Future openFile({ + List? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText, + }) async { + final List paths = await _hostApi.showFileChooser( + PlatformFileChooserActionType.open, + PlatformFileChooserOptions( + allowedFileTypes: _platformTypeGroupsFromXTypeGroups( + acceptedTypeGroups, + ), + currentFolderPath: initialDirectory, + acceptButtonLabel: confirmButtonText, + selectMultiple: false, + ), + ); + return paths.isEmpty ? null : XFile(paths.first); + } + + @override + Future> openFiles({ + List? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText, + }) async { + final List paths = await _hostApi.showFileChooser( + PlatformFileChooserActionType.open, + PlatformFileChooserOptions( + allowedFileTypes: _platformTypeGroupsFromXTypeGroups( + acceptedTypeGroups, + ), + currentFolderPath: initialDirectory, + acceptButtonLabel: confirmButtonText, + selectMultiple: true, + ), + ); + return paths.map((String path) => XFile(path)).toList(); + } + + @override + Future getSavePath({ + List? acceptedTypeGroups, + String? initialDirectory, + String? suggestedName, + String? confirmButtonText, + }) async { + final FileSaveLocation? location = await getSaveLocation( + acceptedTypeGroups: acceptedTypeGroups, + options: SaveDialogOptions( + initialDirectory: initialDirectory, + suggestedName: suggestedName, + confirmButtonText: confirmButtonText, + ), + ); + return location?.path; + } + + @override + Future getSaveLocation({ + List? acceptedTypeGroups, + SaveDialogOptions options = const SaveDialogOptions(), + }) async { + // TODO(stuartmorgan): Add the selected type group here and return it. See + // https://github.com/flutter/flutter/issues/107093 + final List paths = await _hostApi.showFileChooser( + PlatformFileChooserActionType.save, + PlatformFileChooserOptions( + allowedFileTypes: _platformTypeGroupsFromXTypeGroups( + acceptedTypeGroups, + ), + currentFolderPath: options.initialDirectory, + currentName: options.suggestedName, + acceptButtonLabel: options.confirmButtonText, + createFolders: options.canCreateDirectories, + ), + ); + return paths.isEmpty ? null : FileSaveLocation(paths.first); + } + + @override + Future getDirectoryPath({ + String? initialDirectory, + String? confirmButtonText, + }) async { + return getDirectoryPathWithOptions( + FileDialogOptions( + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText, + ), + ); + } + + @override + Future getDirectoryPathWithOptions(FileDialogOptions options) async { + final List paths = await _hostApi.showFileChooser( + PlatformFileChooserActionType.chooseDirectory, + PlatformFileChooserOptions( + currentFolderPath: options.initialDirectory, + acceptButtonLabel: options.confirmButtonText, + createFolders: options.canCreateDirectories, + selectMultiple: false, + ), + ); + return paths.isEmpty ? null : paths.first; + } + + @override + Future> getDirectoryPaths({ + String? initialDirectory, + String? confirmButtonText, + }) async { + return getDirectoryPathsWithOptions( + FileDialogOptions( + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText, + ), + ); + } + + @override + Future> getDirectoryPathsWithOptions( + FileDialogOptions options, + ) async { + return _hostApi.showFileChooser( + PlatformFileChooserActionType.chooseDirectory, + PlatformFileChooserOptions( + currentFolderPath: options.initialDirectory, + acceptButtonLabel: options.confirmButtonText, + createFolders: options.canCreateDirectories, + selectMultiple: true, + ), + ); + } +} + +List? _platformTypeGroupsFromXTypeGroups( + List? groups, +) { + return groups?.map(_platformTypeGroupFromXTypeGroup).toList(); +} + +PlatformTypeGroup _platformTypeGroupFromXTypeGroup(XTypeGroup group) { + final String label = group.label ?? ''; + if (group.allowsAny) { + return PlatformTypeGroup(label: label, extensions: ['*']); + } + if ((group.extensions?.isEmpty ?? true) && + (group.mimeTypes?.isEmpty ?? true)) { + throw ArgumentError( + 'Provided type group $group does not allow ' + 'all files, but does not set any of the Linux-supported filter ' + 'categories. "extensions" or "mimeTypes" must be non-empty for Linux ' + 'if anything is non-empty.', + ); + } + return PlatformTypeGroup( + label: label, + // Covert to GtkFileFilter's *. format. + extensions: + group.extensions?.map((String extension) => '*.$extension').toList() ?? + [], + mimeTypes: group.mimeTypes ?? [], + ); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/lib/src/messages.g.dart b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/lib/src/messages.g.dart new file mode 100644 index 00000000..592db036 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/lib/src/messages.g.dart @@ -0,0 +1,261 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v26.1.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +bool _deepEquals(Object? a, Object? b) { + if (a is List && b is List) { + return a.length == b.length && + a.indexed.every( + ((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]), + ); + } + if (a is Map && b is Map) { + return a.length == b.length && + a.entries.every( + (MapEntry entry) => + (b as Map).containsKey(entry.key) && + _deepEquals(entry.value, b[entry.key]), + ); + } + return a == b; +} + +/// A Pigeon representation of the GTK_FILE_CHOOSER_ACTION_* options. +enum PlatformFileChooserActionType { open, chooseDirectory, save } + +/// A Pigeon representation of the Linux portion of an `XTypeGroup`. +class PlatformTypeGroup { + PlatformTypeGroup({ + this.label = '', + this.extensions = const [], + this.mimeTypes = const [], + }); + + String label; + + List extensions; + + List mimeTypes; + + List _toList() { + return [label, extensions, mimeTypes]; + } + + Object encode() { + return _toList(); + } + + static PlatformTypeGroup decode(Object result) { + result as List; + return PlatformTypeGroup( + label: result[0]! as String, + extensions: (result[1] as List?)!.cast(), + mimeTypes: (result[2] as List?)!.cast(), + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! PlatformTypeGroup || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); +} + +/// Options for GKT file chooser. +/// +/// These correspond to gtk_file_chooser_set_* options. +class PlatformFileChooserOptions { + PlatformFileChooserOptions({ + this.allowedFileTypes, + this.currentFolderPath, + this.currentName, + this.acceptButtonLabel, + this.selectMultiple, + this.createFolders, + }); + + List? allowedFileTypes; + + String? currentFolderPath; + + String? currentName; + + String? acceptButtonLabel; + + /// Whether to allow multiple file selection. + /// + /// Nullable because it does not apply to the "save" action. + bool? selectMultiple; + + /// Whether to allow new folder creation. + /// + /// Nullable because it does not apply to the "open" action. + bool? createFolders; + + List _toList() { + return [ + allowedFileTypes, + currentFolderPath, + currentName, + acceptButtonLabel, + selectMultiple, + createFolders, + ]; + } + + Object encode() { + return _toList(); + } + + static PlatformFileChooserOptions decode(Object result) { + result as List; + return PlatformFileChooserOptions( + allowedFileTypes: (result[0] as List?) + ?.cast(), + currentFolderPath: result[1] as String?, + currentName: result[2] as String?, + acceptButtonLabel: result[3] as String?, + selectMultiple: result[4] as bool?, + createFolders: result[5] as bool?, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! PlatformFileChooserOptions || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); +} + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is PlatformFileChooserActionType) { + buffer.putUint8(129); + writeValue(buffer, value.index); + } else if (value is PlatformTypeGroup) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is PlatformFileChooserOptions) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + final int? value = readValue(buffer) as int?; + return value == null + ? null + : PlatformFileChooserActionType.values[value]; + case 130: + return PlatformTypeGroup.decode(readValue(buffer)!); + case 131: + return PlatformFileChooserOptions.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class FileSelectorApi { + /// Constructor for [FileSelectorApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + FileSelectorApi({ + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + /// Shows an file chooser with the given [type] and [options], returning the + /// list of selected paths. + /// + /// An empty list corresponds to a cancelled selection. + Future> showFileChooser( + PlatformFileChooserActionType type, + PlatformFileChooserOptions options, + ) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.file_selector_linux.FileSelectorApi.showFileChooser$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [type, options], + ); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as List?)!.cast(); + } + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/CMakeLists.txt new file mode 100644 index 00000000..148819dc --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/CMakeLists.txt @@ -0,0 +1,69 @@ +cmake_minimum_required(VERSION 3.10) +set(PROJECT_NAME "file_selector_linux") +project(${PROJECT_NAME} LANGUAGES CXX) + +cmake_policy(VERSION 3.10...3.24) + +set(PLUGIN_NAME "${PROJECT_NAME}_plugin") + +list(APPEND PLUGIN_SOURCES + "file_selector_plugin.cc" + "messages.g.cc" +) + +add_library(${PLUGIN_NAME} SHARED + ${PLUGIN_SOURCES} +) +apply_standard_settings(${PLUGIN_NAME}) +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter) +target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK) + + +# === Tests === + +if(${include_${PROJECT_NAME}_tests}) +if(${CMAKE_VERSION} VERSION_LESS "3.11.0") +message("Unit tests require CMake 3.11.0 or later") +else() +set(TEST_RUNNER "${PROJECT_NAME}_test") +enable_testing() +# TODO(stuartmorgan): Consider using a single shared, pre-checked-in googletest +# instance rather than downloading for each plugin. This approach makes sense +# for a template, but not for a monorepo with many plugins. +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/release-1.11.0.zip +) +# Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +# Disable install commands for gtest so it doesn't end up in the bundle. +set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE) + +FetchContent_MakeAvailable(googletest) + +# The plugin's exported API is not very useful for unit testing, so build the +# sources directly into the test binary rather than using the shared library. +add_executable(${TEST_RUNNER} + test/file_selector_plugin_test.cc + test/test_main.cc + ${PLUGIN_SOURCES} +) +apply_standard_settings(${TEST_RUNNER}) +target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(${TEST_RUNNER} PRIVATE flutter) +target_link_libraries(${TEST_RUNNER} PRIVATE PkgConfig::GTK) +target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock) + +include(GoogleTest) +gtest_add_tests(TARGET ${TEST_RUNNER}) +# TODO(stuartmorgan): Ensure that all of the necessary steps are running under +# xvfb, and re-enable this. +#gtest_discover_tests(${TEST_RUNNER}) +endif() # CMake version check +endif() # include_${PROJECT_NAME}_tests diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/file_selector_plugin.cc b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/file_selector_plugin.cc new file mode 100644 index 00000000..bd679792 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/file_selector_plugin.cc @@ -0,0 +1,202 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "include/file_selector_linux/file_selector_plugin.h" + +#include +#include + +#include "file_selector_plugin_private.h" +#include "messages.g.h" + +// Error codes. +const char kBadArgumentsError[] = "Bad Arguments"; +const char kNoScreenError[] = "No Screen"; + +struct _FlFileSelectorPlugin { + GObject parent_instance; + + FlPluginRegistrar* registrar; +}; + +G_DEFINE_TYPE(FlFileSelectorPlugin, fl_file_selector_plugin, G_TYPE_OBJECT) + +// Converts a type group received from Flutter into a GTK file filter. +static GtkFileFilter* type_group_to_filter(FfsPlatformTypeGroup* group) { + g_autoptr(GtkFileFilter) filter = gtk_file_filter_new(); + + const gchar* label = ffs_platform_type_group_get_label(group); + gtk_file_filter_set_name(filter, label); + + FlValue* extensions = ffs_platform_type_group_get_extensions(group); + for (size_t i = 0; i < fl_value_get_length(extensions); i++) { + FlValue* v = fl_value_get_list_value(extensions, i); + const gchar* pattern = fl_value_get_string(v); + gtk_file_filter_add_pattern(filter, pattern); + } + FlValue* mime_types = ffs_platform_type_group_get_mime_types(group); + for (size_t i = 0; i < fl_value_get_length(mime_types); i++) { + FlValue* v = fl_value_get_list_value(mime_types, i); + const gchar* pattern = fl_value_get_string(v); + gtk_file_filter_add_mime_type(filter, pattern); + } + + return GTK_FILE_FILTER(g_object_ref(filter)); +} + +// Creates a GtkFileChooserNative for the given method call details. +static GtkFileChooserNative* create_dialog( + GtkWindow* window, GtkFileChooserAction action, const gchar* title, + const gchar* default_confirm_button_text, + FfsPlatformFileChooserOptions* options) { + const gchar* confirm_button_text = + ffs_platform_file_chooser_options_get_accept_button_label(options); + if (confirm_button_text == nullptr) { + confirm_button_text = default_confirm_button_text; + } + + g_autoptr(GtkFileChooserNative) dialog = + GTK_FILE_CHOOSER_NATIVE(gtk_file_chooser_native_new( + title, window, action, confirm_button_text, "_Cancel")); + + const gboolean* select_multiple = + ffs_platform_file_chooser_options_get_select_multiple(options); + if (select_multiple != nullptr) { + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), + *select_multiple); + } + + const gchar* current_folder = + ffs_platform_file_chooser_options_get_current_folder_path(options); + if (current_folder != nullptr) { + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), + current_folder); + } + + const gchar* current_name = + ffs_platform_file_chooser_options_get_current_name(options); + if (current_name != nullptr) { + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), current_name); + } + + FlValue* type_groups = + ffs_platform_file_chooser_options_get_allowed_file_types(options); + if (type_groups != nullptr) { + for (size_t i = 0; i < fl_value_get_length(type_groups); i++) { + FlValue* type_group = fl_value_get_list_value(type_groups, i); + GtkFileFilter* filter = type_group_to_filter(FFS_PLATFORM_TYPE_GROUP( + fl_value_get_custom_value_object(type_group))); + if (filter == nullptr) { + return nullptr; + } + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); + } + } + + const gboolean* create_folders = + ffs_platform_file_chooser_options_get_create_folders(options); + if (create_folders != nullptr) { + gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dialog), + *create_folders); + } + + return GTK_FILE_CHOOSER_NATIVE(g_object_ref(dialog)); +} + +GtkFileChooserNative* create_dialog_of_type( + GtkWindow* window, FfsPlatformFileChooserActionType type, + FfsPlatformFileChooserOptions* options) { + switch (type) { + case FILE_SELECTOR_LINUX_PLATFORM_FILE_CHOOSER_ACTION_TYPE_OPEN: + return create_dialog(window, GTK_FILE_CHOOSER_ACTION_OPEN, "Open File", + "_Open", options); + case FILE_SELECTOR_LINUX_PLATFORM_FILE_CHOOSER_ACTION_TYPE_CHOOSE_DIRECTORY: + return create_dialog(window, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + "Choose Directory", "_Open", options); + case FILE_SELECTOR_LINUX_PLATFORM_FILE_CHOOSER_ACTION_TYPE_SAVE: + return create_dialog(window, GTK_FILE_CHOOSER_ACTION_SAVE, "Save File", + "_Save", options); + } + return nullptr; +} + +// Shows the requested dialog type. +static FfsFileSelectorApiShowFileChooserResponse* handle_show_file_chooser( + FfsPlatformFileChooserActionType type, + FfsPlatformFileChooserOptions* options, gpointer user_data) { + FlFileSelectorPlugin* self = FL_FILE_SELECTOR_PLUGIN(user_data); + + FlView* view = fl_plugin_registrar_get_view(self->registrar); + if (view == nullptr) { + return ffs_file_selector_api_show_file_chooser_response_new_error( + kNoScreenError, nullptr, nullptr); + } + GtkWindow* window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view))); + + g_autoptr(GtkFileChooserNative) dialog = + create_dialog_of_type(window, type, options); + + if (dialog == nullptr) { + return ffs_file_selector_api_show_file_chooser_response_new_error( + kBadArgumentsError, "Unable to create dialog from arguments", nullptr); + } + return show_file_chooser(GTK_FILE_CHOOSER_NATIVE(dialog), + gtk_native_dialog_run); +} + +FfsFileSelectorApiShowFileChooserResponse* show_file_chooser( + GtkFileChooserNative* dialog, gint (*run_dialog)(GtkNativeDialog*)) { + gint response = run_dialog(GTK_NATIVE_DIALOG(dialog)); + g_autoptr(FlValue) result = fl_value_new_list(); + if (response == GTK_RESPONSE_ACCEPT) { + g_autoptr(GSList) filenames = + gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); + for (GSList* link = filenames; link != nullptr; link = link->next) { + g_autofree gchar* filename = static_cast(link->data); + fl_value_append_take(result, fl_value_new_string(filename)); + } + } + + return ffs_file_selector_api_show_file_chooser_response_new(result); +} + +static void fl_file_selector_plugin_dispose(GObject* object) { + FlFileSelectorPlugin* self = FL_FILE_SELECTOR_PLUGIN(object); + + ffs_file_selector_api_clear_method_handlers( + fl_plugin_registrar_get_messenger(self->registrar), nullptr); + g_clear_object(&self->registrar); + + G_OBJECT_CLASS(fl_file_selector_plugin_parent_class)->dispose(object); +} + +static void fl_file_selector_plugin_class_init( + FlFileSelectorPluginClass* klass) { + G_OBJECT_CLASS(klass)->dispose = fl_file_selector_plugin_dispose; +} + +static void fl_file_selector_plugin_init(FlFileSelectorPlugin* self) {} + +FlFileSelectorPlugin* fl_file_selector_plugin_new( + FlPluginRegistrar* registrar) { + FlFileSelectorPlugin* self = FL_FILE_SELECTOR_PLUGIN( + g_object_new(fl_file_selector_plugin_get_type(), nullptr)); + + self->registrar = FL_PLUGIN_REGISTRAR(g_object_ref(registrar)); + + static FfsFileSelectorApiVTable api_vtable = { + .show_file_chooser = handle_show_file_chooser, + }; + ffs_file_selector_api_set_method_handlers( + fl_plugin_registrar_get_messenger(registrar), nullptr, &api_vtable, + g_object_ref(self), g_object_unref); + + return self; +} + +void file_selector_plugin_register_with_registrar( + FlPluginRegistrar* registrar) { + FlFileSelectorPlugin* plugin = fl_file_selector_plugin_new(registrar); + g_object_unref(plugin); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/file_selector_plugin_private.h b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/file_selector_plugin_private.h new file mode 100644 index 00000000..9483ae7c --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/file_selector_plugin_private.h @@ -0,0 +1,26 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "include/file_selector_linux/file_selector_plugin.h" +#include "messages.g.h" + +// Creates a GtkFileChooserNative for the given method call. +// +// TODO(stuartmorgan): Make this private/static once the tests are restructured +// as descibed in the file_selector_plugin_test.cc TODOs, and then test through +// the Pigeon API handler instead (making that non-static). This only exists to +// move as much logic as possible behind an entry point currently callable by +// unit tests. +GtkFileChooserNative* create_dialog_of_type( + GtkWindow* window, FfsPlatformFileChooserActionType type, + FfsPlatformFileChooserOptions* options); + +// TODO(stuartmorgan): Fold this into handle_show_file_chooser as part of the +// above TODO. This only exists to allow testing response generation without +// mocking out all of the GTK calls. +FfsFileSelectorApiShowFileChooserResponse* show_file_chooser( + GtkFileChooserNative* dialog, gint (*run_dialog)(GtkNativeDialog*)); diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/include/file_selector_linux/file_selector_plugin.h b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/include/file_selector_linux/file_selector_plugin.h new file mode 100644 index 00000000..350c0605 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/include/file_selector_linux/file_selector_plugin.h @@ -0,0 +1,31 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PLUGINS_FILE_SELECTOR_LINUX_FILE_SELECTOR_PLUGIN_H_ +#define PLUGINS_FILE_SELECTOR_LINUX_FILE_SELECTOR_PLUGIN_H_ + +// A plugin to show native save/open file choosers. + +#include + +G_BEGIN_DECLS + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default"))) +#else +#define FLUTTER_PLUGIN_EXPORT +#endif + +G_DECLARE_FINAL_TYPE(FlFileSelectorPlugin, fl_file_selector_plugin, FL, + FILE_SELECTOR_PLUGIN, GObject) + +FLUTTER_PLUGIN_EXPORT FlFileSelectorPlugin* fl_file_selector_plugin_new( + FlPluginRegistrar* registrar); + +FLUTTER_PLUGIN_EXPORT void file_selector_plugin_register_with_registrar( + FlPluginRegistrar* registrar); + +G_END_DECLS + +#endif // PLUGINS_FILE_SELECTOR_LINUX_FILE_SELECTOR_PLUGIN_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/messages.g.cc b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/messages.g.cc new file mode 100644 index 00000000..5788006d --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/messages.g.cc @@ -0,0 +1,570 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v26.1.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#include "messages.g.h" + +struct _FfsPlatformTypeGroup { + GObject parent_instance; + + gchar* label; + FlValue* extensions; + FlValue* mime_types; +}; + +G_DEFINE_TYPE(FfsPlatformTypeGroup, ffs_platform_type_group, G_TYPE_OBJECT) + +static void ffs_platform_type_group_dispose(GObject* object) { + FfsPlatformTypeGroup* self = FFS_PLATFORM_TYPE_GROUP(object); + g_clear_pointer(&self->label, g_free); + g_clear_pointer(&self->extensions, fl_value_unref); + g_clear_pointer(&self->mime_types, fl_value_unref); + G_OBJECT_CLASS(ffs_platform_type_group_parent_class)->dispose(object); +} + +static void ffs_platform_type_group_init(FfsPlatformTypeGroup* self) {} + +static void ffs_platform_type_group_class_init( + FfsPlatformTypeGroupClass* klass) { + G_OBJECT_CLASS(klass)->dispose = ffs_platform_type_group_dispose; +} + +FfsPlatformTypeGroup* ffs_platform_type_group_new(const gchar* label, + FlValue* extensions, + FlValue* mime_types) { + FfsPlatformTypeGroup* self = FFS_PLATFORM_TYPE_GROUP( + g_object_new(ffs_platform_type_group_get_type(), nullptr)); + self->label = g_strdup(label); + self->extensions = fl_value_ref(extensions); + self->mime_types = fl_value_ref(mime_types); + return self; +} + +const gchar* ffs_platform_type_group_get_label(FfsPlatformTypeGroup* self) { + g_return_val_if_fail(FFS_IS_PLATFORM_TYPE_GROUP(self), nullptr); + return self->label; +} + +FlValue* ffs_platform_type_group_get_extensions(FfsPlatformTypeGroup* self) { + g_return_val_if_fail(FFS_IS_PLATFORM_TYPE_GROUP(self), nullptr); + return self->extensions; +} + +FlValue* ffs_platform_type_group_get_mime_types(FfsPlatformTypeGroup* self) { + g_return_val_if_fail(FFS_IS_PLATFORM_TYPE_GROUP(self), nullptr); + return self->mime_types; +} + +static FlValue* ffs_platform_type_group_to_list(FfsPlatformTypeGroup* self) { + FlValue* values = fl_value_new_list(); + fl_value_append_take(values, fl_value_new_string(self->label)); + fl_value_append_take(values, fl_value_ref(self->extensions)); + fl_value_append_take(values, fl_value_ref(self->mime_types)); + return values; +} + +static FfsPlatformTypeGroup* ffs_platform_type_group_new_from_list( + FlValue* values) { + FlValue* value0 = fl_value_get_list_value(values, 0); + const gchar* label = fl_value_get_string(value0); + FlValue* value1 = fl_value_get_list_value(values, 1); + FlValue* extensions = value1; + FlValue* value2 = fl_value_get_list_value(values, 2); + FlValue* mime_types = value2; + return ffs_platform_type_group_new(label, extensions, mime_types); +} + +struct _FfsPlatformFileChooserOptions { + GObject parent_instance; + + FlValue* allowed_file_types; + gchar* current_folder_path; + gchar* current_name; + gchar* accept_button_label; + gboolean* select_multiple; + gboolean* create_folders; +}; + +G_DEFINE_TYPE(FfsPlatformFileChooserOptions, ffs_platform_file_chooser_options, + G_TYPE_OBJECT) + +static void ffs_platform_file_chooser_options_dispose(GObject* object) { + FfsPlatformFileChooserOptions* self = + FFS_PLATFORM_FILE_CHOOSER_OPTIONS(object); + g_clear_pointer(&self->allowed_file_types, fl_value_unref); + g_clear_pointer(&self->current_folder_path, g_free); + g_clear_pointer(&self->current_name, g_free); + g_clear_pointer(&self->accept_button_label, g_free); + g_clear_pointer(&self->select_multiple, g_free); + g_clear_pointer(&self->create_folders, g_free); + G_OBJECT_CLASS(ffs_platform_file_chooser_options_parent_class) + ->dispose(object); +} + +static void ffs_platform_file_chooser_options_init( + FfsPlatformFileChooserOptions* self) {} + +static void ffs_platform_file_chooser_options_class_init( + FfsPlatformFileChooserOptionsClass* klass) { + G_OBJECT_CLASS(klass)->dispose = ffs_platform_file_chooser_options_dispose; +} + +FfsPlatformFileChooserOptions* ffs_platform_file_chooser_options_new( + FlValue* allowed_file_types, const gchar* current_folder_path, + const gchar* current_name, const gchar* accept_button_label, + gboolean* select_multiple, gboolean* create_folders) { + FfsPlatformFileChooserOptions* self = FFS_PLATFORM_FILE_CHOOSER_OPTIONS( + g_object_new(ffs_platform_file_chooser_options_get_type(), nullptr)); + if (allowed_file_types != nullptr) { + self->allowed_file_types = fl_value_ref(allowed_file_types); + } else { + self->allowed_file_types = nullptr; + } + if (current_folder_path != nullptr) { + self->current_folder_path = g_strdup(current_folder_path); + } else { + self->current_folder_path = nullptr; + } + if (current_name != nullptr) { + self->current_name = g_strdup(current_name); + } else { + self->current_name = nullptr; + } + if (accept_button_label != nullptr) { + self->accept_button_label = g_strdup(accept_button_label); + } else { + self->accept_button_label = nullptr; + } + if (select_multiple != nullptr) { + self->select_multiple = static_cast(malloc(sizeof(gboolean))); + *self->select_multiple = *select_multiple; + } else { + self->select_multiple = nullptr; + } + if (create_folders != nullptr) { + self->create_folders = static_cast(malloc(sizeof(gboolean))); + *self->create_folders = *create_folders; + } else { + self->create_folders = nullptr; + } + return self; +} + +FlValue* ffs_platform_file_chooser_options_get_allowed_file_types( + FfsPlatformFileChooserOptions* self) { + g_return_val_if_fail(FFS_IS_PLATFORM_FILE_CHOOSER_OPTIONS(self), nullptr); + return self->allowed_file_types; +} + +const gchar* ffs_platform_file_chooser_options_get_current_folder_path( + FfsPlatformFileChooserOptions* self) { + g_return_val_if_fail(FFS_IS_PLATFORM_FILE_CHOOSER_OPTIONS(self), nullptr); + return self->current_folder_path; +} + +const gchar* ffs_platform_file_chooser_options_get_current_name( + FfsPlatformFileChooserOptions* self) { + g_return_val_if_fail(FFS_IS_PLATFORM_FILE_CHOOSER_OPTIONS(self), nullptr); + return self->current_name; +} + +const gchar* ffs_platform_file_chooser_options_get_accept_button_label( + FfsPlatformFileChooserOptions* self) { + g_return_val_if_fail(FFS_IS_PLATFORM_FILE_CHOOSER_OPTIONS(self), nullptr); + return self->accept_button_label; +} + +gboolean* ffs_platform_file_chooser_options_get_select_multiple( + FfsPlatformFileChooserOptions* self) { + g_return_val_if_fail(FFS_IS_PLATFORM_FILE_CHOOSER_OPTIONS(self), nullptr); + return self->select_multiple; +} + +gboolean* ffs_platform_file_chooser_options_get_create_folders( + FfsPlatformFileChooserOptions* self) { + g_return_val_if_fail(FFS_IS_PLATFORM_FILE_CHOOSER_OPTIONS(self), nullptr); + return self->create_folders; +} + +static FlValue* ffs_platform_file_chooser_options_to_list( + FfsPlatformFileChooserOptions* self) { + FlValue* values = fl_value_new_list(); + fl_value_append_take(values, self->allowed_file_types != nullptr + ? fl_value_ref(self->allowed_file_types) + : fl_value_new_null()); + fl_value_append_take(values, + self->current_folder_path != nullptr + ? fl_value_new_string(self->current_folder_path) + : fl_value_new_null()); + fl_value_append_take(values, self->current_name != nullptr + ? fl_value_new_string(self->current_name) + : fl_value_new_null()); + fl_value_append_take(values, + self->accept_button_label != nullptr + ? fl_value_new_string(self->accept_button_label) + : fl_value_new_null()); + fl_value_append_take(values, self->select_multiple != nullptr + ? fl_value_new_bool(*self->select_multiple) + : fl_value_new_null()); + fl_value_append_take(values, self->create_folders != nullptr + ? fl_value_new_bool(*self->create_folders) + : fl_value_new_null()); + return values; +} + +static FfsPlatformFileChooserOptions* +ffs_platform_file_chooser_options_new_from_list(FlValue* values) { + FlValue* value0 = fl_value_get_list_value(values, 0); + FlValue* allowed_file_types = nullptr; + if (fl_value_get_type(value0) != FL_VALUE_TYPE_NULL) { + allowed_file_types = value0; + } + FlValue* value1 = fl_value_get_list_value(values, 1); + const gchar* current_folder_path = nullptr; + if (fl_value_get_type(value1) != FL_VALUE_TYPE_NULL) { + current_folder_path = fl_value_get_string(value1); + } + FlValue* value2 = fl_value_get_list_value(values, 2); + const gchar* current_name = nullptr; + if (fl_value_get_type(value2) != FL_VALUE_TYPE_NULL) { + current_name = fl_value_get_string(value2); + } + FlValue* value3 = fl_value_get_list_value(values, 3); + const gchar* accept_button_label = nullptr; + if (fl_value_get_type(value3) != FL_VALUE_TYPE_NULL) { + accept_button_label = fl_value_get_string(value3); + } + FlValue* value4 = fl_value_get_list_value(values, 4); + gboolean* select_multiple = nullptr; + gboolean select_multiple_value; + if (fl_value_get_type(value4) != FL_VALUE_TYPE_NULL) { + select_multiple_value = fl_value_get_bool(value4); + select_multiple = &select_multiple_value; + } + FlValue* value5 = fl_value_get_list_value(values, 5); + gboolean* create_folders = nullptr; + gboolean create_folders_value; + if (fl_value_get_type(value5) != FL_VALUE_TYPE_NULL) { + create_folders_value = fl_value_get_bool(value5); + create_folders = &create_folders_value; + } + return ffs_platform_file_chooser_options_new( + allowed_file_types, current_folder_path, current_name, + accept_button_label, select_multiple, create_folders); +} + +struct _FfsMessageCodec { + FlStandardMessageCodec parent_instance; +}; + +G_DEFINE_TYPE(FfsMessageCodec, ffs_message_codec, + fl_standard_message_codec_get_type()) + +const int ffs_platform_file_chooser_action_type_type_id = 129; +const int ffs_platform_type_group_type_id = 130; +const int ffs_platform_file_chooser_options_type_id = 131; + +static gboolean ffs_message_codec_write_ffs_platform_file_chooser_action_type( + FlStandardMessageCodec* codec, GByteArray* buffer, FlValue* value, + GError** error) { + uint8_t type = ffs_platform_file_chooser_action_type_type_id; + g_byte_array_append(buffer, &type, sizeof(uint8_t)); + return fl_standard_message_codec_write_value(codec, buffer, value, error); +} + +static gboolean ffs_message_codec_write_ffs_platform_type_group( + FlStandardMessageCodec* codec, GByteArray* buffer, + FfsPlatformTypeGroup* value, GError** error) { + uint8_t type = ffs_platform_type_group_type_id; + g_byte_array_append(buffer, &type, sizeof(uint8_t)); + g_autoptr(FlValue) values = ffs_platform_type_group_to_list(value); + return fl_standard_message_codec_write_value(codec, buffer, values, error); +} + +static gboolean ffs_message_codec_write_ffs_platform_file_chooser_options( + FlStandardMessageCodec* codec, GByteArray* buffer, + FfsPlatformFileChooserOptions* value, GError** error) { + uint8_t type = ffs_platform_file_chooser_options_type_id; + g_byte_array_append(buffer, &type, sizeof(uint8_t)); + g_autoptr(FlValue) values = ffs_platform_file_chooser_options_to_list(value); + return fl_standard_message_codec_write_value(codec, buffer, values, error); +} + +static gboolean ffs_message_codec_write_value(FlStandardMessageCodec* codec, + GByteArray* buffer, + FlValue* value, GError** error) { + if (fl_value_get_type(value) == FL_VALUE_TYPE_CUSTOM) { + switch (fl_value_get_custom_type(value)) { + case ffs_platform_file_chooser_action_type_type_id: + return ffs_message_codec_write_ffs_platform_file_chooser_action_type( + codec, buffer, + reinterpret_cast( + const_cast(fl_value_get_custom_value(value))), + error); + case ffs_platform_type_group_type_id: + return ffs_message_codec_write_ffs_platform_type_group( + codec, buffer, + FFS_PLATFORM_TYPE_GROUP(fl_value_get_custom_value_object(value)), + error); + case ffs_platform_file_chooser_options_type_id: + return ffs_message_codec_write_ffs_platform_file_chooser_options( + codec, buffer, + FFS_PLATFORM_FILE_CHOOSER_OPTIONS( + fl_value_get_custom_value_object(value)), + error); + } + } + + return FL_STANDARD_MESSAGE_CODEC_CLASS(ffs_message_codec_parent_class) + ->write_value(codec, buffer, value, error); +} + +static FlValue* ffs_message_codec_read_ffs_platform_file_chooser_action_type( + FlStandardMessageCodec* codec, GBytes* buffer, size_t* offset, + GError** error) { + return fl_value_new_custom( + ffs_platform_file_chooser_action_type_type_id, + fl_standard_message_codec_read_value(codec, buffer, offset, error), + (GDestroyNotify)fl_value_unref); +} + +static FlValue* ffs_message_codec_read_ffs_platform_type_group( + FlStandardMessageCodec* codec, GBytes* buffer, size_t* offset, + GError** error) { + g_autoptr(FlValue) values = + fl_standard_message_codec_read_value(codec, buffer, offset, error); + if (values == nullptr) { + return nullptr; + } + + g_autoptr(FfsPlatformTypeGroup) value = + ffs_platform_type_group_new_from_list(values); + if (value == nullptr) { + g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, + "Invalid data received for MessageData"); + return nullptr; + } + + return fl_value_new_custom_object(ffs_platform_type_group_type_id, + G_OBJECT(value)); +} + +static FlValue* ffs_message_codec_read_ffs_platform_file_chooser_options( + FlStandardMessageCodec* codec, GBytes* buffer, size_t* offset, + GError** error) { + g_autoptr(FlValue) values = + fl_standard_message_codec_read_value(codec, buffer, offset, error); + if (values == nullptr) { + return nullptr; + } + + g_autoptr(FfsPlatformFileChooserOptions) value = + ffs_platform_file_chooser_options_new_from_list(values); + if (value == nullptr) { + g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED, + "Invalid data received for MessageData"); + return nullptr; + } + + return fl_value_new_custom_object(ffs_platform_file_chooser_options_type_id, + G_OBJECT(value)); +} + +static FlValue* ffs_message_codec_read_value_of_type( + FlStandardMessageCodec* codec, GBytes* buffer, size_t* offset, int type, + GError** error) { + switch (type) { + case ffs_platform_file_chooser_action_type_type_id: + return ffs_message_codec_read_ffs_platform_file_chooser_action_type( + codec, buffer, offset, error); + case ffs_platform_type_group_type_id: + return ffs_message_codec_read_ffs_platform_type_group(codec, buffer, + offset, error); + case ffs_platform_file_chooser_options_type_id: + return ffs_message_codec_read_ffs_platform_file_chooser_options( + codec, buffer, offset, error); + default: + return FL_STANDARD_MESSAGE_CODEC_CLASS(ffs_message_codec_parent_class) + ->read_value_of_type(codec, buffer, offset, type, error); + } +} + +static void ffs_message_codec_init(FfsMessageCodec* self) {} + +static void ffs_message_codec_class_init(FfsMessageCodecClass* klass) { + FL_STANDARD_MESSAGE_CODEC_CLASS(klass)->write_value = + ffs_message_codec_write_value; + FL_STANDARD_MESSAGE_CODEC_CLASS(klass)->read_value_of_type = + ffs_message_codec_read_value_of_type; +} + +static FfsMessageCodec* ffs_message_codec_new() { + FfsMessageCodec* self = + FFS_MESSAGE_CODEC(g_object_new(ffs_message_codec_get_type(), nullptr)); + return self; +} + +struct _FfsFileSelectorApiShowFileChooserResponse { + GObject parent_instance; + + FlValue* value; +}; + +G_DEFINE_TYPE(FfsFileSelectorApiShowFileChooserResponse, + ffs_file_selector_api_show_file_chooser_response, G_TYPE_OBJECT) + +static void ffs_file_selector_api_show_file_chooser_response_dispose( + GObject* object) { + FfsFileSelectorApiShowFileChooserResponse* self = + FFS_FILE_SELECTOR_API_SHOW_FILE_CHOOSER_RESPONSE(object); + g_clear_pointer(&self->value, fl_value_unref); + G_OBJECT_CLASS(ffs_file_selector_api_show_file_chooser_response_parent_class) + ->dispose(object); +} + +static void ffs_file_selector_api_show_file_chooser_response_init( + FfsFileSelectorApiShowFileChooserResponse* self) {} + +static void ffs_file_selector_api_show_file_chooser_response_class_init( + FfsFileSelectorApiShowFileChooserResponseClass* klass) { + G_OBJECT_CLASS(klass)->dispose = + ffs_file_selector_api_show_file_chooser_response_dispose; +} + +FfsFileSelectorApiShowFileChooserResponse* +ffs_file_selector_api_show_file_chooser_response_new(FlValue* return_value) { + FfsFileSelectorApiShowFileChooserResponse* self = + FFS_FILE_SELECTOR_API_SHOW_FILE_CHOOSER_RESPONSE(g_object_new( + ffs_file_selector_api_show_file_chooser_response_get_type(), + nullptr)); + self->value = fl_value_new_list(); + fl_value_append_take(self->value, fl_value_ref(return_value)); + return self; +} + +FfsFileSelectorApiShowFileChooserResponse* +ffs_file_selector_api_show_file_chooser_response_new_error(const gchar* code, + const gchar* message, + FlValue* details) { + FfsFileSelectorApiShowFileChooserResponse* self = + FFS_FILE_SELECTOR_API_SHOW_FILE_CHOOSER_RESPONSE(g_object_new( + ffs_file_selector_api_show_file_chooser_response_get_type(), + nullptr)); + self->value = fl_value_new_list(); + fl_value_append_take(self->value, fl_value_new_string(code)); + fl_value_append_take(self->value, + fl_value_new_string(message != nullptr ? message : "")); + fl_value_append_take(self->value, details != nullptr ? fl_value_ref(details) + : fl_value_new_null()); + return self; +} + +struct _FfsFileSelectorApi { + GObject parent_instance; + + const FfsFileSelectorApiVTable* vtable; + gpointer user_data; + GDestroyNotify user_data_free_func; +}; + +G_DEFINE_TYPE(FfsFileSelectorApi, ffs_file_selector_api, G_TYPE_OBJECT) + +static void ffs_file_selector_api_dispose(GObject* object) { + FfsFileSelectorApi* self = FFS_FILE_SELECTOR_API(object); + if (self->user_data != nullptr) { + self->user_data_free_func(self->user_data); + } + self->user_data = nullptr; + G_OBJECT_CLASS(ffs_file_selector_api_parent_class)->dispose(object); +} + +static void ffs_file_selector_api_init(FfsFileSelectorApi* self) {} + +static void ffs_file_selector_api_class_init(FfsFileSelectorApiClass* klass) { + G_OBJECT_CLASS(klass)->dispose = ffs_file_selector_api_dispose; +} + +static FfsFileSelectorApi* ffs_file_selector_api_new( + const FfsFileSelectorApiVTable* vtable, gpointer user_data, + GDestroyNotify user_data_free_func) { + FfsFileSelectorApi* self = FFS_FILE_SELECTOR_API( + g_object_new(ffs_file_selector_api_get_type(), nullptr)); + self->vtable = vtable; + self->user_data = user_data; + self->user_data_free_func = user_data_free_func; + return self; +} + +static void ffs_file_selector_api_show_file_chooser_cb( + FlBasicMessageChannel* channel, FlValue* message_, + FlBasicMessageChannelResponseHandle* response_handle, gpointer user_data) { + FfsFileSelectorApi* self = FFS_FILE_SELECTOR_API(user_data); + + if (self->vtable == nullptr || self->vtable->show_file_chooser == nullptr) { + return; + } + + FlValue* value0 = fl_value_get_list_value(message_, 0); + FfsPlatformFileChooserActionType type = + static_cast( + fl_value_get_int(reinterpret_cast( + const_cast(fl_value_get_custom_value(value0))))); + FlValue* value1 = fl_value_get_list_value(message_, 1); + FfsPlatformFileChooserOptions* options = FFS_PLATFORM_FILE_CHOOSER_OPTIONS( + fl_value_get_custom_value_object(value1)); + g_autoptr(FfsFileSelectorApiShowFileChooserResponse) response = + self->vtable->show_file_chooser(type, options, self->user_data); + if (response == nullptr) { + g_warning("No response returned to %s.%s", "FileSelectorApi", + "showFileChooser"); + return; + } + + g_autoptr(GError) error = NULL; + if (!fl_basic_message_channel_respond(channel, response_handle, + response->value, &error)) { + g_warning("Failed to send response to %s.%s: %s", "FileSelectorApi", + "showFileChooser", error->message); + } +} + +void ffs_file_selector_api_set_method_handlers( + FlBinaryMessenger* messenger, const gchar* suffix, + const FfsFileSelectorApiVTable* vtable, gpointer user_data, + GDestroyNotify user_data_free_func) { + g_autofree gchar* dot_suffix = + suffix != nullptr ? g_strdup_printf(".%s", suffix) : g_strdup(""); + g_autoptr(FfsFileSelectorApi) api_data = + ffs_file_selector_api_new(vtable, user_data, user_data_free_func); + + g_autoptr(FfsMessageCodec) codec = ffs_message_codec_new(); + g_autofree gchar* show_file_chooser_channel_name = g_strdup_printf( + "dev.flutter.pigeon.file_selector_linux.FileSelectorApi.showFileChooser%" + "s", + dot_suffix); + g_autoptr(FlBasicMessageChannel) show_file_chooser_channel = + fl_basic_message_channel_new(messenger, show_file_chooser_channel_name, + FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler( + show_file_chooser_channel, ffs_file_selector_api_show_file_chooser_cb, + g_object_ref(api_data), g_object_unref); +} + +void ffs_file_selector_api_clear_method_handlers(FlBinaryMessenger* messenger, + const gchar* suffix) { + g_autofree gchar* dot_suffix = + suffix != nullptr ? g_strdup_printf(".%s", suffix) : g_strdup(""); + + g_autoptr(FfsMessageCodec) codec = ffs_message_codec_new(); + g_autofree gchar* show_file_chooser_channel_name = g_strdup_printf( + "dev.flutter.pigeon.file_selector_linux.FileSelectorApi.showFileChooser%" + "s", + dot_suffix); + g_autoptr(FlBasicMessageChannel) show_file_chooser_channel = + fl_basic_message_channel_new(messenger, show_file_chooser_channel_name, + FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler(show_file_chooser_channel, + nullptr, nullptr, nullptr); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/messages.g.h b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/messages.g.h new file mode 100644 index 00000000..3d5c8c65 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/linux/messages.g.h @@ -0,0 +1,269 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v26.1.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#ifndef PIGEON_MESSAGES_G_H_ +#define PIGEON_MESSAGES_G_H_ + +#include + +G_BEGIN_DECLS + +/** + * FfsPlatformFileChooserActionType: + * FILE_SELECTOR_LINUX_PLATFORM_FILE_CHOOSER_ACTION_TYPE_OPEN: + * FILE_SELECTOR_LINUX_PLATFORM_FILE_CHOOSER_ACTION_TYPE_CHOOSE_DIRECTORY: + * FILE_SELECTOR_LINUX_PLATFORM_FILE_CHOOSER_ACTION_TYPE_SAVE: + * + * A Pigeon representation of the GTK_FILE_CHOOSER_ACTION_* options. + */ +typedef enum { + FILE_SELECTOR_LINUX_PLATFORM_FILE_CHOOSER_ACTION_TYPE_OPEN = 0, + FILE_SELECTOR_LINUX_PLATFORM_FILE_CHOOSER_ACTION_TYPE_CHOOSE_DIRECTORY = 1, + FILE_SELECTOR_LINUX_PLATFORM_FILE_CHOOSER_ACTION_TYPE_SAVE = 2 +} FfsPlatformFileChooserActionType; + +/** + * FfsPlatformTypeGroup: + * + * A Pigeon representation of the Linux portion of an `XTypeGroup`. + */ + +G_DECLARE_FINAL_TYPE(FfsPlatformTypeGroup, ffs_platform_type_group, FFS, + PLATFORM_TYPE_GROUP, GObject) + +/** + * ffs_platform_type_group_new: + * label: field in this object. + * extensions: field in this object. + * mime_types: field in this object. + * + * Creates a new #PlatformTypeGroup object. + * + * Returns: a new #FfsPlatformTypeGroup + */ +FfsPlatformTypeGroup* ffs_platform_type_group_new(const gchar* label, + FlValue* extensions, + FlValue* mime_types); + +/** + * ffs_platform_type_group_get_label + * @object: a #FfsPlatformTypeGroup. + * + * Gets the value of the label field of @object. + * + * Returns: the field value. + */ +const gchar* ffs_platform_type_group_get_label(FfsPlatformTypeGroup* object); + +/** + * ffs_platform_type_group_get_extensions + * @object: a #FfsPlatformTypeGroup. + * + * Gets the value of the extensions field of @object. + * + * Returns: the field value. + */ +FlValue* ffs_platform_type_group_get_extensions(FfsPlatformTypeGroup* object); + +/** + * ffs_platform_type_group_get_mime_types + * @object: a #FfsPlatformTypeGroup. + * + * Gets the value of the mimeTypes field of @object. + * + * Returns: the field value. + */ +FlValue* ffs_platform_type_group_get_mime_types(FfsPlatformTypeGroup* object); + +/** + * FfsPlatformFileChooserOptions: + * + * Options for GKT file chooser. + * + * These correspond to gtk_file_chooser_set_* options. + */ + +G_DECLARE_FINAL_TYPE(FfsPlatformFileChooserOptions, + ffs_platform_file_chooser_options, FFS, + PLATFORM_FILE_CHOOSER_OPTIONS, GObject) + +/** + * ffs_platform_file_chooser_options_new: + * allowed_file_types: field in this object. + * current_folder_path: field in this object. + * current_name: field in this object. + * accept_button_label: field in this object. + * select_multiple: field in this object. + * create_folders: field in this object. + * + * Creates a new #PlatformFileChooserOptions object. + * + * Returns: a new #FfsPlatformFileChooserOptions + */ +FfsPlatformFileChooserOptions* ffs_platform_file_chooser_options_new( + FlValue* allowed_file_types, const gchar* current_folder_path, + const gchar* current_name, const gchar* accept_button_label, + gboolean* select_multiple, gboolean* create_folders); + +/** + * ffs_platform_file_chooser_options_get_allowed_file_types + * @object: a #FfsPlatformFileChooserOptions. + * + * Gets the value of the allowedFileTypes field of @object. + * + * Returns: the field value. + */ +FlValue* ffs_platform_file_chooser_options_get_allowed_file_types( + FfsPlatformFileChooserOptions* object); + +/** + * ffs_platform_file_chooser_options_get_current_folder_path + * @object: a #FfsPlatformFileChooserOptions. + * + * Gets the value of the currentFolderPath field of @object. + * + * Returns: the field value. + */ +const gchar* ffs_platform_file_chooser_options_get_current_folder_path( + FfsPlatformFileChooserOptions* object); + +/** + * ffs_platform_file_chooser_options_get_current_name + * @object: a #FfsPlatformFileChooserOptions. + * + * Gets the value of the currentName field of @object. + * + * Returns: the field value. + */ +const gchar* ffs_platform_file_chooser_options_get_current_name( + FfsPlatformFileChooserOptions* object); + +/** + * ffs_platform_file_chooser_options_get_accept_button_label + * @object: a #FfsPlatformFileChooserOptions. + * + * Gets the value of the acceptButtonLabel field of @object. + * + * Returns: the field value. + */ +const gchar* ffs_platform_file_chooser_options_get_accept_button_label( + FfsPlatformFileChooserOptions* object); + +/** + * ffs_platform_file_chooser_options_get_select_multiple + * @object: a #FfsPlatformFileChooserOptions. + * + * Whether to allow multiple file selection. + * + * Nullable because it does not apply to the "save" action. + * + * Returns: the field value. + */ +gboolean* ffs_platform_file_chooser_options_get_select_multiple( + FfsPlatformFileChooserOptions* object); + +/** + * ffs_platform_file_chooser_options_get_create_folders + * @object: a #FfsPlatformFileChooserOptions. + * + * Whether to allow new folder creation. + * + * Nullable because it does not apply to the "open" action. + * + * Returns: the field value. + */ +gboolean* ffs_platform_file_chooser_options_get_create_folders( + FfsPlatformFileChooserOptions* object); + +G_DECLARE_FINAL_TYPE(FfsMessageCodec, ffs_message_codec, FFS, MESSAGE_CODEC, + FlStandardMessageCodec) + +/** + * Custom type ID constants: + * + * Constants used to identify custom types in the codec. + * They are used in the codec to encode and decode custom types. + * They may be used in custom object creation functions to identify the type. + */ +extern const int ffs_platform_file_chooser_action_type_type_id; +extern const int ffs_platform_type_group_type_id; +extern const int ffs_platform_file_chooser_options_type_id; + +G_DECLARE_FINAL_TYPE(FfsFileSelectorApi, ffs_file_selector_api, FFS, + FILE_SELECTOR_API, GObject) + +G_DECLARE_FINAL_TYPE(FfsFileSelectorApiShowFileChooserResponse, + ffs_file_selector_api_show_file_chooser_response, FFS, + FILE_SELECTOR_API_SHOW_FILE_CHOOSER_RESPONSE, GObject) + +/** + * ffs_file_selector_api_show_file_chooser_response_new: + * + * Creates a new response to FileSelectorApi.showFileChooser. + * + * Returns: a new #FfsFileSelectorApiShowFileChooserResponse + */ +FfsFileSelectorApiShowFileChooserResponse* +ffs_file_selector_api_show_file_chooser_response_new(FlValue* return_value); + +/** + * ffs_file_selector_api_show_file_chooser_response_new_error: + * @code: error code. + * @message: error message. + * @details: (allow-none): error details or %NULL. + * + * Creates a new error response to FileSelectorApi.showFileChooser. + * + * Returns: a new #FfsFileSelectorApiShowFileChooserResponse + */ +FfsFileSelectorApiShowFileChooserResponse* +ffs_file_selector_api_show_file_chooser_response_new_error(const gchar* code, + const gchar* message, + FlValue* details); + +/** + * FfsFileSelectorApiVTable: + * + * Table of functions exposed by FileSelectorApi to be implemented by the API + * provider. + */ +typedef struct { + FfsFileSelectorApiShowFileChooserResponse* (*show_file_chooser)( + FfsPlatformFileChooserActionType type, + FfsPlatformFileChooserOptions* options, gpointer user_data); +} FfsFileSelectorApiVTable; + +/** + * ffs_file_selector_api_set_method_handlers: + * + * @messenger: an #FlBinaryMessenger. + * @suffix: (allow-none): a suffix to add to the API or %NULL for none. + * @vtable: implementations of the methods in this API. + * @user_data: (closure): user data to pass to the functions in @vtable. + * @user_data_free_func: (allow-none): a function which gets called to free + * @user_data, or %NULL. + * + * Connects the method handlers in the FileSelectorApi API. + */ +void ffs_file_selector_api_set_method_handlers( + FlBinaryMessenger* messenger, const gchar* suffix, + const FfsFileSelectorApiVTable* vtable, gpointer user_data, + GDestroyNotify user_data_free_func); + +/** + * ffs_file_selector_api_clear_method_handlers: + * + * @messenger: an #FlBinaryMessenger. + * @suffix: (allow-none): a suffix to add to the API or %NULL for none. + * + * Clears the method handlers in the FileSelectorApi API. + */ +void ffs_file_selector_api_clear_method_handlers(FlBinaryMessenger* messenger, + const gchar* suffix); + +G_END_DECLS + +#endif // PIGEON_MESSAGES_G_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/pigeons/copyright.txt b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/pigeons/copyright.txt new file mode 100644 index 00000000..07e5f859 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/pigeons/copyright.txt @@ -0,0 +1,3 @@ +Copyright 2013 The Flutter Authors +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/pigeons/messages.dart b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/pigeons/messages.dart new file mode 100644 index 00000000..e700c6ae --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/pigeons/messages.dart @@ -0,0 +1,71 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon( + PigeonOptions( + input: 'pigeons/messages.dart', + gobjectHeaderOut: 'linux/messages.g.h', + gobjectSourceOut: 'linux/messages.g.cc', + gobjectOptions: GObjectOptions(module: 'Ffs'), + dartOut: 'lib/src/messages.g.dart', + copyrightHeader: 'pigeons/copyright.txt', + ), +) +/// A Pigeon representation of the GTK_FILE_CHOOSER_ACTION_* options. +enum PlatformFileChooserActionType { open, chooseDirectory, save } + +/// A Pigeon representation of the Linux portion of an `XTypeGroup`. +class PlatformTypeGroup { + const PlatformTypeGroup({ + this.label = '', + this.extensions = const [], + this.mimeTypes = const [], + }); + + final String label; + final List extensions; + final List mimeTypes; +} + +/// Options for GKT file chooser. +/// +/// These correspond to gtk_file_chooser_set_* options. +class PlatformFileChooserOptions { + PlatformFileChooserOptions({ + required this.allowedFileTypes, + required this.currentFolderPath, + required this.currentName, + required this.acceptButtonLabel, + this.selectMultiple, + this.createFolders, + }); + + final List? allowedFileTypes; + final String? currentFolderPath; + final String? currentName; + final String? acceptButtonLabel; + + /// Whether to allow multiple file selection. + /// + /// Nullable because it does not apply to the "save" action. + final bool? selectMultiple; + + /// Whether to allow new folder creation. + /// + /// Nullable because it does not apply to the "open" action. + final bool? createFolders; +} + +@HostApi() +abstract class FileSelectorApi { + /// Shows an file chooser with the given [type] and [options], returning the + /// list of selected paths. + /// + /// An empty list corresponds to a cancelled selection. + List showFileChooser( + PlatformFileChooserActionType type, + PlatformFileChooserOptions options, + ); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/pubspec.yaml b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/pubspec.yaml new file mode 100644 index 00000000..810d3789 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/file_selector_linux/pubspec.yaml @@ -0,0 +1,33 @@ +name: file_selector_linux +description: Liunx implementation of the file_selector plugin. +repository: https://github.com/flutter/packages/tree/main/packages/file_selector/file_selector_linux +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 +version: 0.9.4 + +environment: + sdk: ^3.8.0 + flutter: ">=3.32.0" + +flutter: + plugin: + implements: file_selector + platforms: + linux: + pluginClass: FileSelectorPlugin + dartPluginClass: FileSelectorLinux + +dependencies: + cross_file: ^0.3.1 + file_selector_platform_interface: ^2.7.0 + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + pigeon: ^26.1.0 + +topics: + - files + - file-selection + - file-selector diff --git a/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/AUTHORS b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/AUTHORS new file mode 100644 index 00000000..26e81c7f --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/AUTHORS @@ -0,0 +1,7 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +Alexandre Zollinger Chohfi diff --git a/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/CHANGELOG.md b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/CHANGELOG.md new file mode 100644 index 00000000..95207a3b --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/CHANGELOG.md @@ -0,0 +1,22 @@ +## 0.2.2 + +* Adds support for `getMultiVideoWithOptions`. +* Updates minimum supported SDK version to Flutter 3.27/Dart 3.6. + +## 0.2.1+2 + +* Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. +* Fixes `getMedia` mime types. + +## 0.2.1+1 + +* Adds pub topics to package metadata. +* Updates minimum supported SDK version to Flutter 3.7/Dart 2.19. + +## 0.2.1 + +* Adds `getMedia` method. + +## 0.2.0 + +* Implements initial Linux support. diff --git a/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/lib/main.dart b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/lib/main.dart new file mode 100644 index 00000000..b62d3913 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/lib/main.dart @@ -0,0 +1,564 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'package:mime/mime.dart'; +import 'package:video_player/video_player.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return const MaterialApp( + title: 'Image Picker Demo', + home: MyHomePage(title: 'Image Picker Example'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, this.title}); + + final String? title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + List? _mediaFileList; + + // This must be called from within a setState() callback + void _setImageFileListFromFile(XFile? value) { + _mediaFileList = value == null ? null : [value]; + } + + dynamic _pickImageError; + bool _isVideo = false; + + VideoPlayerController? _controller; + VideoPlayerController? _toBeDisposed; + String? _retrieveDataError; + + final ImagePickerPlatform _picker = ImagePickerPlatform.instance; + final TextEditingController maxWidthController = TextEditingController(); + final TextEditingController maxHeightController = TextEditingController(); + final TextEditingController qualityController = TextEditingController(); + + Future _playVideo(XFile? file) async { + if (file != null && mounted) { + await _disposeVideoController(); + final VideoPlayerController controller = + VideoPlayerController.file(File(file.path)); + _controller = controller; + await controller.setVolume(1.0); + await controller.initialize(); + await controller.setLooping(true); + await controller.play(); + setState(() {}); + } + } + + Future _onImageButtonPressed( + ImageSource source, { + required BuildContext context, + bool allowMultiple = false, + bool isMedia = false, + }) async { + if (_controller != null) { + await _controller!.setVolume(0.0); + } + if (context.mounted) { + if (_isVideo) { + final List files; + if (allowMultiple) { + files = await _picker.getMultiVideoWithOptions(); + } else { + final XFile? file = await _picker.getVideo( + source: source, maxDuration: const Duration(seconds: 10)); + files = [if (file != null) file]; + } + if (files.isNotEmpty && context.mounted) { + _showPickedSnackBar(context, files); + // Just play the first file, to keep the example simple. + await _playVideo(files.first); + } + } else if (allowMultiple) { + await _displayPickImageDialog(context, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final ImageOptions imageOptions = ImageOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ); + final List pickedFileList = isMedia + ? await _picker.getMedia( + options: MediaOptions( + allowMultiple: allowMultiple, + imageOptions: imageOptions, + ), + ) + : await _picker.getMultiImageWithOptions( + options: MultiImagePickerOptions( + imageOptions: imageOptions, + ), + ); + if (pickedFileList.isNotEmpty && context.mounted) { + _showPickedSnackBar(context, pickedFileList); + } + setState(() { + _mediaFileList = pickedFileList; + }); + } catch (e) { + setState(() { + _pickImageError = e; + }); + } + }); + } else if (isMedia) { + await _displayPickImageDialog(context, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final List pickedFileList = []; + final XFile? media = _firstOrNull(await _picker.getMedia( + options: MediaOptions( + allowMultiple: allowMultiple, + imageOptions: ImageOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + )), + )); + + if (media != null) { + pickedFileList.add(media); + setState(() { + _mediaFileList = pickedFileList; + }); + } + } catch (e) { + setState(() => _pickImageError = e); + } + }); + } else { + await _displayPickImageDialog(context, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final XFile? pickedFile = await _picker.getImageFromSource( + source: source, + options: ImagePickerOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ), + ); + if (pickedFile != null && context.mounted) { + _showPickedSnackBar(context, [pickedFile]); + } + setState(() => _setImageFileListFromFile(pickedFile)); + } catch (e) { + setState(() => _pickImageError = e); + } + }); + } + } + } + + @override + void deactivate() { + if (_controller != null) { + _controller!.setVolume(0.0); + _controller!.pause(); + } + super.deactivate(); + } + + @override + void dispose() { + _disposeVideoController(); + maxWidthController.dispose(); + maxHeightController.dispose(); + qualityController.dispose(); + super.dispose(); + } + + Future _disposeVideoController() async { + if (_toBeDisposed != null) { + await _toBeDisposed!.dispose(); + } + _toBeDisposed = _controller; + _controller = null; + } + + Widget _previewVideo() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_controller == null) { + return const Text( + 'You have not yet picked a video', + textAlign: TextAlign.center, + ); + } + return Padding( + padding: const EdgeInsets.all(10.0), + child: AspectRatioVideo(_controller), + ); + } + + Widget _previewImages() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_mediaFileList != null) { + return Semantics( + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + final XFile image = _mediaFileList![index]; + final String? mime = lookupMimeType(_mediaFileList![index].path); + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(image.name, + key: const Key('image_picker_example_picked_image_name')), + Semantics( + label: 'image_picker_example_picked_image', + child: mime == null || mime.startsWith('image/') + ? Image.file( + File(_mediaFileList![index].path), + errorBuilder: (BuildContext context, Object error, + StackTrace? stackTrace) { + return const Center( + child: + Text('This image type is not supported')); + }, + ) + : _buildInlineVideoPlayer(index), + ), + ], + ); + }, + itemCount: _mediaFileList!.length, + ), + ); + } else if (_pickImageError != null) { + return Text( + 'Pick image error: $_pickImageError', + textAlign: TextAlign.center, + ); + } else { + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + } + } + + Widget _buildInlineVideoPlayer(int index) { + final VideoPlayerController controller = + VideoPlayerController.file(File(_mediaFileList![index].path)); + controller.setVolume(1.0); + controller.initialize(); + controller.setLooping(true); + controller.play(); + return Center(child: AspectRatioVideo(controller)); + } + + Widget _handlePreview() { + if (_isVideo) { + return _previewVideo(); + } else { + return _previewImages(); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title!), + ), + body: Align( + alignment: Alignment.topCenter, + child: _handlePreview(), + ), + floatingActionButton: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Semantics( + label: 'image_picker_example_from_gallery', + child: FloatingActionButton.extended( + key: const Key('image_picker_example_from_gallery'), + onPressed: () { + _isVideo = false; + _onImageButtonPressed(ImageSource.gallery, context: context); + }, + heroTag: 'image0', + tooltip: 'Pick image from gallery', + label: const Text('Pick image from gallery'), + icon: const Icon(Icons.photo), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton.extended( + onPressed: () { + _isVideo = false; + _onImageButtonPressed( + ImageSource.gallery, + context: context, + allowMultiple: true, + ); + }, + heroTag: 'image1', + tooltip: 'Pick multiple images', + label: const Text('Pick multiple images'), + icon: const Icon(Icons.photo_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton.extended( + onPressed: () { + _isVideo = false; + _onImageButtonPressed( + ImageSource.gallery, + context: context, + isMedia: true, + ); + }, + heroTag: 'media', + tooltip: 'Pick item from gallery', + label: const Text('Pick item from gallery'), + icon: const Icon(Icons.photo_outlined), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton.extended( + onPressed: () { + _isVideo = false; + _onImageButtonPressed( + ImageSource.gallery, + context: context, + allowMultiple: true, + isMedia: true, + ); + }, + heroTag: 'multipleMedia', + tooltip: 'Pick multiple items', + label: const Text('Pick multiple items'), + icon: const Icon(Icons.photo_library_outlined), + ), + ), + if (_picker.supportsImageSource(ImageSource.camera)) + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton.extended( + onPressed: () { + _isVideo = false; + _onImageButtonPressed(ImageSource.camera, context: context); + }, + heroTag: 'image2', + tooltip: 'Take a photo', + label: const Text('Take a photo'), + icon: const Icon(Icons.camera_alt), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton.extended( + backgroundColor: Colors.red, + onPressed: () { + _isVideo = true; + _onImageButtonPressed(ImageSource.gallery, context: context); + }, + heroTag: 'video', + tooltip: 'Pick video from gallery', + label: const Text('Pick video from gallery'), + icon: const Icon(Icons.video_file), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton.extended( + backgroundColor: Colors.red, + onPressed: () { + _isVideo = true; + _onImageButtonPressed(ImageSource.gallery, + context: context, allowMultiple: true); + }, + heroTag: 'multiVideo', + tooltip: 'Pick multiple videos', + label: const Text('Pick multiple videos'), + icon: const Icon(Icons.video_library), + ), + ), + if (_picker.supportsImageSource(ImageSource.camera)) + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton.extended( + backgroundColor: Colors.red, + onPressed: () { + _isVideo = true; + _onImageButtonPressed(ImageSource.camera, context: context); + }, + heroTag: 'takeVideo', + tooltip: 'Take a video', + label: const Text('Take a video'), + icon: const Icon(Icons.videocam), + ), + ), + ], + ), + ); + } + + Text? _getRetrieveErrorWidget() { + if (_retrieveDataError != null) { + final Text result = Text(_retrieveDataError!); + _retrieveDataError = null; + return result; + } + return null; + } + + Future _displayPickImageDialog( + BuildContext context, OnPickImageCallback onPick) async { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Add optional parameters'), + content: Column( + children: [ + TextField( + controller: maxWidthController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxWidth if desired'), + ), + TextField( + controller: maxHeightController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxHeight if desired'), + ), + TextField( + controller: qualityController, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + hintText: 'Enter quality if desired'), + ), + ], + ), + actions: [ + TextButton( + child: const Text('CANCEL'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('PICK'), + onPressed: () { + final double? width = maxWidthController.text.isNotEmpty + ? double.parse(maxWidthController.text) + : null; + final double? height = maxHeightController.text.isNotEmpty + ? double.parse(maxHeightController.text) + : null; + final int? quality = qualityController.text.isNotEmpty + ? int.parse(qualityController.text) + : null; + onPick(width, height, quality); + Navigator.of(context).pop(); + }), + ], + ); + }); + } + + void _showPickedSnackBar(BuildContext context, List files) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Picked: ${files.map((XFile it) => it.name).join(',')}'), + duration: const Duration(seconds: 2), + )); + } +} + +typedef OnPickImageCallback = void Function( + double? maxWidth, double? maxHeight, int? quality); + +class AspectRatioVideo extends StatefulWidget { + const AspectRatioVideo(this.controller, {super.key}); + + final VideoPlayerController? controller; + + @override + AspectRatioVideoState createState() => AspectRatioVideoState(); +} + +class AspectRatioVideoState extends State { + VideoPlayerController? get controller => widget.controller; + bool initialized = false; + + void _onVideoControllerUpdate() { + if (!mounted) { + return; + } + if (initialized != controller!.value.isInitialized) { + initialized = controller!.value.isInitialized; + setState(() {}); + } + } + + @override + void initState() { + super.initState(); + controller!.addListener(_onVideoControllerUpdate); + } + + @override + void dispose() { + controller!.removeListener(_onVideoControllerUpdate); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (initialized) { + return Center( + child: AspectRatio( + aspectRatio: controller!.value.aspectRatio, + child: VideoPlayer(controller!), + ), + ); + } else { + return Container(); + } + } +} + +T? _firstOrNull(List list) { + return list.isEmpty ? null : list.first; +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/CMakeLists.txt new file mode 100644 index 00000000..1fbfa727 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/CMakeLists.txt @@ -0,0 +1,138 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "example") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "dev.flutter.plugins.imagePickerExample") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/flutter/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/flutter/CMakeLists.txt new file mode 100644 index 00000000..d5bd0164 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/flutter/generated_plugins.cmake b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 00000000..2db3c22a --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + file_selector_linux +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/main.cc b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/main.cc new file mode 100644 index 00000000..1507d028 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/main.cc @@ -0,0 +1,10 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/my_application.cc b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/my_application.cc new file mode 100644 index 00000000..3a67810f --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/my_application.cc @@ -0,0 +1,111 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "example"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments( + project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, + gchar*** arguments, + int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = + my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, "flags", + G_APPLICATION_NON_UNIQUE, nullptr)); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/my_application.h b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/my_application.h new file mode 100644 index 00000000..6e9f0c3f --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/linux/my_application.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/pubspec.yaml b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/pubspec.yaml new file mode 100644 index 00000000..b1626f31 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/example/pubspec.yaml @@ -0,0 +1,29 @@ +name: example +description: Example for image_picker_linux implementation. +publish_to: 'none' +version: 1.0.0 + +environment: + sdk: ^3.6.0 + flutter: ">=3.27.0" + +dependencies: + flutter: + sdk: flutter + image_picker_linux: + # When depending on this package from a real application you should use: + # image_picker_linux: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: .. + image_picker_platform_interface: ^2.11.0 + mime: ^2.0.0 + video_player: ^2.1.4 + +dev_dependencies: + flutter_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/lib/image_picker_linux.dart b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/lib/image_picker_linux.dart new file mode 100644 index 00000000..deacebaf --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/lib/image_picker_linux.dart @@ -0,0 +1,193 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_linux/file_selector_linux.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; + +/// The Linux implementation of [ImagePickerPlatform]. +/// +/// This class implements the `package:image_picker` functionality for +/// Linux. +class ImagePickerLinux extends CameraDelegatingImagePickerPlatform { + /// Constructs a platform implementation. + ImagePickerLinux(); + + /// The file selector used to prompt the user to select images or videos. + @visibleForTesting + static FileSelectorPlatform fileSelector = FileSelectorLinux(); + + /// Registers this class as the default instance of [ImagePickerPlatform]. + static void registerWith() { + ImagePickerPlatform.instance = ImagePickerLinux(); + } + + // This is soft-deprecated in the platform interface, and is only implemented + // for compatibility. Callers should be using getImageFromSource. + @override + Future pickImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) async { + final XFile? file = await getImageFromSource( + source: source, + options: ImagePickerOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice)); + if (file != null) { + return PickedFile(file.path); + } + return null; + } + + // This is soft-deprecated in the platform interface, and is only implemented + // for compatibility. Callers should be using getVideo. + @override + Future pickVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) async { + final XFile? file = await getVideo( + source: source, + preferredCameraDevice: preferredCameraDevice, + maxDuration: maxDuration); + if (file != null) { + return PickedFile(file.path); + } + return null; + } + + // This is soft-deprecated in the platform interface, and is only implemented + // for compatibility. Callers should be using getImageFromSource. + @override + Future getImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) async { + return getImageFromSource( + source: source, + options: ImagePickerOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice)); + } + + // [ImagePickerOptions] options are not currently supported. If any + // of its fields are set, they will be silently ignored. + // + // If source is `ImageSource.camera`, a `StateError` will be thrown + // unless a [cameraDelegate] is set. + @override + Future getImageFromSource({ + required ImageSource source, + ImagePickerOptions options = const ImagePickerOptions(), + }) async { + switch (source) { + case ImageSource.camera: + return super.getImageFromSource(source: source); + case ImageSource.gallery: + const XTypeGroup typeGroup = + XTypeGroup(label: 'Images', mimeTypes: ['image/*']); + final XFile? file = await fileSelector + .openFile(acceptedTypeGroups: [typeGroup]); + return file; + } + // Ensure that there's a fallback in case a new source is added. + // ignore: dead_code + throw UnimplementedError('Unknown ImageSource: $source'); + } + + // `preferredCameraDevice` and `maxDuration` arguments are not currently + // supported. If either of these arguments are supplied, they will be silently + // ignored. + // + // If source is `ImageSource.camera`, a `StateError` will be thrown + // unless a [cameraDelegate] is set. + @override + Future getVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) async { + switch (source) { + case ImageSource.camera: + return super.getVideo( + source: source, + preferredCameraDevice: preferredCameraDevice, + maxDuration: maxDuration); + case ImageSource.gallery: + const XTypeGroup typeGroup = + XTypeGroup(label: 'Videos', mimeTypes: ['video/*']); + final XFile? file = await fileSelector + .openFile(acceptedTypeGroups: [typeGroup]); + return file; + } + // Ensure that there's a fallback in case a new source is added. + // ignore: dead_code + throw UnimplementedError('Unknown ImageSource: $source'); + } + + // `maxWidth`, `maxHeight`, and `imageQuality` arguments are not currently + // supported. If any of these arguments are supplied, they will be silently + // ignored. + @override + Future> getMultiImage({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) async { + const XTypeGroup typeGroup = + XTypeGroup(label: 'Images', mimeTypes: ['image/*']); + final List files = await fileSelector + .openFiles(acceptedTypeGroups: [typeGroup]); + return files; + } + + @override + Future> getMultiVideoWithOptions( + {MultiVideoPickerOptions options = + const MultiVideoPickerOptions()}) async { + const XTypeGroup typeGroup = + XTypeGroup(label: 'Videos', mimeTypes: ['video/*']); + final List files = await fileSelector + .openFiles(acceptedTypeGroups: [typeGroup]); + return files; + } + + // `maxWidth`, `maxHeight`, and `imageQuality` arguments are not currently + // supported. If any of these arguments are supplied, they will be silently + // ignored. + @override + Future> getMedia({required MediaOptions options}) async { + const XTypeGroup typeGroup = XTypeGroup( + label: 'Images and videos', + mimeTypes: ['image/*', 'video/*'], + ); + + List files; + + if (options.allowMultiple) { + files = await fileSelector + .openFiles(acceptedTypeGroups: [typeGroup]); + } else { + final XFile? file = await fileSelector + .openFile(acceptedTypeGroups: [typeGroup]); + files = [ + if (file != null) file, + ]; + } + return files; + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/pubspec.yaml b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/pubspec.yaml new file mode 100644 index 00000000..ba1d4c4d --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/image_picker_linux/pubspec.yaml @@ -0,0 +1,34 @@ +name: image_picker_linux +description: Linux platform implementation of image_picker +repository: https://github.com/flutter/packages/tree/main/packages/image_picker/image_picker_linux +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 +version: 0.2.2 + +environment: + sdk: ^3.6.0 + flutter: ">=3.27.0" + +flutter: + plugin: + implements: image_picker + platforms: + linux: + dartPluginClass: ImagePickerLinux + +dependencies: + file_selector_linux: ^0.9.1+3 + file_selector_platform_interface: ^2.2.0 + flutter: + sdk: flutter + image_picker_platform_interface: ^2.11.0 + +dev_dependencies: + build_runner: ^2.1.5 + flutter_test: + sdk: flutter + mockito: ^5.4.4 + +topics: + - image-picker + - files + - file-selection diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/AUTHORS b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/AUTHORS new file mode 100644 index 00000000..493a0b4e --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/AUTHORS @@ -0,0 +1,66 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/CHANGELOG.md b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/CHANGELOG.md new file mode 100644 index 00000000..ace2dad7 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/CHANGELOG.md @@ -0,0 +1,109 @@ +## 2.2.1 + +* Adds pub topics to package metadata. +* Updates minimum supported SDK version to Flutter 3.7/Dart 2.19. + +## 2.2.0 + +* Adds getApplicationCachePath() for storing app-specific cache files. + +## 2.1.11 + +* Removes obsolete null checks on non-nullable values. +* Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. + +## 2.1.10 + +* Clarifies explanation of endorsement in README. +* Aligns Dart and Flutter SDK constraints. + +## 2.1.9 + +* Updates links for the merge of flutter/plugins into flutter/packages. + +## 2.1.8 + +* Adds compatibility with `xdg_directories` 1.0. +* Updates minimum Flutter version to 3.0. + +## 2.1.7 + +* Bumps ffi dependency to match path_provider_windows. + +## 2.1.6 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + +## 2.1.5 + +* Removes dependency on `meta`. + +## 2.1.4 + +* Fixes `getApplicationSupportPath` handling of applications where the + application ID is not set. + +## 2.1.3 + +* Change getApplicationSupportPath from using executable name to application ID (if provided). + * If the executable name based directory exists, continue to use that so existing applications continue with the same behaviour. + +## 2.1.2 + +* Fixes link in README. + +## 2.1.1 + +* Removed obsolete `pluginClass: none` from pubpsec. + +## 2.1.0 + +* Now `getTemporaryPath` returns the value of the `TMPDIR` environment variable primarily. If `TMPDIR` is not set, `/tmp` is returned. + +## 2.0.2 + +* Updated installation instructions in README. + +## 2.0.1 + +* Add `implements` to pubspec.yaml. +* Add `registerWith` method to the main Dart class. + +## 2.0.0 + +* Migrate to null safety. + +## 0.1.1+3 + +* Update Flutter SDK constraint. + +## 0.1.1+2 + +* Log errors in the example when calls to the `path_provider` fail. + +## 0.1.1+1 + +* Check in linux/ directory for example/ + +## 0.1.1 - NOT PUBLISHED + +* Reverts changes on 0.1.0, which broke the tree. + +## 0.1.0 - NOT PUBLISHED + +* This release updates getApplicationSupportPath to use the application ID instead of the executable name. + * No migration is provided, so any older apps that were using this path will now have a different directory. + +## 0.0.1+2 + +* This release updates the example to depend on the endorsed plugin rather than relative path + +## 0.0.1+1 + +* This updates the readme and pubspec and example to reflect the endorsement of this implementation of `path_provider` + +## 0.0.1 + +* The initial implementation of path\_provider for Linux + * Implements getApplicationSupportPath, getApplicationDocumentsPath, getDownloadsPath, and getTemporaryPath diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/integration_test/path_provider_test.dart b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/integration_test/path_provider_test.dart new file mode 100644 index 00000000..3353f561 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/integration_test/path_provider_test.dart @@ -0,0 +1,66 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:path_provider_linux/path_provider_linux.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('getTemporaryDirectory', (WidgetTester tester) async { + final PathProviderLinux provider = PathProviderLinux(); + final String? result = await provider.getTemporaryPath(); + _verifySampleFile(result, 'temporaryDirectory'); + }); + + testWidgets('getDownloadDirectory', (WidgetTester tester) async { + if (!Platform.isLinux) { + return; + } + final PathProviderLinux provider = PathProviderLinux(); + final String? result = await provider.getDownloadsPath(); + _verifySampleFile(result, 'downloadDirectory'); + }); + + testWidgets('getApplicationDocumentsDirectory', (WidgetTester tester) async { + final PathProviderLinux provider = PathProviderLinux(); + final String? result = await provider.getApplicationDocumentsPath(); + _verifySampleFile(result, 'applicationDocuments'); + }); + + testWidgets('getApplicationSupportDirectory', (WidgetTester tester) async { + final PathProviderLinux provider = PathProviderLinux(); + final String? result = await provider.getApplicationSupportPath(); + _verifySampleFile(result, 'applicationSupport'); + }); + + testWidgets('getApplicationCacheDirectory', (WidgetTester tester) async { + final PathProviderLinux provider = PathProviderLinux(); + final String? result = await provider.getApplicationCachePath(); + _verifySampleFile(result, 'applicationCache'); + }); +} + +/// Verify a file called [name] in [directoryPath] by recreating it with test +/// contents when necessary. +void _verifySampleFile(String? directoryPath, String name) { + expect(directoryPath, isNotNull); + if (directoryPath == null) { + return; + } + final Directory directory = Directory(directoryPath); + final File file = File('${directory.path}${Platform.pathSeparator}$name'); + + if (file.existsSync()) { + file.deleteSync(); + expect(file.existsSync(), isFalse); + } + + file.writeAsStringSync('Hello world!'); + expect(file.readAsStringSync(), 'Hello world!'); + expect(directory.listSync(), isNotEmpty); + file.deleteSync(); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/lib/main.dart b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/lib/main.dart new file mode 100644 index 00000000..3cbb6915 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/lib/main.dart @@ -0,0 +1,109 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:path_provider_linux/path_provider_linux.dart'; + +void main() { + runApp(const MyApp()); +} + +/// Sample app +class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({super.key}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + String? _tempDirectory = 'Unknown'; + String? _downloadsDirectory = 'Unknown'; + String? _appSupportDirectory = 'Unknown'; + String? _appCacheDirectory = 'Unknown'; + String? _documentsDirectory = 'Unknown'; + final PathProviderLinux _provider = PathProviderLinux(); + + @override + void initState() { + super.initState(); + initDirectories(); + } + + // Platform messages are asynchronous, so we initialize in an async method. + Future initDirectories() async { + String? tempDirectory; + String? downloadsDirectory; + String? appSupportDirectory; + String? appCacheDirectory; + String? documentsDirectory; + // Platform messages may fail, so we use a try/catch PlatformException. + try { + tempDirectory = await _provider.getTemporaryPath(); + } on PlatformException { + tempDirectory = 'Failed to get temp directory.'; + } + try { + downloadsDirectory = await _provider.getDownloadsPath(); + } on PlatformException { + downloadsDirectory = 'Failed to get downloads directory.'; + } + + try { + documentsDirectory = await _provider.getApplicationDocumentsPath(); + } on PlatformException { + documentsDirectory = 'Failed to get documents directory.'; + } + + try { + appSupportDirectory = await _provider.getApplicationSupportPath(); + } on PlatformException { + appSupportDirectory = 'Failed to get documents directory.'; + } + + try { + appCacheDirectory = await _provider.getApplicationCachePath(); + } on PlatformException { + appCacheDirectory = 'Failed to get cache directory.'; + } + // If the widget was removed from the tree while the asynchronous platform + // message was in flight, we want to discard the reply rather than calling + // setState to update our non-existent appearance. + if (!mounted) { + return; + } + + setState(() { + _tempDirectory = tempDirectory; + _downloadsDirectory = downloadsDirectory; + _appSupportDirectory = appSupportDirectory; + _appCacheDirectory = appCacheDirectory; + _documentsDirectory = documentsDirectory; + }); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Path Provider Linux example app'), + ), + body: Center( + child: Column( + children: [ + Text('Temp Directory: $_tempDirectory\n'), + Text('Documents Directory: $_documentsDirectory\n'), + Text('Downloads Directory: $_downloadsDirectory\n'), + Text('Application Support Directory: $_appSupportDirectory\n'), + Text('Application Cache Directory: $_appCacheDirectory\n'), + ], + ), + ), + ), + ); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/CMakeLists.txt new file mode 100644 index 00000000..4c422c77 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/CMakeLists.txt @@ -0,0 +1,106 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "example") +set(APPLICATION_ID "dev.flutter.plugins.path_provider_linux_example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/flutter/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/flutter/CMakeLists.txt new file mode 100644 index 00000000..94f43ff7 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,86 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + linux-x64 ${CMAKE_BUILD_TYPE} +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/flutter/generated_plugins.cmake b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 00000000..2e1de87a --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/main.cc b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/main.cc new file mode 100644 index 00000000..88a5fd45 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/main.cc @@ -0,0 +1,15 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "my_application.h" + +int main(int argc, char** argv) { + // Only X11 is currently supported. + // Wayland support is being developed: + // https://github.com/flutter/flutter/issues/57932. + gdk_set_allowed_backends("x11"); + + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/my_application.cc b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/my_application.cc new file mode 100644 index 00000000..9cb411ba --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/my_application.cc @@ -0,0 +1,49 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "my_application.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new( + my_application_get_type(), "application-id", APPLICATION_ID, nullptr)); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/my_application.h b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/my_application.h new file mode 100644 index 00000000..6e9f0c3f --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/linux/my_application.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/pubspec.yaml b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/pubspec.yaml new file mode 100644 index 00000000..33fa8914 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/pubspec.yaml @@ -0,0 +1,28 @@ +name: pathproviderexample +description: Demonstrates how to use the path_provider_linux plugin. +publish_to: "none" + +environment: + sdk: ">=2.19.0 <4.0.0" + flutter: ">=3.7.0" + +dependencies: + flutter: + sdk: flutter + + path_provider_linux: + # When depending on this package from a real application you should use: + # path_provider_linux: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + +dev_dependencies: + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/test_driver/integration_test.dart b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/test_driver/integration_test.dart new file mode 100644 index 00000000..4f10f2a5 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/path_provider_linux.dart b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/path_provider_linux.dart new file mode 100644 index 00000000..e32af1bf --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/path_provider_linux.dart @@ -0,0 +1,5 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'src/path_provider_linux.dart'; diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/src/get_application_id.dart b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/src/get_application_id.dart new file mode 100644 index 00000000..e169c025 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/src/get_application_id.dart @@ -0,0 +1,9 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// getApplicationId() is implemented using FFI; export a stub for platforms +// that don't support FFI (e.g., web) to avoid having transitive dependencies +// break web compilation. +export 'get_application_id_stub.dart' + if (dart.library.ffi) 'get_application_id_real.dart'; diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/src/get_application_id_real.dart b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/src/get_application_id_real.dart new file mode 100644 index 00000000..5e16df06 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/src/get_application_id_real.dart @@ -0,0 +1,78 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; + +// GApplication* g_application_get_default(); +typedef _GApplicationGetDefaultC = IntPtr Function(); +typedef _GApplicationGetDefaultDart = int Function(); + +// const gchar* g_application_get_application_id(GApplication* application); +typedef _GApplicationGetApplicationIdC = Pointer Function(IntPtr); +typedef _GApplicationGetApplicationIdDart = Pointer Function(int); + +/// Interface for interacting with libgio. +@visibleForTesting +class GioUtils { + /// Creates a default instance that uses the real libgio. + GioUtils() { + try { + _gio = DynamicLibrary.open('libgio-2.0.so'); + } on ArgumentError { + _gio = null; + } + } + + DynamicLibrary? _gio; + + /// True if libgio was opened successfully. + bool get libraryIsPresent => _gio != null; + + /// Wraps `g_application_get_default`. + int gApplicationGetDefault() { + if (_gio == null) { + return 0; + } + final _GApplicationGetDefaultDart getDefault = _gio! + .lookupFunction<_GApplicationGetDefaultC, _GApplicationGetDefaultDart>( + 'g_application_get_default'); + return getDefault(); + } + + /// Wraps g_application_get_application_id. + Pointer gApplicationGetApplicationId(int app) { + if (_gio == null) { + return nullptr; + } + final _GApplicationGetApplicationIdDart gApplicationGetApplicationId = _gio! + .lookupFunction<_GApplicationGetApplicationIdC, + _GApplicationGetApplicationIdDart>( + 'g_application_get_application_id'); + return gApplicationGetApplicationId(app); + } +} + +/// Allows overriding the default GioUtils instance with a fake for testing. +@visibleForTesting +GioUtils? gioUtilsOverride; + +/// Gets the application ID for this app. +String? getApplicationId() { + final GioUtils gio = gioUtilsOverride ?? GioUtils(); + if (!gio.libraryIsPresent) { + return null; + } + + final int app = gio.gApplicationGetDefault(); + if (app == 0) { + return null; + } + final Pointer appId = gio.gApplicationGetApplicationId(app); + if (appId == nullptr) { + return null; + } + return appId.toDartString(); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/src/get_application_id_stub.dart b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/src/get_application_id_stub.dart new file mode 100644 index 00000000..90999769 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/src/get_application_id_stub.dart @@ -0,0 +1,6 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// Gets the application ID for this app. +String? getApplicationId() => null; diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/src/path_provider_linux.dart b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/src/path_provider_linux.dart new file mode 100644 index 00000000..59097114 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/lib/src/path_provider_linux.dart @@ -0,0 +1,102 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:path/path.dart' as path; +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; +import 'package:xdg_directories/xdg_directories.dart' as xdg; + +import 'get_application_id.dart'; + +/// The linux implementation of [PathProviderPlatform] +/// +/// This class implements the `package:path_provider` functionality for Linux. +class PathProviderLinux extends PathProviderPlatform { + /// Constructs an instance of [PathProviderLinux] + PathProviderLinux() : _environment = Platform.environment; + + /// Constructs an instance of [PathProviderLinux] with the given [environment] + @visibleForTesting + PathProviderLinux.private( + {Map environment = const {}, + String? executableName, + String? applicationId}) + : _environment = environment, + _executableName = executableName, + _applicationId = applicationId; + + final Map _environment; + String? _executableName; + String? _applicationId; + + /// Registers this class as the default instance of [PathProviderPlatform] + static void registerWith() { + PathProviderPlatform.instance = PathProviderLinux(); + } + + @override + Future getTemporaryPath() { + final String environmentTmpDir = _environment['TMPDIR'] ?? ''; + return Future.value( + environmentTmpDir.isEmpty ? '/tmp' : environmentTmpDir, + ); + } + + @override + Future getApplicationSupportPath() async { + final Directory directory = + Directory(path.join(xdg.dataHome.path, await _getId())); + if (directory.existsSync()) { + return directory.path; + } + + // This plugin originally used the executable name as a directory. + // Use that if it exists for backwards compatibility. + final Directory legacyDirectory = + Directory(path.join(xdg.dataHome.path, await _getExecutableName())); + if (legacyDirectory.existsSync()) { + return legacyDirectory.path; + } + + // Create the directory, because mobile implementations assume the directory exists. + await directory.create(recursive: true); + return directory.path; + } + + @override + Future getApplicationDocumentsPath() { + return Future.value(xdg.getUserDirectory('DOCUMENTS')?.path); + } + + @override + Future getApplicationCachePath() async { + final Directory directory = + Directory(path.join(xdg.cacheHome.path, await _getId())); + if (!directory.existsSync()) { + await directory.create(recursive: true); + } + return directory.path; + } + + @override + Future getDownloadsPath() { + return Future.value(xdg.getUserDirectory('DOWNLOAD')?.path); + } + + // Gets the name of this executable. + Future _getExecutableName() async { + _executableName ??= path.basenameWithoutExtension( + await File('/proc/self/exe').resolveSymbolicLinks()); + return _executableName!; + } + + // Gets the unique ID for this application. + Future _getId() async { + _applicationId ??= getApplicationId(); + // If no application ID then fall back to using the executable name. + return _applicationId ?? await _getExecutableName(); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/pubspec.yaml b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/pubspec.yaml new file mode 100644 index 00000000..4ad2805a --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/pubspec.yaml @@ -0,0 +1,33 @@ +name: path_provider_linux +description: Linux implementation of the path_provider plugin +repository: https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_linux +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 +version: 2.2.1 + +environment: + sdk: ">=2.19.0 <4.0.0" + flutter: ">=3.7.0" + +flutter: + plugin: + implements: path_provider + platforms: + linux: + dartPluginClass: PathProviderLinux + +dependencies: + ffi: ">=1.1.2 <3.0.0" + flutter: + sdk: flutter + path: ^1.8.0 + path_provider_platform_interface: ^2.1.0 + xdg_directories: ">=0.2.0 <2.0.0" + +dev_dependencies: + flutter_test: + sdk: flutter + +topics: + - files + - path-provider + - paths diff --git a/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/AUTHORS b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/AUTHORS new file mode 100644 index 00000000..493a0b4e --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/AUTHORS @@ -0,0 +1,66 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/CHANGELOG.md b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/CHANGELOG.md new file mode 100644 index 00000000..43c46f51 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/CHANGELOG.md @@ -0,0 +1,110 @@ +## 2.4.1 + +* Fixes `getStringList` returning immutable list. +* Fixes `getStringList` cast error. +* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. + +## 2.4.0 + +* Adds `SharedPreferencesAsyncLinux` API. +* Updates minimum supported SDK version to Flutter 3.16/Dart 3.2. + +## 2.3.2 + +* Updates `package:file` version constraints. + +## 2.3.1 + +* Adds pub topics to package metadata. +* Updates minimum supported SDK version to Flutter 3.7/Dart 2.19. + +## 2.3.0 + +* Adds `clearWithParameters` and `getAllWithParameters` methods. +* Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. + +## 2.2.0 + +* Adds `getAllWithPrefix` and `clearWithPrefix` methods. + +## 2.1.5 + +* Clarifies explanation of endorsement in README. +* Aligns Dart and Flutter SDK constraints. + +## 2.1.4 + +* Updates links for the merge of flutter/plugins into flutter/packages. +* Updates minimum Flutter version to 3.0. + +## 2.1.3 + +* Updates code for stricter lint checks. + +## 2.1.2 + +* Updates code for stricter lint checks. +* Updates code for `no_leading_underscores_for_local_identifiers` lint. +* Updates minimum Flutter version to 2.10. + +## 2.1.1 + +* Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + +## 2.1.0 + +* Deprecated `SharedPreferencesWindows.instance` in favor of `SharedPreferencesStorePlatform.instance`. + +## 2.0.4 + +* Removes dependency on `meta`. + +## 2.0.3 + +* Removed obsolete `pluginClass: none` from pubpsec. +* Fixes newly enabled analyzer options. + +## 2.0.2 + +* Updated installation instructions in README. + +## 2.0.1 + +* Add `implements` to the pubspec. +* Add `registerWith` to the Dart main class. + +## 2.0.0 + +* Migrate to null-safety. + +## 0.0.3+1 + +* Update Flutter SDK constraint. + +## 0.0.3 + +* Update integration test examples to use `testWidgets` instead of `test`. + +## 0.0.2+4 + +* Remove unused `test` dependency. +* Update Dart SDK constraint in example. + +## 0.0.2+3 + +* Check in linux/ directory for example/ + +## 0.0.2+2 + +* Bump the `file` package dependency to resolve dep conflicts with `flutter_driver`. + +## 0.0.2+1 +* Replace path_provider dependency with path_provider_linux. + +## 0.0.2 +* Add iOS stub. + +## 0.0.1 +* Initial release to support shared_preferences on Linux. diff --git a/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/integration_test/shared_preferences_test.dart b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/integration_test/shared_preferences_test.dart new file mode 100644 index 00000000..fdabea41 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/integration_test/shared_preferences_test.dart @@ -0,0 +1,556 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:shared_preferences_linux/shared_preferences_linux.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('SharedPreferencesLinux', () { + late SharedPreferencesLinux preferences; + + const Map flutterTestValues = { + 'flutter.String': 'hello world', + 'flutter.Bool': true, + 'flutter.Int': 42, + 'flutter.Double': 3.14159, + 'flutter.StringList': ['foo', 'bar'], + }; + + const Map prefixTestValues = { + 'prefix.String': 'hello world', + 'prefix.Bool': true, + 'prefix.Int': 42, + 'prefix.Double': 3.14159, + 'prefix.StringList': ['foo', 'bar'], + }; + + const Map nonPrefixTestValues = { + 'String': 'hello world', + 'Bool': true, + 'Int': 42, + 'Double': 3.14159, + 'StringList': ['foo', 'bar'], + }; + + final Map allTestValues = {}; + + allTestValues.addAll(flutterTestValues); + allTestValues.addAll(prefixTestValues); + allTestValues.addAll(nonPrefixTestValues); + + Future addData() async { + await preferences.setValue('String', 'String', allTestValues['String']!); + await preferences.setValue('Bool', 'Bool', allTestValues['Bool']!); + await preferences.setValue('Int', 'Int', allTestValues['Int']!); + await preferences.setValue('Double', 'Double', allTestValues['Double']!); + await preferences.setValue( + 'StringList', 'StringList', allTestValues['StringList']!); + await preferences.setValue( + 'String', 'prefix.String', allTestValues['prefix.String']!); + await preferences.setValue( + 'Bool', 'prefix.Bool', allTestValues['prefix.Bool']!); + await preferences.setValue( + 'Int', 'prefix.Int', allTestValues['prefix.Int']!); + await preferences.setValue( + 'Double', 'prefix.Double', allTestValues['prefix.Double']!); + await preferences.setValue('StringList', 'prefix.StringList', + allTestValues['prefix.StringList']!); + await preferences.setValue( + 'String', 'flutter.String', allTestValues['flutter.String']!); + await preferences.setValue( + 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!); + await preferences.setValue( + 'Int', 'flutter.Int', allTestValues['flutter.Int']!); + await preferences.setValue( + 'Double', 'flutter.Double', allTestValues['flutter.Double']!); + await preferences.setValue('StringList', 'flutter.StringList', + allTestValues['flutter.StringList']!); + } + + setUp(() async { + preferences = SharedPreferencesLinux(); + await addData(); + }); + + tearDown(() async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + }); + + testWidgets('getAll', (WidgetTester _) async { + final Map values = await preferences.getAll(); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect(values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + group('withPrefix', () { + testWidgets('remove', (WidgetTester _) async { + const String key = 'flutter.String'; + await preferences.remove(key); + final Map values = + await preferences.getAllWithPrefix(''); + expect(values[key], isNull); + }); + + testWidgets('clear', (WidgetTester _) async { + await preferences.clear(); + final Map values = await preferences.getAll(); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); + + testWidgets('get all with prefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithPrefix('prefix.'); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], allTestValues['prefix.StringList']); + }); + + testWidgets('getAllWithNoPrefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithPrefix(''); + expect(values['String'], allTestValues['String']); + expect(values['Bool'], allTestValues['Bool']); + expect(values['Int'], allTestValues['Int']); + expect(values['Double'], allTestValues['Double']); + expect(values['StringList'], allTestValues['StringList']); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithPrefix', (WidgetTester _) async { + await preferences.clearWithPrefix('prefix.'); + Map values = + await preferences.getAllWithPrefix('prefix.'); + expect(values['prefix.String'], null); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithPrefix('flutter.'); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithNoPrefix', (WidgetTester _) async { + await preferences.clearWithPrefix(''); + final Map values = + await preferences.getAllWithPrefix(''); + expect(values['String'], null); + expect(values['Bool'], null); + expect(values['Int'], null); + expect(values['Double'], null); + expect(values['StringList'], null); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); + }); + + group('withParameters', () { + testWidgets('remove', (WidgetTester _) async { + const String key = 'flutter.String'; + await preferences.remove(key); + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values[key], isNull); + }); + + testWidgets('clear', (WidgetTester _) async { + await preferences.clear(); + final Map values = await preferences.getAll(); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); + + testWidgets('get all with prefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], allTestValues['prefix.StringList']); + }); + + testWidgets('get all with allow list', (WidgetTester _) async { + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.String'}, + ), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + }); + + testWidgets('getAllWithNoPrefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values['String'], allTestValues['String']); + expect(values['Bool'], allTestValues['Bool']); + expect(values['Int'], allTestValues['Int']); + expect(values['Double'], allTestValues['Double']); + expect(values['StringList'], allTestValues['StringList']); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithParameters', (WidgetTester _) async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], null); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'flutter.'), + ), + ); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithParameters with allow list', + (WidgetTester _) async { + await addData(); + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.StringList'}, + ), + ), + ); + Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'flutter.'), + ), + ); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithNoPrefix', (WidgetTester _) async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values['String'], null); + expect(values['Bool'], null); + expect(values['Int'], null); + expect(values['Double'], null); + expect(values['StringList'], null); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); + }); + }); + + group('shared_preferences_async', () { + const SharedPreferencesLinuxOptions emptyOptions = + SharedPreferencesLinuxOptions(); + + const String stringKey = 'testString'; + const String boolKey = 'testBool'; + const String intKey = 'testInt'; + const String doubleKey = 'testDouble'; + const String listKey = 'testList'; + + const String testString = 'hello world'; + const bool testBool = true; + const int testInt = 42; + const double testDouble = 3.14159; + const List testList = ['foo', 'bar']; + + Future getPreferences( + {bool clear = true}) async { + final SharedPreferencesAsyncPlatform preferences = + SharedPreferencesAsyncPlatform.instance!; + if (clear) { + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + emptyOptions); + } + return preferences; + } + + testWidgets('set and get String', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setString(stringKey, testString, emptyOptions); + expect(await preferences.getString(stringKey, emptyOptions), testString); + }); + + testWidgets('set and get bool', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setBool(boolKey, testBool, emptyOptions); + expect(await preferences.getBool(boolKey, emptyOptions), testBool); + }); + + testWidgets('set and get int', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setInt(intKey, testInt, emptyOptions); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + }); + + testWidgets('set and get double', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + }); + + testWidgets('set and get StringList', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setStringList(listKey, testList, emptyOptions); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); + testWidgets('getStringList does not throw cast error', + (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setStringList(listKey, testList, emptyOptions); + await (preferences as SharedPreferencesAsyncLinux).reload(emptyOptions); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); + + testWidgets('getStringList returns mutable list', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setStringList(listKey, testList, emptyOptions); + final List? list = + await preferences.getStringList(listKey, emptyOptions); + list?.add('value'); + expect(list?.length, testList.length + 1); + }); + + testWidgets('getPreferences', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(gotAll.length, 5); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + expect(gotAll[intKey], testInt); + expect(gotAll[doubleKey], testDouble); + expect(gotAll[listKey], testList); + }); + + testWidgets('getPreferences with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + + expect(gotAll.length, 2); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + }); + + testWidgets('getKeys', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(keys.length, 5); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + expect(keys, contains(intKey)); + expect(keys, contains(doubleKey)); + expect(keys, contains(listKey)); + }); + + testWidgets('getKeys with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + + expect(keys.length, 2); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + }); + + testWidgets('clear', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), null); + expect(await preferences.getDouble(doubleKey, emptyOptions), null); + expect(await preferences.getStringList(listKey, emptyOptions), null); + }); + + testWidgets('clear with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + await preferences.clear( + const ClearPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); + }); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/lib/main.dart b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/lib/main.dart new file mode 100644 index 00000000..b333716c --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/lib/main.dart @@ -0,0 +1,100 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:shared_preferences_linux/shared_preferences_linux.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return const MaterialApp( + title: 'SharedPreferences Demo', + home: SharedPreferencesDemo(), + ); + } +} + +class SharedPreferencesDemo extends StatefulWidget { + const SharedPreferencesDemo({super.key}); + + @override + SharedPreferencesDemoState createState() => SharedPreferencesDemoState(); +} + +class SharedPreferencesDemoState extends State { + final SharedPreferencesAsyncPlatform? _prefs = + SharedPreferencesAsyncPlatform.instance; + final SharedPreferencesLinuxOptions options = + const SharedPreferencesLinuxOptions(); + static const String _counterKey = 'counter'; + late Future _counter; + + Future _incrementCounter() async { + final int? value = await _prefs!.getInt(_counterKey, options); + final int counter = (value ?? 0) + 1; + + setState(() { + _counter = _prefs.setInt(_counterKey, counter, options).then((_) { + return counter; + }); + }); + } + + Future _getAndSetCounter() async { + setState(() { + _counter = _prefs!.getInt(_counterKey, options).then((int? counter) { + return counter ?? 0; + }); + }); + } + + @override + void initState() { + super.initState(); + _getAndSetCounter(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('SharedPreferences Demo'), + ), + body: Center( + child: FutureBuilder( + future: _counter, + builder: (BuildContext context, AsyncSnapshot snapshot) { + switch (snapshot.connectionState) { + case ConnectionState.none: + case ConnectionState.waiting: + return const CircularProgressIndicator(); + case ConnectionState.active: + case ConnectionState.done: + if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else { + return Text( + 'Button tapped ${snapshot.data} time${snapshot.data == 1 ? '' : 's'}.\n\n' + 'This should persist across restarts.', + ); + } + } + })), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: const Icon(Icons.add), + ), + ); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/CMakeLists.txt new file mode 100644 index 00000000..0236a880 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/flutter/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/flutter/CMakeLists.txt new file mode 100644 index 00000000..94f43ff7 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,86 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + linux-x64 ${CMAKE_BUILD_TYPE} +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/flutter/generated_plugins.cmake b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 00000000..2e1de87a --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/main.cc b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/main.cc new file mode 100644 index 00000000..1507d028 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/main.cc @@ -0,0 +1,10 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/my_application.cc b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/my_application.cc new file mode 100644 index 00000000..878cd973 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/my_application.cc @@ -0,0 +1,48 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "my_application.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), nullptr)); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/my_application.h b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/my_application.h new file mode 100644 index 00000000..6e9f0c3f --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/linux/my_application.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/pubspec.yaml b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/pubspec.yaml new file mode 100644 index 00000000..35da7cfa --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/pubspec.yaml @@ -0,0 +1,28 @@ +name: shared_preferences_linux_example +description: Demonstrates how to use the shared_preferences_linux plugin. +publish_to: none + +environment: + sdk: ^3.3.0 + flutter: ">=3.19.0" + +dependencies: + flutter: + sdk: flutter + shared_preferences_linux: + # When depending on this package from a real application you should use: + # shared_preferences_linux: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + shared_preferences_platform_interface: ^2.4.0 + +dev_dependencies: + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/test_driver/integration_test.dart b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/test_driver/integration_test.dart new file mode 100644 index 00000000..4f10f2a5 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/lib/shared_preferences_linux.dart b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/lib/shared_preferences_linux.dart new file mode 100644 index 00000000..d4ede84e --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/lib/shared_preferences_linux.dart @@ -0,0 +1,420 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:convert' show json; + +import 'package:file/file.dart'; +import 'package:file/local.dart'; +import 'package:flutter/foundation.dart' show debugPrint, visibleForTesting; +import 'package:path/path.dart' as path; +import 'package:path_provider_linux/path_provider_linux.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; + +const String _defaultFileName = 'shared_preferences'; + +const String _defaultPrefix = 'flutter.'; + +/// The Linux implementation of [SharedPreferencesStorePlatform]. +/// +/// This class implements the `package:shared_preferences` functionality for Linux. +class SharedPreferencesLinux extends SharedPreferencesStorePlatform { + /// Deprecated instance of [SharedPreferencesLinux]. + /// Use [SharedPreferencesStorePlatform.instance] instead. + @Deprecated('Use `SharedPreferencesStorePlatform.instance` instead.') + static SharedPreferencesLinux instance = SharedPreferencesLinux(); + + /// Registers the Linux implementation. + static void registerWith() { + SharedPreferencesStorePlatform.instance = SharedPreferencesLinux(); + // A temporary work-around for having two plugins contained in a single package. + SharedPreferencesAsyncLinux.registerWith(); + } + + /// Local copy of preferences + Map? _cachedPreferences; + + /// File system used to store to disk. Exposed for testing only. + @visibleForTesting + FileSystem fs = const LocalFileSystem(); + + /// The path_provider_linux instance used to find the support directory. + @visibleForTesting + PathProviderLinux pathProvider = PathProviderLinux(); + + /// Checks for cached preferences and returns them or loads preferences from + /// file and returns and caches them. + Future> _readPreferences() async { + _cachedPreferences ??= await _reload( + _defaultFileName, + fs: fs, + pathProvider: pathProvider, + ); + return _cachedPreferences!; + } + + @override + Future clear() async { + return clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); + } + + @override + Future clearWithPrefix(String prefix) async { + return clearWithParameters( + ClearParameters(filter: PreferencesFilter(prefix: prefix))); + } + + @override + Future clearWithParameters(ClearParameters parameters) async { + final PreferencesFilter filter = parameters.filter; + + final Map preferences = await _readPreferences(); + preferences.removeWhere((String key, _) => + key.startsWith(filter.prefix) && + (filter.allowList == null || filter.allowList!.contains(key))); + return _writePreferences( + preferences, + _defaultFileName, + fs: fs, + pathProvider: pathProvider, + ); + } + + @override + Future> getAll() async { + return getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); + } + + @override + Future> getAllWithPrefix(String prefix) async { + return getAllWithParameters( + GetAllParameters(filter: PreferencesFilter(prefix: prefix))); + } + + @override + Future> getAllWithParameters( + GetAllParameters parameters) async { + final PreferencesFilter filter = parameters.filter; + final Map withPrefix = + Map.from(await _readPreferences()); + withPrefix.removeWhere((String key, _) => !(key.startsWith(filter.prefix) && + (filter.allowList?.contains(key) ?? true))); + return withPrefix; + } + + @override + Future remove(String key) async { + final Map preferences = await _readPreferences(); + preferences.remove(key); + return _writePreferences( + preferences, + _defaultFileName, + fs: fs, + pathProvider: pathProvider, + ); + } + + @override + Future setValue(String valueType, String key, Object value) async { + final Map preferences = await _readPreferences(); + preferences[key] = value; + return _writePreferences( + preferences, + _defaultFileName, + fs: fs, + pathProvider: pathProvider, + ); + } +} + +/// The Linux implementation of [SharedPreferencesAsyncPlatform]. +/// +/// This class implements the `package:shared_preferences` functionality for Linux. +base class SharedPreferencesAsyncLinux extends SharedPreferencesAsyncPlatform { + /// Registers the Linux implementation. + static void registerWith() { + SharedPreferencesAsyncPlatform.instance = SharedPreferencesAsyncLinux(); + } + + /// Local copy of preferences + Map? _cachedPreferences; + + /// File system used to store to disk. Exposed for testing only. + @visibleForTesting + FileSystem fs = const LocalFileSystem(); + + /// The path_provider_linux instance used to find the support directory. + @visibleForTesting + PathProviderLinux pathProvider = PathProviderLinux(); + + @override + Future> getKeys( + GetPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + return (await getPreferences(parameters, options)).keys.toSet(); + } + + @override + Future setString( + String key, + String value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future setBool( + String key, + bool value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future setDouble( + String key, + double value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future setInt( + String key, + int value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future setStringList( + String key, + List value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future getString( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = await _readAll({key}, options); + return data[key] as String?; + } + + @override + Future getBool( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = await _readAll({key}, options); + return data[key] as bool?; + } + + @override + Future getDouble( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = await _readAll({key}, options); + return data[key] as double?; + } + + @override + Future getInt( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = await _readAll({key}, options); + return data[key] as int?; + } + + @override + Future?> getStringList( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = await _readAll({key}, options); + return (data[key] as List?)?.cast().toList(); + } + + @override + Future clear(ClearPreferencesParameters parameters, + SharedPreferencesOptions options) async { + final SharedPreferencesLinuxOptions linuxOptions = + SharedPreferencesLinuxOptions.fromSharedPreferencesOptions(options); + final PreferencesFilters filter = parameters.filter; + final Map preferences = + await _readPreferences(linuxOptions.fileName); + preferences.removeWhere((String key, _) => + filter.allowList == null || filter.allowList!.contains(key)); + await _writePreferences( + preferences, + linuxOptions.fileName, + fs: fs, + pathProvider: pathProvider, + ); + } + + @override + Future> getPreferences( + GetPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + return _readAll(parameters.filter.allowList, options); + } + + /// Reloads preferences from file. + @visibleForTesting + Future reload( + SharedPreferencesLinuxOptions options, + ) async { + _cachedPreferences = await _reload(options.fileName); + } + + Future> _readAll( + Set? allowList, + SharedPreferencesOptions options, + ) async { + final SharedPreferencesLinuxOptions linuxOptions = + SharedPreferencesLinuxOptions.fromSharedPreferencesOptions(options); + final Map prefs = + Map.from(await _readPreferences(linuxOptions.fileName)); + prefs.removeWhere((String key, _) => !(allowList?.contains(key) ?? true)); + return prefs; + } + + Future _setValue( + String key, Object value, SharedPreferencesOptions options) async { + final SharedPreferencesLinuxOptions linuxOptions = + SharedPreferencesLinuxOptions.fromSharedPreferencesOptions(options); + final Map preferences = + await _readPreferences(linuxOptions.fileName); + preferences[key] = value; + await _writePreferences( + preferences, + linuxOptions.fileName, + fs: fs, + pathProvider: pathProvider, + ); + } + + /// Checks for cached preferences and returns them or loads preferences from + /// file and returns and caches them. + Future> _readPreferences(String fileName) async { + _cachedPreferences ??= await _reload( + fileName, + fs: fs, + pathProvider: pathProvider, + ); + return _cachedPreferences!; + } +} + +/// Gets the file where the preferences are stored. +Future _getLocalDataFile( + String fileName, { + FileSystem fs = const LocalFileSystem(), + PathProviderLinux? pathProvider, +}) async { + pathProvider = pathProvider ?? PathProviderLinux(); + final String? directory = await pathProvider.getApplicationSupportPath(); + if (directory == null) { + return null; + } + final String fileLocation = path.join(directory, '$fileName.json'); + return fs.file(fileLocation); +} + +/// Gets the preferences from the stored file and saves them in cache. +Future> _reload( + String fileName, { + FileSystem fs = const LocalFileSystem(), + PathProviderLinux? pathProvider, +}) async { + Map preferences = {}; + final File? localDataFile = await _getLocalDataFile( + fileName, + fs: fs, + pathProvider: pathProvider, + ); + if (localDataFile != null && localDataFile.existsSync()) { + final String stringMap = localDataFile.readAsStringSync(); + if (stringMap.isNotEmpty) { + final Object? data = json.decode(stringMap); + if (data is Map) { + preferences = data.cast(); + } + } + } + return preferences; +} + +/// Writes the cached preferences to disk. Returns [true] if the operation +/// succeeded. +Future _writePreferences( + Map preferences, + String fileName, { + FileSystem fs = const LocalFileSystem(), + PathProviderLinux? pathProvider, +}) async { + try { + final File? localDataFile = await _getLocalDataFile( + fileName, + fs: fs, + pathProvider: pathProvider, + ); + if (localDataFile == null) { + debugPrint('Unable to determine where to write preferences.'); + return false; + } + if (!localDataFile.existsSync()) { + localDataFile.createSync(recursive: true); + } + final String stringMap = json.encode(preferences); + localDataFile.writeAsStringSync(stringMap); + } catch (e) { + debugPrint('Error saving preferences to disk: $e'); + return false; + } + return true; +} + +/// Linux specific SharedPreferences Options. +class SharedPreferencesLinuxOptions extends SharedPreferencesOptions { + /// Constructor for SharedPreferencesLinuxOptions. + const SharedPreferencesLinuxOptions({ + this.fileName = 'shared_preferences', + }); + + /// The name of the file to store preferences in. + final String fileName; + + /// Returns a new instance of [SharedPreferencesLinuxOptions] from an existing + /// [SharedPreferencesOptions]. + static SharedPreferencesLinuxOptions fromSharedPreferencesOptions( + SharedPreferencesOptions options) { + if (options is SharedPreferencesLinuxOptions) { + return options; + } + return const SharedPreferencesLinuxOptions(); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/pubspec.yaml b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/pubspec.yaml new file mode 100644 index 00000000..c43a21bd --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/shared_preferences_linux/pubspec.yaml @@ -0,0 +1,34 @@ +name: shared_preferences_linux +description: Linux implementation of the shared_preferences plugin +repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_linux +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 +version: 2.4.1 + +environment: + sdk: ^3.3.0 + flutter: ">=3.19.0" + +flutter: + plugin: + implements: shared_preferences + platforms: + linux: + dartPluginClass: SharedPreferencesLinux + +dependencies: + file: ">=6.0.0 <8.0.0" + flutter: + sdk: flutter + path: ^1.8.0 + path_provider_linux: ^2.0.0 + path_provider_platform_interface: ^2.0.0 + shared_preferences_platform_interface: ^2.4.0 + +dev_dependencies: + flutter_test: + sdk: flutter + +topics: + - persistence + - shared-preferences + - storage diff --git a/linux/flutter/ephemeral/.plugin_symlinks/syncfusion_pdfviewer_linux/pubspec.yaml b/linux/flutter/ephemeral/.plugin_symlinks/syncfusion_pdfviewer_linux/pubspec.yaml index 870634f3..ced0bed9 100644 --- a/linux/flutter/ephemeral/.plugin_symlinks/syncfusion_pdfviewer_linux/pubspec.yaml +++ b/linux/flutter/ephemeral/.plugin_symlinks/syncfusion_pdfviewer_linux/pubspec.yaml @@ -1,11 +1,11 @@ name: syncfusion_pdfviewer_linux description: Linux platform implementation of the Flutter PDF Viewer library that lets you view the PDF documents seamlessly and efficiently. -version: 31.1.17 +version: 32.1.19 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_pdfviewer_linux environment: sdk: ^3.7.0 - flutter: '>=3.29.0' + flutter: '>=3.35.1' flutter: plugin: diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/AUTHORS b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/AUTHORS new file mode 100644 index 00000000..493a0b4e --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/AUTHORS @@ -0,0 +1,66 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/CHANGELOG.md b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/CHANGELOG.md new file mode 100644 index 00000000..f0afca15 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/CHANGELOG.md @@ -0,0 +1,110 @@ +## 3.2.2 + +* Updates to Pigeon 26. +* Updates minimum supported SDK version to Flutter 3.32/Dart 3.8. + +## 3.2.1 + +* Updates Pigeon to resolve a compilation failure with some versions of glib. + +## 3.2.0 + +* Updates platform channels to use Pigeon. +* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. + +## 3.1.1 + +* Implements `launchUrl`. +* Simplifies method channel interface by removing unused elements. +* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. + +## 3.1.0 + +* Implements `supportsMode` and `supportsCloseForMode`. + +## 3.0.6 + +* Adds pub topics to package metadata. +* Updates minimum supported SDK version to Flutter 3.7/Dart 2.19. + +## 3.0.5 + +* Sets a cmake_policy compatibility version to fix build warnings. + +## 3.0.4 + +* Clarifies explanation of endorsement in README. +* Aligns Dart and Flutter SDK constraints. + +## 3.0.3 + +* Updates links for the merge of flutter/plugins into flutter/packages. +* Updates minimum Flutter version to 3.0. + +## 3.0.2 + +* Updates code for stricter lint checks. +* Updates minimum Flutter version to 2.10. + +## 3.0.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + +## 3.0.0 + +* Changes the major version since, due to a typo in `default_package` in + existing versions of `url_launcher`, requiring Dart registration in this + package is in practice a breaking change. + * Does not include any API changes; clients can allow both 2.x or 3.x. + +## 2.0.4 + +* **\[Retracted\]** Switches to an in-package method channel implementation. + +## 2.0.3 + +* Updates code for new analysis options. +* Fix minor memory leak in Linux url_launcher tests. +* Fixes canLaunch detection for URIs addressing on local or network file systems + +## 2.0.2 + +* Replaced reference to `shared_preferences` plugin with the `url_launcher` in the README. + +## 2.0.1 + +* Updated installation instructions in README. + +## 2.0.0 + +* Migrate to null safety. +* Update the example app: remove the deprecated `RaisedButton` and `FlatButton` widgets. +* Fix outdated links across a number of markdown files ([#3276](https://github.com/flutter/plugins/pull/3276)) +* Set `implementation` in pubspec.yaml + +## 0.0.2+1 + +* Update Flutter SDK constraint. + +## 0.0.2 + +* Update integration test examples to use `testWidgets` instead of `test`. + +## 0.0.1+4 + +* Update Dart SDK constraint in example. + +## 0.0.1+3 + +* Add a missing include. + +## 0.0.1+2 + +* Check in linux/ directory for example/ + +# 0.0.1+1 +* README update for endorsement by url_launcher. + +# 0.0.1 +* The initial implementation of url_launcher for Linux diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/integration_test/url_launcher_test.dart b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/integration_test/url_launcher_test.dart new file mode 100644 index 00000000..d67d61df --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/integration_test/url_launcher_test.dart @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('canLaunch', (WidgetTester _) async { + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + + expect(await launcher.canLaunch('randomstring'), false); + + // Generally all devices should have some default browser. + expect(await launcher.canLaunch('http://flutter.dev'), true); + }); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/lib/main.dart b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/lib/main.dart new file mode 100644 index 00000000..e8d91be1 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/lib/main.dart @@ -0,0 +1,91 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'URL Launcher', + theme: ThemeData(primarySwatch: Colors.blue), + home: const MyHomePage(title: 'URL Launcher'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, required this.title}); + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + Future? _launched; + + Future _launchInBrowser(String url) async { + if (await UrlLauncherPlatform.instance.canLaunch(url)) { + await UrlLauncherPlatform.instance.launch( + url, + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: {}, + ); + } else { + throw Exception('Could not launch $url'); + } + } + + Widget _launchStatus(BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else { + return const Text(''); + } + } + + @override + Widget build(BuildContext context) { + const String toLaunch = 'https://www.cylog.org/headers/'; + return Scaffold( + appBar: AppBar(title: Text(widget.title)), + body: ListView( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Padding( + padding: EdgeInsets.all(16.0), + child: Text(toLaunch), + ), + ElevatedButton( + onPressed: () => setState(() { + _launched = _launchInBrowser(toLaunch); + }), + child: const Text('Launch in browser'), + ), + const Padding(padding: EdgeInsets.all(16.0)), + FutureBuilder(future: _launched, builder: _launchStatus), + ], + ), + ], + ), + ); + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/CMakeLists.txt new file mode 100644 index 00000000..11219aa5 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/CMakeLists.txt @@ -0,0 +1,100 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Enable the test target. +set(include_url_launcher_linux_tests TRUE) +# Provide an alias for the test target using the name expected by repo tooling. +add_custom_target(unit_tests DEPENDS url_launcher_linux_test) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/flutter/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/flutter/CMakeLists.txt new file mode 100644 index 00000000..33fd5801 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,87 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/flutter/generated_plugins.cmake b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 00000000..f16b4c34 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/main.cc b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/main.cc new file mode 100644 index 00000000..3b03bbf6 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/main.cc @@ -0,0 +1,10 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/my_application.cc b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/my_application.cc new file mode 100644 index 00000000..bb60dbd6 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/my_application.cc @@ -0,0 +1,48 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "my_application.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), nullptr)); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/my_application.h b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/my_application.h new file mode 100644 index 00000000..9ae704a9 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/linux/my_application.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/pubspec.yaml b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/pubspec.yaml new file mode 100644 index 00000000..bba98221 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/pubspec.yaml @@ -0,0 +1,28 @@ +name: url_launcher_example +description: Demonstrates how to use the url_launcher plugin. +publish_to: none + +environment: + sdk: ^3.8.0 + flutter: ">=3.32.0" + +dependencies: + flutter: + sdk: flutter + url_launcher_linux: + # When depending on this package from a real application you should use: + # url_launcher_linux: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + url_launcher_platform_interface: ^2.2.0 + +dev_dependencies: + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/test_driver/integration_test.dart b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/test_driver/integration_test.dart new file mode 100644 index 00000000..fb3dec00 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/example/test_driver/integration_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/lib/src/messages.g.dart b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/lib/src/messages.g.dart new file mode 100644 index 00000000..6187ef54 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/lib/src/messages.g.dart @@ -0,0 +1,119 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v26.1.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + default: + return super.readValueOfType(type, buffer); + } + } +} + +class UrlLauncherApi { + /// Constructor for [UrlLauncherApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + UrlLauncherApi({ + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty + ? '.$messageChannelSuffix' + : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + /// Returns true if the URL can definitely be launched. + Future canLaunchUrl(String url) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.url_launcher_linux.UrlLauncherApi.canLaunchUrl$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [url], + ); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as bool?)!; + } + } + + /// Opens the URL externally, returning an error string on failure. + Future launchUrl(String url) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.url_launcher_linux.UrlLauncherApi.launchUrl$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send( + [url], + ); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return (pigeonVar_replyList[0] as String?); + } + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/lib/url_launcher_linux.dart b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/lib/url_launcher_linux.dart new file mode 100644 index 00000000..6e7acfa0 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/lib/url_launcher_linux.dart @@ -0,0 +1,77 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:url_launcher_platform_interface/link.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +import 'src/messages.g.dart'; + +/// An implementation of [UrlLauncherPlatform] for Linux. +class UrlLauncherLinux extends UrlLauncherPlatform { + /// Creates a new URL launcher instance. + UrlLauncherLinux({@visibleForTesting UrlLauncherApi? api}) + : _hostApi = api ?? UrlLauncherApi(); + + /// Registers this class as the default instance of [UrlLauncherPlatform]. + static void registerWith() { + UrlLauncherPlatform.instance = UrlLauncherLinux(); + } + + final UrlLauncherApi _hostApi; + + @override + final LinkDelegate? linkDelegate = null; + + @override + Future canLaunch(String url) async { + return _hostApi.canLaunchUrl(url); + } + + @override + Future launch( + String url, { + required bool useSafariVC, + required bool useWebView, + required bool enableJavaScript, + required bool enableDomStorage, + required bool universalLinksOnly, + required Map headers, + String? webOnlyWindowName, + }) { + // None of the options are supported, so they don't need to be converted to + // LaunchOptions. + return launchUrl(url, const LaunchOptions()); + } + + @override + Future launchUrl(String url, LaunchOptions options) async { + final String? error = await _hostApi.launchUrl(url); + if (error != null) { + // TODO(stuartmorgan): Standardize errors across the entire plugin, + // instead of using PlatformException. This preserves the pre-Pigeon + // behavior of the C code returning this error response. + throw PlatformException( + code: 'Launch Error', + message: 'Failed to launch URL: $error', + ); + } + return true; + } + + @override + Future supportsMode(PreferredLaunchMode mode) async { + return mode == PreferredLaunchMode.platformDefault || + mode == PreferredLaunchMode.externalApplication; + } + + @override + Future supportsCloseForMode(PreferredLaunchMode mode) async { + // No supported mode is closeable. + return false; + } +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/CMakeLists.txt b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/CMakeLists.txt new file mode 100644 index 00000000..a52bd5ad --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/CMakeLists.txt @@ -0,0 +1,65 @@ +cmake_minimum_required(VERSION 3.10) +set(PROJECT_NAME "url_launcher_linux") +project(${PROJECT_NAME} LANGUAGES CXX) + +cmake_policy(VERSION 3.10...3.24) + +set(PLUGIN_NAME "${PROJECT_NAME}_plugin") + +list(APPEND PLUGIN_SOURCES + "messages.g.cc" + "url_launcher_plugin.cc" +) + +add_library(${PLUGIN_NAME} SHARED + ${PLUGIN_SOURCES} +) +apply_standard_settings(${PLUGIN_NAME}) +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter) +target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK) + + +# === Tests === + +if (${include_${PROJECT_NAME}_tests}) +if(${CMAKE_VERSION} VERSION_LESS "3.11.0") +message("Unit tests require CMake 3.11.0 or later") +else() +set(TEST_RUNNER "${PROJECT_NAME}_test") +enable_testing() +# TODO(stuartmorgan): Consider using a single shared, pre-checked-in googletest +# instance rather than downloading for each plugin. This approach makes sense +# for a template, but not for a monorepo with many plugins. +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/release-1.11.0.zip +) +# Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +# Disable install commands for gtest so it doesn't end up in the bundle. +set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE) + +FetchContent_MakeAvailable(googletest) + +# The plugin's exported API is not very useful for unit testing, so build the +# sources directly into the test binary rather than using the shared library. +add_executable(${TEST_RUNNER} + test/url_launcher_linux_test.cc + ${PLUGIN_SOURCES} +) +apply_standard_settings(${TEST_RUNNER}) +target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(${TEST_RUNNER} PRIVATE flutter) +target_link_libraries(${TEST_RUNNER} PRIVATE PkgConfig::GTK) +target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock) + +include(GoogleTest) +gtest_discover_tests(${TEST_RUNNER}) +endif() # CMake version check +endif() # include_${PROJECT_NAME}_tests diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/include/url_launcher_linux/url_launcher_plugin.h b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/include/url_launcher_linux/url_launcher_plugin.h new file mode 100644 index 00000000..09572ca9 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/include/url_launcher_linux/url_launcher_plugin.h @@ -0,0 +1,31 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PACKAGES_URL_LAUNCHER_URL_LAUNCHER_LINUX_LINUX_INCLUDE_URL_LAUNCHER_URL_LAUNCHER_PLUGIN_H_ +#define PACKAGES_URL_LAUNCHER_URL_LAUNCHER_LINUX_LINUX_INCLUDE_URL_LAUNCHER_URL_LAUNCHER_PLUGIN_H_ + +// A plugin to launch URLs. + +#include + +G_BEGIN_DECLS + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default"))) +#else +#define FLUTTER_PLUGIN_EXPORT +#endif + +G_DECLARE_FINAL_TYPE(FlUrlLauncherPlugin, fl_url_launcher_plugin, FL, + URL_LAUNCHER_PLUGIN, GObject) + +FLUTTER_PLUGIN_EXPORT FlUrlLauncherPlugin* fl_url_launcher_plugin_new( + FlPluginRegistrar* registrar); + +FLUTTER_PLUGIN_EXPORT void url_launcher_plugin_register_with_registrar( + FlPluginRegistrar* registrar); + +G_END_DECLS + +#endif // PACKAGES_URL_LAUNCHER_URL_LAUNCHER_LINUX_LINUX_INCLUDE_URL_LAUNCHER_URL_LAUNCHER_PLUGIN_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/messages.g.cc b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/messages.g.cc new file mode 100644 index 00000000..f91c6cf9 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/messages.g.cc @@ -0,0 +1,300 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v26.1.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#include "messages.g.h" + +struct _FulMessageCodec { + FlStandardMessageCodec parent_instance; +}; + +G_DEFINE_TYPE(FulMessageCodec, ful_message_codec, + fl_standard_message_codec_get_type()) + +static gboolean ful_message_codec_write_value(FlStandardMessageCodec* codec, + GByteArray* buffer, + FlValue* value, GError** error) { + if (fl_value_get_type(value) == FL_VALUE_TYPE_CUSTOM) { + switch (fl_value_get_custom_type(value)) {} + } + + return FL_STANDARD_MESSAGE_CODEC_CLASS(ful_message_codec_parent_class) + ->write_value(codec, buffer, value, error); +} + +static FlValue* ful_message_codec_read_value_of_type( + FlStandardMessageCodec* codec, GBytes* buffer, size_t* offset, int type, + GError** error) { + switch (type) { + default: + return FL_STANDARD_MESSAGE_CODEC_CLASS(ful_message_codec_parent_class) + ->read_value_of_type(codec, buffer, offset, type, error); + } +} + +static void ful_message_codec_init(FulMessageCodec* self) {} + +static void ful_message_codec_class_init(FulMessageCodecClass* klass) { + FL_STANDARD_MESSAGE_CODEC_CLASS(klass)->write_value = + ful_message_codec_write_value; + FL_STANDARD_MESSAGE_CODEC_CLASS(klass)->read_value_of_type = + ful_message_codec_read_value_of_type; +} + +static FulMessageCodec* ful_message_codec_new() { + FulMessageCodec* self = + FUL_MESSAGE_CODEC(g_object_new(ful_message_codec_get_type(), nullptr)); + return self; +} + +struct _FulUrlLauncherApiCanLaunchUrlResponse { + GObject parent_instance; + + FlValue* value; +}; + +G_DEFINE_TYPE(FulUrlLauncherApiCanLaunchUrlResponse, + ful_url_launcher_api_can_launch_url_response, G_TYPE_OBJECT) + +static void ful_url_launcher_api_can_launch_url_response_dispose( + GObject* object) { + FulUrlLauncherApiCanLaunchUrlResponse* self = + FUL_URL_LAUNCHER_API_CAN_LAUNCH_URL_RESPONSE(object); + g_clear_pointer(&self->value, fl_value_unref); + G_OBJECT_CLASS(ful_url_launcher_api_can_launch_url_response_parent_class) + ->dispose(object); +} + +static void ful_url_launcher_api_can_launch_url_response_init( + FulUrlLauncherApiCanLaunchUrlResponse* self) {} + +static void ful_url_launcher_api_can_launch_url_response_class_init( + FulUrlLauncherApiCanLaunchUrlResponseClass* klass) { + G_OBJECT_CLASS(klass)->dispose = + ful_url_launcher_api_can_launch_url_response_dispose; +} + +FulUrlLauncherApiCanLaunchUrlResponse* +ful_url_launcher_api_can_launch_url_response_new(gboolean return_value) { + FulUrlLauncherApiCanLaunchUrlResponse* self = + FUL_URL_LAUNCHER_API_CAN_LAUNCH_URL_RESPONSE(g_object_new( + ful_url_launcher_api_can_launch_url_response_get_type(), nullptr)); + self->value = fl_value_new_list(); + fl_value_append_take(self->value, fl_value_new_bool(return_value)); + return self; +} + +FulUrlLauncherApiCanLaunchUrlResponse* +ful_url_launcher_api_can_launch_url_response_new_error(const gchar* code, + const gchar* message, + FlValue* details) { + FulUrlLauncherApiCanLaunchUrlResponse* self = + FUL_URL_LAUNCHER_API_CAN_LAUNCH_URL_RESPONSE(g_object_new( + ful_url_launcher_api_can_launch_url_response_get_type(), nullptr)); + self->value = fl_value_new_list(); + fl_value_append_take(self->value, fl_value_new_string(code)); + fl_value_append_take(self->value, + fl_value_new_string(message != nullptr ? message : "")); + fl_value_append_take(self->value, details != nullptr ? fl_value_ref(details) + : fl_value_new_null()); + return self; +} + +struct _FulUrlLauncherApiLaunchUrlResponse { + GObject parent_instance; + + FlValue* value; +}; + +G_DEFINE_TYPE(FulUrlLauncherApiLaunchUrlResponse, + ful_url_launcher_api_launch_url_response, G_TYPE_OBJECT) + +static void ful_url_launcher_api_launch_url_response_dispose(GObject* object) { + FulUrlLauncherApiLaunchUrlResponse* self = + FUL_URL_LAUNCHER_API_LAUNCH_URL_RESPONSE(object); + g_clear_pointer(&self->value, fl_value_unref); + G_OBJECT_CLASS(ful_url_launcher_api_launch_url_response_parent_class) + ->dispose(object); +} + +static void ful_url_launcher_api_launch_url_response_init( + FulUrlLauncherApiLaunchUrlResponse* self) {} + +static void ful_url_launcher_api_launch_url_response_class_init( + FulUrlLauncherApiLaunchUrlResponseClass* klass) { + G_OBJECT_CLASS(klass)->dispose = + ful_url_launcher_api_launch_url_response_dispose; +} + +FulUrlLauncherApiLaunchUrlResponse* +ful_url_launcher_api_launch_url_response_new(const gchar* return_value) { + FulUrlLauncherApiLaunchUrlResponse* self = + FUL_URL_LAUNCHER_API_LAUNCH_URL_RESPONSE(g_object_new( + ful_url_launcher_api_launch_url_response_get_type(), nullptr)); + self->value = fl_value_new_list(); + fl_value_append_take(self->value, return_value != nullptr + ? fl_value_new_string(return_value) + : fl_value_new_null()); + return self; +} + +FulUrlLauncherApiLaunchUrlResponse* +ful_url_launcher_api_launch_url_response_new_error(const gchar* code, + const gchar* message, + FlValue* details) { + FulUrlLauncherApiLaunchUrlResponse* self = + FUL_URL_LAUNCHER_API_LAUNCH_URL_RESPONSE(g_object_new( + ful_url_launcher_api_launch_url_response_get_type(), nullptr)); + self->value = fl_value_new_list(); + fl_value_append_take(self->value, fl_value_new_string(code)); + fl_value_append_take(self->value, + fl_value_new_string(message != nullptr ? message : "")); + fl_value_append_take(self->value, details != nullptr ? fl_value_ref(details) + : fl_value_new_null()); + return self; +} + +struct _FulUrlLauncherApi { + GObject parent_instance; + + const FulUrlLauncherApiVTable* vtable; + gpointer user_data; + GDestroyNotify user_data_free_func; +}; + +G_DEFINE_TYPE(FulUrlLauncherApi, ful_url_launcher_api, G_TYPE_OBJECT) + +static void ful_url_launcher_api_dispose(GObject* object) { + FulUrlLauncherApi* self = FUL_URL_LAUNCHER_API(object); + if (self->user_data != nullptr) { + self->user_data_free_func(self->user_data); + } + self->user_data = nullptr; + G_OBJECT_CLASS(ful_url_launcher_api_parent_class)->dispose(object); +} + +static void ful_url_launcher_api_init(FulUrlLauncherApi* self) {} + +static void ful_url_launcher_api_class_init(FulUrlLauncherApiClass* klass) { + G_OBJECT_CLASS(klass)->dispose = ful_url_launcher_api_dispose; +} + +static FulUrlLauncherApi* ful_url_launcher_api_new( + const FulUrlLauncherApiVTable* vtable, gpointer user_data, + GDestroyNotify user_data_free_func) { + FulUrlLauncherApi* self = FUL_URL_LAUNCHER_API( + g_object_new(ful_url_launcher_api_get_type(), nullptr)); + self->vtable = vtable; + self->user_data = user_data; + self->user_data_free_func = user_data_free_func; + return self; +} + +static void ful_url_launcher_api_can_launch_url_cb( + FlBasicMessageChannel* channel, FlValue* message_, + FlBasicMessageChannelResponseHandle* response_handle, gpointer user_data) { + FulUrlLauncherApi* self = FUL_URL_LAUNCHER_API(user_data); + + if (self->vtable == nullptr || self->vtable->can_launch_url == nullptr) { + return; + } + + FlValue* value0 = fl_value_get_list_value(message_, 0); + const gchar* url = fl_value_get_string(value0); + g_autoptr(FulUrlLauncherApiCanLaunchUrlResponse) response = + self->vtable->can_launch_url(url, self->user_data); + if (response == nullptr) { + g_warning("No response returned to %s.%s", "UrlLauncherApi", + "canLaunchUrl"); + return; + } + + g_autoptr(GError) error = NULL; + if (!fl_basic_message_channel_respond(channel, response_handle, + response->value, &error)) { + g_warning("Failed to send response to %s.%s: %s", "UrlLauncherApi", + "canLaunchUrl", error->message); + } +} + +static void ful_url_launcher_api_launch_url_cb( + FlBasicMessageChannel* channel, FlValue* message_, + FlBasicMessageChannelResponseHandle* response_handle, gpointer user_data) { + FulUrlLauncherApi* self = FUL_URL_LAUNCHER_API(user_data); + + if (self->vtable == nullptr || self->vtable->launch_url == nullptr) { + return; + } + + FlValue* value0 = fl_value_get_list_value(message_, 0); + const gchar* url = fl_value_get_string(value0); + g_autoptr(FulUrlLauncherApiLaunchUrlResponse) response = + self->vtable->launch_url(url, self->user_data); + if (response == nullptr) { + g_warning("No response returned to %s.%s", "UrlLauncherApi", "launchUrl"); + return; + } + + g_autoptr(GError) error = NULL; + if (!fl_basic_message_channel_respond(channel, response_handle, + response->value, &error)) { + g_warning("Failed to send response to %s.%s: %s", "UrlLauncherApi", + "launchUrl", error->message); + } +} + +void ful_url_launcher_api_set_method_handlers( + FlBinaryMessenger* messenger, const gchar* suffix, + const FulUrlLauncherApiVTable* vtable, gpointer user_data, + GDestroyNotify user_data_free_func) { + g_autofree gchar* dot_suffix = + suffix != nullptr ? g_strdup_printf(".%s", suffix) : g_strdup(""); + g_autoptr(FulUrlLauncherApi) api_data = + ful_url_launcher_api_new(vtable, user_data, user_data_free_func); + + g_autoptr(FulMessageCodec) codec = ful_message_codec_new(); + g_autofree gchar* can_launch_url_channel_name = g_strdup_printf( + "dev.flutter.pigeon.url_launcher_linux.UrlLauncherApi.canLaunchUrl%s", + dot_suffix); + g_autoptr(FlBasicMessageChannel) can_launch_url_channel = + fl_basic_message_channel_new(messenger, can_launch_url_channel_name, + FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler( + can_launch_url_channel, ful_url_launcher_api_can_launch_url_cb, + g_object_ref(api_data), g_object_unref); + g_autofree gchar* launch_url_channel_name = g_strdup_printf( + "dev.flutter.pigeon.url_launcher_linux.UrlLauncherApi.launchUrl%s", + dot_suffix); + g_autoptr(FlBasicMessageChannel) launch_url_channel = + fl_basic_message_channel_new(messenger, launch_url_channel_name, + FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler( + launch_url_channel, ful_url_launcher_api_launch_url_cb, + g_object_ref(api_data), g_object_unref); +} + +void ful_url_launcher_api_clear_method_handlers(FlBinaryMessenger* messenger, + const gchar* suffix) { + g_autofree gchar* dot_suffix = + suffix != nullptr ? g_strdup_printf(".%s", suffix) : g_strdup(""); + + g_autoptr(FulMessageCodec) codec = ful_message_codec_new(); + g_autofree gchar* can_launch_url_channel_name = g_strdup_printf( + "dev.flutter.pigeon.url_launcher_linux.UrlLauncherApi.canLaunchUrl%s", + dot_suffix); + g_autoptr(FlBasicMessageChannel) can_launch_url_channel = + fl_basic_message_channel_new(messenger, can_launch_url_channel_name, + FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler(can_launch_url_channel, nullptr, + nullptr, nullptr); + g_autofree gchar* launch_url_channel_name = g_strdup_printf( + "dev.flutter.pigeon.url_launcher_linux.UrlLauncherApi.launchUrl%s", + dot_suffix); + g_autoptr(FlBasicMessageChannel) launch_url_channel = + fl_basic_message_channel_new(messenger, launch_url_channel_name, + FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler(launch_url_channel, nullptr, + nullptr, nullptr); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/messages.g.h b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/messages.g.h new file mode 100644 index 00000000..52a981c7 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/messages.g.h @@ -0,0 +1,121 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v26.1.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#ifndef PIGEON_MESSAGES_G_H_ +#define PIGEON_MESSAGES_G_H_ + +#include + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FulMessageCodec, ful_message_codec, FUL, MESSAGE_CODEC, + FlStandardMessageCodec) + +G_DECLARE_FINAL_TYPE(FulUrlLauncherApi, ful_url_launcher_api, FUL, + URL_LAUNCHER_API, GObject) + +G_DECLARE_FINAL_TYPE(FulUrlLauncherApiCanLaunchUrlResponse, + ful_url_launcher_api_can_launch_url_response, FUL, + URL_LAUNCHER_API_CAN_LAUNCH_URL_RESPONSE, GObject) + +/** + * ful_url_launcher_api_can_launch_url_response_new: + * + * Creates a new response to UrlLauncherApi.canLaunchUrl. + * + * Returns: a new #FulUrlLauncherApiCanLaunchUrlResponse + */ +FulUrlLauncherApiCanLaunchUrlResponse* +ful_url_launcher_api_can_launch_url_response_new(gboolean return_value); + +/** + * ful_url_launcher_api_can_launch_url_response_new_error: + * @code: error code. + * @message: error message. + * @details: (allow-none): error details or %NULL. + * + * Creates a new error response to UrlLauncherApi.canLaunchUrl. + * + * Returns: a new #FulUrlLauncherApiCanLaunchUrlResponse + */ +FulUrlLauncherApiCanLaunchUrlResponse* +ful_url_launcher_api_can_launch_url_response_new_error(const gchar* code, + const gchar* message, + FlValue* details); + +G_DECLARE_FINAL_TYPE(FulUrlLauncherApiLaunchUrlResponse, + ful_url_launcher_api_launch_url_response, FUL, + URL_LAUNCHER_API_LAUNCH_URL_RESPONSE, GObject) + +/** + * ful_url_launcher_api_launch_url_response_new: + * + * Creates a new response to UrlLauncherApi.launchUrl. + * + * Returns: a new #FulUrlLauncherApiLaunchUrlResponse + */ +FulUrlLauncherApiLaunchUrlResponse* +ful_url_launcher_api_launch_url_response_new(const gchar* return_value); + +/** + * ful_url_launcher_api_launch_url_response_new_error: + * @code: error code. + * @message: error message. + * @details: (allow-none): error details or %NULL. + * + * Creates a new error response to UrlLauncherApi.launchUrl. + * + * Returns: a new #FulUrlLauncherApiLaunchUrlResponse + */ +FulUrlLauncherApiLaunchUrlResponse* +ful_url_launcher_api_launch_url_response_new_error(const gchar* code, + const gchar* message, + FlValue* details); + +/** + * FulUrlLauncherApiVTable: + * + * Table of functions exposed by UrlLauncherApi to be implemented by the API + * provider. + */ +typedef struct { + FulUrlLauncherApiCanLaunchUrlResponse* (*can_launch_url)(const gchar* url, + gpointer user_data); + FulUrlLauncherApiLaunchUrlResponse* (*launch_url)(const gchar* url, + gpointer user_data); +} FulUrlLauncherApiVTable; + +/** + * ful_url_launcher_api_set_method_handlers: + * + * @messenger: an #FlBinaryMessenger. + * @suffix: (allow-none): a suffix to add to the API or %NULL for none. + * @vtable: implementations of the methods in this API. + * @user_data: (closure): user data to pass to the functions in @vtable. + * @user_data_free_func: (allow-none): a function which gets called to free + * @user_data, or %NULL. + * + * Connects the method handlers in the UrlLauncherApi API. + */ +void ful_url_launcher_api_set_method_handlers( + FlBinaryMessenger* messenger, const gchar* suffix, + const FulUrlLauncherApiVTable* vtable, gpointer user_data, + GDestroyNotify user_data_free_func); + +/** + * ful_url_launcher_api_clear_method_handlers: + * + * @messenger: an #FlBinaryMessenger. + * @suffix: (allow-none): a suffix to add to the API or %NULL for none. + * + * Clears the method handlers in the UrlLauncherApi API. + */ +void ful_url_launcher_api_clear_method_handlers(FlBinaryMessenger* messenger, + const gchar* suffix); + +G_END_DECLS + +#endif // PIGEON_MESSAGES_G_H_ diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/url_launcher_plugin.cc b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/url_launcher_plugin.cc new file mode 100644 index 00000000..c55fb70d --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/url_launcher_plugin.cc @@ -0,0 +1,109 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "include/url_launcher_linux/url_launcher_plugin.h" + +#include +#include + +#include + +#include "messages.g.h" +#include "url_launcher_plugin_private.h" + +struct _FlUrlLauncherPlugin { + GObject parent_instance; + + FlPluginRegistrar* registrar; +}; + +G_DEFINE_TYPE(FlUrlLauncherPlugin, fl_url_launcher_plugin, g_object_get_type()) + +// Checks if URI has launchable file resource. +static gboolean can_launch_uri_with_file_resource(FlUrlLauncherPlugin* self, + const gchar* url) { + g_autoptr(GError) error = nullptr; + g_autoptr(GFile) file = g_file_new_for_uri(url); + g_autoptr(GAppInfo) app_info = + g_file_query_default_handler(file, NULL, &error); + return app_info != nullptr; +} + +FulUrlLauncherApiCanLaunchUrlResponse* handle_can_launch_url( + const gchar* url, gpointer user_data) { + FlUrlLauncherPlugin* self = FL_URL_LAUNCHER_PLUGIN(user_data); + + gboolean is_launchable = FALSE; + g_autofree gchar* scheme = g_uri_parse_scheme(url); + if (scheme != nullptr) { + g_autoptr(GAppInfo) app_info = + g_app_info_get_default_for_uri_scheme(scheme); + is_launchable = app_info != nullptr; + + if (!is_launchable) { + is_launchable = can_launch_uri_with_file_resource(self, url); + } + } + + return ful_url_launcher_api_can_launch_url_response_new(is_launchable); +} + +// Called when a URL should launch. +static FulUrlLauncherApiLaunchUrlResponse* handle_launch_url( + const gchar* url, gpointer user_data) { + FlUrlLauncherPlugin* self = FL_URL_LAUNCHER_PLUGIN(user_data); + + FlView* view = fl_plugin_registrar_get_view(self->registrar); + g_autoptr(GError) error = nullptr; + gboolean launched; + if (view != nullptr) { + GtkWindow* window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view))); + launched = gtk_show_uri_on_window(window, url, GDK_CURRENT_TIME, &error); + } else { + launched = g_app_info_launch_default_for_uri(url, nullptr, &error); + } + if (!launched) { + return ful_url_launcher_api_launch_url_response_new(error->message); + } + + return ful_url_launcher_api_launch_url_response_new(nullptr); +} + +static void fl_url_launcher_plugin_dispose(GObject* object) { + FlUrlLauncherPlugin* self = FL_URL_LAUNCHER_PLUGIN(object); + + ful_url_launcher_api_clear_method_handlers( + fl_plugin_registrar_get_messenger(self->registrar), nullptr); + g_clear_object(&self->registrar); + + G_OBJECT_CLASS(fl_url_launcher_plugin_parent_class)->dispose(object); +} + +static void fl_url_launcher_plugin_class_init(FlUrlLauncherPluginClass* klass) { + G_OBJECT_CLASS(klass)->dispose = fl_url_launcher_plugin_dispose; +} + +FlUrlLauncherPlugin* fl_url_launcher_plugin_new(FlPluginRegistrar* registrar) { + FlUrlLauncherPlugin* self = FL_URL_LAUNCHER_PLUGIN( + g_object_new(fl_url_launcher_plugin_get_type(), nullptr)); + + self->registrar = FL_PLUGIN_REGISTRAR(g_object_ref(registrar)); + + static FulUrlLauncherApiVTable api_vtable = { + .can_launch_url = handle_can_launch_url, + .launch_url = handle_launch_url, + }; + ful_url_launcher_api_set_method_handlers( + fl_plugin_registrar_get_messenger(registrar), nullptr, &api_vtable, + g_object_ref(self), g_object_unref); + + return self; +} + +static void fl_url_launcher_plugin_init(FlUrlLauncherPlugin* self) {} + +void url_launcher_plugin_register_with_registrar(FlPluginRegistrar* registrar) { + FlUrlLauncherPlugin* plugin = fl_url_launcher_plugin_new(registrar); + g_object_unref(plugin); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/url_launcher_plugin_private.h b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/url_launcher_plugin_private.h new file mode 100644 index 00000000..6e34a52f --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/linux/url_launcher_plugin_private.h @@ -0,0 +1,12 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "include/url_launcher_linux/url_launcher_plugin.h" +#include "messages.g.h" + +// Called to check if a URL can be launched. +FulUrlLauncherApiCanLaunchUrlResponse* handle_can_launch_url( + const gchar* url, gpointer user_data); diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/pigeons/copyright.txt b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/pigeons/copyright.txt new file mode 100644 index 00000000..07e5f859 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/pigeons/copyright.txt @@ -0,0 +1,3 @@ +Copyright 2013 The Flutter Authors +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/pigeons/messages.dart b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/pigeons/messages.dart new file mode 100644 index 00000000..4f77d8f2 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/pigeons/messages.dart @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/src/messages.g.dart', + gobjectHeaderOut: 'linux/messages.g.h', + gobjectSourceOut: 'linux/messages.g.cc', + gobjectOptions: GObjectOptions(module: 'Ful'), + copyrightHeader: 'pigeons/copyright.txt', + ), +) +@HostApi() +abstract class UrlLauncherApi { + /// Returns true if the URL can definitely be launched. + bool canLaunchUrl(String url); + + /// Opens the URL externally, returning an error string on failure. + String? launchUrl(String url); +} diff --git a/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/pubspec.yaml b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/pubspec.yaml new file mode 100644 index 00000000..5f17be53 --- /dev/null +++ b/linux/flutter/ephemeral/.plugin_symlinks/url_launcher_linux/pubspec.yaml @@ -0,0 +1,34 @@ +name: url_launcher_linux +description: Linux implementation of the url_launcher plugin. +repository: https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_linux +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 +version: 3.2.2 + +environment: + sdk: ^3.8.0 + flutter: ">=3.32.0" + +flutter: + plugin: + implements: url_launcher + platforms: + linux: + pluginClass: UrlLauncherPlugin + dartPluginClass: UrlLauncherLinux + +dependencies: + flutter: + sdk: flutter + url_launcher_platform_interface: ^2.2.0 + +dev_dependencies: + flutter_test: + sdk: flutter + pigeon: ^26.1.0 + test: ^1.16.3 + +topics: + - links + - os-integration + - url-launcher + - urls diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements index dc6980c9..1f6a38b1 100644 --- a/macos/Runner/DebugProfile.entitlements +++ b/macos/Runner/DebugProfile.entitlements @@ -10,7 +10,7 @@ com.apple.security.network.client - com.apple.security.network.server + com.apple.security.network.client diff --git a/pubspec.yaml b/pubspec.yaml index 4c1ab30a..1f37a58a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,11 +1,12 @@ + name: flutter_examples description: This project contains the Syncfusion Flutter UI widgets examples. -version: 31.1.17 +version: 32.1.19 homepage: https://github.com/syncfusion/flutter-examples environment: sdk: ^3.7.0 - flutter: '>=3.29.0' + flutter: '>=3.35.1' flutter: uses-material-design: true @@ -98,18 +99,18 @@ dependencies: flutter_markdown: ^0.7.4+1 google_generative_ai: ^0.4.6 path_provider_foundation: ^2.1.0 - syncfusion_localizations: ^31.1.17 - syncfusion_flutter_datagrid_export: ^31.1.17 - syncfusion_flutter_calendar: ^31.1.17 - syncfusion_flutter_datepicker: ^31.1.17 - syncfusion_flutter_charts: ^31.1.17 - syncfusion_flutter_gauges: ^31.1.17 - syncfusion_flutter_sliders: ^31.1.17 - syncfusion_flutter_pdf: ^31.1.17 - syncfusion_flutter_barcodes: ^31.1.17 - syncfusion_officechart: ^31.1.17 - syncfusion_flutter_maps: ^31.1.17 - syncfusion_flutter_signaturepad: ^31.1.17 - syncfusion_flutter_pdfviewer: ^31.1.17 - syncfusion_flutter_treemap: ^31.1.17 - syncfusion_flutter_chat: ^31.1.17 + syncfusion_localizations: ^32.1.19 + syncfusion_flutter_datagrid_export: ^32.1.19 + syncfusion_flutter_calendar: ^32.1.19 + syncfusion_flutter_datepicker: ^32.1.19 + syncfusion_flutter_charts: ^32.1.19 + syncfusion_flutter_gauges: ^32.1.19 + syncfusion_flutter_sliders: ^32.1.19 + syncfusion_flutter_pdf: ^32.1.19 + syncfusion_flutter_barcodes: ^32.1.19 + syncfusion_officechart: ^32.1.19 + syncfusion_flutter_maps: ^32.1.19 + syncfusion_flutter_signaturepad: ^32.1.19 + syncfusion_flutter_pdfviewer: ^32.1.19 + syncfusion_flutter_treemap: ^32.1.19 + syncfusion_flutter_chat: ^32.1.19