From 946f952534cdb05d93535f0dfb81bdf90ec137a1 Mon Sep 17 00:00:00 2001 From: LinXunFeng Date: Fri, 18 Apr 2025 05:35:39 +0800 Subject: [PATCH 01/52] Fix link to engine docs (#167346) --- docs/README.md | 4 ++-- .../Setting-up-the-Framework-development-environment.md | 2 +- docs/contributing/Tree-hygiene.md | 2 +- docs/contributing/testing/Running-and-writing-tests.md | 6 +++--- docs/infra/Rolling-Dart.md | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/README.md b/docs/README.md index 26f7bc4f3d2ad..db9272b4c86e3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,7 +14,7 @@ If you intend to contribute to Flutter, welcome! You are encouraged to start wit * [Cherrypick process](./releases/Flutter-Cherrypick-Process.md) * [Closing issues](./contributing/issue_hygiene/README.md#closing-issues) * [Dashboards](./infra/Dashboards.md) -* [Debugging a broken engine autoroll](./engine/Debugging-the-engine.md#bisecting-a-roll-failure) +* [Debugging a broken engine autoroll](../engine/src/flutter/docs/Debugging-the-engine.md#bisecting-a-roll-failure) * [Deprecations](./contributing/Tree-hygiene.md#deprecations) * [Design documents](./contributing/Design-Documents.md) * [Discord](./contributing/Chat.md) @@ -31,7 +31,7 @@ If you intend to contribute to Flutter, welcome! You are encouraged to start wit * [Style guide for Flutter repo](./contributing/Style-guide-for-Flutter-repo.md) * [Submitting code, process for](./contributing/Tree-hygiene.md#overview) * [Support levels, definitions of](./about/Values.md#support) -* [Symbolicating stack traces](./engine/Crashes.md) +* [Symbolicating stack traces](../engine/src/flutter/docs/Crashes.md) * [Threading in the Engine](./about/The-Engine-architecture.md#threading) * [When will my bug be fixed?](./contributing/issue_hygiene/README.md#when-will-my-bug-be-fixed) * [Security best practices](./infra/Security.md#best-practices) diff --git a/docs/contributing/Setting-up-the-Framework-development-environment.md b/docs/contributing/Setting-up-the-Framework-development-environment.md index 4dfb8591a2ada..c8bf91e2691e7 100644 --- a/docs/contributing/Setting-up-the-Framework-development-environment.md +++ b/docs/contributing/Setting-up-the-Framework-development-environment.md @@ -13,7 +13,7 @@ Verify that `adb` is in your [PATH](https://en.wikipedia.org/wiki/PATH_(variable)) (that `which adb` prints sensible output). If you're - [also working on the Flutter engine](../engine/contributing/Setting-up-the-Engine-development-environment.md), + [also working on the Flutter engine](../../engine/src/flutter/docs/contributing/Setting-up-the-Engine-development-environment.md), you can use the copy of the Android platform tools in `.../engine/src/third_party/android_tools/sdk/platform-tools`. diff --git a/docs/contributing/Tree-hygiene.md b/docs/contributing/Tree-hygiene.md index c1f80c1dd7478..bbc02729748ec 100644 --- a/docs/contributing/Tree-hygiene.md +++ b/docs/contributing/Tree-hygiene.md @@ -140,7 +140,7 @@ feel empowered to ask for tests. ## Using git Assuming your environment has been configured according to the instructions in -[Setting up the Engine development environment](../engine/contributing/Setting-up-the-Engine-development-environment.md), +[Setting up the Engine development environment](../../engine/src/flutter/docs/contributing/Setting-up-the-Engine-development-environment.md), [Setting up the Framework development environment](Setting-up-the-Framework-development-environment.md), or [Setting up the Packages development environment](../ecosystem/contributing/Setting-up-the-Packages-development-environment.md), follow these steps to start working on a patch: diff --git a/docs/contributing/testing/Running-and-writing-tests.md b/docs/contributing/testing/Running-and-writing-tests.md index 41b9d639b078b..993d62752b8ab 100644 --- a/docs/contributing/testing/Running-and-writing-tests.md +++ b/docs/contributing/testing/Running-and-writing-tests.md @@ -47,7 +47,7 @@ runs them, run `dart dev/bots/test.dart` and `dart --enable-asserts dev/bots/ana ### Locally built engines -If you've built your own flutter engine (see [Setting up the Engine development environment](../../engine/contributing/Setting-up-the-Engine-development-environment.md)), you +If you've built your own flutter engine (see [Setting up the Engine development environment](../../../engine/src/flutter/docs/contributing/Setting-up-the-Engine-development-environment.md)), you can pass `--local-engine` to change what flutter shell `flutter test` uses. For example, if you built an engine in the `out/host_debug_unopt` directory, you can use: @@ -120,8 +120,8 @@ The following is an example of what running the local engine command might look The above command would use the local Flutter engine located at `/Users/myname/flutter/engine` to execute the `external_ui_integration_test` test on an Android emulator, which is why the `android_debug_unopt_x86` version of the engine is used. -Note that some tests may require `profile` mode instead of `debug` mode when running with local engine. Make sure to pass in the correct local engine. See [Compiling the engine](../../engine/contributing/Compiling-the-engine.md) for more details. +Note that some tests may require `profile` mode instead of `debug` mode when running with local engine. Make sure to pass in the correct local engine. See [Compiling the engine](../../../engine/src/flutter/docs/contributing/Compiling-the-engine.md) for more details. ## For the engine -See the [Testing the engine](../../engine/testing/Testing-the-engine.md) wiki. +See the [Testing the engine](../../../engine/src/flutter/docs/testing/Testing-the-engine.md) wiki. diff --git a/docs/infra/Rolling-Dart.md b/docs/infra/Rolling-Dart.md index 15d913142e258..d55c29587f964 100644 --- a/docs/infra/Rolling-Dart.md +++ b/docs/infra/Rolling-Dart.md @@ -49,12 +49,12 @@ If the script completes without errors, move on to step 10 in the next section t 1. Set up your Engine and Flutter environments by following the instructions described in the pages linked to from [our contributing guide](../../CONTRIBUTING.md). -2. Build the engine according to the instructions on the [Compiling the engine](../engine/contributing/Compiling-the-engine.md) page. +2. Build the engine according to the instructions on the [Compiling the engine](../../engine/src/flutter/docs/contributing/Compiling-the-engine.md) page. 3. Select a target Dart revision, typically use the [latest revision](https://github.com/dart-lang/sdk/commits/main). Check that the tests for that revision have all passed (all green) on the [Dart buildbot](https://ci.chromium.org/p/flutter/g/engine/console) and the [Internal Dart Flutter buildbot](https://ci.chromium.org/p/dart/g/flutter/console). 4. Create a PR (see [Tree hygiene](../contributing/Tree-hygiene.md)) that updates `dart_revision` in [DEPS](../../DEPS) to your selected revision. Invoke `gclient sync` in the src directory to ensure versions corresponding to the DEPS file are synced up. 5. Update all Dart-dependent DEPS entries using [`create_updated_flutter_deps.py`](../../engine/src/tools/dart/create_updated_flutter_deps.py) script. In case script complains that dart dependency was removed, remove entry from flutter DEPS file manually. If the list of library source files or patch files is modified, update the file [`libraries.yaml`](../../engine/src/flutter/lib/snapshot/libraries.yaml) and regenerate the corresponding json file. 6. Invoke `gclient sync` in the src directory to ensure versions corresponding to the DEPS file are synced up. -7. Build the debug, profile, and release versions of the engine, following the normal [Compiling the engine](../engine/contributing/Compiling-the-engine.md) instructions. You will need to build the host versions of the engine too. Here is a script with the build commands: +7. Build the debug, profile, and release versions of the engine, following the normal [Compiling the engine](../../engine/src/flutter/docs/contributing/Compiling-the-engine.md) instructions. You will need to build the host versions of the engine too. Here is a script with the build commands: ```bash #!/bin/bash -e @@ -88,7 +88,7 @@ cd $FLUTTER_HOME/packages/flutter flutter test --local-engine=host_debug --local-engine-host=host_debug ``` -> See [Running a Flutter app with a local engine](../engine/Debugging-the-engine.md#running-a-flutter-app-with-a-local-engine) for more information. +> See [Running a Flutter app with a local engine](../../engine/src/flutter/docs/Debugging-the-engine.md#running-a-flutter-app-with-a-local-engine) for more information. 8. Make sure your path contains `engine/src/third_party/dart/tools/sdks/dart-sdk/bin`, run the script `flutter/ci/licenses.sh` in the src directory, update `flutter/ci/licenses_golden/licenses_third_party` by copying `out/license_script_output/licenses_third_party` into it. Include this change in your pull request. **If any licenses changed, make sure to also update the actual `LICENSE` file.** From 434cb54e7730ea8780eae7037ced6aff4121e191 Mon Sep 17 00:00:00 2001 From: Kishan Rathore <34465683+rkishan516@users.noreply.github.com> Date: Fri, 18 Apr 2025 03:05:40 +0530 Subject: [PATCH 02/52] Feat: Add equality to NoDefaultCupertinoThemeData (#166655) Feat: Add equality to NoDefaultCupertinoThemeData fixes: #165455 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. --- packages/flutter/lib/src/cupertino/theme.dart | 30 +++++++++++++++++++ .../flutter/test/cupertino/theme_test.dart | 8 +++++ 2 files changed, 38 insertions(+) diff --git a/packages/flutter/lib/src/cupertino/theme.dart b/packages/flutter/lib/src/cupertino/theme.dart index eff62423aa89e..db0a330abc4a9 100644 --- a/packages/flutter/lib/src/cupertino/theme.dart +++ b/packages/flutter/lib/src/cupertino/theme.dart @@ -394,6 +394,7 @@ class CupertinoThemeData extends NoDefaultCupertinoThemeData with Diagnosticable /// /// * [CupertinoThemeData], which uses reasonable default values for /// unspecified theme properties. +@immutable class NoDefaultCupertinoThemeData { /// Creates a [NoDefaultCupertinoThemeData] styling specification. /// @@ -541,6 +542,35 @@ class NoDefaultCupertinoThemeData { applyThemeToAll: applyThemeToAll ?? this.applyThemeToAll, ); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is NoDefaultCupertinoThemeData && + other.brightness == brightness && + other.primaryColor == primaryColor && + other.primaryContrastingColor == primaryContrastingColor && + other.textTheme == textTheme && + other.barBackgroundColor == barBackgroundColor && + other.scaffoldBackgroundColor == scaffoldBackgroundColor && + other.applyThemeToAll == applyThemeToAll; + } + + @override + int get hashCode => Object.hash( + brightness, + primaryColor, + primaryContrastingColor, + textTheme, + barBackgroundColor, + scaffoldBackgroundColor, + applyThemeToAll, + ); } @immutable diff --git a/packages/flutter/test/cupertino/theme_test.dart b/packages/flutter/test/cupertino/theme_test.dart index ae94dfff568c7..78e469276d505 100644 --- a/packages/flutter/test/cupertino/theme_test.dart +++ b/packages/flutter/test/cupertino/theme_test.dart @@ -221,6 +221,14 @@ void main() { expect(c, isNot(equals(b))); }); + testWidgets('NoDefaultCupertinoThemeData equality', (WidgetTester tester) async { + const NoDefaultCupertinoThemeData a = NoDefaultCupertinoThemeData(); + final NoDefaultCupertinoThemeData b = a.copyWith(); + final NoDefaultCupertinoThemeData c = a.copyWith(brightness: Brightness.light); + expect(a, equals(b)); + expect(a, isNot(c)); + }); + late Brightness currentBrightness; void colorMatches(Color? componentColor, Color expectedDynamicColor) { if (expectedDynamicColor is CupertinoDynamicColor) { From 0e4bebffa1a58fc181bb3afcde09c86b2fc604b1 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Thu, 17 Apr 2025 14:35:41 -0700 Subject: [PATCH 03/52] Added docstring for FilterContents::RenderFilter (#167227) fixes https://github.com/flutter/flutter/issues/137704 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --- .../entity/contents/filters/filter_contents.h | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/engine/src/flutter/impeller/entity/contents/filters/filter_contents.h b/engine/src/flutter/impeller/entity/contents/filters/filter_contents.h index baa168f16719b..50daead10558e 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/filter_contents.h +++ b/engine/src/flutter/impeller/entity/contents/filters/filter_contents.h @@ -190,7 +190,29 @@ class FilterContents : public Contents { const Matrix& effect_transform, const Rect& output_limit) const = 0; - /// @brief Converts zero or more filter inputs into a render instruction. + /// Applies the specific filter logic to the given inputs and returns an + /// Entity representing the filtered result. + /// + /// This is the primary method that subclasses must implement to define their + /// filtering behavior. It takes the results of evaluating the filter inputs + /// (as Snapshots) and produces a new Entity containing the filtered output. + /// + /// @param[in] inputs The evaluated inputs to the filter, typically as + /// Snapshots. + /// @param[in] renderer The content context providing rendering resources. + /// @param[in] entity The entity applying this filter, providing transform, + /// blend mode, and other context. + /// @param[in] effect_transform An additional transform applied after the + /// entity's transform, often used in subpass scenarios. + /// @param[in] coverage The calculated coverage area of the filter's output + /// in the coordinate space after applying the entity and effect transforms. + /// @param[in] coverage_hint An optional hint representing the desired output + /// coverage area, which can be used for optimization (e.g., rendering only a + /// portion of the input). + /// + /// @return An optional Entity containing the rendered result of the filter. + /// Returns `std::nullopt` if the filter cannot be applied or results in empty + /// output. virtual std::optional RenderFilter( const FilterInput::Vector& inputs, const ContentContext& renderer, From a23e4ca194f8db39a814e8de9957c2f3433342dd Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Thu, 17 Apr 2025 14:37:00 -0700 Subject: [PATCH 04/52] fixed impeller golden content scale (#167308) fixes https://github.com/flutter/flutter/issues/162036 This fixes the problem my scaling the render texture by the content scalar. This means locally you'll probably be generating 2048x1536 images. This matches the rendering that is happening in playgrounds. On CI the content scalar should be one so there is no change there. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --- .../golden_tests/golden_playground_test_mac.cc | 15 +++++++++++++-- .../impeller/golden_tests/metal_screenshotter.mm | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc b/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc index 12b5103283688..a6e6e3b66d3f4 100644 --- a/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc +++ b/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc @@ -222,10 +222,16 @@ bool GoldenPlaygroundTest::OpenPlaygroundHere( AiksContext renderer(GetContext(), typographer_context_); std::unique_ptr screenshot; + Point content_scale = + pimpl_->screenshotter->GetPlayground().GetContentScale(); + + ISize physical_window_size( + std::round(pimpl_->window_size.width * content_scale.x), + std::round(pimpl_->window_size.height * content_scale.y)); for (int i = 0; i < 2; ++i) { auto display_list = callback(); auto texture = - DisplayListToTexture(display_list, pimpl_->window_size, renderer); + DisplayListToTexture(display_list, physical_window_size, renderer); screenshot = pimpl_->screenshotter->MakeScreenshot(renderer, texture); } return SaveScreenshot(std::move(screenshot)); @@ -320,9 +326,14 @@ fml::Status GoldenPlaygroundTest::SetCapabilities( std::unique_ptr GoldenPlaygroundTest::MakeScreenshot( const sk_sp& list) { AiksContext renderer(GetContext(), typographer_context_); + Point content_scale = + pimpl_->screenshotter->GetPlayground().GetContentScale(); + ISize physical_window_size( + std::round(pimpl_->window_size.width * content_scale.x), + std::round(pimpl_->window_size.height * content_scale.y)); return pimpl_->screenshotter->MakeScreenshot( - renderer, DisplayListToTexture(list, pimpl_->window_size, renderer)); + renderer, DisplayListToTexture(list, physical_window_size, renderer)); } } // namespace impeller diff --git a/engine/src/flutter/impeller/golden_tests/metal_screenshotter.mm b/engine/src/flutter/impeller/golden_tests/metal_screenshotter.mm index 051347dfb5746..28d922f65d415 100644 --- a/engine/src/flutter/impeller/golden_tests/metal_screenshotter.mm +++ b/engine/src/flutter/impeller/golden_tests/metal_screenshotter.mm @@ -43,7 +43,7 @@ imageByApplyingOrientation:kCGImagePropertyOrientationDownMirrored]; CGImageRef cgImage = [cicontext createCGImage:flipped - fromRect:[ciImage extent]]; + fromRect:[flipped extent]]; // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) return std::unique_ptr(new MetalScreenshot(cgImage)); From df5dfdba617a58aa228cd3a8e2d08133b86a3ad7 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Thu, 17 Apr 2025 14:38:52 -0700 Subject: [PATCH 05/52] adds Entity docstrings (#167228) fixes https://github.com/flutter/flutter/issues/144943 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --- engine/src/flutter/impeller/entity/entity.h | 61 ++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/engine/src/flutter/impeller/entity/entity.h b/engine/src/flutter/impeller/entity/entity.h index 1d8cd80e8c7d3..2952554b1ec92 100644 --- a/engine/src/flutter/impeller/entity/entity.h +++ b/engine/src/flutter/impeller/entity/entity.h @@ -17,6 +17,12 @@ namespace impeller { class Renderer; class RenderPass; +/// Represents a renderable object within the Impeller scene. +/// +/// An Entity combines graphical content (`Contents`) with properties +/// like transformation (`Matrix`), blend mode (`BlendMode`), and stencil +/// clip depth. It serves as the primary unit for constructing and rendering +/// scenes. Entities can be created directly or from `Snapshot` objects. class Entity { public: static constexpr BlendMode kLastPipelineBlendMode = BlendMode::kModulate; @@ -78,7 +84,14 @@ class Entity { /// @brief Get the global transform matrix for this Entity. const Matrix& GetTransform() const; - /// @brief Get the vertex shader transform used for drawing this Entity. + /// Calculates the final transformation matrix for rendering in a shader. + /// + /// This combines the entity's transform with the render pass's orthographic + /// projection and applies the necessary adjustments based on the entity's + /// shader clip depth. + /// + /// @param[in] pass The current render pass. + /// @return The combined model-view-projection matrix for the shader. Matrix GetShaderTransform(const RenderPass& pass) const; /// @brief Static utility that computes the vertex shader transform used for @@ -90,18 +103,56 @@ class Entity { /// @brief Set the global transform matrix for this Entity. void SetTransform(const Matrix& transform); + /// Calculates the axis-aligned bounding box covering this entity after its + /// transformation is applied. + /// @return The coverage rectangle in the parent coordinate space, or + /// `std::nullopt` if the entity has no contents. std::optional GetCoverage() const; void SetContents(std::shared_ptr contents); const std::shared_ptr& GetContents() const; + /// Sets the stencil clip depth for this entity. + /// + /// The clip depth determines the entity's level within a stack of stencil + /// clips. Higher values indicate the entity is nested deeper within clips. + /// This value is used during rendering to configure stencil buffer + /// operations. + /// + /// @param[in] clip_depth The integer clip depth level. void SetClipDepth(uint32_t clip_depth); + /// Gets the stencil clip depth level for this entity. + /// + /// @see SetClipDepth(uint32_t) + /// @see GetShaderClipDepth() + /// + /// @return The current integer clip depth level. uint32_t GetClipDepth() const; + /// Gets the shader-compatible depth value based on the entity's current clip + /// depth level (`clip_depth_`). + /// + /// @see GetShaderClipDepth(uint32_t) for details on the conversion logic. + /// + /// @return The floating-point depth value for shaders corresponding to the + /// entity's `clip_depth_`. float GetShaderClipDepth() const; + /// Converts an integer clip depth level into a floating-point depth value + /// suitable for use in shaders. + /// + /// The integer `clip_depth` represents discrete layers used for stencil + /// clipping. This function maps that integer to a depth value within the [0, + /// 1) range for the depth buffer. Each increment in `clip_depth` corresponds + /// to a small step (`kDepthEpsilon`) in the shader depth. + /// + /// The result is clamped to ensure it stays within the valid depth range and + /// slightly below 1.0 to avoid potential issues with the maximum depth value. + /// + /// @param[in] clip_depth The integer clip depth level. + /// @return The corresponding floating-point depth value for shaders. static float GetShaderClipDepth(uint32_t clip_depth); void SetBlendMode(BlendMode blend_mode); @@ -114,6 +165,14 @@ class Entity { bool SetInheritedOpacity(Scalar alpha); + /// Attempts to represent this entity as a solid background color. + /// + /// This is an optimization. If the entity's contents can be represented as a + /// solid color covering the entire target area, this method returns that + /// color. + /// + /// @param[in] target_size The size of the render target. + /// @return The background color if representable, otherwise `std::nullopt`. std::optional AsBackgroundColor(ISize target_size) const; Entity Clone() const; From ffa1891ab8dadbf48a2b23bb28ce2accbdc27eb0 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Thu, 17 Apr 2025 14:38:52 -0700 Subject: [PATCH 06/52] Added docstring for FilterInput::GetSnapshot (#167226) fixes https://github.com/flutter/flutter/issues/137970 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --- .../contents/filters/inputs/filter_input.h | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_input.h b/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_input.h index 7888ac6ef9ec1..436bb07dc8b0d 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_input.h +++ b/engine/src/flutter/impeller/entity/contents/filters/inputs/filter_input.h @@ -45,6 +45,29 @@ class FilterInput { static FilterInput::Vector Make(std::initializer_list inputs); + /// Evaluates the filter input and returns a snapshot of the result. + /// + /// This method renders the input (which could be another filter, contents, + /// or a texture) into a `Snapshot` object, which contains the resulting + /// texture and its transform relative to the current render target. + /// + /// Implementations are typically lazy and may cache the result, ensuring + /// that the input is only rendered once even if `GetSnapshot` is called + /// multiple times. + /// + /// @param[in] label A debug label for the rendering operation and the + /// resulting snapshot texture. + /// @param[in] renderer The content context providing rendering resources. + /// @param[in] entity The entity associated with this filter input, providing + /// transform and other contextual information. + /// @param[in] coverage_limit An optional rectangle to limit the area of the + /// input that needs to be rendered. This can be used as an optimization. + /// @param[in] mip_count The number of mip levels to generate for the snapshot + /// texture. Defaults to 1 (no mips). + /// + /// @return A `Snapshot` containing the rendered texture and its transform, or + /// `std::nullopt` if the input cannot be rendered or results in an empty + /// output. virtual std::optional GetSnapshot( std::string_view label, const ContentContext& renderer, From 9f4fe843a7c729b669a0bbf45302e881bb903682 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 17 Apr 2025 17:38:52 -0400 Subject: [PATCH 07/52] Roll Dart SDK from 992221a362ec to 2bb85834e77e (7 revisions) (#167361) https://dart.googlesource.com/sdk.git/+log/992221a362ec..2bb85834e77e 2025-04-17 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.9.0-28.0.dev 2025-04-17 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.9.0-27.0.dev 2025-04-17 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.9.0-26.0.dev 2025-04-16 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.9.0-25.0.dev 2025-04-16 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.9.0-24.0.dev 2025-04-16 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.9.0-23.0.dev 2025-04-16 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.9.0-22.0.dev If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/dart-sdk-flutter Please CC codefu@google.com,dart-vm-team@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- DEPS | 14 +++++++------- .../src/flutter/ci/licenses_golden/licenses_dart | 4 ++-- engine/src/flutter/sky/packages/sky_engine/LICENSE | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/DEPS b/DEPS index 03fa7fe872042..cfc9bf45a4dce 100644 --- a/DEPS +++ b/DEPS @@ -56,7 +56,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/main/DEPS # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '992221a362ec346834c4cfda9908370c5a461479', + 'dart_revision': '2bb85834e77ef519fed07841b875fee83073089e', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -64,7 +64,7 @@ vars = { 'dart_boringssl_rev': 'ef839bf397fb4ecdb66ef2679a08ac7b3563c50b', 'dart_core_rev': '635dfa32c261ba078438b74de397f2207904ca78', 'dart_devtools_rev': '1fb2f4ce5099042b7f2dfa93dec675a21861d21f', - 'dart_ecosystem_rev': '7f6f1c1d53173307bde1aa4f4ee851d260cc7e69', + 'dart_ecosystem_rev': '815d4ba2e7d11f8695a26f6cbe1262e3b8ff8d0d', 'dart_http_rev': 'e4ddd3e3d3a3d0ba7c9e48506e9d2e8620af30a8', 'dart_i18n_rev': 'de1943629469719bf34269bf90fcdbe9334a73f3', 'dart_libprotobuf_rev': '24487dd1045c7f3d64a21f38a3f0c06cc4cf2edb', @@ -73,10 +73,10 @@ vars = { 'dart_protobuf_rev': '1aaa332af75c61ff32739821f7ec52186ff18d4c', 'dart_pub_rev': 'b2c03b448a47fdd52800609b9222cd737be3a934', 'dart_sync_http_rev': 'dc54465f07d9652875deeade643256dafa2fbc6c', - 'dart_tools_rev': 'd74f9e13dc02736f12ae57d5984c57024113cf5a', - 'dart_vector_math_rev': 'f08d7d2652e9ecf7d8f8605d9983335174511c95', + 'dart_tools_rev': '4a284152c263a5d3429d5ab6acfdb70eca2ebcfb', + 'dart_vector_math_rev': 'dc9d379674f50bb5559e99ee3a9f64729df9d3c8', 'dart_web_rev': '5a39fdc396ae40344308975140343c23b6863261', - 'dart_webdev_rev': 'c8b1cfa9d12c680b612eb1ed7a6b9f99fcce3ac3', + 'dart_webdev_rev': '5bf833d0c277a384ab8bbfc10e7d3d71b8022560', 'dart_webdriver_rev': 'f52afbf72895ae980bd4129d877305c2182d6cbc', 'dart_webkit_inspection_protocol_rev': 'effa75205516757795683d527c3dea9546eb0c32', @@ -332,7 +332,7 @@ deps = { Var('dart_git') + '/leak_tracker.git@f5620600a5ce1c44f65ddaa02001e200b096e14c', 'engine/src/flutter/third_party/dart/third_party/pkg/native': - Var('dart_git') + '/native.git@0bda93b319cf85691999d8bf51ea852c4ea8595f', + Var('dart_git') + '/native.git@49a8912734162e5dc6ad009fc080fe54e9985470', 'engine/src/flutter/third_party/dart/third_party/pkg/protobuf': Var('dart_git') + '/protobuf.git' + '@' + Var('dart_protobuf_rev'), @@ -350,7 +350,7 @@ deps = { Var('dart_git') + '/external/github.com/simolus3/tar.git@5a1ea943e70cdf3fa5e1102cdbb9418bd9b4b81a', 'engine/src/flutter/third_party/dart/third_party/pkg/test': - Var('dart_git') + '/test.git@8643fbf375330cd1099cdc7434306532b4bcaaf1', + Var('dart_git') + '/test.git@84eba115521b74e848f096572a2d3e4d3607e8fa', 'engine/src/flutter/third_party/dart/third_party/pkg/tools': Var('dart_git') + '/tools.git' + '@' + Var('dart_tools_rev'), diff --git a/engine/src/flutter/ci/licenses_golden/licenses_dart b/engine/src/flutter/ci/licenses_golden/licenses_dart index 191d500bae2be..5fa106ff1c0ce 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_dart +++ b/engine/src/flutter/ci/licenses_golden/licenses_dart @@ -1,4 +1,4 @@ -Signature: faec6456243d915cfcd8b9454e190993 +Signature: 90a1accc9b72ddf88abf566f718cb4bc ==================================================================================================== LIBRARY: dart @@ -4864,7 +4864,7 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -You may obtain a copy of this library's Source Code Form from: https://dart.googlesource.com/sdk/+/992221a362ec346834c4cfda9908370c5a461479 +You may obtain a copy of this library's Source Code Form from: https://dart.googlesource.com/sdk/+/2bb85834e77ef519fed07841b875fee83073089e /third_party/fallback_root_certificates/ ==================================================================================================== diff --git a/engine/src/flutter/sky/packages/sky_engine/LICENSE b/engine/src/flutter/sky/packages/sky_engine/LICENSE index 70852d11b2f89..14b876b913792 100644 --- a/engine/src/flutter/sky/packages/sky_engine/LICENSE +++ b/engine/src/flutter/sky/packages/sky_engine/LICENSE @@ -30447,7 +30447,7 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -You may obtain a copy of this library's Source Code Form from: https://dart.googlesource.com/sdk/+/992221a362ec346834c4cfda9908370c5a461479 +You may obtain a copy of this library's Source Code Form from: https://dart.googlesource.com/sdk/+/2bb85834e77ef519fed07841b875fee83073089e /third_party/fallback_root_certificates/ -------------------------------------------------------------------------------- From c53fdbdf24daba4bc81ccc6d274ce13d7d34d05e Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Thu, 17 Apr 2025 21:40:08 +0000 Subject: [PATCH 08/52] Reduce app startup latency by initializing the engine on a separate thread (#166918) If settings.merged_platform_ui_thread is set to kMergeAfterLaunch, then the engine will be started on the UI thread. After engine setup completes and the Dart isolate is loaded, the UI task runner will be merged into the platform thread and all future Dart execution will run on the platform thread. This makes it possible for other work to run on the platform thread while the engine starts. See https://github.com/flutter/flutter/issues/163064 --- engine/src/flutter/common/settings.h | 24 ++++++++--- .../fml/platform/fuchsia/task_observers.cc | 8 ++-- .../fml/platform/fuchsia/task_observers.h | 9 ++-- engine/src/flutter/fml/task_queue_id.h | 2 + .../flutter/lib/ui/painting/image_decoder.cc | 2 +- .../flutter/lib/ui/painting/image_decoder.h | 4 +- .../ui/painting/image_generator_registry.cc | 4 +- .../ui/painting/image_generator_registry.h | 4 +- engine/src/flutter/lib/ui/ui_dart_state.cc | 21 ++++++---- engine/src/flutter/lib/ui/ui_dart_state.h | 16 +++++--- engine/src/flutter/runtime/dart_isolate.cc | 11 ++++- engine/src/flutter/runtime/dart_isolate.h | 4 ++ .../src/flutter/runtime/runtime_controller.cc | 12 +++++- .../src/flutter/runtime/runtime_controller.h | 7 +++- engine/src/flutter/shell/common/animator.h | 2 +- engine/src/flutter/shell/common/engine.cc | 32 +++++++++++++-- engine/src/flutter/shell/common/engine.h | 10 +++-- .../shell/common/pointer_data_dispatcher.h | 3 +- engine/src/flutter/shell/common/shell.cc | 37 +++++++++++++---- engine/src/flutter/shell/common/shell.h | 5 ++- .../flutter/shell/common/shell_benchmarks.cc | 6 ++- engine/src/flutter/shell/common/shell_test.cc | 13 ++++-- .../flutter/shell/common/shell_unittests.cc | 41 +++++++++++++++++++ engine/src/flutter/shell/common/switches.cc | 22 +++++++++- engine/src/flutter/shell/common/switches.h | 5 +++ .../platform/android/android_shell_holder.cc | 6 ++- .../android/android_shell_holder_unittests.cc | 3 +- .../shell/platform/android/flutter_main.cc | 10 +++-- .../engine/loader/FlutterLoader.java | 6 +-- .../framework/Source/FlutterDartProject.mm | 12 ++++-- .../ios/framework/Source/FlutterEngine.mm | 5 ++- .../ios/framework/Source/FlutterEngineTest.mm | 2 +- .../shell/platform/embedder/embedder.cc | 14 +++++-- .../platform/fuchsia/flutter/component_v2.cc | 5 ++- .../src/flutter/shell/testing/tester_main.cc | 10 +++-- engine/src/flutter/testing/dart_fixture.cc | 6 ++- 36 files changed, 292 insertions(+), 91 deletions(-) diff --git a/engine/src/flutter/common/settings.h b/engine/src/flutter/common/settings.h index fbb2dd514e528..1c5a726067e3e 100644 --- a/engine/src/flutter/common/settings.h +++ b/engine/src/flutter/common/settings.h @@ -16,6 +16,7 @@ #include "flutter/fml/build_config.h" #include "flutter/fml/closure.h" #include "flutter/fml/mapping.h" +#include "flutter/fml/task_queue_id.h" #include "flutter/fml/time/time_point.h" #include "flutter/fml/unique_fd.h" @@ -70,8 +71,10 @@ class FrameTiming { }; using TaskObserverAdd = - std::function; -using TaskObserverRemove = std::function; + std::function; +using TaskObserverRemove = + std::function; using UnhandledExceptionCallback = std::function; @@ -359,9 +362,20 @@ struct Settings { /// This is used by the runOnPlatformThread API. bool enable_platform_isolates = false; - // If true, the UI thread is the platform thread on supported - // platforms. - bool merged_platform_ui_thread = true; + enum class MergedPlatformUIThread { + // Use separate threads for the UI and platform task runners. + kDisabled, + // Use the platform thread for both the UI and platform task runners. + kEnabled, + // Start the engine on a separate UI thread and then move the UI task + // runner to the platform thread after the engine is initialized. + // This can improve app launch latency by allowing other work to run on + // the platform thread during engine startup. + kMergeAfterLaunch + }; + + MergedPlatformUIThread merged_platform_ui_thread = + MergedPlatformUIThread::kEnabled; }; } // namespace flutter diff --git a/engine/src/flutter/fml/platform/fuchsia/task_observers.cc b/engine/src/flutter/fml/platform/fuchsia/task_observers.cc index d3a9ea1ede617..beede2e8e04ed 100644 --- a/engine/src/flutter/fml/platform/fuchsia/task_observers.cc +++ b/engine/src/flutter/fml/platform/fuchsia/task_observers.cc @@ -16,14 +16,16 @@ void ExecuteAfterTaskObservers() { } } -void CurrentMessageLoopAddAfterTaskObserver(intptr_t key, - fit::closure observer) { +fml::TaskQueueId CurrentMessageLoopAddAfterTaskObserver(intptr_t key, + fit::closure observer) { if (observer) { tTaskObservers[key] = std::move(observer); } + return fml::TaskQueueId::Invalid(); } -void CurrentMessageLoopRemoveAfterTaskObserver(intptr_t key) { +void CurrentMessageLoopRemoveAfterTaskObserver(fml::TaskQueueId queue_id, + intptr_t key) { tTaskObservers.erase(key); } diff --git a/engine/src/flutter/fml/platform/fuchsia/task_observers.h b/engine/src/flutter/fml/platform/fuchsia/task_observers.h index 21596ad299fce..b634aef786a6f 100644 --- a/engine/src/flutter/fml/platform/fuchsia/task_observers.h +++ b/engine/src/flutter/fml/platform/fuchsia/task_observers.h @@ -7,6 +7,8 @@ #include +#include "flutter/fml/task_queue_id.h" + namespace fml { // Executes all closures that were registered via @@ -30,10 +32,11 @@ namespace fml { // somehow. void ExecuteAfterTaskObservers(); -void CurrentMessageLoopAddAfterTaskObserver(intptr_t key, - fit::closure observer); +fml::TaskQueueId CurrentMessageLoopAddAfterTaskObserver(intptr_t key, + fit::closure observer); -void CurrentMessageLoopRemoveAfterTaskObserver(intptr_t key); +void CurrentMessageLoopRemoveAfterTaskObserver(fml::TaskQueueId queue_id, + intptr_t key); } // namespace fml diff --git a/engine/src/flutter/fml/task_queue_id.h b/engine/src/flutter/fml/task_queue_id.h index 12f4206c7c7b3..4efb6e486a27f 100644 --- a/engine/src/flutter/fml/task_queue_id.h +++ b/engine/src/flutter/fml/task_queue_id.h @@ -25,6 +25,8 @@ class TaskQueueId { /// Intializes a task queue with the given value as it's ID. explicit TaskQueueId(size_t value) : value_(value) {} + static TaskQueueId Invalid() { return TaskQueueId(kInvalid); } + operator size_t() const { // NOLINT(google-explicit-constructor) return value_; } diff --git a/engine/src/flutter/lib/ui/painting/image_decoder.cc b/engine/src/flutter/lib/ui/painting/image_decoder.cc index db2c8dd461c15..da8a84252c19a 100644 --- a/engine/src/flutter/lib/ui/painting/image_decoder.cc +++ b/engine/src/flutter/lib/ui/painting/image_decoder.cc @@ -55,7 +55,7 @@ ImageDecoder::ImageDecoder( ImageDecoder::~ImageDecoder() = default; -fml::WeakPtr ImageDecoder::GetWeakPtr() const { +fml::TaskRunnerAffineWeakPtr ImageDecoder::GetWeakPtr() const { return weak_factory_.GetWeakPtr(); } diff --git a/engine/src/flutter/lib/ui/painting/image_decoder.h b/engine/src/flutter/lib/ui/painting/image_decoder.h index ae87dbf092a46..78fdbe5fb1baf 100644 --- a/engine/src/flutter/lib/ui/painting/image_decoder.h +++ b/engine/src/flutter/lib/ui/painting/image_decoder.h @@ -44,7 +44,7 @@ class ImageDecoder { uint32_t target_height, const ImageResult& result) = 0; - fml::WeakPtr GetWeakPtr() const; + fml::TaskRunnerAffineWeakPtr GetWeakPtr() const; protected: TaskRunners runners_; @@ -57,7 +57,7 @@ class ImageDecoder { fml::WeakPtr io_manager); private: - fml::WeakPtrFactory weak_factory_; + fml::TaskRunnerAffineWeakPtrFactory weak_factory_; FML_DISALLOW_COPY_AND_ASSIGN(ImageDecoder); }; diff --git a/engine/src/flutter/lib/ui/painting/image_generator_registry.cc b/engine/src/flutter/lib/ui/painting/image_generator_registry.cc index ca5d83a36d359..e5a8b9ac621fb 100644 --- a/engine/src/flutter/lib/ui/painting/image_generator_registry.cc +++ b/engine/src/flutter/lib/ui/painting/image_generator_registry.cc @@ -79,8 +79,8 @@ ImageGeneratorRegistry::CreateCompatibleGenerator(const sk_sp& buffer) { return nullptr; } -fml::WeakPtr ImageGeneratorRegistry::GetWeakPtr() - const { +fml::TaskRunnerAffineWeakPtr +ImageGeneratorRegistry::GetWeakPtr() const { return weak_factory_.GetWeakPtr(); } diff --git a/engine/src/flutter/lib/ui/painting/image_generator_registry.h b/engine/src/flutter/lib/ui/painting/image_generator_registry.h index 09227d7c92592..ea78020b93cc0 100644 --- a/engine/src/flutter/lib/ui/painting/image_generator_registry.h +++ b/engine/src/flutter/lib/ui/painting/image_generator_registry.h @@ -58,7 +58,7 @@ class ImageGeneratorRegistry { std::shared_ptr CreateCompatibleGenerator( const sk_sp& buffer); - fml::WeakPtr GetWeakPtr() const; + fml::TaskRunnerAffineWeakPtr GetWeakPtr() const; private: struct PrioritizedFactory { @@ -85,7 +85,7 @@ class ImageGeneratorRegistry { using FactorySet = std::set; FactorySet image_generator_factories_; size_t nonce_; - fml::WeakPtrFactory weak_factory_; + fml::TaskRunnerAffineWeakPtrFactory weak_factory_; }; } // namespace flutter diff --git a/engine/src/flutter/lib/ui/ui_dart_state.cc b/engine/src/flutter/lib/ui/ui_dart_state.cc index 8a7685dd5f4e4..f0483bd979703 100644 --- a/engine/src/flutter/lib/ui/ui_dart_state.cc +++ b/engine/src/flutter/lib/ui/ui_dart_state.cc @@ -24,8 +24,9 @@ UIDartState::Context::Context( fml::TaskRunnerAffineWeakPtr snapshot_delegate, fml::WeakPtr io_manager, fml::RefPtr unref_queue, - fml::WeakPtr image_decoder, - fml::WeakPtr image_generator_registry, + fml::TaskRunnerAffineWeakPtr image_decoder, + fml::TaskRunnerAffineWeakPtr + image_generator_registry, std::string advisory_script_uri, std::string advisory_script_entrypoint, bool deterministic_rendering_enabled, @@ -58,6 +59,7 @@ UIDartState::UIDartState( const UIDartState::Context& context) : add_callback_(std::move(add_callback)), remove_callback_(std::move(remove_callback)), + callback_queue_id_(fml::TaskQueueId::kInvalid), logger_prefix_(std::move(logger_prefix)), is_root_isolate_(is_root_isolate), unhandled_exception_callback_(std::move(unhandled_exception_callback)), @@ -178,10 +180,12 @@ void UIDartState::AddOrRemoveTaskObserver(bool add) { } FML_DCHECK(add_callback_ && remove_callback_); if (add) { - add_callback_(reinterpret_cast(this), - [this]() { this->FlushMicrotasksNow(); }); + callback_queue_id_ = + add_callback_(reinterpret_cast(this), + [this]() { this->FlushMicrotasksNow(); }); } else { - remove_callback_(reinterpret_cast(this)); + remove_callback_(callback_queue_id_, reinterpret_cast(this)); + callback_queue_id_ = fml::TaskQueueId::Invalid(); } } @@ -190,12 +194,13 @@ UIDartState::GetSnapshotDelegate() const { return context_.snapshot_delegate; } -fml::WeakPtr UIDartState::GetImageDecoder() const { +fml::TaskRunnerAffineWeakPtr UIDartState::GetImageDecoder() + const { return context_.image_decoder; } -fml::WeakPtr UIDartState::GetImageGeneratorRegistry() - const { +fml::TaskRunnerAffineWeakPtr +UIDartState::GetImageGeneratorRegistry() const { return context_.image_generator_registry; } diff --git a/engine/src/flutter/lib/ui/ui_dart_state.h b/engine/src/flutter/lib/ui/ui_dart_state.h index 9ab94592081bc..5b60b3de77d62 100644 --- a/engine/src/flutter/lib/ui/ui_dart_state.h +++ b/engine/src/flutter/lib/ui/ui_dart_state.h @@ -48,8 +48,9 @@ class UIDartState : public tonic::DartState { fml::TaskRunnerAffineWeakPtr snapshot_delegate, fml::WeakPtr io_manager, fml::RefPtr unref_queue, - fml::WeakPtr image_decoder, - fml::WeakPtr image_generator_registry, + fml::TaskRunnerAffineWeakPtr image_decoder, + fml::TaskRunnerAffineWeakPtr + image_generator_registry, std::string advisory_script_uri, std::string advisory_script_entrypoint, bool deterministic_rendering_enabled, @@ -76,12 +77,13 @@ class UIDartState : public tonic::DartState { fml::RefPtr unref_queue; /// The image decoder. - fml::WeakPtr image_decoder; + fml::TaskRunnerAffineWeakPtr image_decoder; /// Cascading registry of image generator builders. Given compressed image /// bytes as input, this is used to find and create image generators, which /// can then be used for image decoding. - fml::WeakPtr image_generator_registry; + fml::TaskRunnerAffineWeakPtr + image_generator_registry; /// The advisory script URI (only used for debugging). This does not affect /// the code being run in the isolate in any way. @@ -144,9 +146,10 @@ class UIDartState : public tonic::DartState { fml::TaskRunnerAffineWeakPtr GetSnapshotDelegate() const; - fml::WeakPtr GetImageDecoder() const; + fml::TaskRunnerAffineWeakPtr GetImageDecoder() const; - fml::WeakPtr GetImageGeneratorRegistry() const; + fml::TaskRunnerAffineWeakPtr + GetImageGeneratorRegistry() const; std::shared_ptr GetIsolateNameServer() const; @@ -205,6 +208,7 @@ class UIDartState : public tonic::DartState { const TaskObserverAdd add_callback_; const TaskObserverRemove remove_callback_; + fml::TaskQueueId callback_queue_id_; const std::string logger_prefix_; Dart_Port main_port_ = ILLEGAL_PORT; const bool is_root_isolate_; diff --git a/engine/src/flutter/runtime/dart_isolate.cc b/engine/src/flutter/runtime/dart_isolate.cc index 84e0e9a689424..2dc89b8e611c9 100644 --- a/engine/src/flutter/runtime/dart_isolate.cc +++ b/engine/src/flutter/runtime/dart_isolate.cc @@ -155,7 +155,10 @@ std::weak_ptr DartIsolate::CreateRunningRootIsolate( { tonic::DartState::Scope scope(isolate.get()); - Dart_SetCurrentThreadOwnsIsolate(); + if (settings.merged_platform_ui_thread != + Settings::MergedPlatformUIThread::kMergeAfterLaunch) { + Dart_SetCurrentThreadOwnsIsolate(); + } if (settings.root_isolate_create_callback) { // Isolate callbacks always occur in isolate scope and before user code @@ -349,6 +352,7 @@ Dart_Isolate DartIsolate::CreatePlatformIsolate(Dart_Handle entry_point, } old_task_observer_add(key, callback); }); + return platform_task_runner->GetTaskQueueId(); }; UIDartState::Context context(task_runners); @@ -1373,6 +1377,11 @@ std::weak_ptr DartIsolate::GetWeakIsolatePtr() { return std::static_pointer_cast(shared_from_this()); } +void DartIsolate::SetOwnerToCurrentThread() { + tonic::DartIsolateScope isolate_scope(isolate()); + Dart_SetCurrentThreadOwnsIsolate(); +} + void DartIsolate::AddIsolateShutdownCallback(const fml::closure& closure) { shutdown_callbacks_.emplace_back(std::make_unique(closure)); } diff --git a/engine/src/flutter/runtime/dart_isolate.h b/engine/src/flutter/runtime/dart_isolate.h index 4b2116ed70ab4..3ce5b963fbd11 100644 --- a/engine/src/flutter/runtime/dart_isolate.h +++ b/engine/src/flutter/runtime/dart_isolate.h @@ -411,6 +411,10 @@ class DartIsolate : public UIDartState { static Dart_Handle LoadLibraryFromKernel( const std::shared_ptr& mapping); + // Calls a Dart API that sets the isolate's owner thread to the current + // thread. + void SetOwnerToCurrentThread(); + private: friend class IsolateConfiguration; class AutoFireClosure { diff --git a/engine/src/flutter/runtime/runtime_controller.cc b/engine/src/flutter/runtime/runtime_controller.cc index def621fe3fba7..dcdfa69ef882b 100644 --- a/engine/src/flutter/runtime/runtime_controller.cc +++ b/engine/src/flutter/runtime/runtime_controller.cc @@ -56,8 +56,9 @@ std::unique_ptr RuntimeController::Spawn( const fml::closure& p_isolate_shutdown_callback, const std::shared_ptr& p_persistent_isolate_data, fml::WeakPtr io_manager, - fml::WeakPtr image_decoder, - fml::WeakPtr image_generator_registry, + fml::TaskRunnerAffineWeakPtr image_decoder, + fml::TaskRunnerAffineWeakPtr + image_generator_registry, fml::TaskRunnerAffineWeakPtr snapshot_delegate) const { UIDartState::Context spawned_context{context_.task_runners, std::move(snapshot_delegate), @@ -676,6 +677,13 @@ void RuntimeController::ShutdownPlatformIsolates() { platform_isolate_manager_->ShutdownPlatformIsolates(); } +void RuntimeController::SetRootIsolateOwnerToCurrentThread() { + std::shared_ptr root_isolate = root_isolate_.lock(); + if (root_isolate) { + root_isolate->SetOwnerToCurrentThread(); + } +} + RuntimeController::Locale::Locale(std::string language_code_, std::string country_code_, std::string script_code_, diff --git a/engine/src/flutter/runtime/runtime_controller.h b/engine/src/flutter/runtime/runtime_controller.h index 5b11cf2b819df..e5dbdfdcda51f 100644 --- a/engine/src/flutter/runtime/runtime_controller.h +++ b/engine/src/flutter/runtime/runtime_controller.h @@ -121,8 +121,9 @@ class RuntimeController : public PlatformConfigurationClient, const fml::closure& isolate_shutdown_callback, const std::shared_ptr& persistent_isolate_data, fml::WeakPtr io_manager, - fml::WeakPtr image_decoder, - fml::WeakPtr image_generator_registry, + fml::TaskRunnerAffineWeakPtr image_decoder, + fml::TaskRunnerAffineWeakPtr + image_generator_registry, fml::TaskRunnerAffineWeakPtr snapshot_delegate) const; // |PlatformConfigurationClient| @@ -678,6 +679,8 @@ class RuntimeController : public PlatformConfigurationClient, return platform_isolate_manager_; } + void SetRootIsolateOwnerToCurrentThread(); + //-------------------------------------------------------------------------- /// @brief Shuts down all registered platform isolates. Must be called /// from the platform thread. diff --git a/engine/src/flutter/shell/common/animator.h b/engine/src/flutter/shell/common/animator.h index d559945358b99..e245b1e02bd13 100644 --- a/engine/src/flutter/shell/common/animator.h +++ b/engine/src/flutter/shell/common/animator.h @@ -148,7 +148,7 @@ class Animator final { std::deque trace_flow_ids_; bool has_rendered_ = false; - fml::WeakPtrFactory weak_factory_; + fml::TaskRunnerAffineWeakPtrFactory weak_factory_; friend class testing::ShellTest; diff --git a/engine/src/flutter/shell/common/engine.cc b/engine/src/flutter/shell/common/engine.cc index be7cddf05c898..c1d6795af225b 100644 --- a/engine/src/flutter/shell/common/engine.cc +++ b/engine/src/flutter/shell/common/engine.cc @@ -148,7 +148,7 @@ std::unique_ptr Engine::Spawn( Engine::~Engine() = default; -fml::WeakPtr Engine::GetWeakPtr() const { +fml::TaskRunnerAffineWeakPtr Engine::GetWeakPtr() const { return weak_factory_.GetWeakPtr(); } @@ -161,11 +161,12 @@ std::shared_ptr Engine::GetAssetManager() { return asset_manager_; } -fml::WeakPtr Engine::GetImageDecoderWeakPtr() { +fml::TaskRunnerAffineWeakPtr Engine::GetImageDecoderWeakPtr() { return image_decoder_->GetWeakPtr(); } -fml::WeakPtr Engine::GetImageGeneratorRegistry() { +fml::TaskRunnerAffineWeakPtr +Engine::GetImageGeneratorRegistry() { return image_generator_registry_.GetWeakPtr(); } @@ -239,6 +240,19 @@ Engine::RunStatus Engine::Run(RunConfiguration configuration) { } }; + if (settings_.merged_platform_ui_thread == + Settings::MergedPlatformUIThread::kMergeAfterLaunch) { + // Queue a task to the UI task runner that sets the owner of the root + // isolate. This task runs after the thread merge and will therefore be + // executed on the platform thread. The task will run before any tasks + // queued by LaunchRootIsolate that execute the app's Dart code. + task_runners_.GetUITaskRunner()->PostTask([engine = GetWeakPtr()]() { + if (engine) { + engine->runtime_controller_->SetRootIsolateOwnerToCurrentThread(); + } + }); + } + if (!runtime_controller_->LaunchRootIsolate( settings_, // root_isolate_create_callback, // @@ -260,6 +274,18 @@ Engine::RunStatus Engine::Run(RunConfiguration configuration) { HandlePlatformMessage(std::move(service_id_message)); } + if (settings_.merged_platform_ui_thread == + Settings::MergedPlatformUIThread::kMergeAfterLaunch) { + // Move the UI task runner to the platform thread. + bool success = fml::MessageLoopTaskQueues::GetInstance()->Merge( + task_runners_.GetPlatformTaskRunner()->GetTaskQueueId(), + task_runners_.GetUITaskRunner()->GetTaskQueueId()); + if (!success) { + FML_LOG(ERROR) + << "Unable to move the UI task runner to the platform thread"; + } + } + return Engine::RunStatus::Success; } diff --git a/engine/src/flutter/shell/common/engine.h b/engine/src/flutter/shell/common/engine.h index 8d911741931ad..7226fbf4b99ec 100644 --- a/engine/src/flutter/shell/common/engine.h +++ b/engine/src/flutter/shell/common/engine.h @@ -441,7 +441,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { /// @return The pointer to this instance of the engine. The engine may /// only be accessed safely on the UI task runner. /// - fml::WeakPtr GetWeakPtr() const; + fml::TaskRunnerAffineWeakPtr GetWeakPtr() const; //---------------------------------------------------------------------------- /// @brief Moves the root isolate to the `DartIsolate::Phase::Running` @@ -877,7 +877,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { std::shared_ptr GetAssetManager() override; // Return the weak_ptr of ImageDecoder. - fml::WeakPtr GetImageDecoderWeakPtr(); + fml::TaskRunnerAffineWeakPtr GetImageDecoderWeakPtr(); //---------------------------------------------------------------------------- /// @brief Get the `ImageGeneratorRegistry` associated with the current @@ -885,7 +885,8 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { /// /// @return The engine's `ImageGeneratorRegistry`. /// - fml::WeakPtr GetImageGeneratorRegistry(); + fml::TaskRunnerAffineWeakPtr + GetImageGeneratorRegistry(); // |PointerDataDispatcher::Delegate| void DoDispatchPacket(std::unique_ptr packet, @@ -1084,7 +1085,8 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { const std::unique_ptr image_decoder_; ImageGeneratorRegistry image_generator_registry_; TaskRunners task_runners_; - fml::WeakPtrFactory weak_factory_; // Must be the last member. + fml::TaskRunnerAffineWeakPtrFactory + weak_factory_; // Must be the last member. FML_DISALLOW_COPY_AND_ASSIGN(Engine); }; diff --git a/engine/src/flutter/shell/common/pointer_data_dispatcher.h b/engine/src/flutter/shell/common/pointer_data_dispatcher.h index b749bfeee99de..88c0167fc88fe 100644 --- a/engine/src/flutter/shell/common/pointer_data_dispatcher.h +++ b/engine/src/flutter/shell/common/pointer_data_dispatcher.h @@ -159,7 +159,8 @@ class SmoothPointerDataDispatcher : public DefaultPointerDataDispatcher { bool is_pointer_data_in_progress_ = false; // WeakPtrFactory must be the last member. - fml::WeakPtrFactory weak_factory_; + fml::TaskRunnerAffineWeakPtrFactory + weak_factory_; FML_DISALLOW_COPY_AND_ASSIGN(SmoothPointerDataDispatcher); }; diff --git a/engine/src/flutter/shell/common/shell.cc b/engine/src/flutter/shell/common/shell.cc index f70991ca00612..e348f690ed4b3 100644 --- a/engine/src/flutter/shell/common/shell.cc +++ b/engine/src/flutter/shell/common/shell.cc @@ -567,6 +567,19 @@ Shell::~Shell() { platform_latch.Signal(); })); platform_latch.Wait(); + + if (settings_.merged_platform_ui_thread == + Settings::MergedPlatformUIThread::kMergeAfterLaunch) { + // Move the UI task runner back to its original thread to enable shutdown of + // that thread. + auto task_queues = fml::MessageLoopTaskQueues::GetInstance(); + auto platform_queue_id = + task_runners_.GetPlatformTaskRunner()->GetTaskQueueId(); + auto ui_queue_id = task_runners_.GetUITaskRunner()->GetTaskQueueId(); + if (task_queues->Owns(platform_queue_id, ui_queue_id)) { + task_queues->Unmerge(platform_queue_id, ui_queue_id); + } + } } std::unique_ptr Shell::Spawn( @@ -575,6 +588,16 @@ std::unique_ptr Shell::Spawn( const CreateCallback& on_create_platform_view, const CreateCallback& on_create_rasterizer) const { FML_DCHECK(task_runners_.IsValid()); + + if (settings_.merged_platform_ui_thread == + Settings::MergedPlatformUIThread::kMergeAfterLaunch) { + // Spawning engines that share the same task runners can result in + // deadlocks when the UI task runner is moved to the platform thread. + FML_LOG(ERROR) << "MergedPlatformUIThread::kMergeAfterLaunch does not " + "support spawning"; + return nullptr; + } + // It's safe to store this value since it is set on the platform thread. bool is_gpu_disabled = false; GetIsGpuDisabledSyncSwitch()->Execute( @@ -811,7 +834,7 @@ fml::TaskRunnerAffineWeakPtr Shell::GetRasterizer() const { return weak_rasterizer_; } -fml::WeakPtr Shell::GetEngine() { +fml::TaskRunnerAffineWeakPtr Shell::GetEngine() { FML_DCHECK(is_set_up_); return weak_engine_; } @@ -1079,12 +1102,12 @@ void Shell::OnPlatformViewDispatchPlatformMessage( // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) fml::TaskRunner::RunNowAndFlushMessages( task_runners_.GetUITaskRunner(), - fml::MakeCopyable([engine = engine_->GetWeakPtr(), - message = std::move(message)]() mutable { - if (engine) { - engine->DispatchPlatformMessage(std::move(message)); - } - })); + fml::MakeCopyable( + [engine = weak_engine_, message = std::move(message)]() mutable { + if (engine) { + engine->DispatchPlatformMessage(std::move(message)); + } + })); } // |PlatformView::Delegate| diff --git a/engine/src/flutter/shell/common/shell.h b/engine/src/flutter/shell/common/shell.h index a5305e7abfdc9..0cd7ef5499248 100644 --- a/engine/src/flutter/shell/common/shell.h +++ b/engine/src/flutter/shell/common/shell.h @@ -260,7 +260,7 @@ class Shell final : public PlatformView::Delegate, /// /// @return A weak pointer to the engine. /// - fml::WeakPtr GetEngine(); + fml::TaskRunnerAffineWeakPtr GetEngine(); //---------------------------------------------------------------------------- /// @brief Platform views may only be accessed on the platform task @@ -470,7 +470,8 @@ class Shell final : public PlatformView::Delegate, std::shared_ptr platform_message_handler_; std::atomic route_messages_through_platform_thread_ = false; - fml::WeakPtr weak_engine_; // to be shared across threads + fml::TaskRunnerAffineWeakPtr + weak_engine_; // to be shared across threads fml::TaskRunnerAffineWeakPtr weak_rasterizer_; // to be shared across threads fml::WeakPtr diff --git a/engine/src/flutter/shell/common/shell_benchmarks.cc b/engine/src/flutter/shell/common/shell_benchmarks.cc index a1bba720fe940..151aebd504541 100644 --- a/engine/src/flutter/shell/common/shell_benchmarks.cc +++ b/engine/src/flutter/shell/common/shell_benchmarks.cc @@ -25,8 +25,10 @@ static void StartupAndShutdownShell(benchmark::State& state, { benchmarking::ScopedPauseTiming pause(state, !measure_startup); Settings settings = {}; - settings.task_observer_add = [](intptr_t, const fml::closure&) {}; - settings.task_observer_remove = [](intptr_t) {}; + settings.task_observer_add = [](intptr_t, const fml::closure&) { + return fml::TaskQueueId::Invalid(); + }; + settings.task_observer_remove = [](fml::TaskQueueId, intptr_t) {}; if (DartVM::IsRunningPrecompiledCode()) { aot_symbols = testing::LoadELFSymbolFromFixturesIfNeccessary( diff --git a/engine/src/flutter/shell/common/shell_test.cc b/engine/src/flutter/shell/common/shell_test.cc index 7dc7f80369cf7..24ce62e7a7b50 100644 --- a/engine/src/flutter/shell/common/shell_test.cc +++ b/engine/src/flutter/shell/common/shell_test.cc @@ -226,7 +226,8 @@ void ShellTest::PumpOneFrame(Shell* shell, FrameContent frame_content) { // tree pipeline nonempty. Without either of this, the layer tree below // won't be rasterized. fml::AutoResetWaitableEvent latch; - fml::WeakPtr runtime_delegate = shell->weak_engine_; + fml::TaskRunnerAffineWeakPtr runtime_delegate = + shell->weak_engine_; shell->GetTaskRunners().GetUITaskRunner()->PostTask( [&latch, engine = shell->weak_engine_, &frame_content, runtime_delegate]() { @@ -341,10 +342,14 @@ Settings ShellTest::CreateSettingsForFixture() { Settings settings; settings.leak_vm = false; settings.task_observer_add = [](intptr_t key, const fml::closure& handler) { - fml::MessageLoop::GetCurrent().AddTaskObserver(key, handler); + fml::TaskQueueId queue_id = fml::MessageLoop::GetCurrentTaskQueueId(); + fml::MessageLoopTaskQueues::GetInstance()->AddTaskObserver(queue_id, key, + handler); + return queue_id; }; - settings.task_observer_remove = [](intptr_t key) { - fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + settings.task_observer_remove = [](fml::TaskQueueId queue_id, intptr_t key) { + fml::MessageLoopTaskQueues::GetInstance()->RemoveTaskObserver(queue_id, + key); }; settings.isolate_create_callback = [this]() { native_resolver_->SetNativeResolverForIsolate(); diff --git a/engine/src/flutter/shell/common/shell_unittests.cc b/engine/src/flutter/shell/common/shell_unittests.cc index 2bbf216bd0ceb..8cdcbc5e0c709 100644 --- a/engine/src/flutter/shell/common/shell_unittests.cc +++ b/engine/src/flutter/shell/common/shell_unittests.cc @@ -5072,6 +5072,47 @@ TEST_F(ShellTest, ProvidesNullEngineId) { DestroyShell(std::move(shell), task_runners); } +TEST_F(ShellTest, MergeUIAndPlatformThreadsAfterLaunch) { + Settings settings = CreateSettingsForFixture(); + settings.merged_platform_ui_thread = + Settings::MergedPlatformUIThread::kMergeAfterLaunch; + ThreadHost thread_host(ThreadHost::ThreadHostConfig( + "io.flutter.test." + GetCurrentTestName() + ".", + ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster | + ThreadHost::Type::kIo | ThreadHost::Type::kUi)); + TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(), + thread_host.raster_thread->GetTaskRunner(), + thread_host.ui_thread->GetTaskRunner(), + thread_host.io_thread->GetTaskRunner()); + + std::unique_ptr shell = CreateShell(settings, task_runners); + ASSERT_TRUE(shell); + + ASSERT_FALSE(fml::TaskRunnerChecker::RunsOnTheSameThread( + task_runners.GetUITaskRunner()->GetTaskQueueId(), + task_runners.GetPlatformTaskRunner()->GetTaskQueueId())); + + fml::AutoResetWaitableEvent latch; + AddNativeCallback( + "NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) { + ASSERT_TRUE( + task_runners.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + latch.Signal(); + })); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("mainNotifyNative"); + RunEngine(shell.get(), std::move(configuration)); + + latch.Wait(); + + ASSERT_TRUE(fml::TaskRunnerChecker::RunsOnTheSameThread( + task_runners.GetUITaskRunner()->GetTaskQueueId(), + task_runners.GetPlatformTaskRunner()->GetTaskQueueId())); + + DestroyShell(std::move(shell), task_runners); +} + } // namespace testing } // namespace flutter diff --git a/engine/src/flutter/shell/common/switches.cc b/engine/src/flutter/shell/common/switches.cc index fc2b51fa4f103..a653b57f0e527 100644 --- a/engine/src/flutter/shell/common/switches.cc +++ b/engine/src/flutter/shell/common/switches.cc @@ -529,8 +529,26 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { settings.enable_surface_control = command_line.HasOption( FlagForSwitch(Switch::EnableAndroidSurfaceControl)); - settings.merged_platform_ui_thread = !command_line.HasOption( - FlagForSwitch(Switch::DisableMergedPlatformUIThread)); + if (command_line.HasOption( + FlagForSwitch(Switch::DisableMergedPlatformUIThread))) { + settings.merged_platform_ui_thread = + Settings::MergedPlatformUIThread::kDisabled; + } else if (command_line.HasOption( + FlagForSwitch(Switch::MergedPlatformUIThread))) { + std::string merged_platform_ui; + command_line.GetOptionValue(FlagForSwitch(Switch::MergedPlatformUIThread), + &merged_platform_ui); + if (merged_platform_ui == "enabled") { + settings.merged_platform_ui_thread = + Settings::MergedPlatformUIThread::kEnabled; + } else if (merged_platform_ui == "disabled") { + settings.merged_platform_ui_thread = + Settings::MergedPlatformUIThread::kDisabled; + } else if (merged_platform_ui == "mergeAfterLaunch") { + settings.merged_platform_ui_thread = + Settings::MergedPlatformUIThread::kMergeAfterLaunch; + } + } settings.enable_flutter_gpu = command_line.HasOption(FlagForSwitch(Switch::EnableFlutterGPU)); diff --git a/engine/src/flutter/shell/common/switches.h b/engine/src/flutter/shell/common/switches.h index 7a2d04e5e03c4..238d890a962ed 100644 --- a/engine/src/flutter/shell/common/switches.h +++ b/engine/src/flutter/shell/common/switches.h @@ -294,6 +294,11 @@ DEF_SWITCH(EnableEmbedderAPI, DEF_SWITCH(EnablePlatformIsolates, "enable-platform-isolates", "Enable support for isolates that run on the platform thread.") +DEF_SWITCH(MergedPlatformUIThread, + "merged-platform-ui-thread", + "Sets whether the ui thread and platform thread should be merged.") +// This is a legacy flag that has been superseded by merged-platform-ui-thread. +// TODO(163064): remove this when users have been migrated. DEF_SWITCH(DisableMergedPlatformUIThread, "no-enable-merged-platform-ui-thread", "Merge the ui thread and platform thread.") diff --git a/engine/src/flutter/shell/platform/android/android_shell_holder.cc b/engine/src/flutter/shell/platform/android/android_shell_holder.cc index 58a0eb0707d98..9bf3ddc1fd15b 100644 --- a/engine/src/flutter/shell/platform/android/android_shell_holder.cc +++ b/engine/src/flutter/shell/platform/android/android_shell_holder.cc @@ -90,7 +90,8 @@ AndroidShellHolder::AndroidShellHolder( auto thread_label = std::to_string(thread_host_count++); auto mask = ThreadHost::Type::kRaster | ThreadHost::Type::kIo; - if (!settings.merged_platform_ui_thread) { + if (settings.merged_platform_ui_thread != + Settings::MergedPlatformUIThread::kEnabled) { mask |= ThreadHost::Type::kUi; } @@ -139,7 +140,8 @@ AndroidShellHolder::AndroidShellHolder( fml::RefPtr platform_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); raster_runner = thread_host_->raster_thread->GetTaskRunner(); - if (settings.merged_platform_ui_thread) { + if (settings.merged_platform_ui_thread == + Settings::MergedPlatformUIThread::kEnabled) { ui_runner = platform_runner; } else { ui_runner = thread_host_->ui_thread->GetTaskRunner(); diff --git a/engine/src/flutter/shell/platform/android/android_shell_holder_unittests.cc b/engine/src/flutter/shell/platform/android/android_shell_holder_unittests.cc index 1b324ffbb6321..2b4a5e67e0e7a 100644 --- a/engine/src/flutter/shell/platform/android/android_shell_holder_unittests.cc +++ b/engine/src/flutter/shell/platform/android/android_shell_holder_unittests.cc @@ -201,7 +201,8 @@ TEST(AndroidShellHolder, CreateWithMergedPlatformAndUIThread) { TEST(AndroidShellHolder, CreateWithUnMergedPlatformAndUIThread) { Settings settings; - settings.merged_platform_ui_thread = false; + settings.merged_platform_ui_thread = + Settings::MergedPlatformUIThread::kDisabled; auto jni = std::make_shared(); auto holder = std::make_unique( settings, jni, AndroidRenderingAPI::kImpellerOpenGLES); diff --git a/engine/src/flutter/shell/platform/android/flutter_main.cc b/engine/src/flutter/shell/platform/android/flutter_main.cc index bf97261032ccd..aac7f772c78fa 100644 --- a/engine/src/flutter/shell/platform/android/flutter_main.cc +++ b/engine/src/flutter/shell/platform/android/flutter_main.cc @@ -147,11 +147,15 @@ void FlutterMain::Init(JNIEnv* env, } settings.task_observer_add = [](intptr_t key, const fml::closure& callback) { - fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback); + fml::TaskQueueId queue_id = fml::MessageLoop::GetCurrentTaskQueueId(); + fml::MessageLoopTaskQueues::GetInstance()->AddTaskObserver(queue_id, key, + callback); + return queue_id; }; - settings.task_observer_remove = [](intptr_t key) { - fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + settings.task_observer_remove = [](fml::TaskQueueId queue_id, intptr_t key) { + fml::MessageLoopTaskQueues::GetInstance()->RemoveTaskObserver(queue_id, + key); }; settings.log_message_callback = [](const std::string& tag, diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java index 249309098e0ff..65fa2e55bafe6 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java @@ -368,10 +368,8 @@ public void ensureInitializationComplete( if (metaData.getBoolean(IMPELLER_VULKAN_GPU_TRACING_DATA_KEY, false)) { shellArgs.add("--enable-vulkan-gpu-tracing"); } - if (metaData.containsKey(DISABLE_MERGED_PLATFORM_UI_THREAD_KEY)) { - if (metaData.getBoolean(DISABLE_MERGED_PLATFORM_UI_THREAD_KEY)) { - shellArgs.add("--no-enable-merged-platform-ui-thread"); - } + if (metaData.getBoolean(DISABLE_MERGED_PLATFORM_UI_THREAD_KEY, false)) { + shellArgs.add("--merged-platform-ui-thread=disabled"); } if (metaData.getBoolean(ENABLE_FLUTTER_GPU, false)) { shellArgs.add("--enable-flutter-gpu"); diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm index 183ff85c78e12..bcfd97158a89c 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm @@ -64,11 +64,13 @@ static BOOL DoesHardwareSupportWideGamut() { auto settings = flutter::SettingsFromCommandLine(command_line); settings.task_observer_add = [](intptr_t key, const fml::closure& callback) { - fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback); + fml::TaskQueueId queue_id = fml::MessageLoop::GetCurrentTaskQueueId(); + fml::MessageLoopTaskQueues::GetInstance()->AddTaskObserver(queue_id, key, callback); + return queue_id; }; - settings.task_observer_remove = [](intptr_t key) { - fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + settings.task_observer_remove = [](fml::TaskQueueId queue_id, intptr_t key) { + fml::MessageLoopTaskQueues::GetInstance()->RemoveTaskObserver(queue_id, key); }; settings.log_message_callback = [](const std::string& tag, const std::string& message) { @@ -209,7 +211,9 @@ static BOOL DoesHardwareSupportWideGamut() { NSNumber* enableMergedPlatformUIThread = [mainBundle objectForInfoDictionaryKey:@"FLTEnableMergedPlatformUIThread"]; if (enableMergedPlatformUIThread != nil) { - settings.merged_platform_ui_thread = enableMergedPlatformUIThread.boolValue; + settings.merged_platform_ui_thread = enableMergedPlatformUIThread.boolValue + ? flutter::Settings::MergedPlatformUIThread::kEnabled + : flutter::Settings::MergedPlatformUIThread::kDisabled; } NSNumber* enableFlutterGPU = [mainBundle objectForInfoDictionaryKey:@"FLTEnableFlutterGPU"]; diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 6c3e893c89e7a..47eaff91a3343 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -752,7 +752,7 @@ + (NSString*)generateThreadLabel:(NSString*)labelPrefix { fml::MessageLoop::EnsureInitializedForCurrentThread(); uint32_t threadHostType = flutter::ThreadHost::Type::kRaster | flutter::ThreadHost::Type::kIo; - if (!settings.merged_platform_ui_thread) { + if (settings.merged_platform_ui_thread != flutter::Settings::MergedPlatformUIThread::kEnabled) { threadHostType |= flutter::ThreadHost::Type::kUi; } @@ -839,7 +839,8 @@ - (BOOL)createShell:(NSString*)entrypoint [](flutter::Shell& shell) { return std::make_unique(shell); }; fml::RefPtr ui_runner; - if (settings.enable_impeller && settings.merged_platform_ui_thread) { + if (settings.enable_impeller && + settings.merged_platform_ui_thread == flutter::Settings::MergedPlatformUIThread::kEnabled) { ui_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); } else { ui_runner = _threadHost->ui_thread->GetTaskRunner(); diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm index 3770bf8c6a5ae..6f2249c9f8817 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm @@ -528,7 +528,7 @@ - (void)testCanMergePlatformAndUIThread { - (void)testCanUnMergePlatformAndUIThread { #if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR auto settings = FLTDefaultSettingsForBundle(); - settings.merged_platform_ui_thread = false; + settings.merged_platform_ui_thread = flutter::Settings::MergedPlatformUIThread::kDisabled; FlutterDartProject* project = [[FlutterDartProject alloc] initWithSettings:settings]; FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project]; [engine run]; diff --git a/engine/src/flutter/shell/platform/embedder/embedder.cc b/engine/src/flutter/shell/platform/embedder/embedder.cc index e5f7c4353e265..6551c2e360931 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder.cc @@ -2389,12 +2389,18 @@ FlutterEngineResult FlutterEngineInitialize(size_t version, settings.task_observer_add = [has_ui_thread_message_loop]( intptr_t key, const fml::closure& callback) { if (has_ui_thread_message_loop) { - fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback); + fml::TaskQueueId queue_id = fml::MessageLoop::GetCurrentTaskQueueId(); + fml::MessageLoopTaskQueues::GetInstance()->AddTaskObserver(queue_id, key, + callback); + return queue_id; + } else { + return fml::TaskQueueId::Invalid(); } }; - settings.task_observer_remove = [has_ui_thread_message_loop](intptr_t key) { - if (has_ui_thread_message_loop) { - fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + settings.task_observer_remove = [](fml::TaskQueueId queue_id, intptr_t key) { + if (queue_id.is_valid()) { + fml::MessageLoopTaskQueues::GetInstance()->RemoveTaskObserver(queue_id, + key); } }; diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/component_v2.cc b/engine/src/flutter/shell/platform/fuchsia/flutter/component_v2.cc index 5df9b1b0361c2..6d6b30d7654b5 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/component_v2.cc +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/component_v2.cc @@ -469,8 +469,9 @@ ComponentV2::ComponentV2( std::bind(&fml::CurrentMessageLoopAddAfterTaskObserver, std::placeholders::_1, std::placeholders::_2); - settings_.task_observer_remove = std::bind( - &fml::CurrentMessageLoopRemoveAfterTaskObserver, std::placeholders::_1); + settings_.task_observer_remove = + std::bind(&fml::CurrentMessageLoopRemoveAfterTaskObserver, + std::placeholders::_1, std::placeholders::_2); settings_.log_message_callback = [](const std::string& tag, const std::string& message) { diff --git a/engine/src/flutter/shell/testing/tester_main.cc b/engine/src/flutter/shell/testing/tester_main.cc index 95bdeb52be366..04861d55827c1 100644 --- a/engine/src/flutter/shell/testing/tester_main.cc +++ b/engine/src/flutter/shell/testing/tester_main.cc @@ -666,11 +666,15 @@ int main(int argc, char* argv[]) { }; settings.task_observer_add = [](intptr_t key, const fml::closure& callback) { - fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback); + fml::TaskQueueId queue_id = fml::MessageLoop::GetCurrentTaskQueueId(); + fml::MessageLoopTaskQueues::GetInstance()->AddTaskObserver(queue_id, key, + callback); + return queue_id; }; - settings.task_observer_remove = [](intptr_t key) { - fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + settings.task_observer_remove = [](fml::TaskQueueId queue_id, intptr_t key) { + fml::MessageLoopTaskQueues::GetInstance()->RemoveTaskObserver(queue_id, + key); }; settings.unhandled_exception_callback = [](const std::string& error, diff --git a/engine/src/flutter/testing/dart_fixture.cc b/engine/src/flutter/testing/dart_fixture.cc index 7c765dcb105a5..4ccc4baefc304 100644 --- a/engine/src/flutter/testing/dart_fixture.cc +++ b/engine/src/flutter/testing/dart_fixture.cc @@ -30,8 +30,10 @@ DartFixture::DartFixture(std::string kernel_filename, Settings DartFixture::CreateSettingsForFixture() { Settings settings; settings.leak_vm = false; - settings.task_observer_add = [](intptr_t, const fml::closure&) {}; - settings.task_observer_remove = [](intptr_t) {}; + settings.task_observer_add = [](intptr_t, const fml::closure&) { + return fml::TaskQueueId::Invalid(); + }; + settings.task_observer_remove = [](fml::TaskQueueId, intptr_t) {}; settings.isolate_create_callback = [this]() { native_resolver_->SetNativeResolverForIsolate(); }; From 99c3d037587a827ec2f6de35b5dfe147fbb4f104 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 17 Apr 2025 17:43:48 -0400 Subject: [PATCH 09/52] Roll Skia from cc2b57434651 to a409d685a711 (3 revisions) (#167347) https://skia.googlesource.com/skia.git/+log/cc2b57434651..a409d685a711 2025-04-17 michaelludwig@google.com Revert "[graphite] Update DrawWriter to minimize binds by using pending base offsets." 2025-04-17 jvanverth@google.com [graphite] Use sk_sp for ClipAtlas instead of raw pointer. 2025-04-17 skia-autoroll@skia-public.iam.gserviceaccount.com Roll vulkan-deps from 3bc11f8f2d55 to f6ce0375d108 (4 revisions) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bwils@google.com,codefu@google.com,kjlubick@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- DEPS | 2 +- engine/src/flutter/ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index cfc9bf45a4dce..0e9a5430239c9 100644 --- a/DEPS +++ b/DEPS @@ -14,7 +14,7 @@ vars = { 'flutter_git': 'https://flutter.googlesource.com', 'skia_git': 'https://skia.googlesource.com', 'llvm_git': 'https://llvm.googlesource.com', - 'skia_revision': 'cc2b574346514202c61176272644fd6c3138888c', + 'skia_revision': 'a409d685a7112be6d795be4b8838031112dce86c', # WARNING: DO NOT EDIT canvaskit_cipd_instance MANUALLY # See `lib/web_ui/README.md` for how to roll CanvasKit to a new version. diff --git a/engine/src/flutter/ci/licenses_golden/licenses_skia b/engine/src/flutter/ci/licenses_golden/licenses_skia index efe8ab362fc1f..c432480d45802 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_skia +++ b/engine/src/flutter/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 3590532a1d7d1f32596972aaff9feead +Signature: e15c4e8c8f97351f7556c05d263c8ac9 ==================================================================================================== LIBRARY: etc1 From 1db4930a432721e778c8e7afa0129d911313a103 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 17 Apr 2025 22:06:36 -0400 Subject: [PATCH 10/52] Roll Fuchsia Linux SDK from m8Aln7fTF_8zy1V9N... to MwYckh5OvwwmIYLx0... (#167385) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter Please CC codefu@google.com,zra@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- DEPS | 2 +- engine/src/flutter/ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 0e9a5430239c9..889e6536a08ff 100644 --- a/DEPS +++ b/DEPS @@ -813,7 +813,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'm8Aln7fTF_8zy1V9NZfD1vpUEoV2MluzsQUsHELpaPcC' + 'version': 'MwYckh5OvwwmIYLx0Z34IxTKnqGMEmITvYbeFqrdU4IC' } ], 'condition': 'download_fuchsia_deps and not download_fuchsia_sdk', diff --git a/engine/src/flutter/ci/licenses_golden/licenses_fuchsia b/engine/src/flutter/ci/licenses_golden/licenses_fuchsia index 283e1a1c48c6f..4f31f4708883c 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_fuchsia +++ b/engine/src/flutter/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 3195cae6e3304cfd675758d8b590e37b +Signature: 3a1356a40da2aff62932d5d38cd51477 ==================================================================================================== LIBRARY: fuchsia_sdk From 40f25c9b191a8b4d2d12c74e01517fe47072834a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 18 Apr 2025 00:32:31 -0400 Subject: [PATCH 11/52] Roll Dart SDK from 2bb85834e77e to b1eb743f97f5 (2 revisions) (#167387) https://dart.googlesource.com/sdk.git/+log/2bb85834e77e..b1eb743f97f5 2025-04-18 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.9.0-30.0.dev 2025-04-17 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com Version 3.9.0-29.0.dev If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/dart-sdk-flutter Please CC codefu@google.com,dart-vm-team@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- DEPS | 2 +- engine/src/flutter/sky/packages/sky_engine/LICENSE | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 889e6536a08ff..1c2c5ea0d75c2 100644 --- a/DEPS +++ b/DEPS @@ -56,7 +56,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/main/DEPS # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '2bb85834e77ef519fed07841b875fee83073089e', + 'dart_revision': 'b1eb743f97f5615db73e418b9ef5beea392a2a2e', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/engine/src/flutter/sky/packages/sky_engine/LICENSE b/engine/src/flutter/sky/packages/sky_engine/LICENSE index 14b876b913792..98da831a5f8ad 100644 --- a/engine/src/flutter/sky/packages/sky_engine/LICENSE +++ b/engine/src/flutter/sky/packages/sky_engine/LICENSE @@ -30447,7 +30447,7 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -You may obtain a copy of this library's Source Code Form from: https://dart.googlesource.com/sdk/+/2bb85834e77ef519fed07841b875fee83073089e +You may obtain a copy of this library's Source Code Form from: https://dart.googlesource.com/sdk/+/b1eb743f97f5615db73e418b9ef5beea392a2a2e /third_party/fallback_root_certificates/ -------------------------------------------------------------------------------- From b29d7bc42a135ec79540b6409c8a106f10975e7a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 18 Apr 2025 01:02:08 -0400 Subject: [PATCH 12/52] Roll Skia from a409d685a711 to acc910544da7 (10 revisions) (#167388) https://skia.googlesource.com/skia.git/+log/a409d685a711..acc910544da7 2025-04-18 skia-autoroll@skia-public.iam.gserviceaccount.com Manual roll Dawn from 33862a3ffc8a to 971cc7345000 (191 revisions) 2025-04-17 skia-autoroll@skia-public.iam.gserviceaccount.com Roll vulkan-deps from f6ce0375d108 to da64cc98aa1f (7 revisions) 2025-04-17 robertphillips@google.com Fix ChromePrecompileTest for Intel Macs 2025-04-17 borenet@google.com [infra] Add P30 jobs 2025-04-17 skia-autoroll@skia-public.iam.gserviceaccount.com Roll skottie-base from 868103199143 to f4a4eee934a3 2025-04-17 skia-autoroll@skia-public.iam.gserviceaccount.com Roll shaders-base from 64b9cec3c82d to 11837b5a53d7 2025-04-17 skia-autoroll@skia-public.iam.gserviceaccount.com Roll jsfiddle-base from 05716f858285 to 05b60ca904d9 2025-04-17 skia-autoroll@skia-public.iam.gserviceaccount.com Roll debugger-app-base from 9e80d6bc5f11 to f59c59370426 2025-04-17 nicolettep@google.com [graphite] Run precompile tests when making changes to relevant files 2025-04-17 jvanverth@google.com [graphite] Use sk_sp for PathAtlas instead of raw pointer. If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bwils@google.com,codefu@google.com,kjlubick@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- DEPS | 2 +- engine/src/flutter/ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 1c2c5ea0d75c2..11adb9972e5a0 100644 --- a/DEPS +++ b/DEPS @@ -14,7 +14,7 @@ vars = { 'flutter_git': 'https://flutter.googlesource.com', 'skia_git': 'https://skia.googlesource.com', 'llvm_git': 'https://llvm.googlesource.com', - 'skia_revision': 'a409d685a7112be6d795be4b8838031112dce86c', + 'skia_revision': 'acc910544da7bc283ccd2cdfe6d72fa318ded5b9', # WARNING: DO NOT EDIT canvaskit_cipd_instance MANUALLY # See `lib/web_ui/README.md` for how to roll CanvasKit to a new version. diff --git a/engine/src/flutter/ci/licenses_golden/licenses_skia b/engine/src/flutter/ci/licenses_golden/licenses_skia index c432480d45802..05dacabca1bb2 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_skia +++ b/engine/src/flutter/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: e15c4e8c8f97351f7556c05d263c8ac9 +Signature: d6b2a9d3e2d0e3d4a972a48985cab9db ==================================================================================================== LIBRARY: etc1 From 2af1ecd46a986689042b15c4dc1848e8c9a69653 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 11 Mar 2025 13:31:34 +0100 Subject: [PATCH 13/52] Add multiwindow example app (windows) --- examples/multi_window_ref_app/.metadata | 29 ++ .../multi_window_ref_app/.vscode/launch.json | 13 + examples/multi_window_ref_app/README.md | 5 + .../analysis_options.yaml | 28 ++ .../lib/app/main_window.dart | 275 ++++++++++++++++++ .../lib/app/regular_window_content.dart | 208 +++++++++++++ .../lib/app/regular_window_edit_dialog.dart | 86 ++++++ .../lib/app/window_controller_render.dart | 37 +++ .../lib/app/window_manager_model.dart | 51 ++++ .../lib/app/window_settings.dart | 13 + .../lib/app/window_settings_dialog.dart | 90 ++++++ examples/multi_window_ref_app/lib/main.dart | 13 + examples/multi_window_ref_app/pubspec.yaml | 20 ++ .../multi_window_ref_app/windows/.gitignore | 17 ++ .../windows/CMakeLists.txt | 108 +++++++ .../windows/flutter/CMakeLists.txt | 109 +++++++ .../windows/runner/CMakeLists.txt | 37 +++ .../windows/runner/Runner.rc | 121 ++++++++ .../windows/runner/main.cpp | 37 +++ .../windows/runner/resource.h | 20 ++ .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 14 + .../windows/runner/utils.cpp | 68 +++++ .../windows/runner/utils.h | 23 ++ 24 files changed, 1422 insertions(+) create mode 100644 examples/multi_window_ref_app/.metadata create mode 100644 examples/multi_window_ref_app/.vscode/launch.json create mode 100644 examples/multi_window_ref_app/README.md create mode 100644 examples/multi_window_ref_app/analysis_options.yaml create mode 100644 examples/multi_window_ref_app/lib/app/main_window.dart create mode 100644 examples/multi_window_ref_app/lib/app/regular_window_content.dart create mode 100644 examples/multi_window_ref_app/lib/app/regular_window_edit_dialog.dart create mode 100644 examples/multi_window_ref_app/lib/app/window_controller_render.dart create mode 100644 examples/multi_window_ref_app/lib/app/window_manager_model.dart create mode 100644 examples/multi_window_ref_app/lib/app/window_settings.dart create mode 100644 examples/multi_window_ref_app/lib/app/window_settings_dialog.dart create mode 100644 examples/multi_window_ref_app/lib/main.dart create mode 100644 examples/multi_window_ref_app/pubspec.yaml create mode 100644 examples/multi_window_ref_app/windows/.gitignore create mode 100644 examples/multi_window_ref_app/windows/CMakeLists.txt create mode 100644 examples/multi_window_ref_app/windows/flutter/CMakeLists.txt create mode 100644 examples/multi_window_ref_app/windows/runner/CMakeLists.txt create mode 100644 examples/multi_window_ref_app/windows/runner/Runner.rc create mode 100644 examples/multi_window_ref_app/windows/runner/main.cpp create mode 100644 examples/multi_window_ref_app/windows/runner/resource.h create mode 100644 examples/multi_window_ref_app/windows/runner/resources/app_icon.ico create mode 100644 examples/multi_window_ref_app/windows/runner/runner.exe.manifest create mode 100644 examples/multi_window_ref_app/windows/runner/utils.cpp create mode 100644 examples/multi_window_ref_app/windows/runner/utils.h diff --git a/examples/multi_window_ref_app/.metadata b/examples/multi_window_ref_app/.metadata new file mode 100644 index 0000000000000..7608fd25e066a --- /dev/null +++ b/examples/multi_window_ref_app/.metadata @@ -0,0 +1,29 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "4f182c3ce1f3a60a007b034cd5d9f2122ca2e5f1" + channel: "[user-branch]" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 4f182c3ce1f3a60a007b034cd5d9f2122ca2e5f1 + base_revision: 4f182c3ce1f3a60a007b034cd5d9f2122ca2e5f1 + - platform: windows + create_revision: 4f182c3ce1f3a60a007b034cd5d9f2122ca2e5f1 + base_revision: 4f182c3ce1f3a60a007b034cd5d9f2122ca2e5f1 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' diff --git a/examples/multi_window_ref_app/.vscode/launch.json b/examples/multi_window_ref_app/.vscode/launch.json new file mode 100644 index 0000000000000..1121ee8a51e3b --- /dev/null +++ b/examples/multi_window_ref_app/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(Windows) Attach", + "type": "cppvsdbg", + "request": "attach", + } + ] +} \ No newline at end of file diff --git a/examples/multi_window_ref_app/README.md b/examples/multi_window_ref_app/README.md new file mode 100644 index 0000000000000..39b39942c528b --- /dev/null +++ b/examples/multi_window_ref_app/README.md @@ -0,0 +1,5 @@ +# multi_window_ref_app + +A reference application demonstrating multi-window support for Flutter using a +rich semantics windowing API. At the moment, only the Windows platform is +supported. \ No newline at end of file diff --git a/examples/multi_window_ref_app/analysis_options.yaml b/examples/multi_window_ref_app/analysis_options.yaml new file mode 100644 index 0000000000000..0d2902135caec --- /dev/null +++ b/examples/multi_window_ref_app/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/examples/multi_window_ref_app/lib/app/main_window.dart b/examples/multi_window_ref_app/lib/app/main_window.dart new file mode 100644 index 0000000000000..8a79fa349329d --- /dev/null +++ b/examples/multi_window_ref_app/lib/app/main_window.dart @@ -0,0 +1,275 @@ +import 'package:flutter/material.dart'; +import 'package:multi_window_ref_app/app/window_controller_render.dart'; + +import 'regular_window_content.dart'; +import 'window_settings.dart'; +import 'window_settings_dialog.dart'; +import 'window_manager_model.dart'; +import 'regular_window_edit_dialog.dart'; + +class MainWindow extends StatefulWidget { + MainWindow({super.key, required WindowController mainController}) { + _windowManagerModel.add(KeyedWindowController( + isMainWindow: true, key: UniqueKey(), controller: mainController)); + } + + final WindowManagerModel _windowManagerModel = WindowManagerModel(); + final WindowSettings _settings = WindowSettings(); + + @override + State createState() => _MainWindowState(); +} + +class _MainWindowState extends State { + @override + Widget build(BuildContext context) { + final child = Scaffold( + appBar: AppBar( + title: const Text('Multi Window Reference App'), + ), + body: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 60, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: _ActiveWindowsTable( + windowManagerModel: widget._windowManagerModel), + ), + ), + Expanded( + flex: 40, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ListenableBuilder( + listenable: widget._windowManagerModel, + builder: (BuildContext context, Widget? child) { + return _WindowCreatorCard( + selectedWindow: widget._windowManagerModel.selected, + windowManagerModel: widget._windowManagerModel, + windowSettings: widget._settings); + }) + ], + ), + ), + ], + ), + ); + + return ViewAnchor( + view: ListenableBuilder( + listenable: widget._windowManagerModel, + builder: (BuildContext context, Widget? _) { + final List childViews = []; + for (final KeyedWindowController controller + in widget._windowManagerModel.windows) { + if (controller.parent == null && !controller.isMainWindow) { + childViews.add(WindowControllerRender( + controller: controller.controller, + key: controller.key, + windowSettings: widget._settings, + windowManagerModel: widget._windowManagerModel, + onDestroyed: () => + widget._windowManagerModel.remove(controller.key), + onError: () => + widget._windowManagerModel.remove(controller.key), + )); + } + } + + return ViewCollection(views: childViews); + }), + child: child); + } +} + +class _ActiveWindowsTable extends StatelessWidget { + const _ActiveWindowsTable({required this.windowManagerModel}); + + final WindowManagerModel windowManagerModel; + + @override + Widget build(BuildContext context) { + return ListenableBuilder( + listenable: windowManagerModel, + builder: (BuildContext context, Widget? widget) { + return DataTable( + showBottomBorder: true, + onSelectAll: (selected) { + windowManagerModel.select(null); + }, + columns: const [ + DataColumn( + label: SizedBox( + width: 20, + child: Text( + 'ID', + style: TextStyle( + fontSize: 16, + ), + ), + ), + ), + DataColumn( + label: SizedBox( + width: 120, + child: Text( + 'Type', + style: TextStyle( + fontSize: 16, + ), + ), + ), + ), + DataColumn( + label: SizedBox( + width: 20, + child: Text(''), + ), + numeric: true), + ], + rows: (windowManagerModel.windows) + .map((KeyedWindowController controller) { + return DataRow( + key: controller.key, + color: WidgetStateColor.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return Theme.of(context).colorScheme.primary.withAlpha(20); + } + return Colors.transparent; + }), + selected: controller.controller == windowManagerModel.selected, + onSelectChanged: (selected) { + if (selected != null) { + windowManagerModel.select(selected + ? controller.controller.rootView.viewId + : null); + } + }, + cells: [ + DataCell(Text('$controller.controller.rootView.viewId')), + DataCell( + ListenableBuilder( + listenable: controller.controller, + builder: (BuildContext context, Widget? _) => Text( + controller.controller.type + .toString() + .replaceFirst('WindowArchetype.', ''))), + ), + DataCell( + ListenableBuilder( + listenable: controller.controller, + builder: (BuildContext context, Widget? _) => + Row(children: [ + IconButton( + icon: const Icon(Icons.edit_outlined), + onPressed: () { + if (controller.controller.type == + WindowArchetype.regular) { + showRegularWindowEditDialog(context, + initialWidth: + controller.controller.size.width, + initialHeight: + controller.controller.size.height, + initialTitle: "", + initialState: (controller.controller + as RegularWindowController) + .state, + onSave: (double? width, double? height, + String? title, WindowState? state) { + (controller.controller + as RegularWindowController) + .modify( + size: width != null && + height != null + ? Size(width, height) + : null, + title: title, + state: state); + }); + } + }, + ), + IconButton( + icon: const Icon(Icons.delete_outlined), + onPressed: () async { + controller.controller.destroy(); + }, + ) + ])), + ), + ], + ); + }).toList(), + ); + }); + } +} + +class _WindowCreatorCard extends StatelessWidget { + const _WindowCreatorCard( + {required this.selectedWindow, + required this.windowManagerModel, + required this.windowSettings}); + + final WindowController? selectedWindow; + final WindowManagerModel windowManagerModel; + final WindowSettings windowSettings; + + @override + Widget build(BuildContext context) { + return Card.outlined( + margin: const EdgeInsets.symmetric(horizontal: 25), + child: Padding( + padding: const EdgeInsets.fromLTRB(25, 0, 25, 5), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Padding( + padding: EdgeInsets.only(top: 10, bottom: 10), + child: Text( + 'New Window', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16.0, + ), + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + OutlinedButton( + onPressed: () async { + final UniqueKey key = UniqueKey(); + windowManagerModel.add(KeyedWindowController( + key: key, + controller: RegularWindowController( + delegate: WindowControllerDelegate( + onDestroyed: () => windowManagerModel.remove(key), + ), + title: "Regular", + size: windowSettings.regularSize))); + }, + child: const Text('Regular'), + ), + const SizedBox(height: 8), + Container( + alignment: Alignment.bottomRight, + child: TextButton( + child: const Text('SETTINGS'), + onPressed: () { + windowSettingsDialog(context, windowSettings); + }, + ), + ), + const SizedBox(width: 8), + ], + ), + ], + ), + ), + ); + } +} diff --git a/examples/multi_window_ref_app/lib/app/regular_window_content.dart b/examples/multi_window_ref_app/lib/app/regular_window_content.dart new file mode 100644 index 0000000000000..e26e761cedd51 --- /dev/null +++ b/examples/multi_window_ref_app/lib/app/regular_window_content.dart @@ -0,0 +1,208 @@ +import 'package:flutter/material.dart'; +import 'package:multi_window_ref_app/app/window_controller_render.dart'; +import 'package:multi_window_ref_app/app/window_manager_model.dart'; +import 'package:multi_window_ref_app/app/window_settings.dart'; +import 'dart:math'; +import 'package:vector_math/vector_math_64.dart'; + +class RegularWindowContent extends StatefulWidget { + const RegularWindowContent( + {super.key, + required this.window, + required this.windowSettings, + required this.windowManagerModel}); + + final RegularWindowController window; + final WindowSettings windowSettings; + final WindowManagerModel windowManagerModel; + + @override + State createState() => _RegularWindowContentState(); +} + +class WindowControllerDelegate extends RegularWindowControllerDelegate { + WindowControllerDelegate({required this.onDestroyed}); + + @override + void onWindowDestroyed() { + onDestroyed(); + super.onWindowDestroyed(); + } + + final VoidCallback onDestroyed; +} + +class _RegularWindowContentState extends State + with SingleTickerProviderStateMixin { + late final AnimationController _animation; + late final Color cubeColor; + + @override + void initState() { + super.initState(); + _animation = AnimationController( + vsync: this, + lowerBound: 0, + upperBound: 2 * pi, + duration: const Duration(seconds: 15), + )..repeat(); + cubeColor = _generateRandomDarkColor(); + } + + @override + void dispose() { + _animation.dispose(); + super.dispose(); + } + + Color _generateRandomDarkColor() { + final random = Random(); + const int lowerBound = 32; + const int span = 160; + int red = lowerBound + random.nextInt(span); + int green = lowerBound + random.nextInt(span); + int blue = lowerBound + random.nextInt(span); + return Color.fromARGB(255, red, green, blue); + } + + @override + Widget build(BuildContext context) { + final dpr = MediaQuery.of(context).devicePixelRatio; + + final child = Scaffold( + appBar: AppBar(title: Text('${widget.window.type}')), + body: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + AnimatedBuilder( + animation: _animation, + builder: (context, child) { + return CustomPaint( + size: const Size(200, 200), + painter: _RotatedWireCube( + angle: _animation.value, color: cubeColor), + ); + }, + ), + ], + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: () { + final UniqueKey key = UniqueKey(); + widget.windowManagerModel.add(KeyedWindowController( + key: key, + controller: RegularWindowController( + delegate: WindowControllerDelegate( + onDestroyed: () => + widget.windowManagerModel.remove(key), + ), + title: "Regular", + size: widget.windowSettings.regularSize))); + }, + child: const Text('Create Regular Window'), + ), + const SizedBox(height: 20), + ListenableBuilder( + listenable: widget.window, + builder: (BuildContext context, Widget? _) { + return Text( + 'View #${widget.window.rootView.viewId}\n' + 'Size: ${(widget.window.size.width).toStringAsFixed(1)}\u00D7${(widget.window.size.height).toStringAsFixed(1)}\n' + 'Device Pixel Ratio: $dpr', + textAlign: TextAlign.center, + ); + }) + ], + ), + ], + )), + ); + + return ViewAnchor( + view: ListenableBuilder( + listenable: widget.windowManagerModel, + builder: (BuildContext context, Widget? _) { + final List childViews = []; + for (final KeyedWindowController controller + in widget.windowManagerModel.windows) { + if (controller.parent == widget.window) { + childViews.add(WindowControllerRender( + controller: controller.controller, + key: controller.key, + windowSettings: widget.windowSettings, + windowManagerModel: widget.windowManagerModel, + onDestroyed: () => + widget.windowManagerModel.remove(controller.key), + onError: () => + widget.windowManagerModel.remove(controller.key), + )); + } + } + + return ViewCollection(views: childViews); + }), + child: child); + } +} + +class _RotatedWireCube extends CustomPainter { + static List vertices = [ + Vector3(-0.5, -0.5, -0.5), + Vector3(0.5, -0.5, -0.5), + Vector3(0.5, 0.5, -0.5), + Vector3(-0.5, 0.5, -0.5), + Vector3(-0.5, -0.5, 0.5), + Vector3(0.5, -0.5, 0.5), + Vector3(0.5, 0.5, 0.5), + Vector3(-0.5, 0.5, 0.5), + ]; + + static const List> edges = [ + [0, 1], [1, 2], [2, 3], [3, 0], // Front face + [4, 5], [5, 6], [6, 7], [7, 4], // Back face + [0, 4], [1, 5], [2, 6], [3, 7], // Connecting front and back + ]; + + final double angle; + final Color color; + + _RotatedWireCube({required this.angle, required this.color}); + + Offset scaleAndCenter(Vector3 point, double size, Offset center) { + final scale = size / 2; + return Offset(center.dx + point.x * scale, center.dy - point.y * scale); + } + + @override + void paint(Canvas canvas, Size size) { + final rotatedVertices = vertices + .map((vertex) => Matrix4.rotationX(angle).transformed3(vertex)) + .map((vertex) => Matrix4.rotationY(angle).transformed3(vertex)) + .map((vertex) => Matrix4.rotationZ(angle).transformed3(vertex)) + .toList(); + + final center = Offset(size.width / 2, size.height / 2); + + final paint = Paint() + ..color = color + ..style = PaintingStyle.stroke + ..strokeWidth = 2; + + for (var edge in edges) { + final p1 = scaleAndCenter(rotatedVertices[edge[0]], size.width, center); + final p2 = scaleAndCenter(rotatedVertices[edge[1]], size.width, center); + canvas.drawLine(p1, p2, paint); + } + } + + @override + bool shouldRepaint(_RotatedWireCube oldDelegate) => true; +} diff --git a/examples/multi_window_ref_app/lib/app/regular_window_edit_dialog.dart b/examples/multi_window_ref_app/lib/app/regular_window_edit_dialog.dart new file mode 100644 index 0000000000000..f6a5b4b3dd025 --- /dev/null +++ b/examples/multi_window_ref_app/lib/app/regular_window_edit_dialog.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; + +void showRegularWindowEditDialog(BuildContext context, + {double? initialWidth, + double? initialHeight, + String? initialTitle, + WindowState? initialState, + Function(double?, double?, String?, WindowState)? onSave}) { + final TextEditingController widthController = + TextEditingController(text: initialWidth?.toString() ?? ''); + final TextEditingController heightController = + TextEditingController(text: initialHeight?.toString() ?? ''); + final TextEditingController titleController = + TextEditingController(text: initialTitle ?? ''); + + showDialog( + context: context, + builder: (context) { + WindowState selectedState = initialState ?? WindowState.restored; + + return AlertDialog( + title: Text("Edit Window Properties"), + content: StatefulBuilder( + builder: (context, setState) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: widthController, + keyboardType: TextInputType.number, + decoration: InputDecoration(labelText: "Width"), + ), + TextField( + controller: heightController, + keyboardType: TextInputType.number, + decoration: InputDecoration(labelText: "Height"), + ), + TextField( + controller: titleController, + decoration: InputDecoration(labelText: "Title"), + ), + DropdownButton( + value: selectedState, + onChanged: (WindowState? newState) { + if (newState != null) { + setState(() { + selectedState = newState; + }); + } + }, + items: WindowState.values.map((WindowState state) { + return DropdownMenuItem( + value: state, + child: Text( + state.toString().split('.').last[0].toUpperCase() + + state.toString().split('.').last.substring(1), + ), + ); + }).toList(), + ), + ], + ); + }, + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text("Cancel"), + ), + TextButton( + onPressed: () { + double? width = double.tryParse(widthController.text); + double? height = double.tryParse(heightController.text); + String? title = + titleController.text.isEmpty ? null : titleController.text; + + onSave?.call(width, height, title, selectedState); + Navigator.of(context).pop(); + }, + child: Text("Save"), + ), + ], + ); + }, + ); +} diff --git a/examples/multi_window_ref_app/lib/app/window_controller_render.dart b/examples/multi_window_ref_app/lib/app/window_controller_render.dart new file mode 100644 index 0000000000000..1ad041b80be11 --- /dev/null +++ b/examples/multi_window_ref_app/lib/app/window_controller_render.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'regular_window_content.dart'; +import 'window_manager_model.dart'; +import 'window_settings.dart'; + +class WindowControllerRender extends StatelessWidget { + const WindowControllerRender( + {required this.controller, + required this.onDestroyed, + required this.onError, + required this.windowSettings, + required this.windowManagerModel, + required super.key}); + + final WindowController controller; + final VoidCallback onDestroyed; + final VoidCallback onError; + final WindowSettings windowSettings; + final WindowManagerModel windowManagerModel; + + @override + Widget build(BuildContext context) { + switch (controller.type) { + case WindowArchetype.regular: + return RegularWindow( + key: key, + controller: controller as RegularWindowController, + child: RegularWindowContent( + window: controller as RegularWindowController, + windowSettings: windowSettings, + windowManagerModel: windowManagerModel)); + default: + throw UnimplementedError( + "The provided window type does not have an implementation"); + } + } +} diff --git a/examples/multi_window_ref_app/lib/app/window_manager_model.dart b/examples/multi_window_ref_app/lib/app/window_manager_model.dart new file mode 100644 index 0000000000000..27927dd4e413c --- /dev/null +++ b/examples/multi_window_ref_app/lib/app/window_manager_model.dart @@ -0,0 +1,51 @@ +import 'package:flutter/widgets.dart'; + +class KeyedWindowController { + KeyedWindowController( + {this.parent, + this.isMainWindow = false, + required this.key, + required this.controller}); + + final WindowController? parent; + final bool isMainWindow; + final UniqueKey key; + final WindowController controller; +} + +/// Manages a flat list of all of the [WindowController]s that have been +/// created by the application as well as which controller is currently +/// selected by the UI. +class WindowManagerModel extends ChangeNotifier { + final List _windows = []; + List get windows => _windows; + int? _selectedViewId; + WindowController? get selected { + if (_selectedViewId == null) { + return null; + } + + for (final KeyedWindowController controller in _windows) { + if (controller.controller.rootView.viewId == _selectedViewId) { + return controller.controller; + } + } + + return null; + } + + void add(KeyedWindowController window) { + _windows.add(window); + notifyListeners(); + } + + void remove(UniqueKey key) { + _windows.removeWhere((KeyedWindowController window) => window.key == key); + notifyListeners(); + } + + void select(int? viewId) { + _selectedViewId = viewId; + notifyListeners(); + } +} diff --git a/examples/multi_window_ref_app/lib/app/window_settings.dart b/examples/multi_window_ref_app/lib/app/window_settings.dart new file mode 100644 index 0000000000000..8cb0d5ba13498 --- /dev/null +++ b/examples/multi_window_ref_app/lib/app/window_settings.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +class WindowSettings extends ChangeNotifier { + WindowSettings({Size regularSize = const Size(400, 300)}) + : _regularSize = regularSize; + + Size _regularSize; + Size get regularSize => _regularSize; + set regularSize(Size value) { + _regularSize = value; + notifyListeners(); + } +} diff --git a/examples/multi_window_ref_app/lib/app/window_settings_dialog.dart b/examples/multi_window_ref_app/lib/app/window_settings_dialog.dart new file mode 100644 index 0000000000000..2d8fe433b133f --- /dev/null +++ b/examples/multi_window_ref_app/lib/app/window_settings_dialog.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:multi_window_ref_app/app/window_settings.dart'; + +Future windowSettingsDialog( + BuildContext context, WindowSettings settings) async { + return await showDialog( + barrierDismissible: true, + context: context, + builder: (BuildContext ctx) { + return SimpleDialog( + contentPadding: const EdgeInsets.all(4), + titlePadding: const EdgeInsets.fromLTRB(24, 10, 24, 0), + title: const Center( + child: Text('Window Settings'), + ), + children: [ + SizedBox( + width: 600, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded( + child: ListTile( + title: const Text('Regular'), + subtitle: ListenableBuilder( + listenable: settings, + builder: (BuildContext ctx, Widget? _) { + return Row( + children: [ + Expanded( + child: TextFormField( + initialValue: settings.regularSize.width + .toString(), + decoration: const InputDecoration( + labelText: 'Initial width', + ), + onChanged: (String value) => + settings.regularSize = Size( + double.tryParse(value) ?? 0, + settings.regularSize.height), + ), + ), + const SizedBox( + width: 20, + ), + Expanded( + child: TextFormField( + initialValue: settings + .regularSize.height + .toString(), + decoration: const InputDecoration( + labelText: 'Initial height', + ), + onChanged: (String value) => + settings.regularSize = Size( + settings.regularSize.width, + double.tryParse(value) ?? 0), + ), + ), + ], + ); + }), + ), + ), + const SizedBox( + width: 10, + ), + ], + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: TextButton( + onPressed: () { + Navigator.of(context, rootNavigator: true).pop(); + }, + child: const Text('Apply'), + ), + ), + const SizedBox( + height: 2, + ), + ], + ); + }); +} diff --git a/examples/multi_window_ref_app/lib/main.dart b/examples/multi_window_ref_app/lib/main.dart new file mode 100644 index 0000000000000..79332ce7bc8da --- /dev/null +++ b/examples/multi_window_ref_app/lib/main.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; +import 'app/main_window.dart'; + +void main() { + final RegularWindowController controller = RegularWindowController( + size: const Size(800, 600), + sizeConstraints: const BoxConstraints(minWidth: 640, minHeight: 480), + title: "Multi-Window Reference Application", + ); + runWidget(RegularWindow( + controller: controller, + child: MaterialApp(home: MainWindow(mainController: controller)))); +} diff --git a/examples/multi_window_ref_app/pubspec.yaml b/examples/multi_window_ref_app/pubspec.yaml new file mode 100644 index 0000000000000..02e7bcaa02760 --- /dev/null +++ b/examples/multi_window_ref_app/pubspec.yaml @@ -0,0 +1,20 @@ +name: multi_window_ref_app +description: "Reference app for the multi-view windowing API." +version: 1.0.0+1 + +environment: + sdk: '>=3.5.0-180.0.dev <4.0.0' + +dependencies: + flutter: + sdk: flutter + stack_trace: ^1.11.1 + vector_math: ^2.1.4 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^4.0.0 + +flutter: + uses-material-design: true diff --git a/examples/multi_window_ref_app/windows/.gitignore b/examples/multi_window_ref_app/windows/.gitignore new file mode 100644 index 0000000000000..d492d0d98c8fd --- /dev/null +++ b/examples/multi_window_ref_app/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/examples/multi_window_ref_app/windows/CMakeLists.txt b/examples/multi_window_ref_app/windows/CMakeLists.txt new file mode 100644 index 0000000000000..4450980427578 --- /dev/null +++ b/examples/multi_window_ref_app/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(multi_window_ref_app 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 "multi_window_ref_app") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +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() +# Define settings for the Profile build mode. +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. +# +# 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_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() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +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() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# 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/examples/multi_window_ref_app/windows/flutter/CMakeLists.txt b/examples/multi_window_ref_app/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000000000..a71c6e2c5e4f3 --- /dev/null +++ b/examples/multi_window_ref_app/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") +set(CMAKE_CXX_STANDARD 20) + +# 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") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === 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" +) +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" + ${FLUTTER_TARGET_PLATFORM} $ + 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/examples/multi_window_ref_app/windows/runner/CMakeLists.txt b/examples/multi_window_ref_app/windows/runner/CMakeLists.txt new file mode 100644 index 0000000000000..697f43451ac08 --- /dev/null +++ b/examples/multi_window_ref_app/windows/runner/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, 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} WIN32 + "main.cpp" + "utils.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/examples/multi_window_ref_app/windows/runner/Runner.rc b/examples/multi_window_ref_app/windows/runner/Runner.rc new file mode 100644 index 0000000000000..d81714ff69411 --- /dev/null +++ b/examples/multi_window_ref_app/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 +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#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", "The Flutter Authors" "\0" + VALUE "FileDescription", "A reference application demonstrating Flutter's multi-window API." "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "Flutter Multi-Window Reference App" "\0" + VALUE "LegalCopyright", "Copyright 2014 The Flutter Authors. All rights reserved." "\0" + VALUE "OriginalFilename", "multi_window_ref_app.exe" "\0" + VALUE "ProductName", "Flutter Multi-Window Reference App" "\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/examples/multi_window_ref_app/windows/runner/main.cpp b/examples/multi_window_ref_app/windows/runner/main.cpp new file mode 100644 index 0000000000000..6f944fa05a109 --- /dev/null +++ b/examples/multi_window_ref_app/windows/runner/main.cpp @@ -0,0 +1,37 @@ +#include +#include +#include + +#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); + + flutter::DartProject project(L"data"); + + auto command_line_arguments{GetCommandLineArguments()}; + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + auto const engine{std::make_shared(project)}; + RegisterPlugins(engine.get()); + engine->Run(); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/examples/multi_window_ref_app/windows/runner/resource.h b/examples/multi_window_ref_app/windows/runner/resource.h new file mode 100644 index 0000000000000..c245ff19cb580 --- /dev/null +++ b/examples/multi_window_ref_app/windows/runner/resource.h @@ -0,0 +1,20 @@ +// Copyright 2014 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. + +//{{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/examples/multi_window_ref_app/windows/runner/resources/app_icon.ico b/examples/multi_window_ref_app/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/examples/multi_window_ref_app/windows/runner/runner.exe.manifest b/examples/multi_window_ref_app/windows/runner/runner.exe.manifest new file mode 100644 index 0000000000000..153653e8d67f8 --- /dev/null +++ b/examples/multi_window_ref_app/windows/runner/runner.exe.manifest @@ -0,0 +1,14 @@ + + + + + PerMonitorV2 + + + + + + + + + diff --git a/examples/multi_window_ref_app/windows/runner/utils.cpp b/examples/multi_window_ref_app/windows/runner/utils.cpp new file mode 100644 index 0000000000000..6abcd65042070 --- /dev/null +++ b/examples/multi_window_ref_app/windows/runner/utils.cpp @@ -0,0 +1,68 @@ +// Copyright 2014 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 "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(); + } + unsigned int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return 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/examples/multi_window_ref_app/windows/runner/utils.h b/examples/multi_window_ref_app/windows/runner/utils.h new file mode 100644 index 0000000000000..54414c989ba71 --- /dev/null +++ b/examples/multi_window_ref_app/windows/runner/utils.h @@ -0,0 +1,23 @@ +// Copyright 2014 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 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_ From 0eb6f6330a9556ad371d907e497198954a612959 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 11 Mar 2025 13:33:34 +0100 Subject: [PATCH 14/52] Add multiwindow switch --- engine/src/flutter/common/settings.h | 4 ++++ engine/src/flutter/shell/common/switches.cc | 10 ++++++++++ engine/src/flutter/shell/common/switches.h | 4 ++++ .../shell/platform/windows/flutter_windows_engine.cc | 4 ++++ .../shell/platform/windows/flutter_windows_engine.h | 2 ++ packages/flutter_tools/lib/src/commands/run.dart | 4 ++++ packages/flutter_tools/lib/src/commands/test.dart | 1 + .../lib/src/custom_devices/custom_device.dart | 2 ++ packages/flutter_tools/lib/src/desktop_device.dart | 3 +++ packages/flutter_tools/lib/src/device.dart | 7 +++++++ .../flutter_tools/lib/src/runner/flutter_command.dart | 9 +++++++++ 11 files changed, 50 insertions(+) diff --git a/engine/src/flutter/common/settings.h b/engine/src/flutter/common/settings.h index 1c5a726067e3e..2445e4d67edd2 100644 --- a/engine/src/flutter/common/settings.h +++ b/engine/src/flutter/common/settings.h @@ -376,6 +376,10 @@ struct Settings { MergedPlatformUIThread merged_platform_ui_thread = MergedPlatformUIThread::kEnabled; + + // Enable support for multiple windows. Ignored if not supported on the + // platform. + bool enable_multi_window = false; }; } // namespace flutter diff --git a/engine/src/flutter/shell/common/switches.cc b/engine/src/flutter/shell/common/switches.cc index a653b57f0e527..bba735c907592 100644 --- a/engine/src/flutter/shell/common/switches.cc +++ b/engine/src/flutter/shell/common/switches.cc @@ -556,6 +556,16 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { command_line.HasOption(FlagForSwitch(Switch::ImpellerLazyShaderMode)); settings.impeller_antialiased_lines = command_line.HasOption(FlagForSwitch(Switch::ImpellerAntialiasLines)); +#if FML_OS_WIN + // Process the EnableMultiWindow switch on Windows. + { + std::string enable_multi_window_value; + if (command_line.GetOptionValue(FlagForSwitch(Switch::EnableMultiWindow), + &enable_multi_window_value)) { + settings.enable_multi_window = "true" == enable_multi_window_value; + } + } +#endif // FML_OS_WIN return settings; } diff --git a/engine/src/flutter/shell/common/switches.h b/engine/src/flutter/shell/common/switches.h index 238d890a962ed..487093f41c982 100644 --- a/engine/src/flutter/shell/common/switches.h +++ b/engine/src/flutter/shell/common/switches.h @@ -302,6 +302,10 @@ DEF_SWITCH(MergedPlatformUIThread, DEF_SWITCH(DisableMergedPlatformUIThread, "no-enable-merged-platform-ui-thread", "Merge the ui thread and platform thread.") +DEF_SWITCH(EnableMultiWindow, + "enable-multi-window", + "Enable support for multiple windows. Ignored if not supported on " + "the platform.") DEF_SWITCH(EnableAndroidSurfaceControl, "enable-surface-control", "Enable the SurfaceControl backed swapchain when supported.") diff --git a/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc b/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc index 3b6d76db1852a..f409043d6284b 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc +++ b/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc @@ -195,6 +195,10 @@ FlutterWindowsEngine::FlutterWindowsEngine( enable_impeller_ = std::find(switches.begin(), switches.end(), "--enable-impeller=true") != switches.end(); + enable_multi_window_ = + std::find(switches.begin(), switches.end(), + "--enable-multi-window=true") != switches.end(); + egl_manager_ = egl::Manager::Create( static_cast(project_->gpu_preference())); window_proc_delegate_manager_ = std::make_unique(); diff --git a/engine/src/flutter/shell/platform/windows/flutter_windows_engine.h b/engine/src/flutter/shell/platform/windows/flutter_windows_engine.h index 2fa8784054606..706bff3869249 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_windows_engine.h +++ b/engine/src/flutter/shell/platform/windows/flutter_windows_engine.h @@ -481,6 +481,8 @@ class FlutterWindowsEngine { bool enable_impeller_ = false; + bool enable_multi_window_ = false; + // The manager for WindowProc delegate registration and callbacks. std::unique_ptr window_proc_delegate_manager_; diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index 8f6e89eae721d..c85d80d22f835 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart @@ -220,6 +220,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment addEnableImpellerFlag(verboseHelp: verboseHelp); addEnableVulkanValidationFlag(verboseHelp: verboseHelp); addEnableEmbedderApiFlag(verboseHelp: verboseHelp); + addMultiWindowFlag(verboseHelp: verboseHelp); } bool get traceStartup => boolArg('trace-startup'); @@ -235,6 +236,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment bool get enableVulkanValidation => boolArg('enable-vulkan-validation'); bool get uninstallFirst => boolArg('uninstall-first'); bool get enableEmbedderApi => boolArg('enable-embedder-api'); + bool get enableMultiWindow => boolArg('enable-multi-window'); @override bool get refreshWirelessDevices => true; @@ -301,6 +303,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment uninstallFirst: uninstallFirst, enableDartProfiling: enableDartProfiling, enableEmbedderApi: enableEmbedderApi, + enableMultiWindow: enableMultiWindow, usingCISystem: usingCISystem, debugLogsDirectoryPath: debugLogsDirectoryPath, ); @@ -366,6 +369,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment serveObservatory: boolArg('serve-observatory'), enableDartProfiling: enableDartProfiling, enableEmbedderApi: enableEmbedderApi, + enableMultiWindow: enableMultiWindow, usingCISystem: usingCISystem, debugLogsDirectoryPath: debugLogsDirectoryPath, enableDevTools: boolArg(FlutterCommand.kEnableDevTools), diff --git a/packages/flutter_tools/lib/src/commands/test.dart b/packages/flutter_tools/lib/src/commands/test.dart index 96ee6d8632ec3..8472b63bc9336 100644 --- a/packages/flutter_tools/lib/src/commands/test.dart +++ b/packages/flutter_tools/lib/src/commands/test.dart @@ -78,6 +78,7 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts { usesDeviceUserOption(); usesFlavorOption(); addEnableImpellerFlag(verboseHelp: verboseHelp); + addMultiWindowFlag(verboseHelp: verboseHelp); argParser ..addFlag( diff --git a/packages/flutter_tools/lib/src/custom_devices/custom_device.dart b/packages/flutter_tools/lib/src/custom_devices/custom_device.dart index f362f7aface38..60940c32e7109 100644 --- a/packages/flutter_tools/lib/src/custom_devices/custom_device.dart +++ b/packages/flutter_tools/lib/src/custom_devices/custom_device.dart @@ -306,6 +306,8 @@ class CustomDeviceAppSession { if (debuggingOptions.useTestFonts) 'use-test-fonts=true', if (debuggingOptions.verboseSystemLogs) 'verbose-logging=true', ], + if (debuggingOptions.enableMultiWindow) + 'enable-multi-window=true', ]; } diff --git a/packages/flutter_tools/lib/src/desktop_device.dart b/packages/flutter_tools/lib/src/desktop_device.dart index a1349adc580cf..056b53e00e7cf 100644 --- a/packages/flutter_tools/lib/src/desktop_device.dart +++ b/packages/flutter_tools/lib/src/desktop_device.dart @@ -295,6 +295,9 @@ abstract class DesktopDevice extends Device { case ImpellerStatus.platformDefault: addFlag('enable-impeller=false'); } + if (debuggingOptions.enableMultiWindow) { + addFlag('enable-multi-window=true'); + } // Options only supported when there is a VM Service connection between the // tool and the device, usually in debug or profile mode. if (debuggingOptions.debuggingEnabled) { diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 3cdf50eaf8d94..ac7f213be6422 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -971,6 +971,7 @@ class DebuggingOptions { this.serveObservatory = false, this.enableDartProfiling = true, this.enableEmbedderApi = false, + this.enableMultiWindow = false, this.usingCISystem = false, this.debugLogsDirectoryPath, this.enableDevTools = true, @@ -1004,6 +1005,7 @@ class DebuggingOptions { this.uninstallFirst = false, this.enableDartProfiling = true, this.enableEmbedderApi = false, + this.enableMultiWindow = false, this.usingCISystem = false, this.debugLogsDirectoryPath, }) : debuggingEnabled = false, @@ -1088,6 +1090,7 @@ class DebuggingOptions { required this.serveObservatory, required this.enableDartProfiling, required this.enableEmbedderApi, + required this.enableMultiWindow, required this.usingCISystem, required this.debugLogsDirectoryPath, required this.enableDevTools, @@ -1134,6 +1137,7 @@ class DebuggingOptions { final bool serveObservatory; final bool enableDartProfiling; final bool enableEmbedderApi; + final bool enableMultiWindow; final bool usingCISystem; final String? debugLogsDirectoryPath; final bool enableDevTools; @@ -1233,6 +1237,7 @@ class DebuggingOptions { if (interfaceType == DeviceConnectionInterface.wireless) '--vm-service-host=${ipv6 ? '::0' : '0.0.0.0'}', if (enableEmbedderApi) '--enable-embedder-api', + if (enableMultiWindow) '--enable-multi-window=true', ]; } @@ -1284,6 +1289,7 @@ class DebuggingOptions { 'serveObservatory': serveObservatory, 'enableDartProfiling': enableDartProfiling, 'enableEmbedderApi': enableEmbedderApi, + 'enableMultiWindow': enableMultiWindow, 'usingCISystem': usingCISystem, 'debugLogsDirectoryPath': debugLogsDirectoryPath, 'enableDevTools': enableDevTools, @@ -1354,6 +1360,7 @@ class DebuggingOptions { serveObservatory: (json['serveObservatory'] as bool?) ?? false, enableDartProfiling: (json['enableDartProfiling'] as bool?) ?? true, enableEmbedderApi: (json['enableEmbedderApi'] as bool?) ?? false, + enableMultiWindow: (json['enableMultiWindow']! as bool?) ?? false, usingCISystem: (json['usingCISystem'] as bool?) ?? false, debugLogsDirectoryPath: json['debugLogsDirectoryPath'] as String?, enableDevTools: (json['enableDevTools'] as bool?) ?? true, diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 17ab07e321684..9966d67e22544 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -1290,6 +1290,15 @@ abstract class FlutterCommand extends Command { ); } + void addMultiWindowFlag({required bool verboseHelp}) { + argParser.addFlag('enable-multi-window', + hide: !verboseHelp, + help: 'Whether to enable support for multiple windows. ' + 'This flag is only available on Windows, is disabled by default, ' + 'and will be ignored on other platforms.', + ); + } + /// Returns a [FlutterProject] view of the current directory or a ToolExit error, /// if `pubspec.yaml` or `example/pubspec.yaml` is invalid. FlutterProject get project => FlutterProject.current(); From bf6c7b8f9c2fb52479d1a2da97951d24ab01df9f Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 11 Mar 2025 13:37:04 +0100 Subject: [PATCH 15/52] Windows implementation --- .../flutter/shell/platform/common/BUILD.gn | 12 + .../flutter/shell/platform/common/geometry.h | 1 + .../shell/platform/common/isolate_scope.h | 49 ++ .../flutter/shell/platform/common/windowing.h | 105 ++++ .../flutter/shell/platform/windows/BUILD.gn | 6 + .../platform/windows/flutter_host_window.cc | 561 ++++++++++++++++++ .../platform/windows/flutter_host_window.h | 92 +++ .../windows/flutter_host_window_controller.cc | 228 +++++++ .../windows/flutter_host_window_controller.h | 84 +++ ...lutter_host_window_controller_unittests.cc | 347 +++++++++++ .../windows/flutter_windows_engine.cc | 37 +- .../platform/windows/flutter_windows_engine.h | 13 + packages/flutter/.vscode/settings.json | 2 + packages/flutter/lib/src/widgets/binding.dart | 17 + packages/flutter/lib/src/widgets/window.dart | 290 ++++++++- .../flutter/lib/src/widgets/window_win32.dart | 302 ++++++++++ packages/flutter/lib/widgets.dart | 1 + packages/flutter/pubspec.yaml | 1 + .../flutter/test/widgets/window_test.dart | 276 +++++++++ 19 files changed, 2419 insertions(+), 5 deletions(-) create mode 100644 engine/src/flutter/shell/platform/common/isolate_scope.h create mode 100644 engine/src/flutter/shell/platform/common/windowing.h create mode 100644 engine/src/flutter/shell/platform/windows/flutter_host_window.cc create mode 100644 engine/src/flutter/shell/platform/windows/flutter_host_window.h create mode 100644 engine/src/flutter/shell/platform/windows/flutter_host_window_controller.cc create mode 100644 engine/src/flutter/shell/platform/windows/flutter_host_window_controller.h create mode 100644 engine/src/flutter/shell/platform/windows/flutter_host_window_controller_unittests.cc create mode 100644 packages/flutter/.vscode/settings.json create mode 100644 packages/flutter/lib/src/widgets/window_win32.dart create mode 100644 packages/flutter/test/widgets/window_test.dart diff --git a/engine/src/flutter/shell/platform/common/BUILD.gn b/engine/src/flutter/shell/platform/common/BUILD.gn index 5ebfb2236d2c5..5a5f03272666c 100644 --- a/engine/src/flutter/shell/platform/common/BUILD.gn +++ b/engine/src/flutter/shell/platform/common/BUILD.gn @@ -57,6 +57,15 @@ source_set("common_cpp_input") { deps = [ "//flutter/fml:fml" ] } +source_set("common_cpp_isolate_scope") { + public = [ "isolate_scope.h" ] + + deps = [ + "$dart_src/runtime:dart_api", + "//flutter/fml:fml", + ] +} + source_set("common_cpp_enums") { public = [ "app_lifecycle_state.h", @@ -142,11 +151,14 @@ source_set("common_cpp_core") { public = [ "geometry.h", "path_utils.h", + "windowing.h", ] sources = [ "path_utils.cc" ] public_configs = [ "//flutter:config" ] + + deps = [ "//flutter/fml:fml" ] } if (enable_unittests) { diff --git a/engine/src/flutter/shell/platform/common/geometry.h b/engine/src/flutter/shell/platform/common/geometry.h index 4d6e8daded041..89441f9710bba 100644 --- a/engine/src/flutter/shell/platform/common/geometry.h +++ b/engine/src/flutter/shell/platform/common/geometry.h @@ -45,6 +45,7 @@ class Size { bool operator==(const Size& other) const { return width_ == other.width_ && height_ == other.height_; } + bool operator!=(const Size& other) const { return !(*this == other); } private: double width_ = 0.0; diff --git a/engine/src/flutter/shell/platform/common/isolate_scope.h b/engine/src/flutter/shell/platform/common/isolate_scope.h new file mode 100644 index 0000000000000..a81d75091ab3c --- /dev/null +++ b/engine/src/flutter/shell/platform/common/isolate_scope.h @@ -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 "flutter/fml/logging.h" +#include "third_party/dart/runtime/include/dart_api.h" + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_ISOLATE_SCOPE_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_ISOLATE_SCOPE_H_ + +namespace flutter { + +class Isolate { + public: + Isolate() : isolate_(Dart_CurrentIsolate()) { + FML_DCHECK(isolate_ != nullptr); + } + ~Isolate() = default; + + private: + friend class IsolateScope; + Dart_Isolate isolate_; +}; + +class IsolateScope { + public: + explicit IsolateScope(const Isolate& isolate) { + if (Dart_CurrentIsolate() == nullptr) { + Dart_EnterIsolate(isolate.isolate_); + should_exit_isolate_ = true; + } else { + FML_DCHECK(Dart_CurrentIsolate() == isolate.isolate_); + } + }; + + ~IsolateScope() { + if (should_exit_isolate_) { + Dart_ExitIsolate(); + } + } + + private: + bool should_exit_isolate_ = false; + IsolateScope() = delete; + IsolateScope(IsolateScope const&) = delete; +}; +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_ISOLATE_SCOPE_H_ diff --git a/engine/src/flutter/shell/platform/common/windowing.h b/engine/src/flutter/shell/platform/common/windowing.h new file mode 100644 index 0000000000000..4e17d53c32263 --- /dev/null +++ b/engine/src/flutter/shell/platform/common/windowing.h @@ -0,0 +1,105 @@ +// 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_SHELL_PLATFORM_COMMON_WINDOWING_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_WINDOWING_H_ + +#include + +#include "flutter/fml/logging.h" +#include "geometry.h" + +namespace flutter { + +// A unique identifier for a view. +using FlutterViewId = int64_t; + +// Types of windows. +enum class WindowArchetype { + // Regular top-level window. + kRegular, +}; + +// Possible states a window can be in. +enum class WindowState { + // Normal state, neither maximized, nor minimized. + kRestored, + // Maximized, occupying the full screen but still showing the system UI. + kMaximized, + // Minimized and not visible on the screen. + kMinimized, +}; + +// Converts a |flutter::WindowState| to the string representation of +// |WindowState| as defined in the framework. +inline std::string WindowStateToString(WindowState state) { + switch (state) { + case WindowState::kRestored: + return "WindowState.restored"; + case WindowState::kMaximized: + return "WindowState.maximized"; + case WindowState::kMinimized: + return "WindowState.minimized"; + default: + FML_UNREACHABLE(); + } +} + +// Converts the string representation of |WindowState| defined in the framework +// to a |flutter::WindowState|. Returns std::nullopt if the given string is +// invalid. +inline std::optional StringToWindowState(std::string_view str) { + if (str == "WindowState.restored") + return WindowState::kRestored; + if (str == "WindowState.maximized") + return WindowState::kMaximized; + if (str == "WindowState.minimized") + return WindowState::kMinimized; + return std::nullopt; +} + +// Settings used for creating a Flutter window. +struct WindowCreationSettings { + // Type of the window. + WindowArchetype archetype = WindowArchetype::kRegular; + // Requested size of the window's client area, in logical coordinates. + Size size; + // Minimum size of the window's client area, in logical coordinates. + std::optional min_size; + // Maximum size of the window's client area, in logical coordinates. + std::optional max_size; + // Window title. + std::optional title; + // Initial state of the window. + std::optional state; +}; + +// Settings for modifying a Flutter window. +struct WindowModificationSettings { + // The new requested size, in logical coordinates. + std::optional size; + // The new window title. + std::optional title; + // The new window state. + std::optional state; +}; + +// Window metadata returned as the result of creating a Flutter window. +struct WindowMetadata { + // The ID of the view used for this window, which is unique to each window. + FlutterViewId view_id = 0; + // The type of the window. + WindowArchetype archetype = WindowArchetype::kRegular; + // Size of the created window, in logical coordinates. + Size size; + // The ID of the view used by the parent window. If not set, the window is + // assumed a top-level window. + std::optional parent_id; + // The initial state of the window, or std::nullopt if not a regular window. + std::optional state; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_WINDOWING_H_ diff --git a/engine/src/flutter/shell/platform/windows/BUILD.gn b/engine/src/flutter/shell/platform/windows/BUILD.gn index e80662e592a72..7625f8beae96f 100644 --- a/engine/src/flutter/shell/platform/windows/BUILD.gn +++ b/engine/src/flutter/shell/platform/windows/BUILD.gn @@ -73,6 +73,10 @@ source_set("flutter_windows_source") { "external_texture_d3d.h", "external_texture_pixelbuffer.cc", "external_texture_pixelbuffer.h", + "flutter_host_window.cc", + "flutter_host_window.h", + "flutter_host_window_controller.cc", + "flutter_host_window_controller.h", "flutter_key_map.g.cc", "flutter_platform_node_delegate_windows.cc", "flutter_platform_node_delegate_windows.h", @@ -158,6 +162,7 @@ source_set("flutter_windows_source") { "//flutter/impeller/renderer/backend/gles", "//flutter/shell/platform/common:common_cpp", "//flutter/shell/platform/common:common_cpp_input", + "//flutter/shell/platform/common:common_cpp_isolate_scope", "//flutter/shell/platform/common:common_cpp_switches", "//flutter/shell/platform/common/client_wrapper:client_wrapper", "//flutter/shell/platform/embedder:embedder_as_internal_library", @@ -202,6 +207,7 @@ executable("flutter_windows_unittests") { "cursor_handler_unittests.cc", "direct_manipulation_unittests.cc", "dpi_utils_unittests.cc", + "flutter_host_window_controller_unittests.cc", "flutter_project_bundle_unittests.cc", "flutter_window_unittests.cc", "flutter_windows_engine_unittests.cc", diff --git a/engine/src/flutter/shell/platform/windows/flutter_host_window.cc b/engine/src/flutter/shell/platform/windows/flutter_host_window.cc new file mode 100644 index 0000000000000..e04a5ab8ffa41 --- /dev/null +++ b/engine/src/flutter/shell/platform/windows/flutter_host_window.cc @@ -0,0 +1,561 @@ +// 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 "flutter/shell/platform/windows/flutter_host_window.h" + +#include + +#include "flutter/shell/platform/windows/dpi_utils.h" +#include "flutter/shell/platform/windows/flutter_host_window_controller.h" +#include "flutter/shell/platform/windows/flutter_window.h" +#include "flutter/shell/platform/windows/flutter_windows_view_controller.h" + +namespace { + +constexpr wchar_t kWindowClassName[] = L"FLUTTER_HOST_WINDOW"; + +// Clamps |size| to the size of the virtual screen. Both the parameter and +// return size are in physical coordinates. +flutter::Size ClampToVirtualScreen(flutter::Size size) { + double const virtual_screen_width = GetSystemMetrics(SM_CXVIRTUALSCREEN); + double const virtual_screen_height = GetSystemMetrics(SM_CYVIRTUALSCREEN); + + return flutter::Size(std::clamp(size.width(), 0.0, virtual_screen_width), + std::clamp(size.height(), 0.0, virtual_screen_height)); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module +// so that the non-client area automatically responds to changes in DPI. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + + using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + + 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); +} + +// Dynamically loads |SetWindowCompositionAttribute| from the User32 module to +// make the window's background transparent. +void EnableTransparentWindowBackground(HWND hwnd) { + HMODULE const user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + + enum WINDOWCOMPOSITIONATTRIB { WCA_ACCENT_POLICY = 19 }; + + struct WINDOWCOMPOSITIONATTRIBDATA { + WINDOWCOMPOSITIONATTRIB Attrib; + PVOID pvData; + SIZE_T cbData; + }; + + using SetWindowCompositionAttribute = + BOOL(__stdcall*)(HWND, WINDOWCOMPOSITIONATTRIBDATA*); + + auto set_window_composition_attribute = + reinterpret_cast( + GetProcAddress(user32_module, "SetWindowCompositionAttribute")); + if (set_window_composition_attribute != nullptr) { + enum ACCENT_STATE { ACCENT_DISABLED = 0 }; + + struct ACCENT_POLICY { + ACCENT_STATE AccentState; + DWORD AccentFlags; + DWORD GradientColor; + DWORD AnimationId; + }; + + // Set the accent policy to disable window composition. + ACCENT_POLICY accent = {ACCENT_DISABLED, 2, static_cast(0), 0}; + WINDOWCOMPOSITIONATTRIBDATA data = {.Attrib = WCA_ACCENT_POLICY, + .pvData = &accent, + .cbData = sizeof(accent)}; + set_window_composition_attribute(hwnd, &data); + + // Extend the frame into the client area and set the window's system + // backdrop type for visual effects. + MARGINS const margins = {-1}; + ::DwmExtendFrameIntoClientArea(hwnd, &margins); + INT effect_value = 1; + ::DwmSetWindowAttribute(hwnd, DWMWA_SYSTEMBACKDROP_TYPE, &effect_value, + sizeof(BOOL)); + } + + FreeLibrary(user32_module); +} + +// Retrieves the calling thread's last-error code message as a string, +// or a fallback message if the error message cannot be formatted. +std::string GetLastErrorAsString() { + LPWSTR message_buffer = nullptr; + + if (DWORD const size = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&message_buffer), 0, nullptr)) { + std::wstring const wide_message(message_buffer, size); + LocalFree(message_buffer); + message_buffer = nullptr; + + if (int const buffer_size = + WideCharToMultiByte(CP_UTF8, 0, wide_message.c_str(), -1, nullptr, + 0, nullptr, nullptr)) { + std::string message(buffer_size, 0); + WideCharToMultiByte(CP_UTF8, 0, wide_message.c_str(), -1, &message[0], + buffer_size, nullptr, nullptr); + return message; + } + } + + if (message_buffer) { + LocalFree(message_buffer); + } + std::ostringstream oss; + oss << "Format message failed with 0x" << std::hex << std::setfill('0') + << std::setw(8) << GetLastError(); + return oss.str(); +} + +// Calculates the required window size, in physical coordinates, to +// accommodate the given |client_size|, in logical coordinates, constrained by +// optional |min_size| and |max_size|, for a window with the specified +// |window_style| and |extended_window_style|. If |owner_hwnd| is not null, the +// DPI of the display with the largest area of intersection with |owner_hwnd| is +// used for the calculation; otherwise, the primary display's DPI is used. The +// resulting size includes window borders, non-client areas, and drop shadows. +// On error, returns std::nullopt and logs an error message. +std::optional GetWindowSizeForClientSize( + flutter::Size const& client_size, + std::optional min_size, + std::optional max_size, + DWORD window_style, + DWORD extended_window_style, + HWND owner_hwnd) { + UINT const dpi = flutter::GetDpiForHWND(owner_hwnd); + double const scale_factor = + static_cast(dpi) / USER_DEFAULT_SCREEN_DPI; + RECT rect = { + .right = static_cast(client_size.width() * scale_factor), + .bottom = static_cast(client_size.height() * scale_factor)}; + + HMODULE const user32_raw = LoadLibraryA("User32.dll"); + auto free_user32_module = [](HMODULE module) { FreeLibrary(module); }; + std::unique_ptr, decltype(free_user32_module)> + user32_module(user32_raw, free_user32_module); + if (!user32_module) { + FML_LOG(ERROR) << "Failed to load User32.dll.\n"; + return std::nullopt; + } + + using AdjustWindowRectExForDpi = BOOL __stdcall( + LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi); + auto* const adjust_window_rect_ext_for_dpi = + reinterpret_cast( + GetProcAddress(user32_raw, "AdjustWindowRectExForDpi")); + if (!adjust_window_rect_ext_for_dpi) { + FML_LOG(ERROR) << "Failed to retrieve AdjustWindowRectExForDpi address " + "from User32.dll."; + return std::nullopt; + } + + if (!adjust_window_rect_ext_for_dpi(&rect, window_style, FALSE, + extended_window_style, dpi)) { + FML_LOG(ERROR) << "Failed to run AdjustWindowRectExForDpi: " + << GetLastErrorAsString(); + return std::nullopt; + } + + double width = static_cast(rect.right - rect.left); + double height = static_cast(rect.bottom - rect.top); + + // Apply size constraints + double const non_client_width = width - (client_size.width() * scale_factor); + double const non_client_height = + height - (client_size.height() * scale_factor); + if (min_size) { + flutter::Size min_physical_size = ClampToVirtualScreen( + flutter::Size(min_size->width() * scale_factor + non_client_width, + min_size->height() * scale_factor + non_client_height)); + width = std::max(width, min_physical_size.width()); + height = std::max(height, min_physical_size.height()); + } + if (max_size) { + flutter::Size max_physical_size = ClampToVirtualScreen( + flutter::Size(max_size->width() * scale_factor + non_client_width, + max_size->height() * scale_factor + non_client_height)); + width = std::min(width, max_physical_size.width()); + height = std::min(height, max_physical_size.height()); + } + + return flutter::Size{width, height}; +} + +// Checks whether the window class of name |class_name| is registered for the +// current application. +bool IsClassRegistered(LPCWSTR class_name) { + WNDCLASSEX window_class = {}; + return GetClassInfoEx(GetModuleHandle(nullptr), class_name, &window_class) != + 0; +} + +// Converts std::string to std::wstring. +std::wstring StringToWstring(std::string_view str) { + if (str.empty()) { + return {}; + } + if (int buffer_size = + MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), nullptr, 0)) { + std::wstring wide_str(buffer_size, L'\0'); + if (MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), &wide_str[0], + buffer_size)) { + return wide_str; + } + } + return {}; +} + +// Window attribute that enables dark mode window decorations. +// +// Redefined in case the developer's machine has a Windows SDK older than +// version 10.0.22000.0. +// See: +// https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +// Updates the window frame's theme to match the system theme. +void UpdateTheme(HWND window) { + // Registry key for app theme preference. + const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; + const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + + // A value of 0 indicates apps should use dark mode. A non-zero or missing + // value indicates apps should use light mode. + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS const result = + RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, RRF_RT_REG_DWORD, nullptr, + &light_mode, &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} + +} // namespace + +namespace flutter { + +FlutterHostWindow::FlutterHostWindow(FlutterHostWindowController* controller, + WindowCreationSettings const& settings) + : window_controller_(controller) { + archetype_ = settings.archetype; + + // Check preconditions and set window styles based on window type. + DWORD window_style = 0; + DWORD extended_window_style = 0; + switch (archetype_) { + case WindowArchetype::kRegular: + window_style |= WS_OVERLAPPEDWINDOW; + break; + default: + FML_UNREACHABLE(); + } + + // Validate size constraints. + min_size_ = settings.min_size; + max_size_ = settings.max_size; + if (min_size_ && max_size_) { + if (min_size_->width() > max_size_->width() || + min_size_->height() > max_size_->height()) { + FML_LOG(ERROR) << "Invalid size constraints."; + return; + } + } + + // Calculate the screen space window rectangle for the new window. + // Default positioning values (CW_USEDEFAULT) are used + // if the window has no owner. + Rect const initial_window_rect = [&]() -> Rect { + std::optional const window_size = GetWindowSizeForClientSize( + settings.size, min_size_, max_size_, window_style, + extended_window_style, nullptr); + return {{CW_USEDEFAULT, CW_USEDEFAULT}, + window_size ? *window_size : Size{CW_USEDEFAULT, CW_USEDEFAULT}}; + }(); + + // Set up the view. + FlutterWindowsEngine* const engine = window_controller_->engine(); + auto view_window = std::make_unique( + initial_window_rect.width(), initial_window_rect.height(), + engine->windows_proc_table()); + + std::unique_ptr view = + engine->CreateView(std::move(view_window)); + if (!view) { + FML_LOG(ERROR) << "Failed to create view"; + return; + } + + view_controller_ = + std::make_unique(nullptr, std::move(view)); + FML_CHECK(engine->running()); + // Must happen after engine is running. + view_controller_->view()->SendInitialBounds(); + // The Windows embedder listens to accessibility updates using the + // view's HWND. The embedder's accessibility features may be stale if + // the app was in headless mode. + view_controller_->engine()->UpdateAccessibilityFeatures(); + + // Ensure that basic setup of the view controller was successful. + if (!view_controller_->view()) { + FML_LOG(ERROR) << "Failed to set up the view controller"; + return; + } + + // Register the window class. + if (!IsClassRegistered(kWindowClassName)) { + auto const idi_app_icon = 101; + WNDCLASSEX window_class = {}; + window_class.cbSize = sizeof(WNDCLASSEX); + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.lpfnWndProc = FlutterHostWindow::WndProc; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(idi_app_icon)); + if (!window_class.hIcon) { + window_class.hIcon = LoadIcon(nullptr, IDI_APPLICATION); + } + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + + if (!RegisterClassEx(&window_class)) { + FML_LOG(ERROR) << "Cannot register window class " << kWindowClassName + << ": " << GetLastErrorAsString(); + return; + } + } + + // Create the native window. + HWND hwnd = CreateWindowEx( + extended_window_style, kWindowClassName, + StringToWstring(settings.title.value_or("")).c_str(), window_style, + initial_window_rect.left(), initial_window_rect.top(), + initial_window_rect.width(), initial_window_rect.height(), nullptr, + nullptr, GetModuleHandle(nullptr), this); + + if (!hwnd) { + FML_LOG(ERROR) << "Cannot create window: " << GetLastErrorAsString(); + return; + } + + // Adjust the window position so its origin aligns with the top-left corner + // of the window frame, not the window rectangle (which includes the + // drop-shadow). This adjustment must be done post-creation since the frame + // rectangle is only available after the window has been created. + RECT frame_rect; + DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frame_rect, + sizeof(frame_rect)); + RECT window_rect; + GetWindowRect(hwnd, &window_rect); + LONG const left_dropshadow_width = frame_rect.left - window_rect.left; + LONG const top_dropshadow_height = window_rect.top - frame_rect.top; + SetWindowPos(hwnd, nullptr, window_rect.left - left_dropshadow_width, + window_rect.top - top_dropshadow_height, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + + UpdateTheme(hwnd); + + SetChildContent(view_controller_->view()->GetWindowHandle()); + + // TODO(loicsharma): Hide the window until the first frame is rendered. + // Single window apps use the engine's next frame callback to show the + // window. This doesn't work for multi window apps as the engine cannot have + // multiple next frame callbacks. If multiple windows are created, only the + // last one will be shown. + ShowWindow(hwnd, SW_SHOWNORMAL); +} + +FlutterHostWindow::~FlutterHostWindow() { + if (view_controller_) { + // Unregister the window class. Fail silently if other windows are still + // using the class, as only the last window can successfully unregister it. + if (!UnregisterClass(kWindowClassName, GetModuleHandle(nullptr))) { + // Clear the error state after the failed unregistration. + SetLastError(ERROR_SUCCESS); + } + } +} + +FlutterHostWindow* FlutterHostWindow::GetThisFromHandle(HWND hwnd) { + return reinterpret_cast( + GetWindowLongPtr(hwnd, GWLP_USERDATA)); +} + +HWND FlutterHostWindow::GetWindowHandle() const { + return window_handle_; +} + +void FlutterHostWindow::FocusViewOf(FlutterHostWindow* window) { + if (window != nullptr && window->child_content_ != nullptr) { + SetFocus(window->child_content_); + } +}; + +LRESULT FlutterHostWindow::WndProc(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + if (message == WM_NCCREATE) { + auto* const create_struct = reinterpret_cast(lparam); + auto* const window = + static_cast(create_struct->lpCreateParams); + SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(window)); + window->window_handle_ = hwnd; + + EnableFullDpiSupportIfAvailable(hwnd); + EnableTransparentWindowBackground(hwnd); + } else if (FlutterHostWindow* const window = GetThisFromHandle(hwnd)) { + return window->HandleMessage(hwnd, message, wparam, lparam); + } + + return DefWindowProc(hwnd, message, wparam, lparam); +} + +LRESULT FlutterHostWindow::HandleMessage(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + auto result = + view_controller_->engine() + ->window_proc_delegate_manager() + ->OnTopLevelWindowProc(window_handle_, message, wparam, lparam); + if (result) { + return *result; + } + + switch (message) { + case WM_DPICHANGED: { + auto* const new_scaled_window_rect = reinterpret_cast(lparam); + LONG const width = + new_scaled_window_rect->right - new_scaled_window_rect->left; + LONG const height = + new_scaled_window_rect->bottom - new_scaled_window_rect->top; + SetWindowPos(hwnd, nullptr, new_scaled_window_rect->left, + new_scaled_window_rect->top, width, height, + SWP_NOZORDER | SWP_NOACTIVATE); + return 0; + } + + case WM_GETMINMAXINFO: { + RECT window_rect; + GetWindowRect(hwnd, &window_rect); + RECT client_rect; + GetClientRect(hwnd, &client_rect); + LONG const non_client_width = (window_rect.right - window_rect.left) - + (client_rect.right - client_rect.left); + LONG const non_client_height = (window_rect.bottom - window_rect.top) - + (client_rect.bottom - client_rect.top); + + UINT const dpi = flutter::GetDpiForHWND(hwnd); + double const scale_factor = + static_cast(dpi) / USER_DEFAULT_SCREEN_DPI; + + MINMAXINFO* info = reinterpret_cast(lparam); + if (min_size_) { + Size const min_physical_size = ClampToVirtualScreen( + Size(min_size_->width() * scale_factor + non_client_width, + min_size_->height() * scale_factor + non_client_height)); + + info->ptMinTrackSize.x = min_physical_size.width(); + info->ptMinTrackSize.y = min_physical_size.height(); + } + if (max_size_) { + Size const max_physical_size = ClampToVirtualScreen( + Size(max_size_->width() * scale_factor + non_client_width, + max_size_->height() * scale_factor + non_client_height)); + + info->ptMaxTrackSize.x = max_physical_size.width(); + info->ptMaxTrackSize.y = max_physical_size.height(); + } + return 0; + } + + case WM_SIZE: { + if (child_content_ != nullptr) { + // Resize and reposition the child content window. + RECT client_rect; + GetClientRect(hwnd, &client_rect); + MoveWindow(child_content_, client_rect.left, client_rect.top, + client_rect.right - client_rect.left, + client_rect.bottom - client_rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + FocusViewOf(this); + return 0; + + case WM_MOUSEACTIVATE: + FocusViewOf(this); + return MA_ACTIVATE; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + + default: + break; + } + + if (!view_controller_) { + return 0; + } + + return DefWindowProc(hwnd, message, wparam, lparam); +} + +void FlutterHostWindow::SetClientSize(Size const& client_size) const { + WINDOWINFO window_info = {.cbSize = sizeof(WINDOWINFO)}; + GetWindowInfo(window_handle_, &window_info); + + std::optional const window_size = GetWindowSizeForClientSize( + client_size, min_size_, max_size_, window_info.dwStyle, + window_info.dwExStyle, nullptr); + + Size const size = window_size.value_or(client_size); + SetWindowPos(window_handle_, NULL, 0, 0, size.width(), size.height(), + SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); +} + +void FlutterHostWindow::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT client_rect; + GetClientRect(window_handle_, &client_rect); + MoveWindow(content, client_rect.left, client_rect.top, + client_rect.right - client_rect.left, + client_rect.bottom - client_rect.top, true); +} + +} // namespace flutter diff --git a/engine/src/flutter/shell/platform/windows/flutter_host_window.h b/engine/src/flutter/shell/platform/windows/flutter_host_window.h new file mode 100644 index 0000000000000..f5d8e7aff3731 --- /dev/null +++ b/engine/src/flutter/shell/platform/windows/flutter_host_window.h @@ -0,0 +1,92 @@ +// 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_SHELL_PLATFORM_WINDOWS_FLUTTER_HOST_WINDOW_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_HOST_WINDOW_H_ + +#include + +#include + +#include "flutter/fml/macros.h" +#include "flutter/shell/platform/common/windowing.h" + +namespace flutter { + +class FlutterHostWindowController; +class FlutterWindowsView; +class FlutterWindowsViewController; + +// A Win32 window that hosts a |FlutterWindow| in its client area. +class FlutterHostWindow { + public: + // Creates a native Win32 window with a child view confined to its client + // area. |controller| is a pointer to the controller that manages the + // |FlutterHostWindow|. On success, a valid window handle can be retrieved + // via |FlutterHostWindow::GetWindowHandle|. + FlutterHostWindow(FlutterHostWindowController* controller, + WindowCreationSettings const& settings); + + virtual ~FlutterHostWindow(); + + // Returns the instance pointer for |hwnd| or nullptr if invalid. + static FlutterHostWindow* GetThisFromHandle(HWND hwnd); + + // Returns the backing window handle, or nullptr if the native window is not + // created or has already been destroyed. + HWND GetWindowHandle() const; + + // Resizes the window to accommodate a client area of the given + // |client_size|. + void SetClientSize(Size const& client_size) const; + + private: + friend FlutterHostWindowController; + + // Sets the focus to the child view window of |window|. + static void FocusViewOf(FlutterHostWindow* window); + + // 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 + // responds to changes in DPI. Delegates other messages to the controller. + static LRESULT WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); + + // Processes and routes salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + LRESULT HandleMessage(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Controller for this window. + FlutterHostWindowController* const window_controller_ = nullptr; + + // Controller for the view hosted in this window. Value-initialized if the + // window is created from an existing top-level native window created by the + // runner. + std::unique_ptr view_controller_; + + // The window archetype. + WindowArchetype archetype_ = WindowArchetype::kRegular; + + // Backing handle for this window. + HWND window_handle_ = nullptr; + + // Backing handle for the hosted view window. + HWND child_content_ = nullptr; + + // The minimum size of the window's client area, if defined. + std::optional min_size_; + + // The maximum size of the window's client area, if defined. + std::optional max_size_; + + FML_DISALLOW_COPY_AND_ASSIGN(FlutterHostWindow); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_HOST_WINDOW_H_ diff --git a/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.cc b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.cc new file mode 100644 index 0000000000000..50b61d9cba2f9 --- /dev/null +++ b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.cc @@ -0,0 +1,228 @@ +// 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 "flutter/shell/platform/windows/flutter_host_window_controller.h" + +#include +#include +#include + +#include "embedder.h" +#include "flutter/shell/platform/common/windowing.h" +#include "flutter/shell/platform/windows/flutter_windows_engine.h" +#include "flutter/shell/platform/windows/flutter_windows_view_controller.h" +#include "fml/logging.h" +#include "shell/platform/windows/client_wrapper/include/flutter/flutter_view.h" +#include "shell/platform/windows/flutter_host_window.h" +#include "shell/platform/windows/flutter_windows_view.h" + +namespace flutter { + +struct WindowingInitRequest { + void (*on_message)(WindowsMessage*); +}; + +struct WindowCreationRequest { + double width; + double height; + double min_width; + double min_height; + double max_width; + double max_height; +}; +} // namespace flutter + +extern "C" { + +FLUTTER_EXPORT +void flutter_windowing_initialize( + int64_t engine_id, + const flutter::WindowingInitRequest* request) { + flutter::FlutterWindowsEngine* engine = + flutter::FlutterWindowsEngine::GetEngineForId(engine_id); + engine->get_host_window_controller()->Initialize(request); +} + +FLUTTER_EXPORT +bool flutter_windowing_has_top_level_windows(int64_t engine_id) { + flutter::FlutterWindowsEngine* engine = + flutter::FlutterWindowsEngine::GetEngineForId(engine_id); + return engine->get_host_window_controller()->HasTopLevelWindows(); +} + +FLUTTER_EXPORT +int64_t flutter_create_regular_window( + int64_t engine_id, + const flutter::WindowCreationRequest* request) { + flutter::FlutterWindowsEngine* engine = + flutter::FlutterWindowsEngine::GetEngineForId(engine_id); + return engine->get_host_window_controller()->CreateRegularWindow(request); +} + +FLUTTER_EXPORT +HWND flutter_get_window_handle(int64_t engine_id, FlutterViewId view_id) { + flutter::FlutterWindowsEngine* engine = + flutter::FlutterWindowsEngine::GetEngineForId(engine_id); + flutter::FlutterWindowsView* view = engine->view(view_id); + if (view == nullptr) { + return nullptr; + } else { + return GetAncestor(view->GetWindowHandle(), GA_ROOT); + } +} + +struct Size { + double width; + double height; +}; + +FLUTTER_EXPORT +void flutter_get_window_size(HWND hwnd, Size* size) { + RECT rect; + GetClientRect(hwnd, &rect); + double const dpr = FlutterDesktopGetDpiForHWND(hwnd) / + static_cast(USER_DEFAULT_SCREEN_DPI); + double const width = rect.right / dpr; + double const height = rect.bottom / dpr; + size->width = width; + size->height = height; +} + +FLUTTER_EXPORT +int64_t flutter_get_window_state(HWND hwnd) { + if (IsIconic(hwnd)) { + return static_cast(flutter::WindowState::kMinimized); + } else if (IsZoomed(hwnd)) { + return static_cast(flutter::WindowState::kMaximized); + } else { + return static_cast(flutter::WindowState::kRestored); + } +} + +FLUTTER_EXPORT +void flutter_set_window_state(HWND hwnd, int64_t state) { + switch (static_cast(state)) { + case flutter::WindowState::kRestored: + ShowWindow(hwnd, SW_RESTORE); + break; + case flutter::WindowState::kMaximized: + ShowWindow(hwnd, SW_MAXIMIZE); + break; + case flutter::WindowState::kMinimized: + ShowWindow(hwnd, SW_MINIMIZE); + break; + } +} + +FLUTTER_EXPORT +void flutter_set_window_size(HWND hwnd, double width, double height) { + flutter::FlutterHostWindow* window = + flutter::FlutterHostWindow::GetThisFromHandle(hwnd); + if (window) { + window->SetClientSize(flutter::Size(width, height)); + } +} + +} // extern "C" + +namespace flutter { + +FlutterHostWindowController::FlutterHostWindowController( + FlutterWindowsEngine* engine) + : engine_(engine) {} + +void FlutterHostWindowController::Initialize( + const WindowingInitRequest* request) { + on_message_ = request->on_message; + isolate_ = Isolate(); + + // Send messages accumulated before isolate called this method. + for (WindowsMessage& message : pending_messages_) { + IsolateScope scope(*isolate_); + on_message_(&message); + } + pending_messages_.clear(); +} + +bool FlutterHostWindowController::HasTopLevelWindows() const { + return !active_windows_.empty(); +} + +FlutterViewId FlutterHostWindowController::CreateRegularWindow( + const WindowCreationRequest* request) { + WindowCreationSettings settings; + settings.size = Size(request->width, request->height); + settings.min_size = Size(request->min_width, request->min_height); + if (request->max_width != 0 && request->max_height != 0) { + settings.max_size = Size(request->max_width, request->max_height); + } + auto window = std::make_unique(this, settings); + if (!window->GetWindowHandle()) { + FML_LOG(ERROR) << "Failed to create host window"; + return 0; + } + FlutterViewId const view_id = window->view_controller_->view()->view_id(); + active_windows_[window->GetWindowHandle()] = std::move(window); + return view_id; +} + +void FlutterHostWindowController::OnEngineShutdown() { + // Don't send any more messages to isolate. + on_message_ = nullptr; + std::vector active_handles; + active_handles.reserve(active_windows_.size()); + for (auto& [hwnd, window] : active_windows_) { + active_handles.push_back(hwnd); + } + for (auto hwnd : active_handles) { + // This will destroy the window, which will in turn remove the + // FlutterHostWindow from map when handling WM_NCDESTROY inside + // HandleMessage. + DestroyWindow(hwnd); + } +} + +std::optional FlutterHostWindowController::HandleMessage( + HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + if (message == WM_NCDESTROY) { + active_windows_.erase(hwnd); + } + + FlutterWindowsView* view = engine_->GetViewFromTopLevelWindow(hwnd); + if (!view) { + FML_LOG(WARNING) << "Received message for unknown view"; + return std::nullopt; + } + + WindowsMessage message_struct = {.view_id = view->view_id(), + .hwnd = hwnd, + .message = message, + .wParam = wparam, + .lParam = lparam, + .result = 0, + .handled = false}; + + // Not initialized yet. + if (!isolate_) { + pending_messages_.push_back(message_struct); + return std::nullopt; + } + + IsolateScope scope(*isolate_); + on_message_(&message_struct); + if (message_struct.handled) { + return message_struct.result; + } else { + return std::nullopt; + } +} + +FlutterWindowsEngine* FlutterHostWindowController::engine() const { + return engine_; +} + +} // namespace flutter diff --git a/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.h b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.h new file mode 100644 index 0000000000000..40f2781001699 --- /dev/null +++ b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.h @@ -0,0 +1,84 @@ +// 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_SHELL_PLATFORM_WINDOWS_FLUTTER_HOST_WINDOW_CONTROLLER_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_HOST_WINDOW_CONTROLLER_H_ + +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/shell/platform/common/isolate_scope.h" +#include "flutter/shell/platform/windows/flutter_host_window.h" + +namespace flutter { + +class FlutterWindowsEngine; +struct WindowingInitRequest; +struct WindowCreationRequest; + +struct WindowsMessage { + int64_t view_id; + HWND hwnd; + UINT message; + WPARAM wParam; + LPARAM lParam; + LRESULT result; + bool handled; +}; + +// A controller class for managing |FlutterHostWindow| instances. +// A unique instance of this class is owned by |FlutterWindowsEngine| and used +// in |WindowingHandler| to handle methods and messages enabling multi-window +// support. +class FlutterHostWindowController { + public: + explicit FlutterHostWindowController(FlutterWindowsEngine* engine); + virtual ~FlutterHostWindowController() = default; + + void Initialize(const WindowingInitRequest* request); + + bool HasTopLevelWindows() const; + + FlutterViewId CreateRegularWindow(const WindowCreationRequest* request); + + // Message handler called by |FlutterHostWindow::WndProc| to process window + // messages before delegating them to the host window. This allows the + // controller to process messages that affect the state of other host windows. + std::optional HandleMessage(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam); + + // Gets the engine that owns this controller. + FlutterWindowsEngine* engine() const; + + void OnEngineShutdown(); + + private: + // The Flutter engine that owns this controller. + FlutterWindowsEngine* const engine_; + + // Callback that relays windows messages to the isolate. Set + // during Initialize(). + void (*on_message_)(WindowsMessage*) = nullptr; + + // Isolate that runs the Dart code. Set during Initialize(). + std::optional isolate_; + + // Messages received before the controller is initialized from dart + // code. Buffered until Initialize() is called. + std::vector pending_messages_; + + // A map of active windows. Used to destroy remaining windows on engine + // shutdown. + std::unordered_map> active_windows_; + + FML_DISALLOW_COPY_AND_ASSIGN(FlutterHostWindowController); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_HOST_WINDOW_CONTROLLER_H_ diff --git a/engine/src/flutter/shell/platform/windows/flutter_host_window_controller_unittests.cc b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller_unittests.cc new file mode 100644 index 0000000000000..ba955eb7f8080 --- /dev/null +++ b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller_unittests.cc @@ -0,0 +1,347 @@ +// 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. + +#if 0 + +#include "flutter/shell/platform/windows/windowing_handler.h" + +#include "flutter/fml/macros.h" +#include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_method_codec.h" +#include "flutter/shell/platform/windows/flutter_host_window_controller.h" +#include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h" +#include "flutter/shell/platform/windows/testing/test_binary_messenger.h" +#include "flutter/shell/platform/windows/testing/windows_test.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +namespace { + +constexpr char kChannelName[] = "flutter/windowing"; +constexpr char kOnWindowChangedMethod[] = "onWindowChanged"; +constexpr char kOnWindowDestroyedMethod[] = "onWindowDestroyed"; +constexpr char kSizeKey[] = "size"; +constexpr char kStateKey[] = "state"; +constexpr char kTitleKey[] = "title"; +constexpr char kViewIdKey[] = "viewId"; + +// Process the next Win32 message if there is one. This can be used to +// pump the Windows platform thread task runner. +void PumpMessage() { + ::MSG msg; + if (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } +} + +Size GetLogicalClientSize(HWND hwnd) { + RECT rect; + GetClientRect(hwnd, &rect); + double const dpr = FlutterDesktopGetDpiForHWND(hwnd) / + static_cast(USER_DEFAULT_SCREEN_DPI); + double const width = rect.right / dpr; + double const height = rect.bottom / dpr; + return {width, height}; +} + +std::wstring GetWindowTitle(HWND hwnd) { + int length = GetWindowTextLengthW(hwnd); + if (length == 0) + return L""; + + std::vector buffer(length + 1); + GetWindowTextW(hwnd, buffer.data(), length + 1); + return std::wstring(buffer.data()); +} + +class FlutterHostWindowControllerTest : public WindowsTest { + public: + FlutterHostWindowControllerTest() = default; + virtual ~FlutterHostWindowControllerTest() = default; + + protected: + void SetUp() override { + InitializeCOM(); + SetDpiAwareness(); + FlutterWindowsEngineBuilder builder(GetContext()); + engine_ = builder.Build(); + engine_->Run(); + controller_ = std::make_unique(engine_.get()); + } + + FlutterWindowsEngine* engine() { return engine_.get(); } + FlutterHostWindowController* host_window_controller() { + return controller_.get(); + } + + private: + void InitializeCOM() const { + FML_CHECK(SUCCEEDED(::CoInitializeEx(nullptr, COINIT_MULTITHREADED))); + } + + void SetDpiAwareness() const { + HMODULE user32_module = LoadLibraryA("user32.dll"); + if (!user32_module) { + return; + } + using SetProcessDpiAwarenessContext = BOOL WINAPI(DPI_AWARENESS_CONTEXT); + auto set_process_dpi_awareness_context = + reinterpret_cast( + GetProcAddress(user32_module, "SetProcessDpiAwarenessContext")); + if (set_process_dpi_awareness_context != nullptr) { + set_process_dpi_awareness_context( + DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + } + FreeLibrary(user32_module); + } + + std::unique_ptr engine_; + std::unique_ptr controller_; + + FML_DISALLOW_COPY_AND_ASSIGN(FlutterHostWindowControllerTest); +}; + +} // namespace + +TEST_F(FlutterHostWindowControllerTest, CreateRegularWindow) { + // Define parameters for the window to be created. + WindowCreationSettings const settings = { + .archetype = WindowArchetype::kRegular, + .size = {800.0, 600.0}, + .title = "window", + }; + + // Create the window. + std::optional const result = + host_window_controller()->CreateHostWindow(settings); + + // Validate the returned metadata. + ASSERT_TRUE(result.has_value()); + EXPECT_NE(engine()->view(result->view_id), nullptr); + EXPECT_EQ(result->archetype, settings.archetype); + EXPECT_EQ(result->size.width(), settings.size.width()); + EXPECT_EQ(result->size.height(), settings.size.height()); + EXPECT_FALSE(result->parent_id.has_value()); + + // Ensure the window was successfully retrieved. + FlutterHostWindow* const window = + host_window_controller()->GetHostWindow(result->view_id); + ASSERT_NE(window, nullptr); +} + +TEST_F(FlutterHostWindowControllerTest, ModifyRegularWindowSize) { + // Define settings for the window to be created. + WindowCreationSettings const creation_settings = { + .archetype = WindowArchetype::kRegular, + .size = {800.0, 600.0}, + }; + + // Create the window. + std::optional const metadata = + host_window_controller()->CreateHostWindow(creation_settings); + ASSERT_TRUE(metadata.has_value()); + // Retrieve the created window and verify it exists. + FlutterHostWindow* const window = + host_window_controller()->GetHostWindow(metadata->view_id); + ASSERT_NE(window, nullptr); + + // Define the modifications to be applied to the window. + WindowModificationSettings const modification_settings = { + .size = Size{200.0, 200.0}, + }; + + // Test messenger with a handler for onWindowChanged. + bool done = false; + TestBinaryMessenger messenger([&](const std::string& channel, + const uint8_t* message, size_t size, + BinaryReply reply) { + // Ensure the message is sent on the windowing channel. + ASSERT_EQ(channel, kChannelName); + + // Ensure the decoded method call is valid. + auto const method = StandardMethodCodec::GetInstance().DecodeMethodCall( + std::vector(message, message + size)); + ASSERT_NE(method, nullptr); + + // Handle the onWindowChanged method. + if (method->method_name() == kOnWindowChangedMethod) { + // Validate the method arguments. + auto const& args = *method->arguments(); + ASSERT_TRUE(std::holds_alternative(args)); + auto const& args_map = std::get(args); + + // Ensure 'viewId' is present and valid. + auto const& it_viewId = args_map.find(EncodableValue(kViewIdKey)); + ASSERT_NE(it_viewId, args_map.end()); + auto const* value_viewId = std::get_if(&it_viewId->second); + ASSERT_NE(value_viewId, nullptr); + EXPECT_GE(*value_viewId, metadata->view_id); + EXPECT_NE(engine()->view(*value_viewId), nullptr); + + // Ensure 'size' is present and valid. + auto const& it_size = args_map.find(EncodableValue(kSizeKey)); + ASSERT_NE(it_size, args_map.end()); + auto const* value_size = + std::get_if>(&it_size->second); + ASSERT_NE(value_size, nullptr); + ASSERT_EQ(value_size->size(), 2); + auto const* value_width = std::get_if(&value_size->at(0)); + ASSERT_NE(value_width, nullptr); + auto const* value_height = std::get_if(&value_size->at(1)); + ASSERT_NE(value_height, nullptr); + EXPECT_EQ(*value_width, modification_settings.size->width()); + EXPECT_EQ(*value_height, modification_settings.size->height()); + + done = true; + } + }); + + // Create the windowing handler with the test messenger. + WindowingHandler windowing_handler(&messenger, host_window_controller()); + + // Apply the modifications. + EXPECT_TRUE(host_window_controller()->ModifyHostWindow( + metadata->view_id, modification_settings)); + + // Validate the modified settings. + HWND const window_handle = host_window_controller() + ->GetHostWindow(metadata->view_id) + ->GetWindowHandle(); + Size const new_size = GetLogicalClientSize(window_handle); + EXPECT_EQ(new_size.width(), modification_settings.size->width()); + EXPECT_EQ(new_size.height(), modification_settings.size->height()); + + // Pump messages for the Windows platform task runner. + while (!done) { + PumpMessage(); + } +} + +TEST_F(FlutterHostWindowControllerTest, ModifyRegularWindowTitle) { + // Define settings for the window to be created. + WindowCreationSettings const creation_settings = { + .archetype = WindowArchetype::kRegular, + .title = "window", + }; + + // Create the window. + std::optional const metadata = + host_window_controller()->CreateHostWindow(creation_settings); + ASSERT_TRUE(metadata.has_value()); + // Retrieve the created window and verify it exists. + FlutterHostWindow* const window = + host_window_controller()->GetHostWindow(metadata->view_id); + ASSERT_NE(window, nullptr); + + // Define the modifications to be applied to the window. + WindowModificationSettings const modification_settings = { + .title = "new title 😉", + }; + + // Apply the modifications. + EXPECT_TRUE(host_window_controller()->ModifyHostWindow( + metadata->view_id, modification_settings)); + + // Validate the modified settings. + HWND const window_handle = host_window_controller() + ->GetHostWindow(metadata->view_id) + ->GetWindowHandle(); + std::wstring const new_title = GetWindowTitle(window_handle); + EXPECT_STREQ(new_title.c_str(), L"new title 😉"); +} + +TEST_F(FlutterHostWindowControllerTest, ModifyRegularWindowState) { + // Define settings for the window to be created. + WindowCreationSettings const creation_settings = { + .archetype = WindowArchetype::kRegular, + .state = WindowState::kRestored, + }; + + // Create the window. + std::optional const metadata = + host_window_controller()->CreateHostWindow(creation_settings); + ASSERT_TRUE(metadata.has_value()); + // Retrieve the created window and verify it exists. + FlutterHostWindow* const window = + host_window_controller()->GetHostWindow(metadata->view_id); + ASSERT_NE(window, nullptr); + EXPECT_EQ(window->GetState(), creation_settings.state); + + // Define the modifications to be applied to the window. + WindowModificationSettings const modification_settings = { + .state = WindowState::kMinimized, + }; + + // Apply the modifications. + EXPECT_TRUE(host_window_controller()->ModifyHostWindow( + metadata->view_id, modification_settings)); + + // Validate the modified settings. + EXPECT_EQ(window->GetState(), modification_settings.state); +} + +TEST_F(FlutterHostWindowControllerTest, DestroyWindow) { + bool done = false; + + // Test messenger with a handler for onWindowDestroyed. + TestBinaryMessenger messenger([&](const std::string& channel, + const uint8_t* message, size_t size, + BinaryReply reply) { + // Ensure the message is sent on the windowing channel. + ASSERT_EQ(channel, kChannelName); + + // Ensure the decoded method call is valid. + auto const method = StandardMethodCodec::GetInstance().DecodeMethodCall( + std::vector(message, message + size)); + ASSERT_NE(method, nullptr); + + // Handle the onWindowDestroyed method. + if (method->method_name() == kOnWindowDestroyedMethod) { + // Validate the method arguments. + auto const& args = *method->arguments(); + ASSERT_TRUE(std::holds_alternative(args)); + auto const& args_map = std::get(args); + + // Ensure the viewId is present but not valid anymore. + auto const& it_viewId = args_map.find(EncodableValue(kViewIdKey)); + ASSERT_NE(it_viewId, args_map.end()); + auto const* value_viewId = std::get_if(&it_viewId->second); + ASSERT_NE(value_viewId, nullptr); + EXPECT_GE(*value_viewId, 0); + EXPECT_EQ(engine()->view(*value_viewId), nullptr); + + done = true; + } + }); + + // Create the windowing handler with the test messenger. + WindowingHandler windowing_handler(&messenger, host_window_controller()); + + // Define parameters for the window to be created. + WindowCreationSettings const settings = { + .archetype = WindowArchetype::kRegular, + .size = {800.0, 600.0}, + .title = "window", + }; + + // Create the window. + std::optional const result = + host_window_controller()->CreateHostWindow(settings); + ASSERT_TRUE(result.has_value()); + + // Destroy the window. + EXPECT_TRUE(host_window_controller()->DestroyHostWindow(result->view_id)); + + // Pump messages for the Windows platform task runner. + while (!done) { + PumpMessage(); + } +} + +} // namespace testing +} // namespace flutter + +#endif \ No newline at end of file diff --git a/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc b/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc index f409043d6284b..f3134a0bd89d4 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc +++ b/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc @@ -21,6 +21,7 @@ #include "flutter/shell/platform/windows/accessibility_bridge_windows.h" #include "flutter/shell/platform/windows/compositor_opengl.h" #include "flutter/shell/platform/windows/compositor_software.h" +#include "flutter/shell/platform/windows/flutter_host_window_controller.h" #include "flutter/shell/platform/windows/flutter_windows_view.h" #include "flutter/shell/platform/windows/keyboard_key_channel_handler.h" #include "flutter/shell/platform/windows/system_utils.h" @@ -209,8 +210,18 @@ FlutterWindowsEngine::FlutterWindowsEngine( FlutterWindowsEngine* that = static_cast(user_data); BASE_DCHECK(that->lifecycle_manager_); - return that->lifecycle_manager_->WindowProc(hwnd, msg, wpar, lpar, - result); + bool handled = + that->lifecycle_manager_->WindowProc(hwnd, msg, wpar, lpar, result); + if (handled) { + return true; + } + auto message_result = + that->host_window_controller_->HandleMessage(hwnd, msg, wpar, lpar); + if (message_result) { + *result = *message_result; + return true; + } + return false; }, static_cast(this)); @@ -228,12 +239,17 @@ FlutterWindowsEngine::FlutterWindowsEngine( std::make_unique(messenger_wrapper_.get(), this); platform_handler_ = std::make_unique(messenger_wrapper_.get(), this); + if (enable_multi_window_) { + host_window_controller_ = + std::make_unique(this); + } settings_plugin_ = std::make_unique(messenger_wrapper_.get(), task_runner_.get()); } FlutterWindowsEngine::~FlutterWindowsEngine() { messenger_->SetEngine(nullptr); + host_window_controller_.reset(); Stop(); } @@ -304,7 +320,7 @@ bool FlutterWindowsEngine::Run(std::string_view entrypoint) { &WindowsPlatformThreadPrioritySetter; if (project_->ui_thread_policy() != - FlutterUIThreadPolicy::RunOnSeparateThread) { + FlutterUIThreadPolicy::RunOnSeparateThread && !enable_multi_window) { custom_task_runners.ui_task_runner = &platform_task_runner; } else { FML_LOG(WARNING) << "Running with unmerged platform and UI threads. This " @@ -503,6 +519,7 @@ bool FlutterWindowsEngine::Run(std::string_view entrypoint) { bool FlutterWindowsEngine::Stop() { if (engine_) { + host_window_controller_->OnEngineShutdown(); for (const auto& [callback, registrar] : plugin_registrar_destruction_callbacks_) { callback(registrar); @@ -843,6 +860,20 @@ HCURSOR FlutterWindowsEngine::GetCursorByName( return windows_proc_table_->LoadCursor(nullptr, idc_name); } +FlutterWindowsView* FlutterWindowsEngine::GetViewFromTopLevelWindow( + HWND hwnd) const { + std::shared_lock read_lock(views_mutex_); + auto const iterator = + std::find_if(views_.begin(), views_.end(), [hwnd](auto const& pair) { + FlutterWindowsView* const view = pair.second; + return GetAncestor(view->GetWindowHandle(), GA_ROOT) == hwnd; + }); + if (iterator != views_.end()) { + return iterator->second; + } + return nullptr; +} + void FlutterWindowsEngine::SendSystemLocales() { std::vector languages = GetPreferredLanguageInfo(*windows_proc_table_); diff --git a/engine/src/flutter/shell/platform/windows/flutter_windows_engine.h b/engine/src/flutter/shell/platform/windows/flutter_windows_engine.h index 706bff3869249..4abf1580768d8 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_windows_engine.h +++ b/engine/src/flutter/shell/platform/windows/flutter_windows_engine.h @@ -30,6 +30,7 @@ #include "flutter/shell/platform/windows/egl/manager.h" #include "flutter/shell/platform/windows/egl/proc_table.h" #include "flutter/shell/platform/windows/flutter_desktop_messenger.h" +#include "flutter/shell/platform/windows/flutter_host_window.h" #include "flutter/shell/platform/windows/flutter_project_bundle.h" #include "flutter/shell/platform/windows/flutter_windows_texture_registrar.h" #include "flutter/shell/platform/windows/keyboard_handler_base.h" @@ -315,6 +316,14 @@ class FlutterWindowsEngine { // Sets the cursor directly from a cursor handle. void SetFlutterCursor(HCURSOR cursor) const; + FlutterHostWindowController* get_host_window_controller() { + return host_window_controller_.get(); + } + + // Returns the root view associated with the top-level window with |hwnd| as + // the window handle. + FlutterWindowsView* GetViewFromTopLevelWindow(HWND hwnd) const; + protected: // Creates the keyboard key handler. // @@ -453,6 +462,10 @@ class FlutterWindowsEngine { // Handlers for keyboard events from Windows. std::unique_ptr keyboard_key_handler_; + // The controller that manages the lifecycle of |FlutterHostWindow|s, native + // Win32 windows hosting a Flutter view in their client area. + std::unique_ptr host_window_controller_; + // Handlers for text events from Windows. std::unique_ptr text_input_plugin_; diff --git a/packages/flutter/.vscode/settings.json b/packages/flutter/.vscode/settings.json new file mode 100644 index 0000000000000..7a73a41bfdf76 --- /dev/null +++ b/packages/flutter/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/packages/flutter/lib/src/widgets/binding.dart b/packages/flutter/lib/src/widgets/binding.dart index 22bf618056a53..bf00cf361d226 100644 --- a/packages/flutter/lib/src/widgets/binding.dart +++ b/packages/flutter/lib/src/widgets/binding.dart @@ -44,6 +44,7 @@ import 'router.dart'; import 'service_extensions.dart'; import 'view.dart'; import 'widget_inspector.dart'; +import 'window.dart'; export 'dart:ui' show AppLifecycleState, Locale; @@ -459,6 +460,7 @@ mixin WidgetsBinding return true; }()); platformMenuDelegate = DefaultPlatformMenuDelegate(); + _windowingOwner = createWindowingOwner(); } /// The current [WidgetsBinding], if one has been created. @@ -1408,6 +1410,21 @@ mixin WidgetsBinding Locale? computePlatformResolvedLocale(List supportedLocales) { return platformDispatcher.computePlatformResolvedLocale(supportedLocales); } + + /// The [WindowingOwner] is responsible for creating and managing [WindowController]s. + /// Default [WindowingOwner] supports standard Flutter desktop embedders. + /// + /// Custom [WindowingOwner] can be provided by overriding [createWindowingOwner]. + WindowingOwner get windowingOwner => _windowingOwner; + late WindowingOwner _windowingOwner; + + /// Creates the [WindowingOwner] instance available via [windowingOwner]. + /// Can be overriden in subclasses to create embedder-specific [WindowingOwner] + /// implementation. + @protected + WindowingOwner createWindowingOwner() { + return WindowingOwner.createDefaultOwner(); + } } /// Inflate the given widget and attach it to the view. diff --git a/packages/flutter/lib/src/widgets/window.dart b/packages/flutter/lib/src/widgets/window.dart index a9e827862b0e0..dc0b29b299fea 100644 --- a/packages/flutter/lib/src/widgets/window.dart +++ b/packages/flutter/lib/src/widgets/window.dart @@ -2,5 +2,291 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/// Placeholder to be used in a future version of Flutter. -abstract final class Window {} +import 'dart:ui' show FlutterView, AppExitType; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; + +import 'window_win32.dart'; + +/// Defines the possible archetypes for a window. +enum WindowArchetype { + /// Defines a traditional window + regular, +} + +/// Defines the possible states that a window can be in. +enum WindowState { + /// Window is in its normal state, neither maximized, nor minimized. + restored, + + /// Window is maximized, occupying the full screen but still showing the system UI. + maximized, + + /// Window is minimized and not visible on the screen. + minimized, +} + +/// Base class for window controllers. +/// +/// A window controller must provide a [future] that resolves to a +/// a [WindowCreationResult] object. This object contains the view +/// associated with the window, the archetype of the window, the size +/// of the window, and the state of the window. +/// +/// The caller may also provide a callback to be called when the window +/// is destroyed, and a callback to be called when an error is encountered +/// during the creation of the window. +/// +/// Each [WindowController] is associated with exactly one root [FlutterView]. +/// +/// When the window is destroyed for any reason (either by the caller or by the +/// platform), the content of the controller will thereafter be invalid. Callers +/// may check if this content is invalid via the [isReady] property. +/// +/// This class implements the [Listenable] interface, so callers can listen +/// for changes to the window's properties. +abstract class WindowController with ChangeNotifier { + @protected + /// Sets the view associated with this window. + // ignore: use_setters_to_change_properties + void setView(FlutterView view) { + _view = view; + } + + /// The archetype of the window. + WindowArchetype get type; + + /// The current size of the window. This may differ from the requested size. + Size get size; + + /// Destroys this window. It is permissible to call this method multiple times. + void destroy(); + + /// The root view associated to this window, which is unique to each window. + FlutterView get rootView => _view; + late final FlutterView _view; +} + +/// Delegate class for regular window controller. +mixin class RegularWindowControllerDelegate { + /// Invoked when user attempts to close the window. Default implementation + /// destroys the window. Subclass can override the behavior to delay + /// or prevent the window from closing. + void onWindowCloseRequested(RegularWindowController controller) { + controller.destroy(); + } + + /// Invoked when the window is closed. Default implementation exits the + /// application if this was the last top-level window. + void onWindowDestroyed() { + final WindowingOwner owner = WidgetsBinding.instance.windowingOwner; + if (!owner.hasTopLevelWindows()) { + // No more top-level windows, exit the application. + ServicesBinding.instance.exitApplication(AppExitType.cancelable); + } + } +} + +/// A controller for a regular window. +/// +/// A regular window is a traditional window that can be resized, minimized, +/// maximized, and closed. Upon construction, the window is created for the +/// platform with the provided properties. +/// +/// This class does not interact with the widget tree. Instead, it is typically +/// provided to the [RegularWindow] widget, who does the work of rendering the +/// content inside of this window. +/// +/// An example usage might look like: +/// ```dart +/// final RegularWindowController controller = RegularWindowController( +/// size: const Size(800, 600), +/// sizeConstraints: const BoxConstraints(minWidth: 640, minHeight: 480), +/// title: "Example Window", +/// ); +/// runWidget(RegularWindow( +/// controller: controller, +/// child: MaterialApp(home: Container()))); +/// ``` +/// +/// When provided to a [RegularWindow] widget, widgets inside of the [child] +/// parameter will have access to the [RegularWindowController] via the +/// [WindowControllerContext] widget. +abstract class RegularWindowController extends WindowController { + /// Creates a [RegularWindowController] with the provided properties. + /// Upon construction, the window is created for the platform. + /// + /// [title] the title of the window + /// [state] the initial state of the window + /// [sizeConstraints] the size constraints of the window + /// [delegate] optional delegate for the controller controller. + /// [size] the size of the window + factory RegularWindowController({ + String? title, + WindowState? state, + BoxConstraints? sizeConstraints, + RegularWindowControllerDelegate? delegate, + required Size size, + }) { + WidgetsFlutterBinding.ensureInitialized(); + final WindowingOwner owner = WidgetsBinding.instance.windowingOwner; + final RegularWindowController controller = owner.createRegularWindowController( + size: size, + delegate: delegate ?? RegularWindowControllerDelegate(), + sizeConstraints: sizeConstraints, + ); + if (title != null || state != null) { + controller.modify(title: title, state: state); + } + return controller; + } + + @protected + /// Creates an empty [RegularWindowController]. + RegularWindowController.empty(); + + @override + WindowArchetype get type => WindowArchetype.regular; + + /// The current state of the window. + WindowState get state; + + /// Modify the properties of the window. The window must be ready before + /// calling this method. If the window is not ready, an assertion will be + /// thrown. The caller must provide at least one of the following parameters: + /// + /// [size] the new size of the window + /// [title] the new title of the window + /// [state] the new state of the window + /// + /// If no parameters are provided, then an assertion will be thrown. + void modify({Size? size, String? title, WindowState? state}); +} + +/// [WindowingOwner] is responsible for creating and managing window controllers. +/// +/// Custom subclass can be provided by subclassing [WidgetsBinding] and +/// and overriding the [createWindowingOwner] method. +abstract class WindowingOwner { + /// Creates a [RegularWindowController] with the provided properties. + RegularWindowController createRegularWindowController({ + required Size size, + required RegularWindowControllerDelegate delegate, + BoxConstraints? sizeConstraints, + }); + + /// Returns whether application has any top level windows created by this + /// windowing owner. + bool hasTopLevelWindows(); + + /// Creates default windowing owner for standard desktop embedders. + static WindowingOwner createDefaultOwner() { + if (defaultTargetPlatform == TargetPlatform.windows) { + return WindowingOwnerWin32(); + } else { + return _FallbackWindowingOwner(); + } + } +} + +/// Windowing delegate used on platforms that do not support windowing. +class _FallbackWindowingOwner extends WindowingOwner { + @override + RegularWindowController createRegularWindowController({ + required Size size, + required RegularWindowControllerDelegate delegate, + BoxConstraints? sizeConstraints, + }) { + throw UnsupportedError( + 'Current platform does not support windowing.\n' + 'Implement a WindowingDelegate for this platform.', + ); + } + + @override + bool hasTopLevelWindows() { + return false; + } +} + +/// The [RegularWindow] widget provides a way to render a regular window in the +/// widget tree. The provided [controller] creates the native window that backs +/// the widget. The [child] widget is rendered into this newly created window. +/// +/// While the window is being created, the [RegularWindow] widget will render +/// an empty [ViewCollection] widget. Once the window is created, the [child] +/// widget will be rendered into the window inside of a [View]. +/// +/// An example usage might look like: +/// ```dart +/// final RegularWindowController controller = RegularWindowController( +/// size: const Size(800, 600), +/// sizeConstraints: const BoxConstraints(minWidth: 640, minHeight: 480), +/// title: "Example Window", +/// ); +/// runApp(RegularWindow( +/// controller: controller, +/// child: MaterialApp(home: Container()))); +/// ``` +/// +/// When a [RegularWindow] widget is removed from the tree, the window that was created +/// by the [controller] is automatically destroyed if it has not yet been destroyed. +/// +/// Widgets in the same tree as the [child] widget will have access to the +/// [RegularWindowController] via the [WindowControllerContext] widget. +class RegularWindow extends StatefulWidget { + /// Creates a regular window widget. + /// [controller] the controller for this window + /// [child] the content to render into this window + /// [key] the key for this widget + const RegularWindow({super.key, required this.controller, required this.child}); + + /// Controller for this widget. + final RegularWindowController controller; + + /// The content rendered into this window. + final Widget child; + + @override + State createState() => _RegularWindowState(); +} + +class _RegularWindowState extends State { + @override + void dispose() { + super.dispose(); + widget.controller.destroy(); + } + + @override + Widget build(BuildContext context) { + return View( + view: widget.controller.rootView, + child: WindowControllerContext(controller: widget.controller, child: widget.child), + ); + } +} + +/// Provides descendents with access to the [WindowController] associated with +/// the window that is being rendered. +class WindowControllerContext extends InheritedWidget { + /// Creates a new [WindowControllerContext] + /// [controller] the controller associated with this window + /// [child] the child widget + const WindowControllerContext({super.key, required this.controller, required super.child}); + + /// The controller associated with this window. + final WindowController controller; + + /// Returns the [WindowContext] if any + static WindowController? of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType()?.controller; + } + + @override + bool updateShouldNotify(WindowControllerContext oldWidget) { + return controller != oldWidget.controller; + } +} diff --git a/packages/flutter/lib/src/widgets/window_win32.dart b/packages/flutter/lib/src/widgets/window_win32.dart new file mode 100644 index 0000000000000..e402dbf325489 --- /dev/null +++ b/packages/flutter/lib/src/widgets/window_win32.dart @@ -0,0 +1,302 @@ +// ignore_for_file: public_member_api_docs, avoid_unused_constructor_parameters + +import 'dart:ffi' hide Size; +import 'dart:ui' show FlutterView; +import 'package:ffi/ffi.dart' as ffi; +import 'package:flutter/foundation.dart'; + +import 'package:flutter/material.dart'; + +abstract class WindowsMessageHandler { + /// Handles a window message. Returned value, if not null will be + /// returned to the system as LRESULT and will stop all other + /// handlers from being called. + int? handleWindowsMessage( + FlutterView view, + Pointer windowHandle, + int message, + int wParam, + int lParam, + ); +} + +class WindowingOwnerWin32 extends WindowingOwner { + WindowingOwnerWin32() { + final Pointer<_WindowingInitRequest> request = + ffi.calloc<_WindowingInitRequest>() + ..ref.onMessage = + NativeCallable)>.isolateLocal( + _onMessage, + ).nativeFunction; + _initializeWindowing(PlatformDispatcher.instance.engineId!, request); + ffi.calloc.free(request); + } + + @override + RegularWindowController createRegularWindowController({ + required Size size, + required RegularWindowControllerDelegate delegate, + BoxConstraints? sizeConstraints, + }) { + return RegularWindowControllerWin32( + owner: this, + delegate: delegate, + size: size, + sizeConstraints: sizeConstraints, + ); + } + + void addMessageHandler(WindowsMessageHandler handler) { + _messageHandlers.add(handler); + } + + void removeMessageHandler(WindowsMessageHandler handler) { + _messageHandlers.remove(handler); + } + + final List _messageHandlers = []; + + void _onMessage(Pointer<_WindowsMessage> message) { + final List handlers = List.from(_messageHandlers); + final FlutterView flutterView = WidgetsBinding.instance.platformDispatcher.views.firstWhere( + (FlutterView view) => view.viewId == message.ref.viewId, + ); + for (final WindowsMessageHandler handler in handlers) { + final int? result = handler.handleWindowsMessage( + flutterView, + message.ref.windowHandle, + message.ref.message, + message.ref.wParam, + message.ref.lParam, + ); + if (result != null) { + message.ref.handled = true; + message.ref.lResult = result; + return; + } + } + } + + @override + bool hasTopLevelWindows() { + return _hasTopLevelWindows(PlatformDispatcher.instance.engineId!); + } + + @Native(symbol: 'flutter_windowing_has_top_level_windows') + external static bool _hasTopLevelWindows(int engineId); + + @Native)>( + symbol: 'flutter_windowing_initialize', + ) + external static void _initializeWindowing(int engineId, Pointer<_WindowingInitRequest> request); +} + +class RegularWindowControllerWin32 extends RegularWindowController + implements WindowsMessageHandler { + RegularWindowControllerWin32({ + required WindowingOwnerWin32 owner, + required RegularWindowControllerDelegate delegate, + BoxConstraints? sizeConstraints, + required Size size, + }) : _owner = owner, + _delegate = delegate, + super.empty() { + owner.addMessageHandler(this); + final Pointer<_WindowCreationRequest> request = + ffi.calloc<_WindowCreationRequest>() + ..ref.width = size.width + ..ref.height = size.height + ..ref.minWidth = sizeConstraints?.minWidth ?? 0 + ..ref.minHeight = sizeConstraints?.minHeight ?? 0 + ..ref.maxWidth = sizeConstraints?.maxWidth ?? 0 + ..ref.maxHeight = sizeConstraints?.maxHeight ?? 0; + final int viewId = _createWindow(PlatformDispatcher.instance.engineId!, request); + ffi.calloc.free(request); + final FlutterView flutterView = WidgetsBinding.instance.platformDispatcher.views.firstWhere( + (FlutterView view) => view.viewId == viewId, + ); + setView(flutterView); + } + + @override + Size get size { + _ensureNotDestroyed(); + final Pointer<_Size> size = ffi.calloc<_Size>(); + _getWindowSize(getWindowHandle(), size); + final Size result = Size(size.ref.width, size.ref.height); + ffi.calloc.free(size); + return result; + } + + @override + WindowState get state { + _ensureNotDestroyed(); + final int state = _getWindowState(getWindowHandle()); + return WindowState.values[state]; + } + + @override + void modify({Size? size, String? title, WindowState? state}) { + _ensureNotDestroyed(); + if (state != null) { + setWindowState(state); + } + if (title != null) { + setWindowTitle(title); + } + if (size != null) { + setWindowSize(size); + } + } + + void setWindowState(WindowState state) { + _ensureNotDestroyed(); + _setWindowState(getWindowHandle(), state.index); + } + + void setWindowTitle(String title) { + _ensureNotDestroyed(); + final Pointer titlePointer = title.toNativeUtf16(); + _setWindowTitle(getWindowHandle(), titlePointer); + ffi.calloc.free(titlePointer); + } + + void setWindowSize(Size size) { + _ensureNotDestroyed(); + _setWindowSize(getWindowHandle(), size.width, size.height); + } + + Pointer getWindowHandle() { + _ensureNotDestroyed(); + return _getWindowHandle(PlatformDispatcher.instance.engineId!, rootView.viewId); + } + + void _ensureNotDestroyed() { + if (_destroyed) { + throw StateError('Window has been destroyed.'); + } + } + + final RegularWindowControllerDelegate _delegate; + bool _destroyed = false; + + @override + void destroy() { + if (_destroyed) { + return; + } + _destroyWindow(getWindowHandle());; + _destroyed = true; + _delegate.onWindowDestroyed(); + _owner.removeMessageHandler(this); + } + + static const int WM_SIZE = 0x0005; + static const int WM_CLOSE = 0x0010; + + @override + int? handleWindowsMessage( + FlutterView view, + Pointer windowHandle, + int message, + int wParam, + int lParam, + ) { + if (view.viewId != rootView.viewId) { + return null; + } + + if (message == WM_CLOSE) { + _delegate.onWindowCloseRequested(this); + return 0; + } else if (message == WM_SIZE) { + notifyListeners(); + } + return null; + } + + final WindowingOwnerWin32 _owner; + + @Native)>( + symbol: 'flutter_create_regular_window', + ) + external static int _createWindow(int engineId, Pointer<_WindowCreationRequest> request); + + @Native Function(Int64, Int64)>(symbol: 'flutter_get_window_handle') + external static Pointer _getWindowHandle(int engineId, int viewId); + + @Native)>(symbol: 'DestroyWindow') + external static void _destroyWindow(Pointer windowHandle); + + @Native, Pointer<_Size>)>(symbol: 'flutter_get_window_size') + external static void _getWindowSize(Pointer windowHandle, Pointer<_Size> size); + + @Native)>(symbol: 'flutter_get_window_state') + external static int _getWindowState(Pointer windowHandle); + + @Native, Int64)>(symbol: 'flutter_set_window_state') + external static void _setWindowState(Pointer windowHandle, int state); + + @Native, Pointer)>(symbol: 'SetWindowTextW') + external static void _setWindowTitle(Pointer windowHandle, Pointer title); + + @Native, Double, Double)>(symbol: 'flutter_set_window_size') + external static void _setWindowSize(Pointer windowHandle, double width, double height); +} + +/// Request to initialize windowing system. +final class _WindowingInitRequest extends Struct { + external Pointer)>> onMessage; +} + +final class _WindowCreationRequest extends Struct { + @Double() + external double width; + + @Double() + external double height; + + @Double() + external double minWidth; + + @Double() + external double minHeight; + + @Double() + external double maxWidth; + + @Double() + external double maxHeight; +} + +/// Windows message received for all top level windows (regardless whether +/// they are created using a windowing controller). +final class _WindowsMessage extends Struct { + @Int64() + external int viewId; + + external Pointer windowHandle; + + @Int32() + external int message; + + @Int64() + external int wParam; + + @Int64() + external int lParam; + + @Int64() + external int lResult; + + @Bool() + external bool handled; +} + +final class _Size extends Struct { + @Double() + external double width; + + @Double() + external double height; +} diff --git a/packages/flutter/lib/widgets.dart b/packages/flutter/lib/widgets.dart index b8a47695a9da3..4b1d527e03025 100644 --- a/packages/flutter/lib/widgets.dart +++ b/packages/flutter/lib/widgets.dart @@ -177,3 +177,4 @@ export 'src/widgets/widget_inspector.dart'; export 'src/widgets/widget_span.dart'; export 'src/widgets/widget_state.dart'; export 'src/widgets/will_pop_scope.dart'; +export 'src/widgets/window.dart'; diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml index 2c869cf03645a..dd275852f6968 100644 --- a/packages/flutter/pubspec.yaml +++ b/packages/flutter/pubspec.yaml @@ -12,6 +12,7 @@ dependencies: # https://github.com/flutter/flutter/blob/main/docs/infra/Updating-dependencies-in-Flutter.md characters: 1.4.0 collection: 1.19.1 + ffi: 2.1.4 material_color_utilities: 0.11.1 meta: 1.16.0 vector_math: 2.1.4 diff --git a/packages/flutter/test/widgets/window_test.dart b/packages/flutter/test/widgets/window_test.dart new file mode 100644 index 0000000000000..c181298a55b53 --- /dev/null +++ b/packages/flutter/test/widgets/window_test.dart @@ -0,0 +1,276 @@ +// Copyright 2014 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/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; + +Future? Function(MethodCall)? _createWindowMethodCallHandler({ + required WidgetTester tester, + void Function(MethodCall)? onMethodCall, +}) { + return (MethodCall call) async { + onMethodCall?.call(call); + final Map args = call.arguments as Map; + if (call.method == 'createRegular') { + final List size = args['size']! as List; + final String state = args['state'] as String? ?? WindowState.restored.toString(); + + return {'viewId': tester.view.viewId, 'size': size, 'state': state}; + } else if (call.method == 'modifyRegular') { + return null; + } else if (call.method == 'destroyWindow') { + await tester.binding.defaultBinaryMessenger.handlePlatformMessage( + SystemChannels.windowing.name, + SystemChannels.windowing.codec.encodeMethodCall( + MethodCall('onWindowDestroyed', {'viewId': tester.view.viewId}), + ), + (ByteData? data) {}, + ); + + return null; + } + + throw Exception('Unsupported method call: ${call.method}'); + }; +} + +void main() { + testWidgets('RegularWindow widget populates the controller with proper values', ( + WidgetTester tester, + ) async { + const Size windowSize = Size(800, 600); + + tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( + SystemChannels.windowing, + _createWindowMethodCallHandler(tester: tester), + ); + + final RegularWindowController controller = RegularWindowController(size: windowSize); + + await tester.pump(); + + expect(controller.type, WindowArchetype.regular); + expect(controller.size, windowSize); + expect(controller.rootView.viewId, tester.view.viewId); + }); + + testWidgets('RegularWindow.onError is called when creation throws an error', ( + WidgetTester tester, + ) async { + const Size windowSize = Size(800, 600); + + tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.windowing, ( + MethodCall call, + ) async { + throw Exception('Failed to create the window'); + }); + + bool receivedError = false; + final RegularWindowController controller = RegularWindowController(size: windowSize); + + await tester.pump(); + + expect(receivedError, true); + }); + + testWidgets('RegularWindowController.destroy results in the RegularWindow.onDestroyed callback', ( + WidgetTester tester, + ) async { + const Size windowSize = Size(800, 600); + + tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( + SystemChannels.windowing, + _createWindowMethodCallHandler(tester: tester), + ); + + bool destroyed = false; + final RegularWindowController controller = RegularWindowController( + size: windowSize, + onDestroyed: () { + destroyed = true; + }, + ); + await tester.pumpWidget( + wrapWithView: false, + Builder( + builder: (BuildContext context) { + return RegularWindow(controller: controller, child: Container()); + }, + ), + ); + + await tester.pump(); + controller.destroy(); + + await tester.pump(); + expect(destroyed, true); + }); + + testWidgets( + 'RegularWindowController.size is updated when an onWindowChanged event is triggered on the channel', + (WidgetTester tester) async { + const Size initialSize = Size(800, 600); + const Size newSize = Size(400, 300); + + tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( + SystemChannels.windowing, + _createWindowMethodCallHandler(tester: tester), + ); + + final RegularWindowController controller = RegularWindowController(size: initialSize); + await tester.pump(); + + await tester.binding.defaultBinaryMessenger.handlePlatformMessage( + SystemChannels.windowing.name, + SystemChannels.windowing.codec.encodeMethodCall( + MethodCall('onWindowChanged', { + 'viewId': tester.view.viewId, + 'size': [newSize.width, newSize.height], + }), + ), + (ByteData? data) {}, + ); + await tester.pump(); + + expect(controller.size, newSize); + }, + ); + + testWidgets('RegularWindowController.modify can be called when provided with a "size" argument', ( + WidgetTester tester, + ) async { + const Size initialSize = Size(800, 600); + const Size newSize = Size(400, 300); + + bool wasCalled = false; + tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( + SystemChannels.windowing, + _createWindowMethodCallHandler( + tester: tester, + onMethodCall: (MethodCall call) { + if (call.method != 'modifyRegular') { + return; + } + + final Map args = call.arguments as Map; + final int viewId = args['viewId']! as int; + final List? size = args['size'] as List?; + final String? title = args['title'] as String?; + final String? state = args['state'] as String?; + expect(viewId, tester.view.viewId); + expect(size, [newSize.width, newSize.height]); + expect(title, null); + expect(state, null); + wasCalled = true; + }, + ), + ); + + final RegularWindowController controller = RegularWindowController(size: initialSize); + await tester.pump(); + + controller.modify(size: newSize); + await tester.pump(); + + expect(wasCalled, true); + }); + + testWidgets( + 'RegularWindowController.modify can be called when provided with a "title" argument', + (WidgetTester tester) async { + const Size initialSize = Size(800, 600); + const String newTitle = 'New Title'; + + bool wasCalled = false; + tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( + SystemChannels.windowing, + _createWindowMethodCallHandler( + tester: tester, + onMethodCall: (MethodCall call) { + if (call.method != 'modifyRegular') { + return; + } + + final Map args = call.arguments as Map; + final int viewId = args['viewId']! as int; + final List? size = args['size'] as List?; + final String? title = args['title'] as String?; + final String? state = args['state'] as String?; + expect(viewId, tester.view.viewId); + expect(size, null); + expect(title, newTitle); + expect(state, null); + wasCalled = true; + }, + ), + ); + + final RegularWindowController controller = RegularWindowController(size: initialSize); + await tester.pump(); + + controller.modify(title: newTitle); + await tester.pump(); + + expect(wasCalled, true); + }, + ); + + testWidgets( + 'RegularWindowController.modify can be called when provided with a "state" argument', + (WidgetTester tester) async { + const Size initialSize = Size(800, 600); + const WindowState newState = WindowState.minimized; + + bool wasCalled = false; + tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( + SystemChannels.windowing, + _createWindowMethodCallHandler( + tester: tester, + onMethodCall: (MethodCall call) { + if (call.method != 'modifyRegular') { + return; + } + + final Map args = call.arguments as Map; + final int viewId = args['viewId']! as int; + final List? size = args['size'] as List?; + final String? title = args['title'] as String?; + final String? state = args['state'] as String?; + expect(viewId, tester.view.viewId); + expect(size, null); + expect(title, null); + expect(state, newState.toString()); + wasCalled = true; + }, + ), + ); + + final RegularWindowController controller = RegularWindowController(size: initialSize); + await tester.pump(); + + controller.modify(state: newState); + await tester.pump(); + + expect(wasCalled, true); + }, + ); + + testWidgets('RegularWindowController.modify throws when no arguments are provided', ( + WidgetTester tester, + ) async { + const Size initialSize = Size(800, 600); + const WindowState newState = WindowState.minimized; + + tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( + SystemChannels.windowing, + _createWindowMethodCallHandler(tester: tester), + ); + + final RegularWindowController controller = RegularWindowController(size: initialSize); + await tester.pump(); + + expect(() async => controller.modify(), throwsA(isA())); + }); +} From f37cac10d16a0d1a4ce463d8a0944e42226d2532 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 11 Mar 2025 13:38:18 +0100 Subject: [PATCH 16/52] Disable platform channel tests --- .../flutter/test/widgets/window_test.dart | 536 +++++++++--------- 1 file changed, 268 insertions(+), 268 deletions(-) diff --git a/packages/flutter/test/widgets/window_test.dart b/packages/flutter/test/widgets/window_test.dart index c181298a55b53..c4a087eb05b89 100644 --- a/packages/flutter/test/widgets/window_test.dart +++ b/packages/flutter/test/widgets/window_test.dart @@ -6,271 +6,271 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -Future? Function(MethodCall)? _createWindowMethodCallHandler({ - required WidgetTester tester, - void Function(MethodCall)? onMethodCall, -}) { - return (MethodCall call) async { - onMethodCall?.call(call); - final Map args = call.arguments as Map; - if (call.method == 'createRegular') { - final List size = args['size']! as List; - final String state = args['state'] as String? ?? WindowState.restored.toString(); - - return {'viewId': tester.view.viewId, 'size': size, 'state': state}; - } else if (call.method == 'modifyRegular') { - return null; - } else if (call.method == 'destroyWindow') { - await tester.binding.defaultBinaryMessenger.handlePlatformMessage( - SystemChannels.windowing.name, - SystemChannels.windowing.codec.encodeMethodCall( - MethodCall('onWindowDestroyed', {'viewId': tester.view.viewId}), - ), - (ByteData? data) {}, - ); - - return null; - } - - throw Exception('Unsupported method call: ${call.method}'); - }; -} - -void main() { - testWidgets('RegularWindow widget populates the controller with proper values', ( - WidgetTester tester, - ) async { - const Size windowSize = Size(800, 600); - - tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( - SystemChannels.windowing, - _createWindowMethodCallHandler(tester: tester), - ); - - final RegularWindowController controller = RegularWindowController(size: windowSize); - - await tester.pump(); - - expect(controller.type, WindowArchetype.regular); - expect(controller.size, windowSize); - expect(controller.rootView.viewId, tester.view.viewId); - }); - - testWidgets('RegularWindow.onError is called when creation throws an error', ( - WidgetTester tester, - ) async { - const Size windowSize = Size(800, 600); - - tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.windowing, ( - MethodCall call, - ) async { - throw Exception('Failed to create the window'); - }); - - bool receivedError = false; - final RegularWindowController controller = RegularWindowController(size: windowSize); - - await tester.pump(); - - expect(receivedError, true); - }); - - testWidgets('RegularWindowController.destroy results in the RegularWindow.onDestroyed callback', ( - WidgetTester tester, - ) async { - const Size windowSize = Size(800, 600); - - tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( - SystemChannels.windowing, - _createWindowMethodCallHandler(tester: tester), - ); - - bool destroyed = false; - final RegularWindowController controller = RegularWindowController( - size: windowSize, - onDestroyed: () { - destroyed = true; - }, - ); - await tester.pumpWidget( - wrapWithView: false, - Builder( - builder: (BuildContext context) { - return RegularWindow(controller: controller, child: Container()); - }, - ), - ); - - await tester.pump(); - controller.destroy(); - - await tester.pump(); - expect(destroyed, true); - }); - - testWidgets( - 'RegularWindowController.size is updated when an onWindowChanged event is triggered on the channel', - (WidgetTester tester) async { - const Size initialSize = Size(800, 600); - const Size newSize = Size(400, 300); - - tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( - SystemChannels.windowing, - _createWindowMethodCallHandler(tester: tester), - ); - - final RegularWindowController controller = RegularWindowController(size: initialSize); - await tester.pump(); - - await tester.binding.defaultBinaryMessenger.handlePlatformMessage( - SystemChannels.windowing.name, - SystemChannels.windowing.codec.encodeMethodCall( - MethodCall('onWindowChanged', { - 'viewId': tester.view.viewId, - 'size': [newSize.width, newSize.height], - }), - ), - (ByteData? data) {}, - ); - await tester.pump(); - - expect(controller.size, newSize); - }, - ); - - testWidgets('RegularWindowController.modify can be called when provided with a "size" argument', ( - WidgetTester tester, - ) async { - const Size initialSize = Size(800, 600); - const Size newSize = Size(400, 300); - - bool wasCalled = false; - tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( - SystemChannels.windowing, - _createWindowMethodCallHandler( - tester: tester, - onMethodCall: (MethodCall call) { - if (call.method != 'modifyRegular') { - return; - } - - final Map args = call.arguments as Map; - final int viewId = args['viewId']! as int; - final List? size = args['size'] as List?; - final String? title = args['title'] as String?; - final String? state = args['state'] as String?; - expect(viewId, tester.view.viewId); - expect(size, [newSize.width, newSize.height]); - expect(title, null); - expect(state, null); - wasCalled = true; - }, - ), - ); - - final RegularWindowController controller = RegularWindowController(size: initialSize); - await tester.pump(); - - controller.modify(size: newSize); - await tester.pump(); - - expect(wasCalled, true); - }); - - testWidgets( - 'RegularWindowController.modify can be called when provided with a "title" argument', - (WidgetTester tester) async { - const Size initialSize = Size(800, 600); - const String newTitle = 'New Title'; - - bool wasCalled = false; - tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( - SystemChannels.windowing, - _createWindowMethodCallHandler( - tester: tester, - onMethodCall: (MethodCall call) { - if (call.method != 'modifyRegular') { - return; - } - - final Map args = call.arguments as Map; - final int viewId = args['viewId']! as int; - final List? size = args['size'] as List?; - final String? title = args['title'] as String?; - final String? state = args['state'] as String?; - expect(viewId, tester.view.viewId); - expect(size, null); - expect(title, newTitle); - expect(state, null); - wasCalled = true; - }, - ), - ); - - final RegularWindowController controller = RegularWindowController(size: initialSize); - await tester.pump(); - - controller.modify(title: newTitle); - await tester.pump(); - - expect(wasCalled, true); - }, - ); - - testWidgets( - 'RegularWindowController.modify can be called when provided with a "state" argument', - (WidgetTester tester) async { - const Size initialSize = Size(800, 600); - const WindowState newState = WindowState.minimized; - - bool wasCalled = false; - tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( - SystemChannels.windowing, - _createWindowMethodCallHandler( - tester: tester, - onMethodCall: (MethodCall call) { - if (call.method != 'modifyRegular') { - return; - } - - final Map args = call.arguments as Map; - final int viewId = args['viewId']! as int; - final List? size = args['size'] as List?; - final String? title = args['title'] as String?; - final String? state = args['state'] as String?; - expect(viewId, tester.view.viewId); - expect(size, null); - expect(title, null); - expect(state, newState.toString()); - wasCalled = true; - }, - ), - ); - - final RegularWindowController controller = RegularWindowController(size: initialSize); - await tester.pump(); - - controller.modify(state: newState); - await tester.pump(); - - expect(wasCalled, true); - }, - ); - - testWidgets('RegularWindowController.modify throws when no arguments are provided', ( - WidgetTester tester, - ) async { - const Size initialSize = Size(800, 600); - const WindowState newState = WindowState.minimized; - - tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( - SystemChannels.windowing, - _createWindowMethodCallHandler(tester: tester), - ); - - final RegularWindowController controller = RegularWindowController(size: initialSize); - await tester.pump(); - - expect(() async => controller.modify(), throwsA(isA())); - }); -} +// Future? Function(MethodCall)? _createWindowMethodCallHandler({ +// required WidgetTester tester, +// void Function(MethodCall)? onMethodCall, +// }) { +// return (MethodCall call) async { +// onMethodCall?.call(call); +// final Map args = call.arguments as Map; +// if (call.method == 'createRegular') { +// final List size = args['size']! as List; +// final String state = args['state'] as String? ?? WindowState.restored.toString(); + +// return {'viewId': tester.view.viewId, 'size': size, 'state': state}; +// } else if (call.method == 'modifyRegular') { +// return null; +// } else if (call.method == 'destroyWindow') { +// await tester.binding.defaultBinaryMessenger.handlePlatformMessage( +// SystemChannels.windowing.name, +// SystemChannels.windowing.codec.encodeMethodCall( +// MethodCall('onWindowDestroyed', {'viewId': tester.view.viewId}), +// ), +// (ByteData? data) {}, +// ); + +// return null; +// } + +// throw Exception('Unsupported method call: ${call.method}'); +// }; +// } + +// void main() { +// testWidgets('RegularWindow widget populates the controller with proper values', ( +// WidgetTester tester, +// ) async { +// const Size windowSize = Size(800, 600); + +// tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( +// SystemChannels.windowing, +// _createWindowMethodCallHandler(tester: tester), +// ); + +// final RegularWindowController controller = RegularWindowController(size: windowSize); + +// await tester.pump(); + +// expect(controller.type, WindowArchetype.regular); +// expect(controller.size, windowSize); +// expect(controller.rootView.viewId, tester.view.viewId); +// }); + +// testWidgets('RegularWindow.onError is called when creation throws an error', ( +// WidgetTester tester, +// ) async { +// const Size windowSize = Size(800, 600); + +// tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.windowing, ( +// MethodCall call, +// ) async { +// throw Exception('Failed to create the window'); +// }); + +// bool receivedError = false; +// final RegularWindowController controller = RegularWindowController(size: windowSize); + +// await tester.pump(); + +// expect(receivedError, true); +// }); + +// testWidgets('RegularWindowController.destroy results in the RegularWindow.onDestroyed callback', ( +// WidgetTester tester, +// ) async { +// const Size windowSize = Size(800, 600); + +// tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( +// SystemChannels.windowing, +// _createWindowMethodCallHandler(tester: tester), +// ); + +// bool destroyed = false; +// final RegularWindowController controller = RegularWindowController( +// size: windowSize, +// onDestroyed: () { +// destroyed = true; +// }, +// ); +// await tester.pumpWidget( +// wrapWithView: false, +// Builder( +// builder: (BuildContext context) { +// return RegularWindow(controller: controller, child: Container()); +// }, +// ), +// ); + +// await tester.pump(); +// controller.destroy(); + +// await tester.pump(); +// expect(destroyed, true); +// }); + +// testWidgets( +// 'RegularWindowController.size is updated when an onWindowChanged event is triggered on the channel', +// (WidgetTester tester) async { +// const Size initialSize = Size(800, 600); +// const Size newSize = Size(400, 300); + +// tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( +// SystemChannels.windowing, +// _createWindowMethodCallHandler(tester: tester), +// ); + +// final RegularWindowController controller = RegularWindowController(size: initialSize); +// await tester.pump(); + +// await tester.binding.defaultBinaryMessenger.handlePlatformMessage( +// SystemChannels.windowing.name, +// SystemChannels.windowing.codec.encodeMethodCall( +// MethodCall('onWindowChanged', { +// 'viewId': tester.view.viewId, +// 'size': [newSize.width, newSize.height], +// }), +// ), +// (ByteData? data) {}, +// ); +// await tester.pump(); + +// expect(controller.size, newSize); +// }, +// ); + +// testWidgets('RegularWindowController.modify can be called when provided with a "size" argument', ( +// WidgetTester tester, +// ) async { +// const Size initialSize = Size(800, 600); +// const Size newSize = Size(400, 300); + +// bool wasCalled = false; +// tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( +// SystemChannels.windowing, +// _createWindowMethodCallHandler( +// tester: tester, +// onMethodCall: (MethodCall call) { +// if (call.method != 'modifyRegular') { +// return; +// } + +// final Map args = call.arguments as Map; +// final int viewId = args['viewId']! as int; +// final List? size = args['size'] as List?; +// final String? title = args['title'] as String?; +// final String? state = args['state'] as String?; +// expect(viewId, tester.view.viewId); +// expect(size, [newSize.width, newSize.height]); +// expect(title, null); +// expect(state, null); +// wasCalled = true; +// }, +// ), +// ); + +// final RegularWindowController controller = RegularWindowController(size: initialSize); +// await tester.pump(); + +// controller.modify(size: newSize); +// await tester.pump(); + +// expect(wasCalled, true); +// }); + +// testWidgets( +// 'RegularWindowController.modify can be called when provided with a "title" argument', +// (WidgetTester tester) async { +// const Size initialSize = Size(800, 600); +// const String newTitle = 'New Title'; + +// bool wasCalled = false; +// tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( +// SystemChannels.windowing, +// _createWindowMethodCallHandler( +// tester: tester, +// onMethodCall: (MethodCall call) { +// if (call.method != 'modifyRegular') { +// return; +// } + +// final Map args = call.arguments as Map; +// final int viewId = args['viewId']! as int; +// final List? size = args['size'] as List?; +// final String? title = args['title'] as String?; +// final String? state = args['state'] as String?; +// expect(viewId, tester.view.viewId); +// expect(size, null); +// expect(title, newTitle); +// expect(state, null); +// wasCalled = true; +// }, +// ), +// ); + +// final RegularWindowController controller = RegularWindowController(size: initialSize); +// await tester.pump(); + +// controller.modify(title: newTitle); +// await tester.pump(); + +// expect(wasCalled, true); +// }, +// ); + +// testWidgets( +// 'RegularWindowController.modify can be called when provided with a "state" argument', +// (WidgetTester tester) async { +// const Size initialSize = Size(800, 600); +// const WindowState newState = WindowState.minimized; + +// bool wasCalled = false; +// tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( +// SystemChannels.windowing, +// _createWindowMethodCallHandler( +// tester: tester, +// onMethodCall: (MethodCall call) { +// if (call.method != 'modifyRegular') { +// return; +// } + +// final Map args = call.arguments as Map; +// final int viewId = args['viewId']! as int; +// final List? size = args['size'] as List?; +// final String? title = args['title'] as String?; +// final String? state = args['state'] as String?; +// expect(viewId, tester.view.viewId); +// expect(size, null); +// expect(title, null); +// expect(state, newState.toString()); +// wasCalled = true; +// }, +// ), +// ); + +// final RegularWindowController controller = RegularWindowController(size: initialSize); +// await tester.pump(); + +// controller.modify(state: newState); +// await tester.pump(); + +// expect(wasCalled, true); +// }, +// ); + +// testWidgets('RegularWindowController.modify throws when no arguments are provided', ( +// WidgetTester tester, +// ) async { +// const Size initialSize = Size(800, 600); +// const WindowState newState = WindowState.minimized; + +// tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( +// SystemChannels.windowing, +// _createWindowMethodCallHandler(tester: tester), +// ); + +// final RegularWindowController controller = RegularWindowController(size: initialSize); +// await tester.pump(); + +// expect(() async => controller.modify(), throwsA(isA())); +// }); +// } From 42e8b9830a15e16787c87b6409444d4c194fdc79 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Thu, 20 Feb 2025 16:22:57 +0100 Subject: [PATCH 17/52] Multiwindow example (macOS) --- examples/multi_window_ref_app/.gitignore | 45 ++ examples/multi_window_ref_app/.metadata | 13 +- .../multi_window_ref_app/.vscode/launch.json | 22 + .../multi_window_ref_app/macos/.gitignore | 7 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../macos/Runner.xcodeproj/project.pbxproj | 705 ++++++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 99 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../macos/Runner/AppDelegate.swift | 18 + .../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 | 332 +++++++++ .../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 + .../macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../macos/Runner/Release.entitlements | 8 + .../macos/RunnerTests/RunnerTests.swift | 12 + .../test/widget_test.dart | 30 + 31 files changed, 1468 insertions(+), 6 deletions(-) create mode 100644 examples/multi_window_ref_app/.gitignore create mode 100644 examples/multi_window_ref_app/macos/.gitignore create mode 100644 examples/multi_window_ref_app/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 examples/multi_window_ref_app/macos/Flutter/Flutter-Release.xcconfig create mode 100644 examples/multi_window_ref_app/macos/Runner.xcodeproj/project.pbxproj create mode 100644 examples/multi_window_ref_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 examples/multi_window_ref_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 examples/multi_window_ref_app/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 examples/multi_window_ref_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 examples/multi_window_ref_app/macos/Runner/AppDelegate.swift create mode 100644 examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 examples/multi_window_ref_app/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 examples/multi_window_ref_app/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 examples/multi_window_ref_app/macos/Runner/Configs/Debug.xcconfig create mode 100644 examples/multi_window_ref_app/macos/Runner/Configs/Release.xcconfig create mode 100644 examples/multi_window_ref_app/macos/Runner/Configs/Warnings.xcconfig create mode 100644 examples/multi_window_ref_app/macos/Runner/DebugProfile.entitlements create mode 100644 examples/multi_window_ref_app/macos/Runner/Info.plist create mode 100644 examples/multi_window_ref_app/macos/Runner/MainFlutterWindow.swift create mode 100644 examples/multi_window_ref_app/macos/Runner/Release.entitlements create mode 100644 examples/multi_window_ref_app/macos/RunnerTests/RunnerTests.swift create mode 100644 examples/multi_window_ref_app/test/widget_test.dart diff --git a/examples/multi_window_ref_app/.gitignore b/examples/multi_window_ref_app/.gitignore new file mode 100644 index 0000000000000..79c113f9b5017 --- /dev/null +++ b/examples/multi_window_ref_app/.gitignore @@ -0,0 +1,45 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/examples/multi_window_ref_app/.metadata b/examples/multi_window_ref_app/.metadata index 7608fd25e066a..c9660e951c922 100644 --- a/examples/multi_window_ref_app/.metadata +++ b/examples/multi_window_ref_app/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "4f182c3ce1f3a60a007b034cd5d9f2122ca2e5f1" + revision: "f07dbe9f9b40ecc5557632d6feb70a198dab5668" channel: "[user-branch]" project_type: app @@ -13,11 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: 4f182c3ce1f3a60a007b034cd5d9f2122ca2e5f1 - base_revision: 4f182c3ce1f3a60a007b034cd5d9f2122ca2e5f1 - - platform: windows - create_revision: 4f182c3ce1f3a60a007b034cd5d9f2122ca2e5f1 - base_revision: 4f182c3ce1f3a60a007b034cd5d9f2122ca2e5f1 + create_revision: f07dbe9f9b40ecc5557632d6feb70a198dab5668 + base_revision: f07dbe9f9b40ecc5557632d6feb70a198dab5668 + - platform: macos + create_revision: f07dbe9f9b40ecc5557632d6feb70a198dab5668 + base_revision: f07dbe9f9b40ecc5557632d6feb70a198dab5668 # User provided section @@ -27,3 +27,4 @@ migration: # Files that are not part of the templates will be ignored by default. unmanaged_files: - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/examples/multi_window_ref_app/.vscode/launch.json b/examples/multi_window_ref_app/.vscode/launch.json index 1121ee8a51e3b..c846434f923e5 100644 --- a/examples/multi_window_ref_app/.vscode/launch.json +++ b/examples/multi_window_ref_app/.vscode/launch.json @@ -4,6 +4,28 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "name": "multi_window_ref_app", + "request": "launch", + "type": "dart", + "program": "lib/main.dart", + "toolArgs": [ + "--local-engine=host_debug_unopt_arm64", + "--local-engine-host=host_debug_unopt_arm64", + ] + }, + { + "name": "multi_window_ref_app (profile mode)", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "multi_window_ref_app (release mode)", + "request": "launch", + "type": "dart", + "flutterMode": "release" + }, { "name": "(Windows) Attach", "type": "cppvsdbg", diff --git a/examples/multi_window_ref_app/macos/.gitignore b/examples/multi_window_ref_app/macos/.gitignore new file mode 100644 index 0000000000000..746adbb6b9e14 --- /dev/null +++ b/examples/multi_window_ref_app/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/examples/multi_window_ref_app/macos/Flutter/Flutter-Debug.xcconfig b/examples/multi_window_ref_app/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000000000..c2efd0b608ba8 --- /dev/null +++ b/examples/multi_window_ref_app/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/examples/multi_window_ref_app/macos/Flutter/Flutter-Release.xcconfig b/examples/multi_window_ref_app/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000000000..c2efd0b608ba8 --- /dev/null +++ b/examples/multi_window_ref_app/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/examples/multi_window_ref_app/macos/Runner.xcodeproj/project.pbxproj b/examples/multi_window_ref_app/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000000000..4fe23bf3fc61a --- /dev/null +++ b/examples/multi_window_ref_app/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,705 @@ +// !$*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 */ + 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 */; }; +/* 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 */ + 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 /* multi_window_ref_app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "multi_window_ref_app.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 = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + 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 */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* multi_window_ref_app.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 = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 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 = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* multi_window_ref_app.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + 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"; + }; +/* 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; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.multiWindowRefApp.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/multi_window_ref_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/multi_window_ref_app"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.multiWindowRefApp.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/multi_window_ref_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/multi_window_ref_app"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.multiWindowRefApp.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/multi_window_ref_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/multi_window_ref_app"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + 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; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + 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; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + 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/examples/multi_window_ref_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/multi_window_ref_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000000..18d981003d68d --- /dev/null +++ b/examples/multi_window_ref_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/examples/multi_window_ref_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/examples/multi_window_ref_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000000000..e7813cac2f1bf --- /dev/null +++ b/examples/multi_window_ref_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/multi_window_ref_app/macos/Runner.xcworkspace/contents.xcworkspacedata b/examples/multi_window_ref_app/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000000..1d526a16ed0f1 --- /dev/null +++ b/examples/multi_window_ref_app/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/multi_window_ref_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/multi_window_ref_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000000..18d981003d68d --- /dev/null +++ b/examples/multi_window_ref_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/examples/multi_window_ref_app/macos/Runner/AppDelegate.swift b/examples/multi_window_ref_app/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000000000..2247b567278c1 --- /dev/null +++ b/examples/multi_window_ref_app/macos/Runner/AppDelegate.swift @@ -0,0 +1,18 @@ +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } + + override func applicationDidFinishLaunching(_ notification: Notification) { + let engine = FlutterEngine(name: "project", project: nil); + engine.run(withEntrypoint:nil); + } +} diff --git a/examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000000..a2ec33f19f110 --- /dev/null +++ b/examples/multi_window_ref_app/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/examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/examples/multi_window_ref_app/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/examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/examples/multi_window_ref_app/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/examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/examples/multi_window_ref_app/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/examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/examples/multi_window_ref_app/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/examples/multi_window_ref_app/macos/Runner/Configs/AppInfo.xcconfig b/examples/multi_window_ref_app/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000000000..35840c478829e --- /dev/null +++ b/examples/multi_window_ref_app/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 = multi_window_ref_app + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.multiWindowRefApp + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2025 com.example. All rights reserved. diff --git a/examples/multi_window_ref_app/macos/Runner/Configs/Debug.xcconfig b/examples/multi_window_ref_app/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000000000..36b0fd9464f45 --- /dev/null +++ b/examples/multi_window_ref_app/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/examples/multi_window_ref_app/macos/Runner/Configs/Release.xcconfig b/examples/multi_window_ref_app/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000000000..dff4f49561c81 --- /dev/null +++ b/examples/multi_window_ref_app/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/examples/multi_window_ref_app/macos/Runner/Configs/Warnings.xcconfig b/examples/multi_window_ref_app/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000000000..42bcbf4780b18 --- /dev/null +++ b/examples/multi_window_ref_app/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/examples/multi_window_ref_app/macos/Runner/DebugProfile.entitlements b/examples/multi_window_ref_app/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000000000..dddb8a30c851e --- /dev/null +++ b/examples/multi_window_ref_app/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/examples/multi_window_ref_app/macos/Runner/Info.plist b/examples/multi_window_ref_app/macos/Runner/Info.plist new file mode 100644 index 0000000000000..4789daa6a443e --- /dev/null +++ b/examples/multi_window_ref_app/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/examples/multi_window_ref_app/macos/Runner/MainFlutterWindow.swift b/examples/multi_window_ref_app/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000000000..3cc05eb234916 --- /dev/null +++ b/examples/multi_window_ref_app/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/examples/multi_window_ref_app/macos/Runner/Release.entitlements b/examples/multi_window_ref_app/macos/Runner/Release.entitlements new file mode 100644 index 0000000000000..852fa1a4728ae --- /dev/null +++ b/examples/multi_window_ref_app/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/examples/multi_window_ref_app/macos/RunnerTests/RunnerTests.swift b/examples/multi_window_ref_app/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000000000..61f3bd1fc504c --- /dev/null +++ b/examples/multi_window_ref_app/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Cocoa +import FlutterMacOS +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/examples/multi_window_ref_app/test/widget_test.dart b/examples/multi_window_ref_app/test/widget_test.dart new file mode 100644 index 0000000000000..66c3df7577f1c --- /dev/null +++ b/examples/multi_window_ref_app/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:multi_window_ref_app/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // // Build our app and trigger a frame. + // await tester.pumpWidget(const MyApp()); + + // // Verify that our counter starts at 0. + // expect(find.text('0'), findsOneWidget); + // expect(find.text('1'), findsNothing); + + // // Tap the '+' icon and trigger a frame. + // await tester.tap(find.byIcon(Icons.add)); + // await tester.pump(); + + // // Verify that our counter has incremented. + // expect(find.text('0'), findsNothing); + // expect(find.text('1'), findsOneWidget); + }); +} From 9f82e217b6481a52af28b23f357e47159224e613 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 11 Mar 2025 15:05:13 +0100 Subject: [PATCH 18/52] Keep engine reference --- .../multi_window_ref_app/macos/Runner/AppDelegate.swift | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/multi_window_ref_app/macos/Runner/AppDelegate.swift b/examples/multi_window_ref_app/macos/Runner/AppDelegate.swift index 2247b567278c1..840ecce51c98f 100644 --- a/examples/multi_window_ref_app/macos/Runner/AppDelegate.swift +++ b/examples/multi_window_ref_app/macos/Runner/AppDelegate.swift @@ -10,9 +10,12 @@ class AppDelegate: FlutterAppDelegate { override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { return true } - + + var engine : FlutterEngine?; + + override func applicationDidFinishLaunching(_ notification: Notification) { - let engine = FlutterEngine(name: "project", project: nil); - engine.run(withEntrypoint:nil); + engine = FlutterEngine(name: "project", project: nil); + engine?.run(withEntrypoint:nil); } } From 36db3a58fdfc321724d38b6004462b01914a3a06 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 11 Mar 2025 15:14:57 +0100 Subject: [PATCH 19/52] Add macOS implementation --- .../shell/platform/darwin/macos/BUILD.gn | 3 + .../macos/framework/Source/FlutterEngine.mm | 120 +++++++++- .../framework/Source/FlutterEngine_Internal.h | 9 + .../Source/FlutterPlatformViewController.h | 2 +- .../framework/Source/FlutterVSyncWaiter.mm | 6 +- .../framework/Source/FlutterViewController.mm | 7 +- .../Source/FlutterViewController_Internal.h | 2 + .../Source/FlutterWindowController.h | 14 ++ .../Source/FlutterWindowController.mm | 220 ++++++++++++++++++ packages/flutter/lib/src/widgets/window.dart | 3 + .../flutter/lib/src/widgets/window_macos.dart | 194 +++++++++++++++ 11 files changed, 564 insertions(+), 16 deletions(-) create mode 100644 engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h create mode 100644 engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm create mode 100644 packages/flutter/lib/src/widgets/window_macos.dart diff --git a/engine/src/flutter/shell/platform/darwin/macos/BUILD.gn b/engine/src/flutter/shell/platform/darwin/macos/BUILD.gn index 09fd490246100..8b5cacdf83ed2 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/BUILD.gn +++ b/engine/src/flutter/shell/platform/darwin/macos/BUILD.gn @@ -119,6 +119,8 @@ source_set("flutter_framework_source") { "framework/Source/FlutterViewEngineProvider.h", "framework/Source/FlutterViewEngineProvider.mm", "framework/Source/FlutterViewProvider.h", + "framework/Source/FlutterWindowController.h", + "framework/Source/FlutterWindowController.mm", "framework/Source/KeyCodeMap.g.mm", ] @@ -131,6 +133,7 @@ source_set("flutter_framework_source") { "//flutter/shell/platform/common:common_cpp_accessibility", "//flutter/shell/platform/common:common_cpp_enums", "//flutter/shell/platform/common:common_cpp_input", + "//flutter/shell/platform/common:common_cpp_isolate_scope", "//flutter/shell/platform/common:common_cpp_switches", "//flutter/shell/platform/darwin/common:availability_version_check", "//flutter/shell/platform/darwin/common:framework_common", diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 620bab516bf52..02a09e3726849 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -450,6 +450,8 @@ @implementation FlutterEngine { // factories. Lifecycle is tied to the engine. FlutterPlatformViewController* _platformViewController; + FlutterWindowController* _windowController; + // A message channel for sending user settings to the flutter engine. FlutterBasicMessageChannel* _settingsChannel; @@ -481,8 +483,13 @@ @implementation FlutterEngine { // The text input plugin that handles text editing state for text fields. FlutterTextInputPlugin* _textInputPlugin; + + BOOL _multiviewEnabled; + FlutterViewIdentifier _nextViewIdentifier; } +@synthesize windowController = _windowController; + - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project { return [self initWithName:labelPrefix project:project allowHeadlessExecution:YES]; } @@ -526,6 +533,8 @@ - (instancetype)initWithName:(NSString*)labelPrefix [_isResponseValid addObject:@YES]; _keyboardManager = [[FlutterKeyboardManager alloc] initWithDelegate:self]; _textInputPlugin = [[FlutterTextInputPlugin alloc] initWithDelegate:self]; + _multiviewEnabled = NO; + _nextViewIdentifier = 1; _embedderAPI.struct_size = sizeof(FlutterEngineProcTable); FlutterEngineGetProcAddresses(&_embedderAPI); @@ -547,6 +556,10 @@ - (instancetype)initWithName:(NSString*)labelPrefix [[FlutterTimeConverter alloc] initWithEngine:self], _platformViewController); [self setUpPlatformViewChannel]; + + _windowController = [[FlutterWindowController alloc] init]; + _windowController.engine = self; + [self setUpAccessibilityChannel]; [self setUpNotificationCenterListeners]; id appDelegate = [[NSApplication sharedApplication] delegate]; @@ -727,6 +740,11 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { [engine onVSync:baton]; }; + flutterArguments.view_focus_change_request_callback = [](const FlutterViewFocusChangeRequest* req, + void* user_data) { + NSLog(@"Focus calllback %lli %i\n", req->view_id, req->state); + }; + FlutterRendererConfig rendererConfig = [_renderer createRendererConfig]; FlutterEngineResult result = _embedderAPI.Initialize( FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge void*)(self), &_engine); @@ -787,10 +805,10 @@ - (void)registerViewController:(FlutterViewController*)controller forIdentifier:(FlutterViewIdentifier)viewIdentifier { _macOSCompositor->AddView(viewIdentifier); NSAssert(controller != nil, @"The controller must not be nil."); - NSAssert(controller.engine == nil, - @"The FlutterViewController is unexpectedly attached to " - @"engine %@ before initialization.", - controller.engine); + // NSAssert(controller.engine == nil, + // @"The FlutterViewController is unexpectedly attached to " + // @"engine %@ before initialization.", + // controller.engine); NSAssert([_viewControllers objectForKey:@(viewIdentifier)] == nil, @"The requested view ID is occupied."); [_viewControllers setObject:controller forKey:@(viewIdentifier)]; @@ -808,6 +826,26 @@ - (void)registerViewController:(FlutterViewController*)controller if (controller.viewLoaded) { [self viewControllerViewDidLoad:controller]; } + + if (viewIdentifier != kFlutterImplicitViewId) { + fml::AutoResetWaitableEvent latch; + FlutterWindowMetricsEvent metrics{ + .struct_size = sizeof(FlutterWindowMetricsEvent), + .width = 100, + .height = 100, + .pixel_ratio = 2.0, + }; + FlutterAddViewInfo info{.struct_size = sizeof(FlutterAddViewInfo), + .view_id = viewIdentifier, + .view_metrics = &metrics, + .user_data = &latch, + .add_view_callback = [](const FlutterAddViewResult* r) { + auto l = reinterpret_cast(r->user_data); + l->Signal(); + }}; + _embedderAPI.AddView(_engine, &info); + latch.Wait(); + } } - (void)viewControllerViewDidLoad:(FlutterViewController*)viewController { @@ -825,14 +863,33 @@ - (void)viewControllerViewDidLoad:(FlutterViewController*)viewController { engine->_embedderAPI.OnVsync(_engine, baton, timeNanos, targetTimeNanos); } }]; - FML_DCHECK([_vsyncWaiters objectForKey:@(viewController.viewIdentifier)] == nil); @synchronized(_vsyncWaiters) { + FML_DCHECK([_vsyncWaiters objectForKey:@(viewController.viewIdentifier)] == nil); [_vsyncWaiters setObject:waiter forKey:@(viewController.viewIdentifier)]; } } - (void)deregisterViewControllerForIdentifier:(FlutterViewIdentifier)viewIdentifier { + if (viewIdentifier != kFlutterImplicitViewId) { + bool removed = false; + FlutterRemoveViewInfo info; + info.struct_size = sizeof(FlutterRemoveViewInfo); + info.view_id = viewIdentifier; + info.user_data = &removed; + info.remove_view_callback = [](const FlutterRemoveViewResult* r) { + auto removed = reinterpret_cast(r->user_data); + [FlutterRunLoop.mainRunLoop performBlock:^{ + *removed = true; + }]; + }; + _embedderAPI.RemoveView(_engine, &info); + while (!removed) { + [[FlutterRunLoop mainRunLoop] pollFlutterMessagesOnce]; + } + } + _macOSCompositor->RemoveView(viewIdentifier); + FlutterViewController* controller = [self viewControllerForIdentifier:viewIdentifier]; // The controller can be nil. The engine stores only a weak ref, and this // method could have been called from the controller's dealloc. @@ -933,11 +990,44 @@ - (FlutterCompositor*)createFlutterCompositor { #pragma mark - Framework-internal methods - (void)addViewController:(FlutterViewController*)controller { - // FlutterEngine can only handle the implicit view for now. Adding more views - // throws an assertion. - NSAssert(self.viewController == nil, - @"The engine already has a view controller for the implicit view."); - self.viewController = controller; + if (!_multiviewEnabled) { + // FlutterEngine can only handle the implicit view for now. Adding more views + // throws an assertion. + NSAssert(self.viewController == nil, + @"The engine already has a view controller for the implicit view."); + self.viewController = controller; + } else { + FlutterViewIdentifier viewIdentifier = _nextViewIdentifier++; + [self registerViewController:controller forIdentifier:viewIdentifier]; + } +} + +- (void)enableMultiView { + if (!_multiviewEnabled) { + NSAssert(self.viewController == nil, + @"Multiview can only be enabled before adding any view controllers."); + _multiviewEnabled = YES; + } +} + +- (void)windowDidBecomeKey:(FlutterViewIdentifier)viewIdentifier { + FlutterViewFocusEvent event{ + .struct_size = sizeof(FlutterViewFocusEvent), + .view_id = viewIdentifier, + .state = kFocused, + .direction = kUndefined, + }; + _embedderAPI.SendViewFocusEvent(_engine, &event); +} + +- (void)windowDidResignKey:(FlutterViewIdentifier)viewIdentifier { + FlutterViewFocusEvent event{ + .struct_size = sizeof(FlutterViewFocusEvent), + .view_id = viewIdentifier, + .state = kUnfocused, + .direction = kUndefined, + }; + _embedderAPI.SendViewFocusEvent(_engine, &event); } - (void)removeViewController:(nonnull FlutterViewController*)viewController { @@ -1161,8 +1251,13 @@ - (void)onVSync:(uintptr_t)baton { auto block = ^{ // TODO(knopp): Use vsync waiter for correct view. // https://github.com/flutter/flutter/issues/142845 - FlutterVSyncWaiter* waiter = [_vsyncWaiters objectForKey:@(kFlutterImplicitViewId)]; - [waiter waitForVSync:baton]; + FlutterVSyncWaiter* waiter = + [_vsyncWaiters objectForKey:[_vsyncWaiters.keyEnumerator nextObject]]; + if (waiter != nil) { + [waiter waitForVSync:baton]; + } else { + self.embedderAPI.OnVsync(_engine, baton, 0, 0); + } }; if ([NSThread isMainThread]) { block(); @@ -1244,6 +1339,7 @@ - (void)addInternalPlugins { [FlutterMouseCursorPlugin registerWithRegistrar:[self registrarForPlugin:@"mousecursor"] delegate:self]; [FlutterMenuPlugin registerWithRegistrar:[self registrarForPlugin:@"menu"]]; + _settingsChannel = [FlutterBasicMessageChannel messageChannelWithName:kFlutterSettingsChannel binaryMessenger:self.binaryMessenger diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h index ec34b267a9246..7c4d90599a799 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h @@ -18,6 +18,7 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h" NS_ASSUME_NONNULL_BEGIN @@ -223,6 +224,13 @@ typedef NS_ENUM(NSInteger, FlutterAppExitResponse) { */ @property(nonatomic, readonly) FlutterTextInputPlugin* textInputPlugin; +@property(nonatomic, readonly) FlutterWindowController* windowController; + +- (void)enableMultiView; + +- (void)windowDidBecomeKey:(FlutterViewIdentifier)viewIdentifier; +- (void)windowDidResignKey:(FlutterViewIdentifier)viewIdentifier; + /** * Returns an array of screen objects representing all of the screens available on the system. */ @@ -238,6 +246,7 @@ typedef NS_ENUM(NSInteger, FlutterAppExitResponse) { * This function must be called on the main thread. */ + (nullable FlutterEngine*)engineForIdentifier:(int64_t)identifier; + @end NS_ASSUME_NONNULL_END diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h index a784a69b94d0b..db89d04f5faa7 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h @@ -14,7 +14,7 @@ #include #include -@interface FlutterPlatformViewController : NSViewController +@interface FlutterPlatformViewController : NSObject @end @interface FlutterPlatformViewController () diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterVSyncWaiter.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterVSyncWaiter.mm index 884ae112167a9..5a4546084b43a 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterVSyncWaiter.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterVSyncWaiter.mm @@ -114,7 +114,7 @@ - (void)waitForVSync:(uintptr_t)baton { TRACE_VSYNC("VSyncRequest", _pendingBaton.value_or(0)); CFTimeInterval tick_interval = _displayLink.nominalOutputRefreshPeriod; - if (_displayLink.paused || tick_interval == 0) { + if (_displayLink.paused || tick_interval == 0 || _lastTargetTimestamp == 0) { // When starting display link the first notification will come in the middle // of next frame, which would incur a whole frame period of latency. // To avoid that, first vsync notification will be fired using a timer @@ -153,7 +153,9 @@ - (void)waitForVSync:(uintptr_t)baton { - (void)dealloc { if (_pendingBaton.has_value()) { - FML_LOG(WARNING) << "Deallocating FlutterVSyncWaiter with a pending vsync"; + CFTimeInterval now = CACurrentMediaTime(); + _block(now, now, _pendingBaton.value()); + _pendingBaton = std::nullopt; } // It is possible that block running on UI thread held the last reference to // the waiter, in which case reschedule to main thread. diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 5346c452dae70..4111bc52937ab 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -327,6 +327,7 @@ static void CommonInit(FlutterViewController* controller, FlutterEngine* engine) @"engine %@ before initialization.", controller.engine); [engine addViewController:controller]; + NSCAssert(controller.engine != nil, @"The FlutterViewController unexpectedly stays unattached after initialization. " @"In unit tests, this is likely because either the FlutterViewController or " @@ -418,13 +419,17 @@ - (void)viewWillDisappear { _keyUpMonitor = nil; } -- (void)dealloc { +- (void)dispose { if ([self attached]) { [_engine removeViewController:self]; } [self.flutterView shutDown]; } +- (void)dealloc { + [self dispose]; +} + #pragma mark - Public methods - (void)setMouseTrackingMode:(FlutterMouseTrackingMode)mode { diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h index 301d584fdf61e..c021add4a9199 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h @@ -53,6 +53,8 @@ */ - (void)updateSemantics:(nonnull const FlutterSemanticsUpdate2*)update; +- (void)dispose; + @end // Private methods made visible for testing diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h new file mode 100644 index 0000000000000..5c5ea66abdf52 --- /dev/null +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h @@ -0,0 +1,14 @@ +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_MACOS_FRAMEWORK_SOURCE_FLUTTERWINDOWCONTROLLER_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_MACOS_FRAMEWORK_SOURCE_FLUTTERWINDOWCONTROLLER_H_ + +#import + +@class FlutterEngine; + +@interface FlutterWindowController : NSObject + +@property(nonatomic, weak) FlutterEngine* engine; + +@end + +#endif // FLUTTER_SHELL_PLATFORM_DARWIN_MACOS_FRAMEWORK_SOURCE_FLUTTERWINDOWCONTROLLER_H_ diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm new file mode 100644 index 0000000000000..9b85f78bf7b0d --- /dev/null +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm @@ -0,0 +1,220 @@ +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h" + +#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" +#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" + +#include "flutter/shell/platform/common/isolate_scope.h" + +struct FlutterWindowCreationRequest { + double width; + double height; + double min_width; + double min_height; + double max_width; + double max_height; + void (*on_close)(); + void (*on_size_change)(); +}; + +/// A delegate for a Flutter managed window. +@interface FlutterWindowOwner : NSObject { + /// Strong reference to the window. This is the only strong reference to the + /// window. + NSWindow* _window; + FlutterViewController* _flutterViewController; + flutter::Isolate _isolate; + FlutterWindowCreationRequest _creationRequest; +} + +@property(readonly, nonatomic) NSWindow* window; +@property(readonly, nonatomic) FlutterViewController* flutterViewController; + +- (instancetype)initWithWindow:(NSWindow*)window + flutterViewController:(FlutterViewController*)viewController + creationRequest:(const FlutterWindowCreationRequest&)creationRequest; + +@end + +@implementation FlutterWindowOwner + +@synthesize window = _window; +@synthesize flutterViewController = _flutterViewController; + +- (instancetype)initWithWindow:(NSWindow*)window + flutterViewController:(FlutterViewController*)viewController + creationRequest:(const FlutterWindowCreationRequest&)creationRequest { + if (self = [super init]) { + _window = window; + _flutterViewController = viewController; + _creationRequest = creationRequest; + } + return self; +} + +- (void)windowDidBecomeKey:(NSNotification*)notification { + [_flutterViewController.engine windowDidBecomeKey:_flutterViewController.viewIdentifier]; +} + +- (void)windowDidResignKey:(NSNotification*)notification { + [_flutterViewController.engine windowDidResignKey:_flutterViewController.viewIdentifier]; +} + +- (BOOL)windowShouldClose:(NSWindow*)sender { + flutter::IsolateScope isolate_scope(_isolate); + _creationRequest.on_close(); + return NO; +} + +- (void)windowDidResize:(NSNotification*)notification { + flutter::IsolateScope isolate_scope(_isolate); + _creationRequest.on_size_change(); +} + +@end + +@interface FlutterWindowController () { + NSMutableArray* _windows; +} + +@end + +@implementation FlutterWindowController + +- (instancetype)init { + self = [super init]; + if (self != nil) { + _windows = [NSMutableArray array]; + } + return self; +} + +- (FlutterViewIdentifier)createRegularWindow:(const FlutterWindowCreationRequest*)request { + FlutterViewController* c = [[FlutterViewController alloc] initWithEngine:_engine + nibName:nil + bundle:nil]; + + NSWindow* window = [[NSWindow alloc] init]; + // If this is not set there will be double free on window close when + // using ARC. + [window setReleasedWhenClosed:NO]; + + window.contentViewController = c; + [window setContentSize:NSMakeSize(request->width, request->height)]; + window.styleMask = NSWindowStyleMaskResizable | NSWindowStyleMaskTitled | + NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable; + [window setIsVisible:YES]; + [window makeKeyAndOrderFront:nil]; + + FlutterWindowOwner* w = [[FlutterWindowOwner alloc] initWithWindow:window + flutterViewController:c + creationRequest:*request]; + window.delegate = w; + [_windows addObject:w]; + + return c.viewIdentifier; +} + +- (void)destroyWindow:(NSWindow*)window { + FlutterWindowOwner* owner = nil; + for (FlutterWindowOwner* o in _windows) { + if (o.window == window) { + owner = o; + break; + } + } + if (owner != nil) { + [_windows removeObject:owner]; + [owner.flutterViewController dispose]; + owner.window.delegate = nil; + [owner.window close]; + } +} + +@end + +extern "C" { +// NOLINTBEGIN(google-objc-function-naming) + +FLUTTER_DARWIN_EXPORT +int64_t flutter_create_regular_window(int64_t engine_id, + const FlutterWindowCreationRequest* request) { + FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id]; + [engine enableMultiView]; + return [engine.windowController createRegularWindow:request]; +} + +FLUTTER_DARWIN_EXPORT +void flutter_destroy_window(int64_t engine_id, void* window) { + NSWindow* w = (__bridge NSWindow*)window; + FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id]; + [engine.windowController destroyWindow:w]; +} + +FLUTTER_DARWIN_EXPORT +void* flutter_get_window_handle(int64_t engine_id, FlutterViewIdentifier view_id) { + FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id]; + FlutterViewController* controller = [engine viewControllerForIdentifier:view_id]; + return (__bridge void*)controller.view.window; +} + +struct FlutterWindowSize { + double width; + double height; +}; + +FLUTTER_DARWIN_EXPORT +void flutter_get_window_size(void* window, FlutterWindowSize* size) { + NSWindow* w = (__bridge NSWindow*)window; + size->width = w.frame.size.width; + size->height = w.frame.size.height; +} + +FLUTTER_DARWIN_EXPORT +void flutter_set_window_size(void* window, double width, double height) { + NSWindow* w = (__bridge NSWindow*)window; + [w setContentSize:NSMakeSize(width, height)]; +} + +FLUTTER_DARWIN_EXPORT +void flutter_set_window_title(void* window, const char* title) { + NSWindow* w = (__bridge NSWindow*)window; + w.title = [NSString stringWithUTF8String:title]; +} + +FLUTTER_DARWIN_EXPORT +int64_t flutter_get_window_state(void* window) { + NSWindow* w = (__bridge NSWindow*)window; + if (w.isZoomed) { + return 1; + } else if (w.isMiniaturized) { + return 2; + } else { + return 0; + } +} + +FLUTTER_DARWIN_EXPORT +void flutter_set_window_state(void* window, int64_t state) { + NSWindow* w = (__bridge NSWindow*)window; + if (state == 1) { + [w zoom:nil]; + } else if (state == 2) { + [w miniaturize:nil]; + } else { + if (w.isMiniaturized) { + [w deminiaturize:nil]; + } else if (w.isZoomed) { + [w zoom:nil]; + } else { + bool isFullScreen = (w.styleMask & NSWindowStyleMaskFullScreen) != 0; + if (isFullScreen) { + [w toggleFullScreen:nil]; + } + } + } +} + +// NOLINTEND(google-objc-function-naming) +} // extern "C" diff --git a/packages/flutter/lib/src/widgets/window.dart b/packages/flutter/lib/src/widgets/window.dart index dc0b29b299fea..f07c8efe42bd6 100644 --- a/packages/flutter/lib/src/widgets/window.dart +++ b/packages/flutter/lib/src/widgets/window.dart @@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; +import 'window_macos.dart'; import 'window_win32.dart'; /// Defines the possible archetypes for a window. @@ -185,6 +186,8 @@ abstract class WindowingOwner { static WindowingOwner createDefaultOwner() { if (defaultTargetPlatform == TargetPlatform.windows) { return WindowingOwnerWin32(); + } else if (defaultTargetPlatform == TargetPlatform.macOS) { + return WindowingOwnerMacOS(); } else { return _FallbackWindowingOwner(); } diff --git a/packages/flutter/lib/src/widgets/window_macos.dart b/packages/flutter/lib/src/widgets/window_macos.dart new file mode 100644 index 0000000000000..b1ea43deaf41e --- /dev/null +++ b/packages/flutter/lib/src/widgets/window_macos.dart @@ -0,0 +1,194 @@ +import 'dart:ffi' hide Size; +import 'dart:ui' show FlutterView; + +import 'package:ffi/ffi.dart' as ffi; +import 'package:flutter/material.dart'; +import 'package:flutter/src/foundation/binding.dart'; + +class WindowingOwnerMacOS extends WindowingOwner { + @override + RegularWindowController createRegularWindowController({ + required Size size, + required RegularWindowControllerDelegate delegate, + BoxConstraints? sizeConstraints, + }) { + final RegularWindowControllerMacOS res = RegularWindowControllerMacOS( + owner: this, + delegate: delegate, + size: size, + sizeConstraints: sizeConstraints, + ); + _activeControllers.add(res); + return res; + } + + @override + bool hasTopLevelWindows() { + return _activeControllers.isNotEmpty; + } + + final List _activeControllers = []; +} + +class RegularWindowControllerMacOS extends RegularWindowController { + RegularWindowControllerMacOS({ + required WindowingOwnerMacOS owner, + required RegularWindowControllerDelegate delegate, + BoxConstraints? sizeConstraints, + required Size size, + String? title, + }) : _owner = owner, + _delegate = delegate, + super.empty() { + _onClose = NativeCallable.isolateLocal(_handleOnClose); + _onResize = NativeCallable.isolateLocal(_handleOnResize); + final Pointer<_WindowCreationRequest> request = + ffi.calloc<_WindowCreationRequest>() + ..ref.width = size.width + ..ref.height = size.height + ..ref.minWidth = sizeConstraints?.minWidth ?? 0 + ..ref.minHeight = sizeConstraints?.minHeight ?? 0 + ..ref.maxWidth = sizeConstraints?.maxWidth ?? 0 + ..ref.maxHeight = sizeConstraints?.maxHeight ?? 0 + ..ref.onClose = _onClose.nativeFunction + ..ref.onSizeChange = _onResize.nativeFunction; + + final int viewId = _createWindow(PlatformDispatcher.instance.engineId!, request); + ffi.calloc.free(request); + final FlutterView flutterView = WidgetsBinding.instance.platformDispatcher.views.firstWhere( + (FlutterView view) => view.viewId == viewId, + ); + setView(flutterView); + if (title != null) { + setTitle(title); + } + } + + Pointer getWindowHandle() { + return _getWindowHandle(PlatformDispatcher.instance.engineId!, rootView.viewId); + } + + bool _destroyed = false; + + @override + void destroy() { + if (_destroyed) { + return; + } + _destroyed = true; + _owner._activeControllers.remove(this); + _destroyWindow(PlatformDispatcher.instance.engineId!, getWindowHandle()); + _delegate.onWindowDestroyed(); + _onClose.close(); + _onResize.close(); + } + + void _handleOnClose() { + _delegate.onWindowCloseRequested(this); + } + + void _handleOnResize() { + notifyListeners(); + } + + void setSize(Size size) { + _setWindowSize(getWindowHandle(), size.width, size.height); + } + + void setTitle(String title) { + final Pointer titlePointer = title.toNativeUtf8(); + _setWindowTitle(getWindowHandle(), titlePointer); + ffi.calloc.free(titlePointer); + } + + @override + void modify({Size? size, String? title, WindowState? state}) { + if (size != null) { + setSize(size); + } + if (title != null) { + setTitle(title); + } + if (state != null) { + setState(state); + } + } + + final WindowingOwnerMacOS _owner; + final RegularWindowControllerDelegate _delegate; + late final NativeCallable _onClose; + late final NativeCallable _onResize; + + @override + Size get size { + final Pointer<_Size> size = ffi.calloc<_Size>(); + _getWindowSize(getWindowHandle(), size); + final Size result = Size(size.ref.width, size.ref.height); + ffi.calloc.free(size); + return result; + } + + @override + WindowState get state => WindowState.values[_getWindowState(getWindowHandle())]; + + void setState(WindowState state) { + _setWindowState(getWindowHandle(), state.index); + } + + @Native)>( + symbol: 'flutter_create_regular_window', + ) + external static int _createWindow(int engineId, Pointer<_WindowCreationRequest> request); + + @Native Function(Int64, Int64)>(symbol: 'flutter_get_window_handle') + external static Pointer _getWindowHandle(int engineId, int viewId); + + @Native)>(symbol: 'flutter_destroy_window') + external static void _destroyWindow(int engineId, Pointer handle); + + @Native, Pointer<_Size>)>(symbol: 'flutter_get_window_size') + external static void _getWindowSize(Pointer windowHandle, Pointer<_Size> size); + + @Native, Double, Double)>(symbol: 'flutter_set_window_size') + external static void _setWindowSize(Pointer windowHandle, double width, double height); + + @Native, Pointer)>(symbol: 'flutter_set_window_title') + external static void _setWindowTitle(Pointer windowHandle, Pointer title); + + @Native)>(symbol: 'flutter_get_window_state') + external static int _getWindowState(Pointer windowHandle); + + @Native, Int64)>(symbol: 'flutter_set_window_state') + external static void _setWindowState(Pointer windowHandle, int state); +} + +final class _WindowCreationRequest extends Struct { + @Double() + external double width; + + @Double() + external double height; + + @Double() + external double minWidth; + + @Double() + external double minHeight; + + @Double() + external double maxWidth; + + @Double() + external double maxHeight; + + external Pointer> onClose; + external Pointer> onSizeChange; +} + +final class _Size extends Struct { + @Double() + external double width; + + @Double() + external double height; +} From 3302efd86b7475d33920af19362bc04566c41a16 Mon Sep 17 00:00:00 2001 From: Matej Date: Tue, 11 Mar 2025 20:02:16 +0100 Subject: [PATCH 20/52] Linux windowing --- .../src/flutter/shell/platform/linux/BUILD.gn | 3 + .../flutter/shell/platform/linux/fl_engine.cc | 11 + .../shell/platform/linux/fl_engine_private.h | 3 + .../shell/platform/linux/fl_windowing.cc | 299 ++++++++++++++++++ .../shell/platform/linux/fl_windowing.h | 28 ++ packages/flutter/lib/src/widgets/window.dart | 3 + .../flutter/lib/src/widgets/window_linux.dart | 194 ++++++++++++ 7 files changed, 541 insertions(+) create mode 100644 engine/src/flutter/shell/platform/linux/fl_windowing.cc create mode 100644 engine/src/flutter/shell/platform/linux/fl_windowing.h create mode 100644 packages/flutter/lib/src/widgets/window_linux.dart diff --git a/engine/src/flutter/shell/platform/linux/BUILD.gn b/engine/src/flutter/shell/platform/linux/BUILD.gn index f44abc08c260f..c210813fb9697 100644 --- a/engine/src/flutter/shell/platform/linux/BUILD.gn +++ b/engine/src/flutter/shell/platform/linux/BUILD.gn @@ -161,6 +161,8 @@ source_set("flutter_linux_sources") { "fl_window_state_monitor.cc", "fl_windowing_channel.cc", "fl_windowing_handler.cc", + "fl_windowing.cc", + "fl_windowing.h", "key_mapping.g.cc", ] @@ -174,6 +176,7 @@ source_set("flutter_linux_sources") { "//flutter/fml", "//flutter/shell/platform/common:common_cpp_enums", "//flutter/shell/platform/common:common_cpp_input", + "//flutter/shell/platform/common:common_cpp_isolate_scope", "//flutter/shell/platform/common:common_cpp_switches", "//flutter/shell/platform/embedder:embedder_headers", "//flutter/third_party/rapidjson", diff --git a/engine/src/flutter/shell/platform/linux/fl_engine.cc b/engine/src/flutter/shell/platform/linux/fl_engine.cc index 6a3481e9dbc87..a6612c70da3b2 100644 --- a/engine/src/flutter/shell/platform/linux/fl_engine.cc +++ b/engine/src/flutter/shell/platform/linux/fl_engine.cc @@ -25,6 +25,7 @@ #include "flutter/shell/platform/linux/fl_settings_handler.h" #include "flutter/shell/platform/linux/fl_texture_gl_private.h" #include "flutter/shell/platform/linux/fl_texture_registrar_private.h" +#include "flutter/shell/platform/linux/fl_windowing.h" #include "flutter/shell/platform/linux/fl_windowing_handler.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h" @@ -66,6 +67,8 @@ struct _FlEngine { // Implements the flutter/windowing channel. FlWindowingHandler* windowing_handler; + FlWindowingController* windowing_controller; + // Process keyboard events. FlKeyboardManager* keyboard_manager; @@ -490,6 +493,7 @@ static void fl_engine_dispose(GObject* object) { g_clear_object(&self->settings_handler); g_clear_object(&self->platform_handler); g_clear_object(&self->windowing_handler); + g_clear_object(&self->windowing_controller); g_clear_object(&self->keyboard_manager); g_clear_object(&self->text_input_handler); g_clear_object(&self->keyboard_handler); @@ -571,6 +575,7 @@ static FlEngine* fl_engine_new_full(FlDartProject* project, self->mouse_cursor_handler = fl_mouse_cursor_handler_new(self->binary_messenger); self->windowing_handler = fl_windowing_handler_new(self); + self->windowing_controller = fl_windowing_controller_new(self); return self; } @@ -636,6 +641,7 @@ gboolean fl_engine_start(FlEngine* self, GError** error) { FlutterCustomTaskRunners custom_task_runners = {}; custom_task_runners.struct_size = sizeof(FlutterCustomTaskRunners); custom_task_runners.platform_task_runner = &platform_task_runner; + custom_task_runners.ui_task_runner = &platform_task_runner; g_autoptr(GPtrArray) command_line_args = g_ptr_array_new_with_free_func(g_free); @@ -1321,6 +1327,11 @@ FlWindowingHandler* fl_engine_get_windowing_handler(FlEngine* self) { return self->windowing_handler; } +FlWindowingController* fl_engine_get_windowing_controller(FlEngine* self) { + g_return_val_if_fail(FL_IS_ENGINE(self), nullptr); + return self->windowing_controller; +} + FlKeyboardManager* fl_engine_get_keyboard_manager(FlEngine* self) { g_return_val_if_fail(FL_IS_ENGINE(self), nullptr); return self->keyboard_manager; diff --git a/engine/src/flutter/shell/platform/linux/fl_engine_private.h b/engine/src/flutter/shell/platform/linux/fl_engine_private.h index 6447ea96b44ef..33df2a943237a 100644 --- a/engine/src/flutter/shell/platform/linux/fl_engine_private.h +++ b/engine/src/flutter/shell/platform/linux/fl_engine_private.h @@ -16,6 +16,7 @@ #include "flutter/shell/platform/linux/fl_renderable.h" #include "flutter/shell/platform/linux/fl_task_runner.h" #include "flutter/shell/platform/linux/fl_text_input_handler.h" +#include "flutter/shell/platform/linux/fl_windowing.h" #include "flutter/shell/platform/linux/fl_windowing_handler.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h" @@ -583,6 +584,8 @@ void fl_engine_request_app_exit(FlEngine* engine); */ FlWindowingHandler* fl_engine_get_windowing_handler(FlEngine* engine); +FlWindowingController* fl_engine_get_windowing_controller(FlEngine* engine); + /** * fl_engine_get_keyboard_manager: * @engine: an #FlEngine. diff --git a/engine/src/flutter/shell/platform/linux/fl_windowing.cc b/engine/src/flutter/shell/platform/linux/fl_windowing.cc new file mode 100644 index 0000000000000..551ca7182edbe --- /dev/null +++ b/engine/src/flutter/shell/platform/linux/fl_windowing.cc @@ -0,0 +1,299 @@ +// 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 "flutter/shell/platform/linux/fl_windowing.h" +#include "flutter/shell/platform/common/isolate_scope.h" +#include "flutter/shell/platform/linux/fl_engine_private.h" + +typedef struct { + GWeakRef engine; + GHashTable* windows_by_view_id; +} FlWindowingControllerPrivate; + +struct FlutterWindowCreationRequest { + double width; + double height; + double min_width; + double min_height; + double max_width; + double max_height; + void (*on_close)(); + void (*on_size_change)(); +}; + +G_DEFINE_TYPE_WITH_PRIVATE(FlWindowingController, + fl_windowing_controller, + G_TYPE_OBJECT) + +typedef struct { + GtkWindow* window; + FlView* view; + guint delete_id; + guint size_allocate_id; + void (*on_close)(); + void (*on_size_change)(); + flutter::Isolate* isolate; +} WindowData; + +static WindowData* window_data_new(GtkWindow* window, FlView* view) { + WindowData* data = g_new0(WindowData, 1); + data->window = GTK_WINDOW(g_object_ref(window)); + data->view = FL_VIEW(g_object_ref(view)); + data->isolate = new flutter::Isolate(); + return data; +} + +static void window_data_free(WindowData* data) { + g_signal_handler_disconnect(data->window, data->delete_id); + g_signal_handler_disconnect(data->window, data->size_allocate_id); + g_object_unref(data->window); + g_object_unref(data->view); + delete data->isolate; + g_free(data); +} + +static void fl_windowing_controller_dispose(GObject* object) { + G_OBJECT_CLASS(fl_windowing_controller_parent_class)->dispose(object); + + FlWindowingController* self = FL_WINDOWING_CONTROLLER(object); + FlWindowingControllerPrivate* priv = + reinterpret_cast( + fl_windowing_controller_get_instance_private(self)); + + g_weak_ref_clear(&priv->engine); + g_clear_pointer(&priv->windows_by_view_id, g_hash_table_unref); +} + +static void fl_windowing_controller_class_init( + FlWindowingControllerClass* klass) { + G_OBJECT_CLASS(klass)->dispose = fl_windowing_controller_dispose; +} + +static void fl_windowing_controller_init(FlWindowingController* self) { + FlWindowingControllerPrivate* priv = + reinterpret_cast( + fl_windowing_controller_get_instance_private(self)); + priv->windows_by_view_id = + g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr, + reinterpret_cast(window_data_free)); +} + +FlWindowingController* fl_windowing_controller_new(FlEngine* engine) { + g_return_val_if_fail(FL_IS_ENGINE(engine), nullptr); + + FlWindowingController* self = FL_WINDOWING_CONTROLLER( + g_object_new(fl_windowing_controller_get_type(), nullptr)); + FlWindowingControllerPrivate* priv = + reinterpret_cast( + fl_windowing_controller_get_instance_private(self)); + + g_weak_ref_init(&priv->engine, engine); + + return self; +} + +static gboolean on_window_delete(GtkWidget* widget, + GdkEvent* event, + gpointer user_data) { + WindowData* data = static_cast(user_data); + if (data->on_close) { + flutter::IsolateScope isolate_scope(*data->isolate); + data->on_close(); + } + return TRUE; +} + +static gboolean on_size_allocate(GtkWidget* widget, + GdkRectangle* allocation, + gpointer user_data) { + WindowData* data = static_cast(user_data); + if (data->on_size_change) { + flutter::IsolateScope isolate_scope(*data->isolate); + data->on_size_change(); + } + return FALSE; +} + +static int64_t fl_windowing_controller_create_regular_window( + FlWindowingController* self, + const FlutterWindowCreationRequest* request) { + FlWindowingControllerPrivate* priv = + reinterpret_cast( + fl_windowing_controller_get_instance_private(self)); + g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&priv->engine)); + g_return_val_if_fail(engine != nullptr, 0); + + FlView* view = fl_view_new_for_engine(engine); + gtk_widget_show(GTK_WIDGET(view)); + + GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + gtk_window_set_default_size(GTK_WINDOW(window), request->width, + request->height); + + WindowData* data = window_data_new(GTK_WINDOW(window), view); + + data->delete_id = g_signal_connect(window, "delete-event", + G_CALLBACK(on_window_delete), data); + data->size_allocate_id = g_signal_connect(window, "size-allocate", + G_CALLBACK(on_size_allocate), data); + data->on_close = request->on_close; + data->on_size_change = request->on_size_change; + + GdkGeometry geometry; + + GdkWindowHints geometry_mask = static_cast(0); + if (request->min_width != 0 && request->min_height != 0) { + geometry.min_width = request->min_width; + geometry.min_height = request->min_height; + geometry_mask = + static_cast(geometry_mask | GDK_HINT_MIN_SIZE); + } + if (request->max_width != 0 && request->max_height != 0) { + geometry.max_width = request->max_width; + geometry.max_height = request->max_height; + geometry_mask = + static_cast(geometry_mask | GDK_HINT_MAX_SIZE); + } + if (geometry_mask != 0) { + gtk_window_set_geometry_hints(GTK_WINDOW(window), nullptr, &geometry, + geometry_mask); + } + + // Make the resources for the view so rendering can start. + // We'll show the view when we have the first frame. + gtk_widget_realize(GTK_WIDGET(view)); + + // TODO(knopp): Do this when we have content rendered. + gtk_widget_show(window); + + g_hash_table_insert(priv->windows_by_view_id, + GINT_TO_POINTER(fl_view_get_id(view)), data); + + return fl_view_get_id(view); +} + +static void fl_windowing_controller_destroy_window(FlWindowingController* self, + int64_t view_id) { + FlWindowingControllerPrivate* priv = + reinterpret_cast( + fl_windowing_controller_get_instance_private(self)); + WindowData* data = static_cast( + g_hash_table_lookup(priv->windows_by_view_id, GINT_TO_POINTER(view_id))); + if (data == nullptr) { + return; + } + gtk_widget_destroy(GTK_WIDGET(data->window)); + g_hash_table_remove(priv->windows_by_view_id, GINT_TO_POINTER(view_id)); +} + +static WindowData* get_window_data(FlWindowingController* self, + int64_t view_id) { + FlWindowingControllerPrivate* priv = + reinterpret_cast( + fl_windowing_controller_get_instance_private(self)); + return static_cast( + g_hash_table_lookup(priv->windows_by_view_id, GINT_TO_POINTER(view_id))); +} + +extern "C" { +// NOLINTBEGIN(google-objc-function-naming) + +G_MODULE_EXPORT +int64_t flutter_create_regular_window( + int64_t engine_id, + const FlutterWindowCreationRequest* request) { + FlEngine* engine = fl_engine_for_id(engine_id); + FlWindowingController* controller = + fl_engine_get_windowing_controller(engine); + return fl_windowing_controller_create_regular_window(controller, request); +} + +G_MODULE_EXPORT +void* flutter_get_window_handle(int64_t engine_id, int64_t view_id) { + FlEngine* engine = fl_engine_for_id(engine_id); + FlWindowingController* controller = + fl_engine_get_windowing_controller(engine); + WindowData* data = get_window_data(controller, view_id); + if (data == nullptr) { + return 0; + } + return data->window; +} + +struct FlutterWindowSize { + double width; + double height; +}; + +G_MODULE_EXPORT +void flutter_get_window_size(void* window, FlutterWindowSize* size) { + GtkWindow* gtk_window = GTK_WINDOW(window); + gint width, height; + gtk_window_get_size(gtk_window, &width, &height); + size->width = width; + size->height = height; +} + +G_MODULE_EXPORT +void flutter_set_window_size(void* window, const FlutterWindowSize* size) { + GtkWindow* gtk_window = GTK_WINDOW(window); + gtk_window_resize(gtk_window, size->width, size->height); +} + +G_MODULE_EXPORT +void flutter_set_window_title(void* window, const char* title) { + GtkWindow* gtk_window = GTK_WINDOW(window); + gtk_window_set_title(gtk_window, title); +} + +G_MODULE_EXPORT +int64_t flutter_get_window_state(void* window) { + GtkWindow* gtk_window = GTK_WINDOW(window); + GdkWindowState window_state = + gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(gtk_window))); + if (window_state & GDK_WINDOW_STATE_ICONIFIED) { + return 2; + } + if (gtk_window_is_maximized(gtk_window)) { + return 1; + } + return 0; +} + +G_MODULE_EXPORT +void flutter_set_window_state(void* window, int64_t state) { + GtkWindow* gtk_window = GTK_WINDOW(window); + GdkWindowState window_state; + switch (state) { + case 0: + if (gtk_window_is_maximized(gtk_window)) { + gtk_window_unmaximize(gtk_window); + } + window_state = + gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(window))); + if (window_state & GDK_WINDOW_STATE_ICONIFIED) { + gtk_window_deiconify(gtk_window); + } + break; + case 1: + gtk_window_maximize(gtk_window); + break; + case 2: + gtk_window_iconify(gtk_window); + break; + } +} + +G_MODULE_EXPORT +void flutter_destroy_window(int64_t engine_id, int64_t view_id) { + FlEngine* engine = fl_engine_for_id(engine_id); + FlWindowingController* controller = + fl_engine_get_windowing_controller(engine); + fl_windowing_controller_destroy_window(controller, view_id); +} + +// NOLINTEND(google-objc-function-naming) +} diff --git a/engine/src/flutter/shell/platform/linux/fl_windowing.h b/engine/src/flutter/shell/platform/linux/fl_windowing.h new file mode 100644 index 0000000000000..cf94b41b94825 --- /dev/null +++ b/engine/src/flutter/shell/platform/linux/fl_windowing.h @@ -0,0 +1,28 @@ +// 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_SHELL_PLATFORM_LINUX_FL_WINDOWING_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_WINDOWING_H_ + +#include + +#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h" + +G_BEGIN_DECLS + +G_DECLARE_DERIVABLE_TYPE(FlWindowingController, + fl_windowing_controller, + FL, + WINDOWING_CONTROLLER, + GObject); + +struct _FlWindowingControllerClass { + GObjectClass parent_class; +}; + +FlWindowingController* fl_windowing_controller_new(FlEngine* engine); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_WINDOWING_H_ diff --git a/packages/flutter/lib/src/widgets/window.dart b/packages/flutter/lib/src/widgets/window.dart index f07c8efe42bd6..feb9b9e01d33c 100644 --- a/packages/flutter/lib/src/widgets/window.dart +++ b/packages/flutter/lib/src/widgets/window.dart @@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; +import 'window_linux.dart'; import 'window_macos.dart'; import 'window_win32.dart'; @@ -188,6 +189,8 @@ abstract class WindowingOwner { return WindowingOwnerWin32(); } else if (defaultTargetPlatform == TargetPlatform.macOS) { return WindowingOwnerMacOS(); + } else if (defaultTargetPlatform == TargetPlatform.linux) { + return WindowingOwnerLinux(); } else { return _FallbackWindowingOwner(); } diff --git a/packages/flutter/lib/src/widgets/window_linux.dart b/packages/flutter/lib/src/widgets/window_linux.dart new file mode 100644 index 0000000000000..0a37dfbdb2af8 --- /dev/null +++ b/packages/flutter/lib/src/widgets/window_linux.dart @@ -0,0 +1,194 @@ +import 'dart:ffi' hide Size; +import 'dart:ui' show FlutterView; + +import 'package:ffi/ffi.dart' as ffi; +import 'package:flutter/material.dart'; +import 'package:flutter/src/foundation/binding.dart'; + +class WindowingOwnerLinux extends WindowingOwner { + @override + RegularWindowController createRegularWindowController({ + required Size size, + required RegularWindowControllerDelegate delegate, + BoxConstraints? sizeConstraints, + }) { + final RegularWindowControllerLinux res = RegularWindowControllerLinux( + owner: this, + delegate: delegate, + size: size, + sizeConstraints: sizeConstraints, + ); + _activeControllers.add(res); + return res; + } + + @override + bool hasTopLevelWindows() { + return _activeControllers.isNotEmpty; + } + + final List _activeControllers = []; +} + +class RegularWindowControllerLinux extends RegularWindowController { + RegularWindowControllerLinux({ + required WindowingOwnerLinux owner, + required RegularWindowControllerDelegate delegate, + BoxConstraints? sizeConstraints, + required Size size, + String? title, + }) : _owner = owner, + _delegate = delegate, + super.empty() { + _onClose = NativeCallable.isolateLocal(_handleOnClose); + _onResize = NativeCallable.isolateLocal(_handleOnResize); + final Pointer<_WindowCreationRequest> request = + ffi.calloc<_WindowCreationRequest>() + ..ref.width = size.width + ..ref.height = size.height + ..ref.minWidth = sizeConstraints?.minWidth ?? 0 + ..ref.minHeight = sizeConstraints?.minHeight ?? 0 + ..ref.maxWidth = sizeConstraints?.maxWidth ?? 0 + ..ref.maxHeight = sizeConstraints?.maxHeight ?? 0 + ..ref.onClose = _onClose.nativeFunction + ..ref.onSizeChange = _onResize.nativeFunction; + + final int viewId = _createWindow(PlatformDispatcher.instance.engineId!, request); + ffi.calloc.free(request); + final FlutterView flutterView = WidgetsBinding.instance.platformDispatcher.views.firstWhere( + (FlutterView view) => view.viewId == viewId, + ); + setView(flutterView); + if (title != null) { + setTitle(title); + } + } + + Pointer getWindowHandle() { + return _getWindowHandle(PlatformDispatcher.instance.engineId!, rootView.viewId); + } + + bool _destroyed = false; + + @override + void destroy() { + if (_destroyed) { + return; + } + _destroyed = true; + _owner._activeControllers.remove(this); + _destroyWindow(PlatformDispatcher.instance.engineId!, rootView.viewId); + _delegate.onWindowDestroyed(); + _onClose.close(); + _onResize.close(); + } + + void _handleOnClose() { + _delegate.onWindowCloseRequested(this); + } + + void _handleOnResize() { + notifyListeners(); + } + + void setSize(Size size) { + _setWindowSize(getWindowHandle(), size.width, size.height); + } + + void setTitle(String title) { + final Pointer titlePointer = title.toNativeUtf8(); + _setWindowTitle(getWindowHandle(), titlePointer); + ffi.calloc.free(titlePointer); + } + + @override + void modify({Size? size, String? title, WindowState? state}) { + if (size != null) { + setSize(size); + } + if (title != null) { + setTitle(title); + } + if (state != null) { + setState(state); + } + } + + final WindowingOwnerLinux _owner; + final RegularWindowControllerDelegate _delegate; + late final NativeCallable _onClose; + late final NativeCallable _onResize; + + @override + Size get size { + final Pointer<_Size> size = ffi.calloc<_Size>(); + _getWindowSize(getWindowHandle(), size); + final Size result = Size(size.ref.width, size.ref.height); + ffi.calloc.free(size); + return result; + } + + @override + WindowState get state => WindowState.values[_getWindowState(getWindowHandle())]; + + void setState(WindowState state) { + _setWindowState(getWindowHandle(), state.index); + } + + @Native)>( + symbol: 'flutter_create_regular_window', + ) + external static int _createWindow(int engineId, Pointer<_WindowCreationRequest> request); + + @Native Function(Int64, Int64)>(symbol: 'flutter_get_window_handle') + external static Pointer _getWindowHandle(int engineId, int viewId); + + @Native(symbol: 'flutter_destroy_window') + external static void _destroyWindow(int engineId, int viewId); + + @Native, Pointer<_Size>)>(symbol: 'flutter_get_window_size') + external static void _getWindowSize(Pointer windowHandle, Pointer<_Size> size); + + @Native, Double, Double)>(symbol: 'flutter_set_window_size') + external static void _setWindowSize(Pointer windowHandle, double width, double height); + + @Native, Pointer)>(symbol: 'flutter_set_window_title') + external static void _setWindowTitle(Pointer windowHandle, Pointer title); + + @Native)>(symbol: 'flutter_get_window_state') + external static int _getWindowState(Pointer windowHandle); + + @Native, Int64)>(symbol: 'flutter_set_window_state') + external static void _setWindowState(Pointer windowHandle, int state); +} + +final class _WindowCreationRequest extends Struct { + @Double() + external double width; + + @Double() + external double height; + + @Double() + external double minWidth; + + @Double() + external double minHeight; + + @Double() + external double maxWidth; + + @Double() + external double maxHeight; + + external Pointer> onClose; + external Pointer> onSizeChange; +} + +final class _Size extends Struct { + @Double() + external double width; + + @Double() + external double height; +} From 52ebe3b3f6a9d133b70a2f0e61db2c8ebbb59ff9 Mon Sep 17 00:00:00 2001 From: Matej Date: Tue, 11 Mar 2025 20:02:29 +0100 Subject: [PATCH 21/52] temporarily disable resize syncrhonization on linux --- .../src/flutter/shell/platform/linux/fl_compositor_opengl.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/src/flutter/shell/platform/linux/fl_compositor_opengl.cc b/engine/src/flutter/shell/platform/linux/fl_compositor_opengl.cc index 20d2a6ca84bfa..2b141a654a043 100644 --- a/engine/src/flutter/shell/platform/linux/fl_compositor_opengl.cc +++ b/engine/src/flutter/shell/platform/linux/fl_compositor_opengl.cc @@ -152,7 +152,7 @@ static void fl_compositor_opengl_unblock_main_thread(FlCompositorOpenGL* self) { g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine)); if (engine != nullptr) { - fl_task_runner_release_main_thread(fl_engine_get_task_runner(engine)); + // fl_task_runner_release_main_thread(fl_engine_get_task_runner(engine)); } } } @@ -491,7 +491,7 @@ static void fl_compositor_opengl_wait_for_frame(FlCompositor* compositor, self->blocking_main_thread = true; g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine)); if (engine != nullptr) { - fl_task_runner_block_main_thread(fl_engine_get_task_runner(engine)); + // fl_task_runner_block_main_thread(fl_engine_get_task_runner(engine)); } } } From 6d41b33c9738aa55a48b6e719d23069dec16002a Mon Sep 17 00:00:00 2001 From: Matej Date: Tue, 11 Mar 2025 20:13:06 +0100 Subject: [PATCH 22/52] Use Gtk methods --- .../src/flutter/shell/platform/linux/fl_windowing.cc | 12 ------------ packages/flutter/lib/src/widgets/window_linux.dart | 4 ++-- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/engine/src/flutter/shell/platform/linux/fl_windowing.cc b/engine/src/flutter/shell/platform/linux/fl_windowing.cc index 551ca7182edbe..feb5df6271410 100644 --- a/engine/src/flutter/shell/platform/linux/fl_windowing.cc +++ b/engine/src/flutter/shell/platform/linux/fl_windowing.cc @@ -237,18 +237,6 @@ void flutter_get_window_size(void* window, FlutterWindowSize* size) { size->height = height; } -G_MODULE_EXPORT -void flutter_set_window_size(void* window, const FlutterWindowSize* size) { - GtkWindow* gtk_window = GTK_WINDOW(window); - gtk_window_resize(gtk_window, size->width, size->height); -} - -G_MODULE_EXPORT -void flutter_set_window_title(void* window, const char* title) { - GtkWindow* gtk_window = GTK_WINDOW(window); - gtk_window_set_title(gtk_window, title); -} - G_MODULE_EXPORT int64_t flutter_get_window_state(void* window) { GtkWindow* gtk_window = GTK_WINDOW(window); diff --git a/packages/flutter/lib/src/widgets/window_linux.dart b/packages/flutter/lib/src/widgets/window_linux.dart index 0a37dfbdb2af8..ed385039cbc5e 100644 --- a/packages/flutter/lib/src/widgets/window_linux.dart +++ b/packages/flutter/lib/src/widgets/window_linux.dart @@ -149,10 +149,10 @@ class RegularWindowControllerLinux extends RegularWindowController { @Native, Pointer<_Size>)>(symbol: 'flutter_get_window_size') external static void _getWindowSize(Pointer windowHandle, Pointer<_Size> size); - @Native, Double, Double)>(symbol: 'flutter_set_window_size') + @Native, Double, Double)>(symbol: 'gtk_window_resize') external static void _setWindowSize(Pointer windowHandle, double width, double height); - @Native, Pointer)>(symbol: 'flutter_set_window_title') + @Native, Pointer)>(symbol: 'gtk_window_set_title') external static void _setWindowTitle(Pointer windowHandle, Pointer title); @Native)>(symbol: 'flutter_get_window_state') From 79af168f7d00a0b2ca24385d05424b997655083c Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Thu, 3 Apr 2025 18:08:30 +0200 Subject: [PATCH 23/52] Reformat --- engine/src/flutter/shell/platform/linux/BUILD.gn | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/src/flutter/shell/platform/linux/BUILD.gn b/engine/src/flutter/shell/platform/linux/BUILD.gn index c210813fb9697..43c8f7c17c4fb 100644 --- a/engine/src/flutter/shell/platform/linux/BUILD.gn +++ b/engine/src/flutter/shell/platform/linux/BUILD.gn @@ -159,10 +159,10 @@ source_set("flutter_linux_sources") { "fl_view.cc", "fl_view_accessible.cc", "fl_window_state_monitor.cc", - "fl_windowing_channel.cc", - "fl_windowing_handler.cc", "fl_windowing.cc", "fl_windowing.h", + "fl_windowing_channel.cc", + "fl_windowing_handler.cc", "key_mapping.g.cc", ] From d7c9ad3934729e68446080b3db764f6b664ecf20 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Wed, 9 Apr 2025 16:37:13 +0200 Subject: [PATCH 24/52] Add macOS test --- .../Source/FlutterWindowController.h | 57 +++++++++++++++++++ .../Source/FlutterWindowController.mm | 47 ++++----------- .../Source/FlutterWindowControllerTest.mm | 47 +++++++++++++++ .../Source/fixtures/flutter_desktop_test.dart | 5 ++ 4 files changed, 121 insertions(+), 35 deletions(-) create mode 100644 engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowControllerTest.mm diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h index 5c5ea66abdf52..8f0465fe5c53d 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h @@ -1,8 +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. + #ifndef FLUTTER_SHELL_PLATFORM_DARWIN_MACOS_FRAMEWORK_SOURCE_FLUTTERWINDOWCONTROLLER_H_ #define FLUTTER_SHELL_PLATFORM_DARWIN_MACOS_FRAMEWORK_SOURCE_FLUTTERWINDOWCONTROLLER_H_ #import +#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" +#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h" + @class FlutterEngine; @interface FlutterWindowController : NSObject @@ -11,4 +18,54 @@ @end +struct FlutterWindowCreationRequest { + double width; + double height; + double min_width; + double min_height; + double max_width; + double max_height; + void (*on_close)(); + void (*on_size_change)(); +}; + +struct FlutterWindowSize { + double width; + double height; +}; + +extern "C" { + +// NOLINTBEGIN(google-objc-function-naming) + +FLUTTER_DARWIN_EXPORT +int64_t FlutterCreateRegularWindow(int64_t engine_id, const FlutterWindowCreationRequest* request); + +FLUTTER_DARWIN_EXPORT +void FlutterDestroyWindow(int64_t engine_id, void* window); + +FLUTTER_DARWIN_EXPORT +void* FlutterGetWindowHandle(int64_t engine_id, FlutterViewIdentifier view_id); + +FLUTTER_DARWIN_EXPORT +void* FlutterGetWindowHandle(int64_t engine_id, FlutterViewIdentifier view_id); + +FLUTTER_DARWIN_EXPORT +void FlutterGetWindowSize(void* window, FlutterWindowSize* size); + +FLUTTER_DARWIN_EXPORT +void FlutterSetWindowSize(void* window, double width, double height); + +FLUTTER_DARWIN_EXPORT +void FlutterSetWindowTitle(void* window, const char* title); + +FLUTTER_DARWIN_EXPORT +int64_t FlutterGetWindowState(void* window); + +FLUTTER_DARWIN_EXPORT +void FlutterSetWindowState(void* window, int64_t state); + +// NOLINTEND(google-objc-function-naming) +} + #endif // FLUTTER_SHELL_PLATFORM_DARWIN_MACOS_FRAMEWORK_SOURCE_FLUTTERWINDOWCONTROLLER_H_ diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm index 9b85f78bf7b0d..80cd2948b8ead 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm @@ -1,3 +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 "flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h" #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" @@ -7,17 +11,6 @@ #include "flutter/shell/platform/common/isolate_scope.h" -struct FlutterWindowCreationRequest { - double width; - double height; - double min_width; - double min_height; - double max_width; - double max_height; - void (*on_close)(); - void (*on_size_change)(); -}; - /// A delegate for a Flutter managed window. @interface FlutterWindowOwner : NSObject { /// Strong reference to the window. This is the only strong reference to the @@ -134,57 +127,43 @@ - (void)destroyWindow:(NSWindow*)window { @end -extern "C" { // NOLINTBEGIN(google-objc-function-naming) -FLUTTER_DARWIN_EXPORT -int64_t flutter_create_regular_window(int64_t engine_id, - const FlutterWindowCreationRequest* request) { +int64_t FlutterCreateRegularWindow(int64_t engine_id, const FlutterWindowCreationRequest* request) { FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id]; [engine enableMultiView]; return [engine.windowController createRegularWindow:request]; } -FLUTTER_DARWIN_EXPORT -void flutter_destroy_window(int64_t engine_id, void* window) { +void FlutterDestroyWindow(int64_t engine_id, void* window) { NSWindow* w = (__bridge NSWindow*)window; FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id]; [engine.windowController destroyWindow:w]; } -FLUTTER_DARWIN_EXPORT -void* flutter_get_window_handle(int64_t engine_id, FlutterViewIdentifier view_id) { +void* FlutterGetWindowHandle(int64_t engine_id, FlutterViewIdentifier view_id) { FlutterEngine* engine = [FlutterEngine engineForIdentifier:engine_id]; FlutterViewController* controller = [engine viewControllerForIdentifier:view_id]; return (__bridge void*)controller.view.window; } -struct FlutterWindowSize { - double width; - double height; -}; - -FLUTTER_DARWIN_EXPORT -void flutter_get_window_size(void* window, FlutterWindowSize* size) { +void FlutterGetWindowSize(void* window, FlutterWindowSize* size) { NSWindow* w = (__bridge NSWindow*)window; size->width = w.frame.size.width; size->height = w.frame.size.height; } -FLUTTER_DARWIN_EXPORT -void flutter_set_window_size(void* window, double width, double height) { +void FlutterSetWindowSize(void* window, double width, double height) { NSWindow* w = (__bridge NSWindow*)window; [w setContentSize:NSMakeSize(width, height)]; } -FLUTTER_DARWIN_EXPORT -void flutter_set_window_title(void* window, const char* title) { +void FlutterSetWindowTitle(void* window, const char* title) { NSWindow* w = (__bridge NSWindow*)window; w.title = [NSString stringWithUTF8String:title]; } -FLUTTER_DARWIN_EXPORT -int64_t flutter_get_window_state(void* window) { +int64_t FlutterGetWindowState(void* window) { NSWindow* w = (__bridge NSWindow*)window; if (w.isZoomed) { return 1; @@ -195,8 +174,7 @@ int64_t flutter_get_window_state(void* window) { } } -FLUTTER_DARWIN_EXPORT -void flutter_set_window_state(void* window, int64_t state) { +void FlutterSetWindowState(void* window, int64_t state) { NSWindow* w = (__bridge NSWindow*)window; if (state == 1) { [w zoom:nil]; @@ -217,4 +195,3 @@ void flutter_set_window_state(void* window, int64_t state) { } // NOLINTEND(google-objc-function-naming) -} // extern "C" diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowControllerTest.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowControllerTest.mm new file mode 100644 index 0000000000000..30adc224288e1 --- /dev/null +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowControllerTest.mm @@ -0,0 +1,47 @@ +// 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 "flutter/shell/platform/common/isolate_scope.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineTestUtils.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h" +#import "flutter/testing/testing.h" +#import "third_party/googletest/googletest/include/gtest/gtest.h" + +namespace flutter::testing { + +using FlutterWindowControllerTest = FlutterEngineTest; + +TEST_F(FlutterWindowControllerTest, Test1) { + FlutterEngine* engine = GetFlutterEngine(); + FlutterWindowController* controller = [[FlutterWindowController alloc] init]; + controller.engine = engine; + + std::unique_ptr isolate; + bool signalled = false; + + AddNativeCallback("SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { + fprintf(stderr, "Signal native test\n"); + isolate = std::make_unique(); + signalled = true; + })); + + EXPECT_TRUE([engine runWithEntrypoint:@"testWindowController"]); + while (!signalled) { + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES); + } + + FlutterWindowCreationRequest request{ + .width = 800, + .height = 600, + .on_close = [] {}, + .on_size_change = [] {}, + }; + + int64_t engine_id = reinterpret_cast(engine); + + IsolateScope isolate_scope(*isolate.get()); + int64_t handle = FlutterCreateRegularWindow(engine_id, &request); + EXPECT_EQ(handle, 1); +} +} // namespace flutter::testing diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/fixtures/flutter_desktop_test.dart b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/fixtures/flutter_desktop_test.dart index 208d466cbf487..5099dd364127d 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/fixtures/flutter_desktop_test.dart +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/fixtures/flutter_desktop_test.dart @@ -93,3 +93,8 @@ external void notifyEngineId(int? engineId); void testEngineId() { notifyEngineId(PlatformDispatcher.instance.engineId); } + +@pragma('vm:entry-point') +void testWindowController() { + signalNativeTest(); +} From 22ccae8ccc45b4ee36c5fef653112a2bce861592 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Wed, 9 Apr 2025 16:37:32 +0200 Subject: [PATCH 25/52] Remove linux implementation --- .../src/flutter/shell/platform/linux/BUILD.gn | 2 - .../shell/platform/linux/fl_windowing.cc | 287 ------------------ .../shell/platform/linux/fl_windowing.h | 28 -- .../flutter/lib/src/widgets/window_linux.dart | 194 ------------ 4 files changed, 511 deletions(-) delete mode 100644 engine/src/flutter/shell/platform/linux/fl_windowing.cc delete mode 100644 engine/src/flutter/shell/platform/linux/fl_windowing.h delete mode 100644 packages/flutter/lib/src/widgets/window_linux.dart diff --git a/engine/src/flutter/shell/platform/linux/BUILD.gn b/engine/src/flutter/shell/platform/linux/BUILD.gn index 43c8f7c17c4fb..25a006fa5589e 100644 --- a/engine/src/flutter/shell/platform/linux/BUILD.gn +++ b/engine/src/flutter/shell/platform/linux/BUILD.gn @@ -159,8 +159,6 @@ source_set("flutter_linux_sources") { "fl_view.cc", "fl_view_accessible.cc", "fl_window_state_monitor.cc", - "fl_windowing.cc", - "fl_windowing.h", "fl_windowing_channel.cc", "fl_windowing_handler.cc", "key_mapping.g.cc", diff --git a/engine/src/flutter/shell/platform/linux/fl_windowing.cc b/engine/src/flutter/shell/platform/linux/fl_windowing.cc deleted file mode 100644 index feb5df6271410..0000000000000 --- a/engine/src/flutter/shell/platform/linux/fl_windowing.cc +++ /dev/null @@ -1,287 +0,0 @@ -// 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 "flutter/shell/platform/linux/fl_windowing.h" -#include "flutter/shell/platform/common/isolate_scope.h" -#include "flutter/shell/platform/linux/fl_engine_private.h" - -typedef struct { - GWeakRef engine; - GHashTable* windows_by_view_id; -} FlWindowingControllerPrivate; - -struct FlutterWindowCreationRequest { - double width; - double height; - double min_width; - double min_height; - double max_width; - double max_height; - void (*on_close)(); - void (*on_size_change)(); -}; - -G_DEFINE_TYPE_WITH_PRIVATE(FlWindowingController, - fl_windowing_controller, - G_TYPE_OBJECT) - -typedef struct { - GtkWindow* window; - FlView* view; - guint delete_id; - guint size_allocate_id; - void (*on_close)(); - void (*on_size_change)(); - flutter::Isolate* isolate; -} WindowData; - -static WindowData* window_data_new(GtkWindow* window, FlView* view) { - WindowData* data = g_new0(WindowData, 1); - data->window = GTK_WINDOW(g_object_ref(window)); - data->view = FL_VIEW(g_object_ref(view)); - data->isolate = new flutter::Isolate(); - return data; -} - -static void window_data_free(WindowData* data) { - g_signal_handler_disconnect(data->window, data->delete_id); - g_signal_handler_disconnect(data->window, data->size_allocate_id); - g_object_unref(data->window); - g_object_unref(data->view); - delete data->isolate; - g_free(data); -} - -static void fl_windowing_controller_dispose(GObject* object) { - G_OBJECT_CLASS(fl_windowing_controller_parent_class)->dispose(object); - - FlWindowingController* self = FL_WINDOWING_CONTROLLER(object); - FlWindowingControllerPrivate* priv = - reinterpret_cast( - fl_windowing_controller_get_instance_private(self)); - - g_weak_ref_clear(&priv->engine); - g_clear_pointer(&priv->windows_by_view_id, g_hash_table_unref); -} - -static void fl_windowing_controller_class_init( - FlWindowingControllerClass* klass) { - G_OBJECT_CLASS(klass)->dispose = fl_windowing_controller_dispose; -} - -static void fl_windowing_controller_init(FlWindowingController* self) { - FlWindowingControllerPrivate* priv = - reinterpret_cast( - fl_windowing_controller_get_instance_private(self)); - priv->windows_by_view_id = - g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr, - reinterpret_cast(window_data_free)); -} - -FlWindowingController* fl_windowing_controller_new(FlEngine* engine) { - g_return_val_if_fail(FL_IS_ENGINE(engine), nullptr); - - FlWindowingController* self = FL_WINDOWING_CONTROLLER( - g_object_new(fl_windowing_controller_get_type(), nullptr)); - FlWindowingControllerPrivate* priv = - reinterpret_cast( - fl_windowing_controller_get_instance_private(self)); - - g_weak_ref_init(&priv->engine, engine); - - return self; -} - -static gboolean on_window_delete(GtkWidget* widget, - GdkEvent* event, - gpointer user_data) { - WindowData* data = static_cast(user_data); - if (data->on_close) { - flutter::IsolateScope isolate_scope(*data->isolate); - data->on_close(); - } - return TRUE; -} - -static gboolean on_size_allocate(GtkWidget* widget, - GdkRectangle* allocation, - gpointer user_data) { - WindowData* data = static_cast(user_data); - if (data->on_size_change) { - flutter::IsolateScope isolate_scope(*data->isolate); - data->on_size_change(); - } - return FALSE; -} - -static int64_t fl_windowing_controller_create_regular_window( - FlWindowingController* self, - const FlutterWindowCreationRequest* request) { - FlWindowingControllerPrivate* priv = - reinterpret_cast( - fl_windowing_controller_get_instance_private(self)); - g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&priv->engine)); - g_return_val_if_fail(engine != nullptr, 0); - - FlView* view = fl_view_new_for_engine(engine); - gtk_widget_show(GTK_WIDGET(view)); - - GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); - - gtk_window_set_default_size(GTK_WINDOW(window), request->width, - request->height); - - WindowData* data = window_data_new(GTK_WINDOW(window), view); - - data->delete_id = g_signal_connect(window, "delete-event", - G_CALLBACK(on_window_delete), data); - data->size_allocate_id = g_signal_connect(window, "size-allocate", - G_CALLBACK(on_size_allocate), data); - data->on_close = request->on_close; - data->on_size_change = request->on_size_change; - - GdkGeometry geometry; - - GdkWindowHints geometry_mask = static_cast(0); - if (request->min_width != 0 && request->min_height != 0) { - geometry.min_width = request->min_width; - geometry.min_height = request->min_height; - geometry_mask = - static_cast(geometry_mask | GDK_HINT_MIN_SIZE); - } - if (request->max_width != 0 && request->max_height != 0) { - geometry.max_width = request->max_width; - geometry.max_height = request->max_height; - geometry_mask = - static_cast(geometry_mask | GDK_HINT_MAX_SIZE); - } - if (geometry_mask != 0) { - gtk_window_set_geometry_hints(GTK_WINDOW(window), nullptr, &geometry, - geometry_mask); - } - - // Make the resources for the view so rendering can start. - // We'll show the view when we have the first frame. - gtk_widget_realize(GTK_WIDGET(view)); - - // TODO(knopp): Do this when we have content rendered. - gtk_widget_show(window); - - g_hash_table_insert(priv->windows_by_view_id, - GINT_TO_POINTER(fl_view_get_id(view)), data); - - return fl_view_get_id(view); -} - -static void fl_windowing_controller_destroy_window(FlWindowingController* self, - int64_t view_id) { - FlWindowingControllerPrivate* priv = - reinterpret_cast( - fl_windowing_controller_get_instance_private(self)); - WindowData* data = static_cast( - g_hash_table_lookup(priv->windows_by_view_id, GINT_TO_POINTER(view_id))); - if (data == nullptr) { - return; - } - gtk_widget_destroy(GTK_WIDGET(data->window)); - g_hash_table_remove(priv->windows_by_view_id, GINT_TO_POINTER(view_id)); -} - -static WindowData* get_window_data(FlWindowingController* self, - int64_t view_id) { - FlWindowingControllerPrivate* priv = - reinterpret_cast( - fl_windowing_controller_get_instance_private(self)); - return static_cast( - g_hash_table_lookup(priv->windows_by_view_id, GINT_TO_POINTER(view_id))); -} - -extern "C" { -// NOLINTBEGIN(google-objc-function-naming) - -G_MODULE_EXPORT -int64_t flutter_create_regular_window( - int64_t engine_id, - const FlutterWindowCreationRequest* request) { - FlEngine* engine = fl_engine_for_id(engine_id); - FlWindowingController* controller = - fl_engine_get_windowing_controller(engine); - return fl_windowing_controller_create_regular_window(controller, request); -} - -G_MODULE_EXPORT -void* flutter_get_window_handle(int64_t engine_id, int64_t view_id) { - FlEngine* engine = fl_engine_for_id(engine_id); - FlWindowingController* controller = - fl_engine_get_windowing_controller(engine); - WindowData* data = get_window_data(controller, view_id); - if (data == nullptr) { - return 0; - } - return data->window; -} - -struct FlutterWindowSize { - double width; - double height; -}; - -G_MODULE_EXPORT -void flutter_get_window_size(void* window, FlutterWindowSize* size) { - GtkWindow* gtk_window = GTK_WINDOW(window); - gint width, height; - gtk_window_get_size(gtk_window, &width, &height); - size->width = width; - size->height = height; -} - -G_MODULE_EXPORT -int64_t flutter_get_window_state(void* window) { - GtkWindow* gtk_window = GTK_WINDOW(window); - GdkWindowState window_state = - gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(gtk_window))); - if (window_state & GDK_WINDOW_STATE_ICONIFIED) { - return 2; - } - if (gtk_window_is_maximized(gtk_window)) { - return 1; - } - return 0; -} - -G_MODULE_EXPORT -void flutter_set_window_state(void* window, int64_t state) { - GtkWindow* gtk_window = GTK_WINDOW(window); - GdkWindowState window_state; - switch (state) { - case 0: - if (gtk_window_is_maximized(gtk_window)) { - gtk_window_unmaximize(gtk_window); - } - window_state = - gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(window))); - if (window_state & GDK_WINDOW_STATE_ICONIFIED) { - gtk_window_deiconify(gtk_window); - } - break; - case 1: - gtk_window_maximize(gtk_window); - break; - case 2: - gtk_window_iconify(gtk_window); - break; - } -} - -G_MODULE_EXPORT -void flutter_destroy_window(int64_t engine_id, int64_t view_id) { - FlEngine* engine = fl_engine_for_id(engine_id); - FlWindowingController* controller = - fl_engine_get_windowing_controller(engine); - fl_windowing_controller_destroy_window(controller, view_id); -} - -// NOLINTEND(google-objc-function-naming) -} diff --git a/engine/src/flutter/shell/platform/linux/fl_windowing.h b/engine/src/flutter/shell/platform/linux/fl_windowing.h deleted file mode 100644 index cf94b41b94825..0000000000000 --- a/engine/src/flutter/shell/platform/linux/fl_windowing.h +++ /dev/null @@ -1,28 +0,0 @@ -// 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_SHELL_PLATFORM_LINUX_FL_WINDOWING_H_ -#define FLUTTER_SHELL_PLATFORM_LINUX_FL_WINDOWING_H_ - -#include - -#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h" - -G_BEGIN_DECLS - -G_DECLARE_DERIVABLE_TYPE(FlWindowingController, - fl_windowing_controller, - FL, - WINDOWING_CONTROLLER, - GObject); - -struct _FlWindowingControllerClass { - GObjectClass parent_class; -}; - -FlWindowingController* fl_windowing_controller_new(FlEngine* engine); - -G_END_DECLS - -#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_WINDOWING_H_ diff --git a/packages/flutter/lib/src/widgets/window_linux.dart b/packages/flutter/lib/src/widgets/window_linux.dart deleted file mode 100644 index ed385039cbc5e..0000000000000 --- a/packages/flutter/lib/src/widgets/window_linux.dart +++ /dev/null @@ -1,194 +0,0 @@ -import 'dart:ffi' hide Size; -import 'dart:ui' show FlutterView; - -import 'package:ffi/ffi.dart' as ffi; -import 'package:flutter/material.dart'; -import 'package:flutter/src/foundation/binding.dart'; - -class WindowingOwnerLinux extends WindowingOwner { - @override - RegularWindowController createRegularWindowController({ - required Size size, - required RegularWindowControllerDelegate delegate, - BoxConstraints? sizeConstraints, - }) { - final RegularWindowControllerLinux res = RegularWindowControllerLinux( - owner: this, - delegate: delegate, - size: size, - sizeConstraints: sizeConstraints, - ); - _activeControllers.add(res); - return res; - } - - @override - bool hasTopLevelWindows() { - return _activeControllers.isNotEmpty; - } - - final List _activeControllers = []; -} - -class RegularWindowControllerLinux extends RegularWindowController { - RegularWindowControllerLinux({ - required WindowingOwnerLinux owner, - required RegularWindowControllerDelegate delegate, - BoxConstraints? sizeConstraints, - required Size size, - String? title, - }) : _owner = owner, - _delegate = delegate, - super.empty() { - _onClose = NativeCallable.isolateLocal(_handleOnClose); - _onResize = NativeCallable.isolateLocal(_handleOnResize); - final Pointer<_WindowCreationRequest> request = - ffi.calloc<_WindowCreationRequest>() - ..ref.width = size.width - ..ref.height = size.height - ..ref.minWidth = sizeConstraints?.minWidth ?? 0 - ..ref.minHeight = sizeConstraints?.minHeight ?? 0 - ..ref.maxWidth = sizeConstraints?.maxWidth ?? 0 - ..ref.maxHeight = sizeConstraints?.maxHeight ?? 0 - ..ref.onClose = _onClose.nativeFunction - ..ref.onSizeChange = _onResize.nativeFunction; - - final int viewId = _createWindow(PlatformDispatcher.instance.engineId!, request); - ffi.calloc.free(request); - final FlutterView flutterView = WidgetsBinding.instance.platformDispatcher.views.firstWhere( - (FlutterView view) => view.viewId == viewId, - ); - setView(flutterView); - if (title != null) { - setTitle(title); - } - } - - Pointer getWindowHandle() { - return _getWindowHandle(PlatformDispatcher.instance.engineId!, rootView.viewId); - } - - bool _destroyed = false; - - @override - void destroy() { - if (_destroyed) { - return; - } - _destroyed = true; - _owner._activeControllers.remove(this); - _destroyWindow(PlatformDispatcher.instance.engineId!, rootView.viewId); - _delegate.onWindowDestroyed(); - _onClose.close(); - _onResize.close(); - } - - void _handleOnClose() { - _delegate.onWindowCloseRequested(this); - } - - void _handleOnResize() { - notifyListeners(); - } - - void setSize(Size size) { - _setWindowSize(getWindowHandle(), size.width, size.height); - } - - void setTitle(String title) { - final Pointer titlePointer = title.toNativeUtf8(); - _setWindowTitle(getWindowHandle(), titlePointer); - ffi.calloc.free(titlePointer); - } - - @override - void modify({Size? size, String? title, WindowState? state}) { - if (size != null) { - setSize(size); - } - if (title != null) { - setTitle(title); - } - if (state != null) { - setState(state); - } - } - - final WindowingOwnerLinux _owner; - final RegularWindowControllerDelegate _delegate; - late final NativeCallable _onClose; - late final NativeCallable _onResize; - - @override - Size get size { - final Pointer<_Size> size = ffi.calloc<_Size>(); - _getWindowSize(getWindowHandle(), size); - final Size result = Size(size.ref.width, size.ref.height); - ffi.calloc.free(size); - return result; - } - - @override - WindowState get state => WindowState.values[_getWindowState(getWindowHandle())]; - - void setState(WindowState state) { - _setWindowState(getWindowHandle(), state.index); - } - - @Native)>( - symbol: 'flutter_create_regular_window', - ) - external static int _createWindow(int engineId, Pointer<_WindowCreationRequest> request); - - @Native Function(Int64, Int64)>(symbol: 'flutter_get_window_handle') - external static Pointer _getWindowHandle(int engineId, int viewId); - - @Native(symbol: 'flutter_destroy_window') - external static void _destroyWindow(int engineId, int viewId); - - @Native, Pointer<_Size>)>(symbol: 'flutter_get_window_size') - external static void _getWindowSize(Pointer windowHandle, Pointer<_Size> size); - - @Native, Double, Double)>(symbol: 'gtk_window_resize') - external static void _setWindowSize(Pointer windowHandle, double width, double height); - - @Native, Pointer)>(symbol: 'gtk_window_set_title') - external static void _setWindowTitle(Pointer windowHandle, Pointer title); - - @Native)>(symbol: 'flutter_get_window_state') - external static int _getWindowState(Pointer windowHandle); - - @Native, Int64)>(symbol: 'flutter_set_window_state') - external static void _setWindowState(Pointer windowHandle, int state); -} - -final class _WindowCreationRequest extends Struct { - @Double() - external double width; - - @Double() - external double height; - - @Double() - external double minWidth; - - @Double() - external double minHeight; - - @Double() - external double maxWidth; - - @Double() - external double maxHeight; - - external Pointer> onClose; - external Pointer> onSizeChange; -} - -final class _Size extends Struct { - @Double() - external double width; - - @Double() - external double height; -} From 50a8875e1768d1bebe00563bcc9b3a2c88ed3203 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Wed, 9 Apr 2025 16:37:56 +0200 Subject: [PATCH 26/52] Make it work with flutter web --- .../flutter/lib/src/widgets/_window_ffi.dart | 17 +++++++++ .../flutter/lib/src/widgets/_window_web.dart | 7 ++++ packages/flutter/lib/src/widgets/window.dart | 17 ++------- .../flutter/lib/src/widgets/window_macos.dart | 38 +++++++++++++------ 4 files changed, 54 insertions(+), 25 deletions(-) create mode 100644 packages/flutter/lib/src/widgets/_window_ffi.dart create mode 100644 packages/flutter/lib/src/widgets/_window_web.dart diff --git a/packages/flutter/lib/src/widgets/_window_ffi.dart b/packages/flutter/lib/src/widgets/_window_ffi.dart new file mode 100644 index 0000000000000..99522033152c7 --- /dev/null +++ b/packages/flutter/lib/src/widgets/_window_ffi.dart @@ -0,0 +1,17 @@ +import 'dart:io'; + +import 'window.dart'; +import 'window_macos.dart'; +import 'window_win32.dart'; + +/// Creates a default [WindowingOwner] for current platform. +/// Only supported on desktop platforms. +WindowingOwner? createDefaultOwner() { + if (Platform.isMacOS) { + return WindowingOwnerMacOS(); + } else if (Platform.isWindows) { + return WindowingOwnerWin32(); + } else { + return null; + } +} diff --git a/packages/flutter/lib/src/widgets/_window_web.dart b/packages/flutter/lib/src/widgets/_window_web.dart new file mode 100644 index 0000000000000..40d9b638ff38b --- /dev/null +++ b/packages/flutter/lib/src/widgets/_window_web.dart @@ -0,0 +1,7 @@ +import 'window.dart'; + +/// Creates a default [WindowingOwner] for web. Returns `null` as web does not +/// support multiple windows. +WindowingOwner? createDefaultOwner() { + return null; +} diff --git a/packages/flutter/lib/src/widgets/window.dart b/packages/flutter/lib/src/widgets/window.dart index feb9b9e01d33c..cfb05b7081744 100644 --- a/packages/flutter/lib/src/widgets/window.dart +++ b/packages/flutter/lib/src/widgets/window.dart @@ -2,15 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show FlutterView, AppExitType; +import 'dart:ui' show AppExitType, FlutterView; -import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -import 'window_linux.dart'; -import 'window_macos.dart'; -import 'window_win32.dart'; +import '_window_ffi.dart' if (dart.library.js_util) '_window_web.dart' as window_impl; /// Defines the possible archetypes for a window. enum WindowArchetype { @@ -185,15 +182,7 @@ abstract class WindowingOwner { /// Creates default windowing owner for standard desktop embedders. static WindowingOwner createDefaultOwner() { - if (defaultTargetPlatform == TargetPlatform.windows) { - return WindowingOwnerWin32(); - } else if (defaultTargetPlatform == TargetPlatform.macOS) { - return WindowingOwnerMacOS(); - } else if (defaultTargetPlatform == TargetPlatform.linux) { - return WindowingOwnerLinux(); - } else { - return _FallbackWindowingOwner(); - } + return window_impl.createDefaultOwner() ?? _FallbackWindowingOwner(); } } diff --git a/packages/flutter/lib/src/widgets/window_macos.dart b/packages/flutter/lib/src/widgets/window_macos.dart index b1ea43deaf41e..ea475ab3f814a 100644 --- a/packages/flutter/lib/src/widgets/window_macos.dart +++ b/packages/flutter/lib/src/widgets/window_macos.dart @@ -5,6 +5,7 @@ import 'package:ffi/ffi.dart' as ffi; import 'package:flutter/material.dart'; import 'package:flutter/src/foundation/binding.dart'; +/// The macOS implementation of the windowing API. class WindowingOwnerMacOS extends WindowingOwner { @override RegularWindowController createRegularWindowController({ @@ -28,9 +29,22 @@ class WindowingOwnerMacOS extends WindowingOwner { } final List _activeControllers = []; + + /// Returns the window handle for the given [view], or null is the window + /// handle is not available. + /// The window handle is a pointer to NSWindow instance. + static Pointer getWindowHandle(FlutterView view) { + return _getWindowHandle(PlatformDispatcher.instance.engineId!, view.viewId); + } + + @Native Function(Int64, Int64)>(symbol: 'FlutterGetWindowHandle') + external static Pointer _getWindowHandle(int engineId, int viewId); } +/// The macOS implementation of the regular window controller. class RegularWindowControllerMacOS extends RegularWindowController { + /// Creates a new regular window controller for macOS. When this constructor + /// completes the FlutterView is created and framework is aware of it. RegularWindowControllerMacOS({ required WindowingOwnerMacOS owner, required RegularWindowControllerDelegate delegate, @@ -64,8 +78,10 @@ class RegularWindowControllerMacOS extends RegularWindowController { } } + /// Returns window handle for the current window. + /// The handle is a pointer to NSWindow instance. Pointer getWindowHandle() { - return _getWindowHandle(PlatformDispatcher.instance.engineId!, rootView.viewId); + return WindowingOwnerMacOS.getWindowHandle(rootView); } bool _destroyed = false; @@ -91,10 +107,12 @@ class RegularWindowControllerMacOS extends RegularWindowController { notifyListeners(); } + /// Updates the window size. void setSize(Size size) { _setWindowSize(getWindowHandle(), size.width, size.height); } + /// Updates the window title. void setTitle(String title) { final Pointer titlePointer = title.toNativeUtf8(); _setWindowTitle(getWindowHandle(), titlePointer); @@ -131,34 +149,32 @@ class RegularWindowControllerMacOS extends RegularWindowController { @override WindowState get state => WindowState.values[_getWindowState(getWindowHandle())]; + /// Updates window state. void setState(WindowState state) { _setWindowState(getWindowHandle(), state.index); } @Native)>( - symbol: 'flutter_create_regular_window', + symbol: 'FlutterCreateRegularWindow', ) external static int _createWindow(int engineId, Pointer<_WindowCreationRequest> request); - @Native Function(Int64, Int64)>(symbol: 'flutter_get_window_handle') - external static Pointer _getWindowHandle(int engineId, int viewId); - - @Native)>(symbol: 'flutter_destroy_window') + @Native)>(symbol: 'FlutterDestroyWindow') external static void _destroyWindow(int engineId, Pointer handle); - @Native, Pointer<_Size>)>(symbol: 'flutter_get_window_size') + @Native, Pointer<_Size>)>(symbol: 'FlutterGetWindowSize') external static void _getWindowSize(Pointer windowHandle, Pointer<_Size> size); - @Native, Double, Double)>(symbol: 'flutter_set_window_size') + @Native, Double, Double)>(symbol: 'FlutterSetWindowSize') external static void _setWindowSize(Pointer windowHandle, double width, double height); - @Native, Pointer)>(symbol: 'flutter_set_window_title') + @Native, Pointer)>(symbol: 'FlutterSetWindowTitle') external static void _setWindowTitle(Pointer windowHandle, Pointer title); - @Native)>(symbol: 'flutter_get_window_state') + @Native)>(symbol: 'FlutterGetWindowState') external static int _getWindowState(Pointer windowHandle); - @Native, Int64)>(symbol: 'flutter_set_window_state') + @Native, Int64)>(symbol: 'FlutterSetWindowState') external static void _setWindowState(Pointer windowHandle, int state); } From 0ad7d78acff0fab1e2497c6d2577750bb80c6cdf Mon Sep 17 00:00:00 2001 From: Harlen Batagelo Date: Fri, 4 Apr 2025 11:42:52 -0300 Subject: [PATCH 27/52] Add tests for the window controller on Windows using FFI --- .../shell/platform/windows/fixtures/main.dart | 5 + ...lutter_host_window_controller_unittests.cc | 436 ++++++------------ 2 files changed, 157 insertions(+), 284 deletions(-) diff --git a/engine/src/flutter/shell/platform/windows/fixtures/main.dart b/engine/src/flutter/shell/platform/windows/fixtures/main.dart index a9d341cee0539..540c330c242db 100644 --- a/engine/src/flutter/shell/platform/windows/fixtures/main.dart +++ b/engine/src/flutter/shell/platform/windows/fixtures/main.dart @@ -405,6 +405,11 @@ void testEngineId() { notifyEngineId(ui.PlatformDispatcher.instance.engineId); } +@pragma('vm:entry-point') +void testWindowController() { + signal(); +} + @pragma('vm:entry-point') Future sendSemanticsTreeInfo() async { // Wait until semantics are enabled. diff --git a/engine/src/flutter/shell/platform/windows/flutter_host_window_controller_unittests.cc b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller_unittests.cc index ba955eb7f8080..1dc37472a12cc 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_host_window_controller_unittests.cc +++ b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller_unittests.cc @@ -2,61 +2,59 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#if 0 - -#include "flutter/shell/platform/windows/windowing_handler.h" - -#include "flutter/fml/macros.h" -#include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_method_codec.h" #include "flutter/shell/platform/windows/flutter_host_window_controller.h" #include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h" -#include "flutter/shell/platform/windows/testing/test_binary_messenger.h" #include "flutter/shell/platform/windows/testing/windows_test.h" #include "gtest/gtest.h" namespace flutter { -namespace testing { -namespace { +struct WindowingInitRequest { + void (*on_message)(WindowsMessage*); +}; -constexpr char kChannelName[] = "flutter/windowing"; -constexpr char kOnWindowChangedMethod[] = "onWindowChanged"; -constexpr char kOnWindowDestroyedMethod[] = "onWindowDestroyed"; -constexpr char kSizeKey[] = "size"; -constexpr char kStateKey[] = "state"; -constexpr char kTitleKey[] = "title"; -constexpr char kViewIdKey[] = "viewId"; - -// Process the next Win32 message if there is one. This can be used to -// pump the Windows platform thread task runner. -void PumpMessage() { - ::MSG msg; - if (::GetMessage(&msg, nullptr, 0, 0)) { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - } -} +struct WindowCreationRequest { + double width; + double height; + double min_width; + double min_height; + double max_width; + double max_height; +}; -Size GetLogicalClientSize(HWND hwnd) { - RECT rect; - GetClientRect(hwnd, &rect); - double const dpr = FlutterDesktopGetDpiForHWND(hwnd) / - static_cast(USER_DEFAULT_SCREEN_DPI); - double const width = rect.right / dpr; - double const height = rect.bottom / dpr; - return {width, height}; -} +extern "C" { +FLUTTER_EXPORT +void flutter_windowing_initialize(int64_t engine_id, + const flutter::WindowingInitRequest* request); + +FLUTTER_EXPORT +bool flutter_windowing_has_top_level_windows(int64_t engine_id); + +FLUTTER_EXPORT +int64_t flutter_create_regular_window( + int64_t engine_id, + const flutter::WindowCreationRequest* request); + +FLUTTER_EXPORT +HWND flutter_get_window_handle(int64_t engine_id, FlutterViewId view_id); + +FLUTTER_EXPORT +void flutter_get_window_size(HWND hwnd, Size* size); + +FLUTTER_EXPORT +int64_t flutter_get_window_state(HWND hwnd); -std::wstring GetWindowTitle(HWND hwnd) { - int length = GetWindowTextLengthW(hwnd); - if (length == 0) - return L""; +FLUTTER_EXPORT +void flutter_set_window_state(HWND hwnd, int64_t state); - std::vector buffer(length + 1); - GetWindowTextW(hwnd, buffer.data(), length + 1); - return std::wstring(buffer.data()); +FLUTTER_EXPORT +void flutter_set_window_size(HWND hwnd, double width, double height); } +namespace testing { + +namespace { + class FlutterHostWindowControllerTest : public WindowsTest { public: FlutterHostWindowControllerTest() = default; @@ -64,284 +62,154 @@ class FlutterHostWindowControllerTest : public WindowsTest { protected: void SetUp() override { - InitializeCOM(); - SetDpiAwareness(); - FlutterWindowsEngineBuilder builder(GetContext()); - engine_ = builder.Build(); - engine_->Run(); - controller_ = std::make_unique(engine_.get()); - } + auto& context = GetContext(); + FlutterWindowsEngineBuilder builder(context); + builder.SetSwitches({"--enable-multi-window=true"}); - FlutterWindowsEngine* engine() { return engine_.get(); } - FlutterHostWindowController* host_window_controller() { - return controller_.get(); + engine_ = builder.Build(); + ASSERT_TRUE(engine_); + + engine_->SetRootIsolateCreateCallback(context.GetRootIsolateCallback()); + ASSERT_TRUE(engine_->Run("testWindowController")); + + bool signalled = false; + context.AddNativeFunction( + "Signal", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { + isolate_ = std::make_unique(); + signalled = true; + })); + while (!signalled) { + engine_->task_runner()->ProcessTasks(); + } } - private: - void InitializeCOM() const { - FML_CHECK(SUCCEEDED(::CoInitializeEx(nullptr, COINIT_MULTITHREADED))); - } + void TearDown() override { engine_->Stop(); } - void SetDpiAwareness() const { - HMODULE user32_module = LoadLibraryA("user32.dll"); - if (!user32_module) { - return; - } - using SetProcessDpiAwarenessContext = BOOL WINAPI(DPI_AWARENESS_CONTEXT); - auto set_process_dpi_awareness_context = - reinterpret_cast( - GetProcAddress(user32_module, "SetProcessDpiAwarenessContext")); - if (set_process_dpi_awareness_context != nullptr) { - set_process_dpi_awareness_context( - DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - } - FreeLibrary(user32_module); - } + int64_t engine_id() { return reinterpret_cast(engine_.get()); } + flutter::Isolate* isolate() { return isolate_.get(); } + WindowCreationRequest* creation_request() { return &creation_request_; } + private: std::unique_ptr engine_; - std::unique_ptr controller_; + std::unique_ptr isolate_; + WindowCreationRequest creation_request_{ + .width = 800, + .height = 600, + .min_width = 0, + .min_height = 0, + .max_width = 0, + .max_height = 0, + }; FML_DISALLOW_COPY_AND_ASSIGN(FlutterHostWindowControllerTest); }; } // namespace -TEST_F(FlutterHostWindowControllerTest, CreateRegularWindow) { - // Define parameters for the window to be created. - WindowCreationSettings const settings = { - .archetype = WindowArchetype::kRegular, - .size = {800.0, 600.0}, - .title = "window", - }; +TEST_F(FlutterHostWindowControllerTest, WindowingInitialize) { + IsolateScope isolate_scope(*isolate()); - // Create the window. - std::optional const result = - host_window_controller()->CreateHostWindow(settings); - - // Validate the returned metadata. - ASSERT_TRUE(result.has_value()); - EXPECT_NE(engine()->view(result->view_id), nullptr); - EXPECT_EQ(result->archetype, settings.archetype); - EXPECT_EQ(result->size.width(), settings.size.width()); - EXPECT_EQ(result->size.height(), settings.size.height()); - EXPECT_FALSE(result->parent_id.has_value()); - - // Ensure the window was successfully retrieved. - FlutterHostWindow* const window = - host_window_controller()->GetHostWindow(result->view_id); - ASSERT_NE(window, nullptr); + static bool received_message = false; + WindowingInitRequest init_request{ + .on_message = [](WindowsMessage* message) { received_message = true; }}; + + const int64_t view_id = + flutter_create_regular_window(engine_id(), creation_request()); + flutter_windowing_initialize(engine_id(), &init_request); + DestroyWindow(flutter_get_window_handle(engine_id(), view_id)); + + EXPECT_TRUE(received_message); } -TEST_F(FlutterHostWindowControllerTest, ModifyRegularWindowSize) { - // Define settings for the window to be created. - WindowCreationSettings const creation_settings = { - .archetype = WindowArchetype::kRegular, - .size = {800.0, 600.0}, - }; +TEST_F(FlutterHostWindowControllerTest, HasTopLevelWindows) { + IsolateScope isolate_scope(*isolate()); - // Create the window. - std::optional const metadata = - host_window_controller()->CreateHostWindow(creation_settings); - ASSERT_TRUE(metadata.has_value()); - // Retrieve the created window and verify it exists. - FlutterHostWindow* const window = - host_window_controller()->GetHostWindow(metadata->view_id); - ASSERT_NE(window, nullptr); - - // Define the modifications to be applied to the window. - WindowModificationSettings const modification_settings = { - .size = Size{200.0, 200.0}, - }; + bool has_top_level_windows = + flutter_windowing_has_top_level_windows(engine_id()); + EXPECT_FALSE(has_top_level_windows); - // Test messenger with a handler for onWindowChanged. - bool done = false; - TestBinaryMessenger messenger([&](const std::string& channel, - const uint8_t* message, size_t size, - BinaryReply reply) { - // Ensure the message is sent on the windowing channel. - ASSERT_EQ(channel, kChannelName); - - // Ensure the decoded method call is valid. - auto const method = StandardMethodCodec::GetInstance().DecodeMethodCall( - std::vector(message, message + size)); - ASSERT_NE(method, nullptr); - - // Handle the onWindowChanged method. - if (method->method_name() == kOnWindowChangedMethod) { - // Validate the method arguments. - auto const& args = *method->arguments(); - ASSERT_TRUE(std::holds_alternative(args)); - auto const& args_map = std::get(args); - - // Ensure 'viewId' is present and valid. - auto const& it_viewId = args_map.find(EncodableValue(kViewIdKey)); - ASSERT_NE(it_viewId, args_map.end()); - auto const* value_viewId = std::get_if(&it_viewId->second); - ASSERT_NE(value_viewId, nullptr); - EXPECT_GE(*value_viewId, metadata->view_id); - EXPECT_NE(engine()->view(*value_viewId), nullptr); - - // Ensure 'size' is present and valid. - auto const& it_size = args_map.find(EncodableValue(kSizeKey)); - ASSERT_NE(it_size, args_map.end()); - auto const* value_size = - std::get_if>(&it_size->second); - ASSERT_NE(value_size, nullptr); - ASSERT_EQ(value_size->size(), 2); - auto const* value_width = std::get_if(&value_size->at(0)); - ASSERT_NE(value_width, nullptr); - auto const* value_height = std::get_if(&value_size->at(1)); - ASSERT_NE(value_height, nullptr); - EXPECT_EQ(*value_width, modification_settings.size->width()); - EXPECT_EQ(*value_height, modification_settings.size->height()); - - done = true; - } - }); - - // Create the windowing handler with the test messenger. - WindowingHandler windowing_handler(&messenger, host_window_controller()); - - // Apply the modifications. - EXPECT_TRUE(host_window_controller()->ModifyHostWindow( - metadata->view_id, modification_settings)); - - // Validate the modified settings. - HWND const window_handle = host_window_controller() - ->GetHostWindow(metadata->view_id) - ->GetWindowHandle(); - Size const new_size = GetLogicalClientSize(window_handle); - EXPECT_EQ(new_size.width(), modification_settings.size->width()); - EXPECT_EQ(new_size.height(), modification_settings.size->height()); - - // Pump messages for the Windows platform task runner. - while (!done) { - PumpMessage(); - } + flutter_create_regular_window(engine_id(), creation_request()); + has_top_level_windows = flutter_windowing_has_top_level_windows(engine_id()); + EXPECT_TRUE(has_top_level_windows); } -TEST_F(FlutterHostWindowControllerTest, ModifyRegularWindowTitle) { - // Define settings for the window to be created. - WindowCreationSettings const creation_settings = { - .archetype = WindowArchetype::kRegular, - .title = "window", - }; +TEST_F(FlutterHostWindowControllerTest, CreateRegularWindow) { + IsolateScope isolate_scope(*isolate()); - // Create the window. - std::optional const metadata = - host_window_controller()->CreateHostWindow(creation_settings); - ASSERT_TRUE(metadata.has_value()); - // Retrieve the created window and verify it exists. - FlutterHostWindow* const window = - host_window_controller()->GetHostWindow(metadata->view_id); - ASSERT_NE(window, nullptr); - - // Define the modifications to be applied to the window. - WindowModificationSettings const modification_settings = { - .title = "new title 😉", - }; + const int64_t view_id = + flutter_create_regular_window(engine_id(), creation_request()); + EXPECT_EQ(view_id, 0); +} - // Apply the modifications. - EXPECT_TRUE(host_window_controller()->ModifyHostWindow( - metadata->view_id, modification_settings)); +TEST_F(FlutterHostWindowControllerTest, GetWindowHandle) { + IsolateScope isolate_scope(*isolate()); - // Validate the modified settings. - HWND const window_handle = host_window_controller() - ->GetHostWindow(metadata->view_id) - ->GetWindowHandle(); - std::wstring const new_title = GetWindowTitle(window_handle); - EXPECT_STREQ(new_title.c_str(), L"new title 😉"); + const int64_t view_id = + flutter_create_regular_window(engine_id(), creation_request()); + const HWND window_handle = flutter_get_window_handle(engine_id(), view_id); + EXPECT_NE(window_handle, nullptr); } -TEST_F(FlutterHostWindowControllerTest, ModifyRegularWindowState) { - // Define settings for the window to be created. - WindowCreationSettings const creation_settings = { - .archetype = WindowArchetype::kRegular, - .state = WindowState::kRestored, - }; +TEST_F(FlutterHostWindowControllerTest, GetWindowSize) { + IsolateScope isolate_scope(*isolate()); - // Create the window. - std::optional const metadata = - host_window_controller()->CreateHostWindow(creation_settings); - ASSERT_TRUE(metadata.has_value()); - // Retrieve the created window and verify it exists. - FlutterHostWindow* const window = - host_window_controller()->GetHostWindow(metadata->view_id); - ASSERT_NE(window, nullptr); - EXPECT_EQ(window->GetState(), creation_settings.state); - - // Define the modifications to be applied to the window. - WindowModificationSettings const modification_settings = { - .state = WindowState::kMinimized, - }; + const int64_t view_id = + flutter_create_regular_window(engine_id(), creation_request()); + const HWND window_handle = flutter_get_window_handle(engine_id(), view_id); - // Apply the modifications. - EXPECT_TRUE(host_window_controller()->ModifyHostWindow( - metadata->view_id, modification_settings)); + Size size; + flutter_get_window_size(window_handle, &size); - // Validate the modified settings. - EXPECT_EQ(window->GetState(), modification_settings.state); + EXPECT_EQ(size.width(), creation_request()->width); + EXPECT_EQ(size.height(), creation_request()->height); } -TEST_F(FlutterHostWindowControllerTest, DestroyWindow) { - bool done = false; - - // Test messenger with a handler for onWindowDestroyed. - TestBinaryMessenger messenger([&](const std::string& channel, - const uint8_t* message, size_t size, - BinaryReply reply) { - // Ensure the message is sent on the windowing channel. - ASSERT_EQ(channel, kChannelName); - - // Ensure the decoded method call is valid. - auto const method = StandardMethodCodec::GetInstance().DecodeMethodCall( - std::vector(message, message + size)); - ASSERT_NE(method, nullptr); - - // Handle the onWindowDestroyed method. - if (method->method_name() == kOnWindowDestroyedMethod) { - // Validate the method arguments. - auto const& args = *method->arguments(); - ASSERT_TRUE(std::holds_alternative(args)); - auto const& args_map = std::get(args); - - // Ensure the viewId is present but not valid anymore. - auto const& it_viewId = args_map.find(EncodableValue(kViewIdKey)); - ASSERT_NE(it_viewId, args_map.end()); - auto const* value_viewId = std::get_if(&it_viewId->second); - ASSERT_NE(value_viewId, nullptr); - EXPECT_GE(*value_viewId, 0); - EXPECT_EQ(engine()->view(*value_viewId), nullptr); - - done = true; - } - }); +TEST_F(FlutterHostWindowControllerTest, GetWindowState) { + IsolateScope isolate_scope(*isolate()); - // Create the windowing handler with the test messenger. - WindowingHandler windowing_handler(&messenger, host_window_controller()); + const int64_t view_id = + flutter_create_regular_window(engine_id(), creation_request()); + const HWND window_handle = flutter_get_window_handle(engine_id(), view_id); + const int64_t window_state = flutter_get_window_state(window_handle); + EXPECT_EQ(window_state, static_cast(WindowState::kRestored)); +} - // Define parameters for the window to be created. - WindowCreationSettings const settings = { - .archetype = WindowArchetype::kRegular, - .size = {800.0, 600.0}, - .title = "window", - }; +TEST_F(FlutterHostWindowControllerTest, SetWindowState) { + IsolateScope isolate_scope(*isolate()); - // Create the window. - std::optional const result = - host_window_controller()->CreateHostWindow(settings); - ASSERT_TRUE(result.has_value()); + const int64_t view_id = + flutter_create_regular_window(engine_id(), creation_request()); + const HWND window_handle = flutter_get_window_handle(engine_id(), view_id); - // Destroy the window. - EXPECT_TRUE(host_window_controller()->DestroyHostWindow(result->view_id)); + const std::array kWindowStates = { + static_cast(WindowState::kRestored), + static_cast(WindowState::kMaximized), + static_cast(WindowState::kMinimized), + }; - // Pump messages for the Windows platform task runner. - while (!done) { - PumpMessage(); + for (const auto requested_state : kWindowStates) { + flutter_set_window_state(window_handle, requested_state); + const int64_t actual_state = flutter_get_window_state(window_handle); + EXPECT_EQ(actual_state, requested_state); } } +TEST_F(FlutterHostWindowControllerTest, SetWindowSize) { + IsolateScope isolate_scope(*isolate()); + + const int64_t view_id = + flutter_create_regular_window(engine_id(), creation_request()); + const HWND window_handle = flutter_get_window_handle(engine_id(), view_id); + + const Size requested_size{640, 480}; + flutter_set_window_size(window_handle, requested_size.width(), + requested_size.height()); + + Size actual_size; + flutter_get_window_size(window_handle, &actual_size); + EXPECT_EQ(actual_size.width(), requested_size.width()); + EXPECT_EQ(actual_size.height(), requested_size.height()); +} + } // namespace testing } // namespace flutter - -#endif \ No newline at end of file From 57673cfd133e1b4e07759b57038eb90ac16165a1 Mon Sep 17 00:00:00 2001 From: Harlen Batagelo Date: Wed, 2 Apr 2025 15:35:22 -0300 Subject: [PATCH 28/52] Enable windowing with flutter config --- engine/src/flutter/common/settings.h | 2 +- engine/src/flutter/shell/common/switches.cc | 15 ++++++------ engine/src/flutter/shell/common/switches.h | 4 ++-- .../windows/flutter_windows_engine.cc | 9 ++++--- .../platform/windows/flutter_windows_engine.h | 2 +- .../flutter_tools/lib/src/commands/run.dart | 7 +++--- .../flutter_tools/lib/src/commands/test.dart | 2 +- .../lib/src/custom_devices/custom_device.dart | 3 +-- .../flutter_tools/lib/src/desktop_device.dart | 4 ++-- packages/flutter_tools/lib/src/device.dart | 14 +++++------ packages/flutter_tools/lib/src/features.dart | 14 +++++++++++ .../lib/src/flutter_features.dart | 3 +++ .../lib/src/runner/flutter_command.dart | 8 +++---- .../test/general.shard/features_test.dart | 24 +++++++++++++++++++ packages/flutter_tools/test/src/fakes.dart | 5 ++++ 15 files changed, 80 insertions(+), 36 deletions(-) diff --git a/engine/src/flutter/common/settings.h b/engine/src/flutter/common/settings.h index 2445e4d67edd2..4c44ed359c568 100644 --- a/engine/src/flutter/common/settings.h +++ b/engine/src/flutter/common/settings.h @@ -379,7 +379,7 @@ struct Settings { // Enable support for multiple windows. Ignored if not supported on the // platform. - bool enable_multi_window = false; + bool enable_windowing = false; }; } // namespace flutter diff --git a/engine/src/flutter/shell/common/switches.cc b/engine/src/flutter/shell/common/switches.cc index bba735c907592..46613fa474e9c 100644 --- a/engine/src/flutter/shell/common/switches.cc +++ b/engine/src/flutter/shell/common/switches.cc @@ -556,16 +556,17 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { command_line.HasOption(FlagForSwitch(Switch::ImpellerLazyShaderMode)); settings.impeller_antialiased_lines = command_line.HasOption(FlagForSwitch(Switch::ImpellerAntialiasLines)); -#if FML_OS_WIN - // Process the EnableMultiWindow switch on Windows. + +#if FML_OS_MACOSX || FML_OS_LINUX || FML_OS_WIN + // Process the EnableWindowing switch on macOS, Linux, and Windows. { - std::string enable_multi_window_value; - if (command_line.GetOptionValue(FlagForSwitch(Switch::EnableMultiWindow), - &enable_multi_window_value)) { - settings.enable_multi_window = "true" == enable_multi_window_value; + std::string enable_windowing_value; + if (command_line.GetOptionValue(FlagForSwitch(Switch::EnableWindowing), + &enable_windowing_value)) { + settings.enable_windowing = "true" == enable_windowing_value; } } -#endif // FML_OS_WIN +#endif // FML_OS_MACOSX || FML_OS_LINUX || FML_OS_WIN return settings; } diff --git a/engine/src/flutter/shell/common/switches.h b/engine/src/flutter/shell/common/switches.h index 487093f41c982..77cbbef33ede9 100644 --- a/engine/src/flutter/shell/common/switches.h +++ b/engine/src/flutter/shell/common/switches.h @@ -302,8 +302,8 @@ DEF_SWITCH(MergedPlatformUIThread, DEF_SWITCH(DisableMergedPlatformUIThread, "no-enable-merged-platform-ui-thread", "Merge the ui thread and platform thread.") -DEF_SWITCH(EnableMultiWindow, - "enable-multi-window", +DEF_SWITCH(EnableWindowing, + "enable-windowing", "Enable support for multiple windows. Ignored if not supported on " "the platform.") DEF_SWITCH(EnableAndroidSurfaceControl, diff --git a/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc b/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc index f3134a0bd89d4..6c48e3271bc17 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc +++ b/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc @@ -196,9 +196,8 @@ FlutterWindowsEngine::FlutterWindowsEngine( enable_impeller_ = std::find(switches.begin(), switches.end(), "--enable-impeller=true") != switches.end(); - enable_multi_window_ = - std::find(switches.begin(), switches.end(), - "--enable-multi-window=true") != switches.end(); + enable_windowing_ = std::find(switches.begin(), switches.end(), + "--enable-windowing=true") != switches.end(); egl_manager_ = egl::Manager::Create( static_cast(project_->gpu_preference())); @@ -239,7 +238,7 @@ FlutterWindowsEngine::FlutterWindowsEngine( std::make_unique(messenger_wrapper_.get(), this); platform_handler_ = std::make_unique(messenger_wrapper_.get(), this); - if (enable_multi_window_) { + if (enable_windowing_) { host_window_controller_ = std::make_unique(this); } @@ -320,7 +319,7 @@ bool FlutterWindowsEngine::Run(std::string_view entrypoint) { &WindowsPlatformThreadPrioritySetter; if (project_->ui_thread_policy() != - FlutterUIThreadPolicy::RunOnSeparateThread && !enable_multi_window) { + FlutterUIThreadPolicy::RunOnSeparateThread && !enable_windowing_) { custom_task_runners.ui_task_runner = &platform_task_runner; } else { FML_LOG(WARNING) << "Running with unmerged platform and UI threads. This " diff --git a/engine/src/flutter/shell/platform/windows/flutter_windows_engine.h b/engine/src/flutter/shell/platform/windows/flutter_windows_engine.h index 4abf1580768d8..666127e56d809 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_windows_engine.h +++ b/engine/src/flutter/shell/platform/windows/flutter_windows_engine.h @@ -494,7 +494,7 @@ class FlutterWindowsEngine { bool enable_impeller_ = false; - bool enable_multi_window_ = false; + bool enable_windowing_ = false; // The manager for WindowProc delegate registration and callbacks. std::unique_ptr window_proc_delegate_manager_; diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index c85d80d22f835..b75fb29244a4f 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart @@ -220,7 +220,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment addEnableImpellerFlag(verboseHelp: verboseHelp); addEnableVulkanValidationFlag(verboseHelp: verboseHelp); addEnableEmbedderApiFlag(verboseHelp: verboseHelp); - addMultiWindowFlag(verboseHelp: verboseHelp); + addWindowingFlag(verboseHelp: verboseHelp); } bool get traceStartup => boolArg('trace-startup'); @@ -236,7 +236,6 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment bool get enableVulkanValidation => boolArg('enable-vulkan-validation'); bool get uninstallFirst => boolArg('uninstall-first'); bool get enableEmbedderApi => boolArg('enable-embedder-api'); - bool get enableMultiWindow => boolArg('enable-multi-window'); @override bool get refreshWirelessDevices => true; @@ -303,7 +302,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment uninstallFirst: uninstallFirst, enableDartProfiling: enableDartProfiling, enableEmbedderApi: enableEmbedderApi, - enableMultiWindow: enableMultiWindow, + enableWindowing: featureFlags.isWindowingEnabled, usingCISystem: usingCISystem, debugLogsDirectoryPath: debugLogsDirectoryPath, ); @@ -369,7 +368,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment serveObservatory: boolArg('serve-observatory'), enableDartProfiling: enableDartProfiling, enableEmbedderApi: enableEmbedderApi, - enableMultiWindow: enableMultiWindow, + enableWindowing: featureFlags.isWindowingEnabled, usingCISystem: usingCISystem, debugLogsDirectoryPath: debugLogsDirectoryPath, enableDevTools: boolArg(FlutterCommand.kEnableDevTools), diff --git a/packages/flutter_tools/lib/src/commands/test.dart b/packages/flutter_tools/lib/src/commands/test.dart index 8472b63bc9336..88a534aad6dda 100644 --- a/packages/flutter_tools/lib/src/commands/test.dart +++ b/packages/flutter_tools/lib/src/commands/test.dart @@ -78,7 +78,7 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts { usesDeviceUserOption(); usesFlavorOption(); addEnableImpellerFlag(verboseHelp: verboseHelp); - addMultiWindowFlag(verboseHelp: verboseHelp); + addWindowingFlag(verboseHelp: verboseHelp); argParser ..addFlag( diff --git a/packages/flutter_tools/lib/src/custom_devices/custom_device.dart b/packages/flutter_tools/lib/src/custom_devices/custom_device.dart index 60940c32e7109..8f18a0303f04d 100644 --- a/packages/flutter_tools/lib/src/custom_devices/custom_device.dart +++ b/packages/flutter_tools/lib/src/custom_devices/custom_device.dart @@ -306,8 +306,7 @@ class CustomDeviceAppSession { if (debuggingOptions.useTestFonts) 'use-test-fonts=true', if (debuggingOptions.verboseSystemLogs) 'verbose-logging=true', ], - if (debuggingOptions.enableMultiWindow) - 'enable-multi-window=true', + if (debuggingOptions.enableWindowing) 'enable-windowing=true', ]; } diff --git a/packages/flutter_tools/lib/src/desktop_device.dart b/packages/flutter_tools/lib/src/desktop_device.dart index 056b53e00e7cf..ae1c6b59b6b73 100644 --- a/packages/flutter_tools/lib/src/desktop_device.dart +++ b/packages/flutter_tools/lib/src/desktop_device.dart @@ -295,8 +295,8 @@ abstract class DesktopDevice extends Device { case ImpellerStatus.platformDefault: addFlag('enable-impeller=false'); } - if (debuggingOptions.enableMultiWindow) { - addFlag('enable-multi-window=true'); + if (debuggingOptions.enableWindowing) { + addFlag('enable-windowing=true'); } // Options only supported when there is a VM Service connection between the // tool and the device, usually in debug or profile mode. diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index ac7f213be6422..455b22345b75e 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -971,7 +971,7 @@ class DebuggingOptions { this.serveObservatory = false, this.enableDartProfiling = true, this.enableEmbedderApi = false, - this.enableMultiWindow = false, + this.enableWindowing = false, this.usingCISystem = false, this.debugLogsDirectoryPath, this.enableDevTools = true, @@ -1005,7 +1005,7 @@ class DebuggingOptions { this.uninstallFirst = false, this.enableDartProfiling = true, this.enableEmbedderApi = false, - this.enableMultiWindow = false, + this.enableWindowing = false, this.usingCISystem = false, this.debugLogsDirectoryPath, }) : debuggingEnabled = false, @@ -1090,7 +1090,7 @@ class DebuggingOptions { required this.serveObservatory, required this.enableDartProfiling, required this.enableEmbedderApi, - required this.enableMultiWindow, + required this.enableWindowing, required this.usingCISystem, required this.debugLogsDirectoryPath, required this.enableDevTools, @@ -1137,7 +1137,7 @@ class DebuggingOptions { final bool serveObservatory; final bool enableDartProfiling; final bool enableEmbedderApi; - final bool enableMultiWindow; + final bool enableWindowing; final bool usingCISystem; final String? debugLogsDirectoryPath; final bool enableDevTools; @@ -1237,7 +1237,7 @@ class DebuggingOptions { if (interfaceType == DeviceConnectionInterface.wireless) '--vm-service-host=${ipv6 ? '::0' : '0.0.0.0'}', if (enableEmbedderApi) '--enable-embedder-api', - if (enableMultiWindow) '--enable-multi-window=true', + if (enableWindowing) '--enable-windowing=true', ]; } @@ -1289,7 +1289,7 @@ class DebuggingOptions { 'serveObservatory': serveObservatory, 'enableDartProfiling': enableDartProfiling, 'enableEmbedderApi': enableEmbedderApi, - 'enableMultiWindow': enableMultiWindow, + 'enableWindowing': enableWindowing, 'usingCISystem': usingCISystem, 'debugLogsDirectoryPath': debugLogsDirectoryPath, 'enableDevTools': enableDevTools, @@ -1360,7 +1360,7 @@ class DebuggingOptions { serveObservatory: (json['serveObservatory'] as bool?) ?? false, enableDartProfiling: (json['enableDartProfiling'] as bool?) ?? true, enableEmbedderApi: (json['enableEmbedderApi'] as bool?) ?? false, - enableMultiWindow: (json['enableMultiWindow']! as bool?) ?? false, + enableWindowing: (json['enableWindowing']! as bool?) ?? false, usingCISystem: (json['usingCISystem'] as bool?) ?? false, debugLogsDirectoryPath: json['debugLogsDirectoryPath'] as String?, enableDevTools: (json['enableDevTools'] as bool?) ?? true, diff --git a/packages/flutter_tools/lib/src/features.dart b/packages/flutter_tools/lib/src/features.dart index 3b9b21cfe0b96..df7170a0b6dcd 100644 --- a/packages/flutter_tools/lib/src/features.dart +++ b/packages/flutter_tools/lib/src/features.dart @@ -54,6 +54,9 @@ abstract class FeatureFlags { /// Whether explicit package dependency management is enabled. bool get isExplicitPackageDependenciesEnabled => false; + /// Whether multi-window support is enabled. + bool get isWindowingEnabled => false; + /// Whether a particular feature is enabled for the current channel. /// /// Prefer using one of the specific getters above instead of this API. @@ -74,6 +77,7 @@ const List allFeatures = [ nativeAssets, swiftPackageManager, explicitPackageDependencies, + windowing, ]; /// All current Flutter feature flags that can be configured. @@ -180,6 +184,16 @@ const Feature explicitPackageDependencies = Feature.fullyEnabled( '* https://flutter.dev/to/flutter-gen-deprecation.', ); +/// Enable multi-window support. +const Feature windowing = Feature( + name: 'support for multiple windows', + configSetting: 'enable-windowing', + environmentOverride: 'FLUTTER_WINDOWING', + master: FeatureChannelSetting(available: true), + beta: FeatureChannelSetting(available: true), + stable: FeatureChannelSetting(available: true), +); + /// A [Feature] is a process for conditionally enabling tool features. /// /// All settings are optional, and if not provided will generally default to diff --git a/packages/flutter_tools/lib/src/flutter_features.dart b/packages/flutter_tools/lib/src/flutter_features.dart index ff83f64008945..10b7929dd4375 100644 --- a/packages/flutter_tools/lib/src/flutter_features.dart +++ b/packages/flutter_tools/lib/src/flutter_features.dart @@ -61,6 +61,9 @@ class FlutterFeatureFlags implements FeatureFlags { @override bool get isExplicitPackageDependenciesEnabled => isEnabled(explicitPackageDependencies); + @override + bool get isWindowingEnabled => isEnabled(windowing); + @override bool isEnabled(Feature feature) { final String currentChannel = _flutterVersion.channel; diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 9966d67e22544..7289473eba1e1 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -1290,12 +1290,12 @@ abstract class FlutterCommand extends Command { ); } - void addMultiWindowFlag({required bool verboseHelp}) { - argParser.addFlag('enable-multi-window', + void addWindowingFlag({required bool verboseHelp}) { + argParser.addFlag('enable-windowing', hide: !verboseHelp, help: 'Whether to enable support for multiple windows. ' - 'This flag is only available on Windows, is disabled by default, ' - 'and will be ignored on other platforms.', + 'Disabled by default. Ignored on platforms other than ' + 'macOS, Linux, and Windows.', ); } diff --git a/packages/flutter_tools/test/general.shard/features_test.dart b/packages/flutter_tools/test/general.shard/features_test.dart index 10d8fe3e30bc9..d5d585807292c 100644 --- a/packages/flutter_tools/test/general.shard/features_test.dart +++ b/packages/flutter_tools/test/general.shard/features_test.dart @@ -424,5 +424,29 @@ void main() { expect(featureFlags.isSwiftPackageManagerEnabled, isTrue); }); }); + + test('Windowing availability and default enabled', () { + expect(windowing.master.enabledByDefault, false); + expect(windowing.master.available, true); + expect(windowing.beta.enabledByDefault, false); + expect(windowing.beta.available, true); + expect(windowing.stable.enabledByDefault, false); + expect(windowing.stable.available, true); + }); + + for (final String channel in ['master', 'beta', 'stable']) { + testWithoutContext('Windowing can be enabled with flag on $channel', () { + final FeatureFlags featureFlags = createFlags(channel); + testConfig.setValue('enable-windowing', true); + expect(featureFlags.isWindowingEnabled, true); + }); + + testWithoutContext('Windowing can be enabled with environment variable on $channel', () { + final FeatureFlags featureFlags = createFlags(channel); + platform.environment = {'FLUTTER_WINDOWING': 'true'}; + expect(featureFlags.isWindowingEnabled, true); + }); + } + }); } diff --git a/packages/flutter_tools/test/src/fakes.dart b/packages/flutter_tools/test/src/fakes.dart index 2d6499e71b9be..1f67d7fd4bc4d 100644 --- a/packages/flutter_tools/test/src/fakes.dart +++ b/packages/flutter_tools/test/src/fakes.dart @@ -494,6 +494,7 @@ class TestFeatureFlags implements FeatureFlags { this.isNativeAssetsEnabled = false, this.isSwiftPackageManagerEnabled = false, this.isExplicitPackageDependenciesEnabled = false, + this.isWindowingEnabled = false, }); @override @@ -532,6 +533,9 @@ class TestFeatureFlags implements FeatureFlags { @override final bool isExplicitPackageDependenciesEnabled; + @override + final bool isWindowingEnabled; + @override bool isEnabled(Feature feature) { return switch (feature) { @@ -546,6 +550,7 @@ class TestFeatureFlags implements FeatureFlags { cliAnimation => isCliAnimationEnabled, nativeAssets => isNativeAssetsEnabled, explicitPackageDependencies => isExplicitPackageDependenciesEnabled, + windowing => isWindowingEnabled, _ => false, }; } From 1c42efabafcafa5c14bde6c7739abc15d70501d8 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Mon, 14 Apr 2025 16:54:53 +0200 Subject: [PATCH 29/52] macOS cleanup --- .../flutter/shell/platform/common/BUILD.gn | 1 + .../shell/platform/common/isolate_scope.cc | 40 +++++++++ .../shell/platform/common/isolate_scope.h | 31 +++---- .../flutter/shell/platform/common/windowing.h | 84 +------------------ .../shell/platform/darwin/macos/BUILD.gn | 3 + .../macos/framework/Source/FlutterEngine.mm | 57 +++++++++---- .../framework/Source/FlutterEngine_Internal.h | 12 +++ .../framework/Source/FlutterVSyncWaiter.mm | 3 + .../Source/FlutterViewController_Internal.h | 6 ++ .../Source/FlutterWindowController.h | 2 +- .../Source/FlutterWindowController.mm | 24 ++++-- .../Source/FlutterWindowControllerTest.mm | 13 ++- .../flutter/lib/src/widgets/window_macos.dart | 11 +-- 13 files changed, 150 insertions(+), 137 deletions(-) create mode 100644 engine/src/flutter/shell/platform/common/isolate_scope.cc diff --git a/engine/src/flutter/shell/platform/common/BUILD.gn b/engine/src/flutter/shell/platform/common/BUILD.gn index 5a5f03272666c..b27be6bfa0388 100644 --- a/engine/src/flutter/shell/platform/common/BUILD.gn +++ b/engine/src/flutter/shell/platform/common/BUILD.gn @@ -59,6 +59,7 @@ source_set("common_cpp_input") { source_set("common_cpp_isolate_scope") { public = [ "isolate_scope.h" ] + sources = [ "isolate_scope.cc" ] deps = [ "$dart_src/runtime:dart_api", diff --git a/engine/src/flutter/shell/platform/common/isolate_scope.cc b/engine/src/flutter/shell/platform/common/isolate_scope.cc new file mode 100644 index 0000000000000..94eb41bc93c73 --- /dev/null +++ b/engine/src/flutter/shell/platform/common/isolate_scope.cc @@ -0,0 +1,40 @@ +// 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 "flutter/shell/platform/common/isolate_scope.h" + +namespace flutter { + +Isolate Isolate::Current() { + Dart_Isolate isolate = Dart_CurrentIsolate(); + return Isolate(isolate); +} + +IsolateScope::IsolateScope(const Isolate& isolate) { + isolate_ = isolate.isolate_; + previous_ = Dart_CurrentIsolate(); + if (previous_ == isolate_) { + return; + } + if (previous_) { + Dart_ExitIsolate(); + } + Dart_EnterIsolate(isolate_); +}; + +IsolateScope::~IsolateScope() { + Dart_Isolate current = Dart_CurrentIsolate(); + FML_DCHECK(!current || current == isolate_); + if (previous_ == isolate_) { + return; + } + if (current) { + Dart_ExitIsolate(); + } + if (previous_) { + Dart_EnterIsolate(previous_); + } + } + +} // namespace flutter diff --git a/engine/src/flutter/shell/platform/common/isolate_scope.h b/engine/src/flutter/shell/platform/common/isolate_scope.h index a81d75091ab3c..e4d84889466ac 100644 --- a/engine/src/flutter/shell/platform/common/isolate_scope.h +++ b/engine/src/flutter/shell/platform/common/isolate_scope.h @@ -10,37 +10,32 @@ namespace flutter { +/// This class is a thin wrapper around dart isolate. It can be used +/// as argument to IsolateScope constructor to enter and exit the isolate. class Isolate { public: - Isolate() : isolate_(Dart_CurrentIsolate()) { - FML_DCHECK(isolate_ != nullptr); - } + static Isolate Current(); + ~Isolate() = default; private: + explicit Isolate(Dart_Isolate isolate) : isolate_(isolate) { + FML_DCHECK(isolate_ != nullptr); + } + friend class IsolateScope; Dart_Isolate isolate_; }; +// Enters provided isolate for as long as the scope is alive. class IsolateScope { public: - explicit IsolateScope(const Isolate& isolate) { - if (Dart_CurrentIsolate() == nullptr) { - Dart_EnterIsolate(isolate.isolate_); - should_exit_isolate_ = true; - } else { - FML_DCHECK(Dart_CurrentIsolate() == isolate.isolate_); - } - }; - - ~IsolateScope() { - if (should_exit_isolate_) { - Dart_ExitIsolate(); - } - } + explicit IsolateScope(const Isolate& isolate); + ~IsolateScope(); private: - bool should_exit_isolate_ = false; + Dart_Isolate isolate_; + Dart_Isolate previous_; IsolateScope() = delete; IsolateScope(IsolateScope const&) = delete; }; diff --git a/engine/src/flutter/shell/platform/common/windowing.h b/engine/src/flutter/shell/platform/common/windowing.h index 4e17d53c32263..2dd0e0f55c8ea 100644 --- a/engine/src/flutter/shell/platform/common/windowing.h +++ b/engine/src/flutter/shell/platform/common/windowing.h @@ -5,23 +5,10 @@ #ifndef FLUTTER_SHELL_PLATFORM_COMMON_WINDOWING_H_ #define FLUTTER_SHELL_PLATFORM_COMMON_WINDOWING_H_ -#include - -#include "flutter/fml/logging.h" -#include "geometry.h" - namespace flutter { -// A unique identifier for a view. -using FlutterViewId = int64_t; - -// Types of windows. -enum class WindowArchetype { - // Regular top-level window. - kRegular, -}; - // Possible states a window can be in. +// The values must match values from WindowState in the Dart code. enum class WindowState { // Normal state, neither maximized, nor minimized. kRestored, @@ -31,75 +18,6 @@ enum class WindowState { kMinimized, }; -// Converts a |flutter::WindowState| to the string representation of -// |WindowState| as defined in the framework. -inline std::string WindowStateToString(WindowState state) { - switch (state) { - case WindowState::kRestored: - return "WindowState.restored"; - case WindowState::kMaximized: - return "WindowState.maximized"; - case WindowState::kMinimized: - return "WindowState.minimized"; - default: - FML_UNREACHABLE(); - } -} - -// Converts the string representation of |WindowState| defined in the framework -// to a |flutter::WindowState|. Returns std::nullopt if the given string is -// invalid. -inline std::optional StringToWindowState(std::string_view str) { - if (str == "WindowState.restored") - return WindowState::kRestored; - if (str == "WindowState.maximized") - return WindowState::kMaximized; - if (str == "WindowState.minimized") - return WindowState::kMinimized; - return std::nullopt; -} - -// Settings used for creating a Flutter window. -struct WindowCreationSettings { - // Type of the window. - WindowArchetype archetype = WindowArchetype::kRegular; - // Requested size of the window's client area, in logical coordinates. - Size size; - // Minimum size of the window's client area, in logical coordinates. - std::optional min_size; - // Maximum size of the window's client area, in logical coordinates. - std::optional max_size; - // Window title. - std::optional title; - // Initial state of the window. - std::optional state; -}; - -// Settings for modifying a Flutter window. -struct WindowModificationSettings { - // The new requested size, in logical coordinates. - std::optional size; - // The new window title. - std::optional title; - // The new window state. - std::optional state; -}; - -// Window metadata returned as the result of creating a Flutter window. -struct WindowMetadata { - // The ID of the view used for this window, which is unique to each window. - FlutterViewId view_id = 0; - // The type of the window. - WindowArchetype archetype = WindowArchetype::kRegular; - // Size of the created window, in logical coordinates. - Size size; - // The ID of the view used by the parent window. If not set, the window is - // assumed a top-level window. - std::optional parent_id; - // The initial state of the window, or std::nullopt if not a regular window. - std::optional state; -}; - } // namespace flutter #endif // FLUTTER_SHELL_PLATFORM_COMMON_WINDOWING_H_ diff --git a/engine/src/flutter/shell/platform/darwin/macos/BUILD.gn b/engine/src/flutter/shell/platform/darwin/macos/BUILD.gn index 8b5cacdf83ed2..1006b4818e1b4 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/BUILD.gn +++ b/engine/src/flutter/shell/platform/darwin/macos/BUILD.gn @@ -131,6 +131,7 @@ source_set("flutter_framework_source") { "//flutter/flow:flow", "//flutter/fml", "//flutter/shell/platform/common:common_cpp_accessibility", + "//flutter/shell/platform/common:common_cpp_core", "//flutter/shell/platform/common:common_cpp_enums", "//flutter/shell/platform/common:common_cpp_input", "//flutter/shell/platform/common:common_cpp_isolate_scope", @@ -212,6 +213,7 @@ executable("flutter_desktop_darwin_unittests") { "framework/Source/KeyCodeMapTest.mm", "framework/Source/TestFlutterPlatformView.h", "framework/Source/TestFlutterPlatformView.mm", + "framework/source/FlutterWindowControllerTest.mm", ] deps = [ @@ -220,6 +222,7 @@ executable("flutter_desktop_darwin_unittests") { "//flutter/fml", "//flutter/shell/platform/common:common_cpp_accessibility", "//flutter/shell/platform/common:common_cpp_enums", + "//flutter/shell/platform/common:common_cpp_isolate_scope", "//flutter/shell/platform/darwin/common:framework_common", "//flutter/shell/platform/darwin/graphics", "//flutter/shell/platform/embedder:embedder_as_internal_library", diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 02a09e3726849..84f12525f53aa 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -450,6 +450,7 @@ @implementation FlutterEngine { // factories. Lifecycle is tied to the engine. FlutterPlatformViewController* _platformViewController; + // Used to manage Flutter windows. FlutterWindowController* _windowController; // A message channel for sending user settings to the flutter engine. @@ -484,7 +485,12 @@ @implementation FlutterEngine { // The text input plugin that handles text editing state for text fields. FlutterTextInputPlugin* _textInputPlugin; + // Whether the engine is running in multi-window mode. This affects behavior + // when adding view controller (it will fail when calling multiple times without + // _multiviewEnabled). BOOL _multiviewEnabled; + + // View identifier for the next view to be created. FlutterViewIdentifier _nextViewIdentifier; } @@ -634,6 +640,16 @@ - (FlutterTaskRunnerDescription)createPlatformThreadTaskDescription { return cocoa_task_runner_description; } +- (void)onFocusChangeRequest:(const FlutterViewFocusChangeRequest*)request { + FlutterViewController* controller = [self viewControllerForIdentifier:request->view_id]; + if (controller == nil) { + return; + } + if (request->state == kFocused) { + [controller.flutterView.window makeFirstResponder:controller.flutterView]; + } +} + - (BOOL)runWithEntrypoint:(NSString*)entrypoint { if (self.running) { return NO; @@ -740,10 +756,11 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { [engine onVSync:baton]; }; - flutterArguments.view_focus_change_request_callback = [](const FlutterViewFocusChangeRequest* req, - void* user_data) { - NSLog(@"Focus calllback %lli %i\n", req->view_id, req->state); - }; + flutterArguments.view_focus_change_request_callback = + [](const FlutterViewFocusChangeRequest* request, void* user_data) { + FlutterEngine* engine = (__bridge FlutterEngine*)user_data; + [engine onFocusChangeRequest:request]; + }; FlutterRendererConfig rendererConfig = [_renderer createRendererConfig]; FlutterEngineResult result = _embedderAPI.Initialize( @@ -805,10 +822,12 @@ - (void)registerViewController:(FlutterViewController*)controller forIdentifier:(FlutterViewIdentifier)viewIdentifier { _macOSCompositor->AddView(viewIdentifier); NSAssert(controller != nil, @"The controller must not be nil."); - // NSAssert(controller.engine == nil, - // @"The FlutterViewController is unexpectedly attached to " - // @"engine %@ before initialization.", - // controller.engine); + if (!_multiviewEnabled) { + NSAssert(controller.engine == nil, + @"The FlutterViewController is unexpectedly attached to " + @"engine %@ before initialization.", + controller.engine); + } NSAssert([_viewControllers objectForKey:@(viewIdentifier)] == nil, @"The requested view ID is occupied."); [_viewControllers setObject:controller forKey:@(viewIdentifier)]; @@ -828,23 +847,29 @@ - (void)registerViewController:(FlutterViewController*)controller } if (viewIdentifier != kFlutterImplicitViewId) { - fml::AutoResetWaitableEvent latch; + // These will be overriden immediately after the FlutterView is created + // by actual values. FlutterWindowMetricsEvent metrics{ .struct_size = sizeof(FlutterWindowMetricsEvent), - .width = 100, - .height = 100, - .pixel_ratio = 2.0, + .width = 0, + .height = 0, + .pixel_ratio = 1.0, }; + bool added = false; FlutterAddViewInfo info{.struct_size = sizeof(FlutterAddViewInfo), .view_id = viewIdentifier, .view_metrics = &metrics, - .user_data = &latch, + .user_data = &added, .add_view_callback = [](const FlutterAddViewResult* r) { - auto l = reinterpret_cast(r->user_data); - l->Signal(); + auto added = reinterpret_cast(r->user_data); + *added = true; }}; + // The callback should be called synchronously from platform thread. _embedderAPI.AddView(_engine, &info); - latch.Wait(); + FML_DCHECK(added); + if (!added) { + NSLog(@"Failed to add view with ID %llu", viewIdentifier); + } } } diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h index 7c4d90599a799..3c8baaae0d088 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h @@ -226,9 +226,21 @@ typedef NS_ENUM(NSInteger, FlutterAppExitResponse) { @property(nonatomic, readonly) FlutterWindowController* windowController; +/** + * Toggles multi-view support. Called by [FlutterWindowController] before + * creating a new window. This allows registering multiple view controllers + * with the engine. + */ - (void)enableMultiView; +/** + * Notifies the engine that window with the given identifier has been made key. + */ - (void)windowDidBecomeKey:(FlutterViewIdentifier)viewIdentifier; + +/** + * Notifies the engine that window with the given identifier has resigned being key. + */ - (void)windowDidResignKey:(FlutterViewIdentifier)viewIdentifier; /** diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterVSyncWaiter.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterVSyncWaiter.mm index 5a4546084b43a..d81ca84504fbc 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterVSyncWaiter.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterVSyncWaiter.mm @@ -152,6 +152,9 @@ - (void)waitForVSync:(uintptr_t)baton { } - (void)dealloc { + // It is possible that there is pending vsync request while the view for which + // this waiter belongs is being destroyed. In that case trigger the vsync + // immediately to avoid deadlock. if (_pendingBaton.has_value()) { CFTimeInterval now = CACurrentMediaTime(); _block(now, now, _pendingBaton.value()); diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h index c021add4a9199..d2d8434ddf355 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h @@ -53,6 +53,12 @@ */ - (void)updateSemantics:(nonnull const FlutterSemanticsUpdate2*)update; +/** + * Removes this controller from the engine. The controller is removed from the engine + * on dealloc, however in multi-window scenario the controller needs to be unregistered + * from the engine eagerly - because the FlutterView needs to be removed from the + * Flutter isolate before destroying the controller and window. + */ - (void)dispose; @end diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h index 8f0465fe5c53d..eab361619823c 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h @@ -51,7 +51,7 @@ FLUTTER_DARWIN_EXPORT void* FlutterGetWindowHandle(int64_t engine_id, FlutterViewIdentifier view_id); FLUTTER_DARWIN_EXPORT -void FlutterGetWindowSize(void* window, FlutterWindowSize* size); +FlutterWindowSize FlutterGetWindowSize(void* window); FLUTTER_DARWIN_EXPORT void FlutterSetWindowSize(void* window, double width, double height); diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm index 80cd2948b8ead..aa595fdf995ba 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm @@ -10,6 +10,7 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" #include "flutter/shell/platform/common/isolate_scope.h" +#include "flutter/shell/platform/common/windowing.h" /// A delegate for a Flutter managed window. @interface FlutterWindowOwner : NSObject { @@ -17,7 +18,7 @@ @interface FlutterWindowOwner : NSObject { /// window. NSWindow* _window; FlutterViewController* _flutterViewController; - flutter::Isolate _isolate; + std::optional _isolate; FlutterWindowCreationRequest _creationRequest; } @@ -42,6 +43,7 @@ - (instancetype)initWithWindow:(NSWindow*)window _window = window; _flutterViewController = viewController; _creationRequest = creationRequest; + _isolate = flutter::Isolate::Current(); } return self; } @@ -55,13 +57,13 @@ - (void)windowDidResignKey:(NSNotification*)notification { } - (BOOL)windowShouldClose:(NSWindow*)sender { - flutter::IsolateScope isolate_scope(_isolate); + flutter::IsolateScope isolate_scope(*_isolate); _creationRequest.on_close(); return NO; } - (void)windowDidResize:(NSNotification*)notification { - flutter::IsolateScope isolate_scope(_isolate); + flutter::IsolateScope isolate_scope(*_isolate); _creationRequest.on_size_change(); } @@ -119,6 +121,8 @@ - (void)destroyWindow:(NSWindow*)window { } if (owner != nil) { [_windows removeObject:owner]; + // Make sure to unregister the controller from the engine and remove the FlutterView + // before destroying the window and Flutter NSView. [owner.flutterViewController dispose]; owner.window.delegate = nil; [owner.window close]; @@ -147,10 +151,12 @@ void FlutterDestroyWindow(int64_t engine_id, void* window) { return (__bridge void*)controller.view.window; } -void FlutterGetWindowSize(void* window, FlutterWindowSize* size) { +FlutterWindowSize FlutterGetWindowSize(void* window) { NSWindow* w = (__bridge NSWindow*)window; - size->width = w.frame.size.width; - size->height = w.frame.size.height; + return { + .width = w.frame.size.width, + .height = w.frame.size.height, + }; } void FlutterSetWindowSize(void* window, double width, double height) { @@ -166,11 +172,11 @@ void FlutterSetWindowTitle(void* window, const char* title) { int64_t FlutterGetWindowState(void* window) { NSWindow* w = (__bridge NSWindow*)window; if (w.isZoomed) { - return 1; + return static_cast(flutter::WindowState::kMaximized); } else if (w.isMiniaturized) { - return 2; + return static_cast(flutter::WindowState::kMinimized); } else { - return 0; + return static_cast(flutter::WindowState::kRestored); } } diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowControllerTest.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowControllerTest.mm index 30adc224288e1..4f6d23eed6ccc 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowControllerTest.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowControllerTest.mm @@ -4,6 +4,7 @@ #import "flutter/shell/platform/common/isolate_scope.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineTestUtils.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h" #import "flutter/testing/testing.h" #import "third_party/googletest/googletest/include/gtest/gtest.h" @@ -17,12 +18,12 @@ FlutterWindowController* controller = [[FlutterWindowController alloc] init]; controller.engine = engine; - std::unique_ptr isolate; + std::optional isolate; bool signalled = false; AddNativeCallback("SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { fprintf(stderr, "Signal native test\n"); - isolate = std::make_unique(); + isolate = Isolate::Current(); signalled = true; })); @@ -40,8 +41,14 @@ int64_t engine_id = reinterpret_cast(engine); - IsolateScope isolate_scope(*isolate.get()); + IsolateScope isolate_scope(*isolate); int64_t handle = FlutterCreateRegularWindow(engine_id, &request); EXPECT_EQ(handle, 1); + + FlutterViewController* viewController = [engine viewControllerForIdentifier:handle]; + EXPECT_NE(viewController, nil); + CGSize size = viewController.view.frame.size; + EXPECT_EQ(size.width, 800); + EXPECT_EQ(size.height, 600); } } // namespace flutter::testing diff --git a/packages/flutter/lib/src/widgets/window_macos.dart b/packages/flutter/lib/src/widgets/window_macos.dart index ea475ab3f814a..b170248a12ddc 100644 --- a/packages/flutter/lib/src/widgets/window_macos.dart +++ b/packages/flutter/lib/src/widgets/window_macos.dart @@ -139,11 +139,8 @@ class RegularWindowControllerMacOS extends RegularWindowController { @override Size get size { - final Pointer<_Size> size = ffi.calloc<_Size>(); - _getWindowSize(getWindowHandle(), size); - final Size result = Size(size.ref.width, size.ref.height); - ffi.calloc.free(size); - return result; + final _Size size = _getWindowSize(getWindowHandle()); + return Size(size.width, size.height); } @override @@ -162,8 +159,8 @@ class RegularWindowControllerMacOS extends RegularWindowController { @Native)>(symbol: 'FlutterDestroyWindow') external static void _destroyWindow(int engineId, Pointer handle); - @Native, Pointer<_Size>)>(symbol: 'FlutterGetWindowSize') - external static void _getWindowSize(Pointer windowHandle, Pointer<_Size> size); + @Native<_Size Function(Pointer)>(symbol: 'FlutterGetWindowSize') + external static _Size _getWindowSize(Pointer windowHandle); @Native, Double, Double)>(symbol: 'FlutterSetWindowSize') external static void _setWindowSize(Pointer windowHandle, double width, double height); From 04b09abb1eca0906ba876484447d2573d34def52 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Mon, 14 Apr 2025 16:57:11 +0200 Subject: [PATCH 30/52] reformat --- .../shell/platform/common/isolate_scope.cc | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/engine/src/flutter/shell/platform/common/isolate_scope.cc b/engine/src/flutter/shell/platform/common/isolate_scope.cc index 94eb41bc93c73..9d1f537876f1e 100644 --- a/engine/src/flutter/shell/platform/common/isolate_scope.cc +++ b/engine/src/flutter/shell/platform/common/isolate_scope.cc @@ -24,17 +24,17 @@ IsolateScope::IsolateScope(const Isolate& isolate) { }; IsolateScope::~IsolateScope() { - Dart_Isolate current = Dart_CurrentIsolate(); - FML_DCHECK(!current || current == isolate_); - if (previous_ == isolate_) { - return; - } - if (current) { - Dart_ExitIsolate(); - } - if (previous_) { - Dart_EnterIsolate(previous_); - } + Dart_Isolate current = Dart_CurrentIsolate(); + FML_DCHECK(!current || current == isolate_); + if (previous_ == isolate_) { + return; + } + if (current) { + Dart_ExitIsolate(); } + if (previous_) { + Dart_EnterIsolate(previous_); + } +} } // namespace flutter From db77fb287cfb4cca3ff9de68b75f94b20fb285e6 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Mon, 14 Apr 2025 17:55:18 +0200 Subject: [PATCH 31/52] Restore window archetype --- engine/src/flutter/shell/platform/common/windowing.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/engine/src/flutter/shell/platform/common/windowing.h b/engine/src/flutter/shell/platform/common/windowing.h index 2dd0e0f55c8ea..e6cc8e4ef9cc3 100644 --- a/engine/src/flutter/shell/platform/common/windowing.h +++ b/engine/src/flutter/shell/platform/common/windowing.h @@ -7,6 +7,13 @@ namespace flutter { +// Types of windows. +// The value must match value from WindowType in the Dart code. +enum class WindowArchetype { + // Regular top-level window. + kRegular, +}; + // Possible states a window can be in. // The values must match values from WindowState in the Dart code. enum class WindowState { From df79fc970efb791a8754a9f051075f2861d4e450 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Mon, 14 Apr 2025 18:12:38 +0200 Subject: [PATCH 32/52] wip --- .../platform/windows/flutter_host_window.cc | 16 +++++++++++----- .../shell/platform/windows/flutter_host_window.h | 13 +++++++++++-- .../windows/flutter_host_window_controller.cc | 2 +- .../windows/flutter_host_window_controller.h | 1 + 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/engine/src/flutter/shell/platform/windows/flutter_host_window.cc b/engine/src/flutter/shell/platform/windows/flutter_host_window.cc index e04a5ab8ffa41..07be173e79b29 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_host_window.cc +++ b/engine/src/flutter/shell/platform/windows/flutter_host_window.cc @@ -266,10 +266,8 @@ void UpdateTheme(HWND window) { namespace flutter { FlutterHostWindow::FlutterHostWindow(FlutterHostWindowController* controller, - WindowCreationSettings const& settings) - : window_controller_(controller) { - archetype_ = settings.archetype; - + WindowArchetype archetype) + : window_controller_(controller), archetype_(archetype) { // Check preconditions and set window styles based on window type. DWORD window_style = 0; DWORD extended_window_style = 0; @@ -535,7 +533,7 @@ LRESULT FlutterHostWindow::HandleMessage(HWND hwnd, return DefWindowProc(hwnd, message, wparam, lparam); } -void FlutterHostWindow::SetClientSize(Size const& client_size) const { +void FlutterHostWindow::SetClientSize(Size const& client_size) { WINDOWINFO window_info = {.cbSize = sizeof(WINDOWINFO)}; GetWindowInfo(window_handle_, &window_info); @@ -548,6 +546,14 @@ void FlutterHostWindow::SetClientSize(Size const& client_size) const { SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } +void FlutterHostWindow::SetMinClientSize(Size const& min_size) { + min_size_ = min_size; +} + +void FlutterHostWindow::SetMaxClientSize(Size const& max_size) { + max_size_ = max_size; +} + void FlutterHostWindow::SetChildContent(HWND content) { child_content_ = content; SetParent(content, window_handle_); diff --git a/engine/src/flutter/shell/platform/windows/flutter_host_window.h b/engine/src/flutter/shell/platform/windows/flutter_host_window.h index f5d8e7aff3731..14cd88d74fb05 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_host_window.h +++ b/engine/src/flutter/shell/platform/windows/flutter_host_window.h @@ -8,10 +8,13 @@ #include #include +#include #include "flutter/fml/macros.h" +#include "flutter/shell/platform/common/geometry.h" #include "flutter/shell/platform/common/windowing.h" + namespace flutter { class FlutterHostWindowController; @@ -26,7 +29,7 @@ class FlutterHostWindow { // |FlutterHostWindow|. On success, a valid window handle can be retrieved // via |FlutterHostWindow::GetWindowHandle|. FlutterHostWindow(FlutterHostWindowController* controller, - WindowCreationSettings const& settings); + WindowArchetype archetype); virtual ~FlutterHostWindow(); @@ -39,7 +42,13 @@ class FlutterHostWindow { // Resizes the window to accommodate a client area of the given // |client_size|. - void SetClientSize(Size const& client_size) const; + void SetClientSize(Size const& client_size); + + // Sets the minimum client size for the window. + void SetMinClientSize(Size const& min_size); + + // Sets the maximum client size for the window. + void SetMaxClientSize(Size const& max_size); private: friend FlutterHostWindowController; diff --git a/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.cc b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.cc index 50b61d9cba2f9..7a4274bec7c59 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.cc +++ b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.cc @@ -135,7 +135,7 @@ FlutterHostWindowController::FlutterHostWindowController( void FlutterHostWindowController::Initialize( const WindowingInitRequest* request) { on_message_ = request->on_message; - isolate_ = Isolate(); + isolate_ = Isolate::Current(); // Send messages accumulated before isolate called this method. for (WindowsMessage& message : pending_messages_) { diff --git a/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.h b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.h index 40f2781001699..df409deb442df 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.h +++ b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.h @@ -11,6 +11,7 @@ #include "flutter/fml/macros.h" #include "flutter/shell/platform/common/isolate_scope.h" +#include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/windows/flutter_host_window.h" namespace flutter { From 53b8b0ba0730ecb3d8f11b64f98a688cc301ed75 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Mon, 14 Apr 2025 18:13:16 +0200 Subject: [PATCH 33/52] reformat --- engine/src/flutter/shell/platform/windows/flutter_host_window.h | 1 - 1 file changed, 1 deletion(-) diff --git a/engine/src/flutter/shell/platform/windows/flutter_host_window.h b/engine/src/flutter/shell/platform/windows/flutter_host_window.h index 14cd88d74fb05..c7358f510e44b 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_host_window.h +++ b/engine/src/flutter/shell/platform/windows/flutter_host_window.h @@ -14,7 +14,6 @@ #include "flutter/shell/platform/common/geometry.h" #include "flutter/shell/platform/common/windowing.h" - namespace flutter { class FlutterHostWindowController; From 6b30099b4308cedb2d3ea47ff2c1735443f7a8fc Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 15 Apr 2025 11:21:42 +0200 Subject: [PATCH 34/52] window sizing --- .../Source/FlutterWindowController.h | 10 ++- .../Source/FlutterWindowController.mm | 29 ++++++- .../Source/FlutterWindowControllerTest.mm | 3 +- .../lib/app/main_window.dart | 77 ++++++++----------- .../lib/app/regular_window_content.dart | 24 +++--- examples/multi_window_ref_app/lib/main.dart | 9 ++- packages/flutter/lib/src/widgets/window.dart | 29 ++++--- .../flutter/lib/src/widgets/window_macos.dart | 67 +++++++++++----- .../flutter/lib/src/widgets/window_win32.dart | 32 ++++---- 9 files changed, 160 insertions(+), 120 deletions(-) diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h index eab361619823c..9681a0d2c5de1 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h @@ -18,13 +18,19 @@ @end -struct FlutterWindowCreationRequest { +struct FlutterWindowSizing { + bool hasSize; double width; double height; + bool hasConstraints; double min_width; double min_height; double max_width; double max_height; +}; + +struct FlutterWindowCreationRequest { + FlutterWindowSizing contentSize; void (*on_close)(); void (*on_size_change)(); }; @@ -54,7 +60,7 @@ FLUTTER_DARWIN_EXPORT FlutterWindowSize FlutterGetWindowSize(void* window); FLUTTER_DARWIN_EXPORT -void FlutterSetWindowSize(void* window, double width, double height); +void FlutterSetWindowContentSize(void* window, const FlutterWindowSizing* size); FLUTTER_DARWIN_EXPORT void FlutterSetWindowTitle(void* window, const char* title); diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm index aa595fdf995ba..ccf1a922e083d 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm @@ -31,6 +31,29 @@ - (instancetype)initWithWindow:(NSWindow*)window @end +@interface NSWindow (FlutterWindowSizing) + +- (void)flutterSetContentSize:(FlutterWindowSizing)contentSize; + +@end + +@implementation NSWindow (FlutterWindowSizing) +- (void)flutterSetContentSize:(FlutterWindowSizing)contentSize { + if (contentSize.hasSize) { + [self setContentSize:NSMakeSize(contentSize.width, contentSize.height)]; + } + if (contentSize.hasConstraints) { + [self setContentMinSize:NSMakeSize(contentSize.min_width, contentSize.min_height)]; + if (contentSize.max_width > 0 && contentSize.max_height > 0) { + [self setContentMaxSize:NSMakeSize(contentSize.max_width, contentSize.max_height)]; + } else { + [self setContentMaxSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)]; + } + } +} + +@end + @implementation FlutterWindowOwner @synthesize window = _window; @@ -96,9 +119,9 @@ - (FlutterViewIdentifier)createRegularWindow:(const FlutterWindowCreationRequest [window setReleasedWhenClosed:NO]; window.contentViewController = c; - [window setContentSize:NSMakeSize(request->width, request->height)]; window.styleMask = NSWindowStyleMaskResizable | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable; + [window flutterSetContentSize:request->contentSize]; [window setIsVisible:YES]; [window makeKeyAndOrderFront:nil]; @@ -159,9 +182,9 @@ FlutterWindowSize FlutterGetWindowSize(void* window) { }; } -void FlutterSetWindowSize(void* window, double width, double height) { +void FlutterSetWindowContentSize(void* window, const FlutterWindowSizing* size) { NSWindow* w = (__bridge NSWindow*)window; - [w setContentSize:NSMakeSize(width, height)]; + [w flutterSetContentSize:*size]; } void FlutterSetWindowTitle(void* window, const char* title) { diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowControllerTest.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowControllerTest.mm index 4f6d23eed6ccc..50ff3dcdbe6e6 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowControllerTest.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowControllerTest.mm @@ -33,8 +33,7 @@ } FlutterWindowCreationRequest request{ - .width = 800, - .height = 600, + .contentSize = {.hasSize = true, .width = 800, .height = 600}, .on_close = [] {}, .on_size_change = [] {}, }; diff --git a/examples/multi_window_ref_app/lib/app/main_window.dart b/examples/multi_window_ref_app/lib/app/main_window.dart index 8a79fa349329d..3f8acdbc5af6d 100644 --- a/examples/multi_window_ref_app/lib/app/main_window.dart +++ b/examples/multi_window_ref_app/lib/app/main_window.dart @@ -9,8 +9,8 @@ import 'regular_window_edit_dialog.dart'; class MainWindow extends StatefulWidget { MainWindow({super.key, required WindowController mainController}) { - _windowManagerModel.add(KeyedWindowController( - isMainWindow: true, key: UniqueKey(), controller: mainController)); + _windowManagerModel.add( + KeyedWindowController(isMainWindow: true, key: UniqueKey(), controller: mainController)); } final WindowManagerModel _windowManagerModel = WindowManagerModel(); @@ -34,8 +34,7 @@ class _MainWindowState extends State { flex: 60, child: SingleChildScrollView( scrollDirection: Axis.vertical, - child: _ActiveWindowsTable( - windowManagerModel: widget._windowManagerModel), + child: _ActiveWindowsTable(windowManagerModel: widget._windowManagerModel), ), ), Expanded( @@ -63,18 +62,15 @@ class _MainWindowState extends State { listenable: widget._windowManagerModel, builder: (BuildContext context, Widget? _) { final List childViews = []; - for (final KeyedWindowController controller - in widget._windowManagerModel.windows) { + for (final KeyedWindowController controller in widget._windowManagerModel.windows) { if (controller.parent == null && !controller.isMainWindow) { childViews.add(WindowControllerRender( controller: controller.controller, key: controller.key, windowSettings: widget._settings, windowManagerModel: widget._windowManagerModel, - onDestroyed: () => - widget._windowManagerModel.remove(controller.key), - onError: () => - widget._windowManagerModel.remove(controller.key), + onDestroyed: () => widget._windowManagerModel.remove(controller.key), + onError: () => widget._windowManagerModel.remove(controller.key), )); } } @@ -130,8 +126,7 @@ class _ActiveWindowsTable extends StatelessWidget { ), numeric: true), ], - rows: (windowManagerModel.windows) - .map((KeyedWindowController controller) { + rows: (windowManagerModel.windows).map((KeyedWindowController controller) { return DataRow( key: controller.key, color: WidgetStateColor.resolveWith((states) { @@ -143,9 +138,8 @@ class _ActiveWindowsTable extends StatelessWidget { selected: controller.controller == windowManagerModel.selected, onSelectChanged: (selected) { if (selected != null) { - windowManagerModel.select(selected - ? controller.controller.rootView.viewId - : null); + windowManagerModel + .select(selected ? controller.controller.rootView.viewId : null); } }, cells: [ @@ -153,41 +147,33 @@ class _ActiveWindowsTable extends StatelessWidget { DataCell( ListenableBuilder( listenable: controller.controller, - builder: (BuildContext context, Widget? _) => Text( - controller.controller.type - .toString() - .replaceFirst('WindowArchetype.', ''))), + builder: (BuildContext context, Widget? _) => Text(controller + .controller.type + .toString() + .replaceFirst('WindowArchetype.', ''))), ), DataCell( ListenableBuilder( listenable: controller.controller, - builder: (BuildContext context, Widget? _) => - Row(children: [ + builder: (BuildContext context, Widget? _) => Row(children: [ IconButton( icon: const Icon(Icons.edit_outlined), onPressed: () { - if (controller.controller.type == - WindowArchetype.regular) { + if (controller.controller.type == WindowArchetype.regular) { showRegularWindowEditDialog(context, - initialWidth: - controller.controller.size.width, - initialHeight: - controller.controller.size.height, + initialWidth: controller.controller.size.width, + initialHeight: controller.controller.size.height, initialTitle: "", - initialState: (controller.controller - as RegularWindowController) - .state, - onSave: (double? width, double? height, + initialState: + (controller.controller as RegularWindowController) + .state, onSave: (double? width, double? height, String? title, WindowState? state) { - (controller.controller - as RegularWindowController) - .modify( - size: width != null && - height != null - ? Size(width, height) - : null, - title: title, - state: state); + (controller.controller as RegularWindowController).modify( + contentSize: width != null && height != null + ? WindowSizing(size: Size(width, height)) + : null, + title: title, + state: state); }); } }, @@ -246,11 +232,12 @@ class _WindowCreatorCard extends StatelessWidget { windowManagerModel.add(KeyedWindowController( key: key, controller: RegularWindowController( - delegate: WindowControllerDelegate( - onDestroyed: () => windowManagerModel.remove(key), - ), - title: "Regular", - size: windowSettings.regularSize))); + delegate: WindowControllerDelegate( + onDestroyed: () => windowManagerModel.remove(key), + ), + title: "Regular", + contentSize: WindowSizing(size: windowSettings.regularSize), + ))); }, child: const Text('Regular'), ), diff --git a/examples/multi_window_ref_app/lib/app/regular_window_content.dart b/examples/multi_window_ref_app/lib/app/regular_window_content.dart index e26e761cedd51..b7e4ea2159d32 100644 --- a/examples/multi_window_ref_app/lib/app/regular_window_content.dart +++ b/examples/multi_window_ref_app/lib/app/regular_window_content.dart @@ -83,8 +83,7 @@ class _RegularWindowContentState extends State builder: (context, child) { return CustomPaint( size: const Size(200, 200), - painter: _RotatedWireCube( - angle: _animation.value, color: cubeColor), + painter: _RotatedWireCube(angle: _animation.value, color: cubeColor), ); }, ), @@ -100,12 +99,12 @@ class _RegularWindowContentState extends State widget.windowManagerModel.add(KeyedWindowController( key: key, controller: RegularWindowController( - delegate: WindowControllerDelegate( - onDestroyed: () => - widget.windowManagerModel.remove(key), - ), - title: "Regular", - size: widget.windowSettings.regularSize))); + contentSize: WindowSizing(size: widget.windowSettings.regularSize), + delegate: WindowControllerDelegate( + onDestroyed: () => widget.windowManagerModel.remove(key), + ), + title: "Regular", + ))); }, child: const Text('Create Regular Window'), ), @@ -131,18 +130,15 @@ class _RegularWindowContentState extends State listenable: widget.windowManagerModel, builder: (BuildContext context, Widget? _) { final List childViews = []; - for (final KeyedWindowController controller - in widget.windowManagerModel.windows) { + for (final KeyedWindowController controller in widget.windowManagerModel.windows) { if (controller.parent == widget.window) { childViews.add(WindowControllerRender( controller: controller.controller, key: controller.key, windowSettings: widget.windowSettings, windowManagerModel: widget.windowManagerModel, - onDestroyed: () => - widget.windowManagerModel.remove(controller.key), - onError: () => - widget.windowManagerModel.remove(controller.key), + onDestroyed: () => widget.windowManagerModel.remove(controller.key), + onError: () => widget.windowManagerModel.remove(controller.key), )); } } diff --git a/examples/multi_window_ref_app/lib/main.dart b/examples/multi_window_ref_app/lib/main.dart index 79332ce7bc8da..d9c989db712f3 100644 --- a/examples/multi_window_ref_app/lib/main.dart +++ b/examples/multi_window_ref_app/lib/main.dart @@ -3,11 +3,12 @@ import 'app/main_window.dart'; void main() { final RegularWindowController controller = RegularWindowController( - size: const Size(800, 600), - sizeConstraints: const BoxConstraints(minWidth: 640, minHeight: 480), + contentSize: WindowSizing( + size: const Size(800, 600), + constraints: const BoxConstraints(minWidth: 640, minHeight: 480), + ), title: "Multi-Window Reference Application", ); runWidget(RegularWindow( - controller: controller, - child: MaterialApp(home: MainWindow(mainController: controller)))); + controller: controller, child: MaterialApp(home: MainWindow(mainController: controller)))); } diff --git a/packages/flutter/lib/src/widgets/window.dart b/packages/flutter/lib/src/widgets/window.dart index cfb05b7081744..474b4f0926ba1 100644 --- a/packages/flutter/lib/src/widgets/window.dart +++ b/packages/flutter/lib/src/widgets/window.dart @@ -27,6 +27,18 @@ enum WindowState { minimized, } +/// Defines sizing request for a window. +class WindowSizing { + /// Creates a new [WindowSizing] object. + WindowSizing({this.size, this.constraints}); + + /// Preferred size of the window. This may not be honored by the platform. + final Size? size; + + /// Constraints for the window. This may not be honored by the platform. + final BoxConstraints? constraints; +} + /// Base class for window controllers. /// /// A window controller must provide a [future] that resolves to a @@ -117,24 +129,21 @@ abstract class RegularWindowController extends WindowController { /// Creates a [RegularWindowController] with the provided properties. /// Upon construction, the window is created for the platform. /// + /// [contentSize] Initial content size of the window. /// [title] the title of the window /// [state] the initial state of the window - /// [sizeConstraints] the size constraints of the window /// [delegate] optional delegate for the controller controller. - /// [size] the size of the window factory RegularWindowController({ + required WindowSizing contentSize, String? title, WindowState? state, - BoxConstraints? sizeConstraints, RegularWindowControllerDelegate? delegate, - required Size size, }) { WidgetsFlutterBinding.ensureInitialized(); final WindowingOwner owner = WidgetsBinding.instance.windowingOwner; final RegularWindowController controller = owner.createRegularWindowController( - size: size, + contentSize: contentSize, delegate: delegate ?? RegularWindowControllerDelegate(), - sizeConstraints: sizeConstraints, ); if (title != null || state != null) { controller.modify(title: title, state: state); @@ -161,7 +170,7 @@ abstract class RegularWindowController extends WindowController { /// [state] the new state of the window /// /// If no parameters are provided, then an assertion will be thrown. - void modify({Size? size, String? title, WindowState? state}); + void modify({WindowSizing? contentSize, String? title, WindowState? state}); } /// [WindowingOwner] is responsible for creating and managing window controllers. @@ -171,9 +180,8 @@ abstract class RegularWindowController extends WindowController { abstract class WindowingOwner { /// Creates a [RegularWindowController] with the provided properties. RegularWindowController createRegularWindowController({ - required Size size, + required WindowSizing contentSize, required RegularWindowControllerDelegate delegate, - BoxConstraints? sizeConstraints, }); /// Returns whether application has any top level windows created by this @@ -190,9 +198,8 @@ abstract class WindowingOwner { class _FallbackWindowingOwner extends WindowingOwner { @override RegularWindowController createRegularWindowController({ - required Size size, + required WindowSizing contentSize, required RegularWindowControllerDelegate delegate, - BoxConstraints? sizeConstraints, }) { throw UnsupportedError( 'Current platform does not support windowing.\n' diff --git a/packages/flutter/lib/src/widgets/window_macos.dart b/packages/flutter/lib/src/widgets/window_macos.dart index b170248a12ddc..51d6e104c2c30 100644 --- a/packages/flutter/lib/src/widgets/window_macos.dart +++ b/packages/flutter/lib/src/widgets/window_macos.dart @@ -9,15 +9,13 @@ import 'package:flutter/src/foundation/binding.dart'; class WindowingOwnerMacOS extends WindowingOwner { @override RegularWindowController createRegularWindowController({ - required Size size, + required WindowSizing contentSize, required RegularWindowControllerDelegate delegate, - BoxConstraints? sizeConstraints, }) { final RegularWindowControllerMacOS res = RegularWindowControllerMacOS( owner: this, delegate: delegate, - size: size, - sizeConstraints: sizeConstraints, + contentSize: contentSize, ); _activeControllers.add(res); return res; @@ -48,8 +46,7 @@ class RegularWindowControllerMacOS extends RegularWindowController { RegularWindowControllerMacOS({ required WindowingOwnerMacOS owner, required RegularWindowControllerDelegate delegate, - BoxConstraints? sizeConstraints, - required Size size, + required WindowSizing contentSize, String? title, }) : _owner = owner, _delegate = delegate, @@ -58,12 +55,7 @@ class RegularWindowControllerMacOS extends RegularWindowController { _onResize = NativeCallable.isolateLocal(_handleOnResize); final Pointer<_WindowCreationRequest> request = ffi.calloc<_WindowCreationRequest>() - ..ref.width = size.width - ..ref.height = size.height - ..ref.minWidth = sizeConstraints?.minWidth ?? 0 - ..ref.minHeight = sizeConstraints?.minHeight ?? 0 - ..ref.maxWidth = sizeConstraints?.maxWidth ?? 0 - ..ref.maxHeight = sizeConstraints?.maxHeight ?? 0 + ..ref.contentSize.set(contentSize) ..ref.onClose = _onClose.nativeFunction ..ref.onSizeChange = _onResize.nativeFunction; @@ -108,8 +100,11 @@ class RegularWindowControllerMacOS extends RegularWindowController { } /// Updates the window size. - void setSize(Size size) { - _setWindowSize(getWindowHandle(), size.width, size.height); + void setContentSize(WindowSizing size) { + final Pointer<_Sizing> sizing = ffi.calloc<_Sizing>(); + sizing.ref.set(size); + _setWindowContentSize(getWindowHandle(), sizing); + ffi.calloc.free(sizing); } /// Updates the window title. @@ -120,9 +115,9 @@ class RegularWindowControllerMacOS extends RegularWindowController { } @override - void modify({Size? size, String? title, WindowState? state}) { - if (size != null) { - setSize(size); + void modify({WindowSizing? contentSize, String? title, WindowState? state}) { + if (contentSize != null) { + setContentSize(contentSize); } if (title != null) { setTitle(title); @@ -162,8 +157,8 @@ class RegularWindowControllerMacOS extends RegularWindowController { @Native<_Size Function(Pointer)>(symbol: 'FlutterGetWindowSize') external static _Size _getWindowSize(Pointer windowHandle); - @Native, Double, Double)>(symbol: 'FlutterSetWindowSize') - external static void _setWindowSize(Pointer windowHandle, double width, double height); + @Native, Pointer<_Sizing>)>(symbol: 'FlutterSetWindowContentSize') + external static void _setWindowContentSize(Pointer windowHandle, Pointer<_Sizing> size); @Native, Pointer)>(symbol: 'FlutterSetWindowTitle') external static void _setWindowTitle(Pointer windowHandle, Pointer title); @@ -175,13 +170,19 @@ class RegularWindowControllerMacOS extends RegularWindowController { external static void _setWindowState(Pointer windowHandle, int state); } -final class _WindowCreationRequest extends Struct { +final class _Sizing extends Struct { + @Bool() + external bool hasSize; + @Double() external double width; @Double() external double height; + @Bool() + external bool hasConstraints; + @Double() external double minWidth; @@ -194,6 +195,32 @@ final class _WindowCreationRequest extends Struct { @Double() external double maxHeight; + void set(WindowSizing sizing) { + final Size? size = sizing.size; + if (size != null) { + hasSize = true; + width = size.width; + height = size.height; + } else { + hasSize = false; + } + + final BoxConstraints? constraints = sizing.constraints; + if (constraints != null) { + hasConstraints = true; + minWidth = constraints.minWidth; + minHeight = constraints.minHeight; + maxWidth = constraints.maxWidth; + maxHeight = constraints.maxHeight; + } else { + hasConstraints = false; + } + } +} + +final class _WindowCreationRequest extends Struct { + external _Sizing contentSize; + external Pointer> onClose; external Pointer> onSizeChange; } diff --git a/packages/flutter/lib/src/widgets/window_win32.dart b/packages/flutter/lib/src/widgets/window_win32.dart index e402dbf325489..5c744f954c7d0 100644 --- a/packages/flutter/lib/src/widgets/window_win32.dart +++ b/packages/flutter/lib/src/widgets/window_win32.dart @@ -34,16 +34,10 @@ class WindowingOwnerWin32 extends WindowingOwner { @override RegularWindowController createRegularWindowController({ - required Size size, + required WindowSizing contentSize, required RegularWindowControllerDelegate delegate, - BoxConstraints? sizeConstraints, }) { - return RegularWindowControllerWin32( - owner: this, - delegate: delegate, - size: size, - sizeConstraints: sizeConstraints, - ); + return RegularWindowControllerWin32(owner: this, delegate: delegate, contentSize: contentSize); } void addMessageHandler(WindowsMessageHandler handler) { @@ -96,20 +90,19 @@ class RegularWindowControllerWin32 extends RegularWindowController RegularWindowControllerWin32({ required WindowingOwnerWin32 owner, required RegularWindowControllerDelegate delegate, - BoxConstraints? sizeConstraints, - required Size size, + required WindowSizing contentSize, }) : _owner = owner, _delegate = delegate, super.empty() { owner.addMessageHandler(this); final Pointer<_WindowCreationRequest> request = ffi.calloc<_WindowCreationRequest>() - ..ref.width = size.width - ..ref.height = size.height - ..ref.minWidth = sizeConstraints?.minWidth ?? 0 - ..ref.minHeight = sizeConstraints?.minHeight ?? 0 - ..ref.maxWidth = sizeConstraints?.maxWidth ?? 0 - ..ref.maxHeight = sizeConstraints?.maxHeight ?? 0; + ..ref.width = 0 + ..ref.height = 0; + // ..ref.minWidth = sizeConstraints?.minWidth ?? 0 + // ..ref.minHeight = sizeConstraints?.minHeight ?? 0 + // ..ref.maxWidth = sizeConstraints?.maxWidth ?? 0 + // ..ref.maxHeight = sizeConstraints?.maxHeight ?? 0; final int viewId = _createWindow(PlatformDispatcher.instance.engineId!, request); ffi.calloc.free(request); final FlutterView flutterView = WidgetsBinding.instance.platformDispatcher.views.firstWhere( @@ -136,7 +129,7 @@ class RegularWindowControllerWin32 extends RegularWindowController } @override - void modify({Size? size, String? title, WindowState? state}) { + void modify({WindowSizing? contentSize, String? title, WindowState? state}) { _ensureNotDestroyed(); if (state != null) { setWindowState(state); @@ -145,7 +138,7 @@ class RegularWindowControllerWin32 extends RegularWindowController setWindowTitle(title); } if (size != null) { - setWindowSize(size); + // setWindowSize(size); } } @@ -185,7 +178,8 @@ class RegularWindowControllerWin32 extends RegularWindowController if (_destroyed) { return; } - _destroyWindow(getWindowHandle());; + _destroyWindow(getWindowHandle()); + ; _destroyed = true; _delegate.onWindowDestroyed(); _owner.removeMessageHandler(this); From a246874fbcbd589a6e379fcbfc7606b2571c6e13 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 15 Apr 2025 13:10:03 +0200 Subject: [PATCH 35/52] wip --- packages/flutter/lib/src/widgets/window.dart | 2 +- .../flutter/lib/src/widgets/window_macos.dart | 8 +-- .../flutter/lib/src/widgets/window_win32.dart | 63 ++++++++++++++----- 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/packages/flutter/lib/src/widgets/window.dart b/packages/flutter/lib/src/widgets/window.dart index 474b4f0926ba1..85e4706daff58 100644 --- a/packages/flutter/lib/src/widgets/window.dart +++ b/packages/flutter/lib/src/widgets/window.dart @@ -70,7 +70,7 @@ abstract class WindowController with ChangeNotifier { WindowArchetype get type; /// The current size of the window. This may differ from the requested size. - Size get size; + Size get contentSize; /// Destroys this window. It is permissible to call this method multiple times. void destroy(); diff --git a/packages/flutter/lib/src/widgets/window_macos.dart b/packages/flutter/lib/src/widgets/window_macos.dart index 51d6e104c2c30..8a4e4bff2e7e6 100644 --- a/packages/flutter/lib/src/widgets/window_macos.dart +++ b/packages/flutter/lib/src/widgets/window_macos.dart @@ -133,8 +133,8 @@ class RegularWindowControllerMacOS extends RegularWindowController { late final NativeCallable _onResize; @override - Size get size { - final _Size size = _getWindowSize(getWindowHandle()); + Size get contentSize { + final _Size size = _getWindowContentSize(getWindowHandle()); return Size(size.width, size.height); } @@ -154,8 +154,8 @@ class RegularWindowControllerMacOS extends RegularWindowController { @Native)>(symbol: 'FlutterDestroyWindow') external static void _destroyWindow(int engineId, Pointer handle); - @Native<_Size Function(Pointer)>(symbol: 'FlutterGetWindowSize') - external static _Size _getWindowSize(Pointer windowHandle); + @Native<_Size Function(Pointer)>(symbol: 'FlutterGetWindowContentSize') + external static _Size _getWindowContentSize(Pointer windowHandle); @Native, Pointer<_Sizing>)>(symbol: 'FlutterSetWindowContentSize') external static void _setWindowContentSize(Pointer windowHandle, Pointer<_Sizing> size); diff --git a/packages/flutter/lib/src/widgets/window_win32.dart b/packages/flutter/lib/src/widgets/window_win32.dart index 5c744f954c7d0..17b971e80f028 100644 --- a/packages/flutter/lib/src/widgets/window_win32.dart +++ b/packages/flutter/lib/src/widgets/window_win32.dart @@ -96,13 +96,7 @@ class RegularWindowControllerWin32 extends RegularWindowController super.empty() { owner.addMessageHandler(this); final Pointer<_WindowCreationRequest> request = - ffi.calloc<_WindowCreationRequest>() - ..ref.width = 0 - ..ref.height = 0; - // ..ref.minWidth = sizeConstraints?.minWidth ?? 0 - // ..ref.minHeight = sizeConstraints?.minHeight ?? 0 - // ..ref.maxWidth = sizeConstraints?.maxWidth ?? 0 - // ..ref.maxHeight = sizeConstraints?.maxHeight ?? 0; + ffi.calloc<_WindowCreationRequest>()..ref.contentSize.set(contentSize); final int viewId = _createWindow(PlatformDispatcher.instance.engineId!, request); ffi.calloc.free(request); final FlutterView flutterView = WidgetsBinding.instance.platformDispatcher.views.firstWhere( @@ -112,10 +106,10 @@ class RegularWindowControllerWin32 extends RegularWindowController } @override - Size get size { + Size get contentSize { _ensureNotDestroyed(); final Pointer<_Size> size = ffi.calloc<_Size>(); - _getWindowSize(getWindowHandle(), size); + _getWindowContentSize(getWindowHandle(), size); final Size result = Size(size.ref.width, size.ref.height); ffi.calloc.free(size); return result; @@ -137,8 +131,8 @@ class RegularWindowControllerWin32 extends RegularWindowController if (title != null) { setWindowTitle(title); } - if (size != null) { - // setWindowSize(size); + if (contentSize != null) { + setContentSize(contentSize); } } @@ -154,9 +148,12 @@ class RegularWindowControllerWin32 extends RegularWindowController ffi.calloc.free(titlePointer); } - void setWindowSize(Size size) { + void setContentSize(WindowSizing size) { _ensureNotDestroyed(); - _setWindowSize(getWindowHandle(), size.width, size.height); + final Pointer<_Sizing> sizing = ffi.calloc<_Sizing>(); + sizing.ref.set(size); + _setWindowContentSize(getWindowHandle(), sizing); + ffi.calloc.free(sizing); } Pointer getWindowHandle() { @@ -223,7 +220,7 @@ class RegularWindowControllerWin32 extends RegularWindowController external static void _destroyWindow(Pointer windowHandle); @Native, Pointer<_Size>)>(symbol: 'flutter_get_window_size') - external static void _getWindowSize(Pointer windowHandle, Pointer<_Size> size); + external static void _getWindowContentSize(Pointer windowHandle, Pointer<_Size> size); @Native)>(symbol: 'flutter_get_window_state') external static int _getWindowState(Pointer windowHandle); @@ -234,8 +231,8 @@ class RegularWindowControllerWin32 extends RegularWindowController @Native, Pointer)>(symbol: 'SetWindowTextW') external static void _setWindowTitle(Pointer windowHandle, Pointer title); - @Native, Double, Double)>(symbol: 'flutter_set_window_size') - external static void _setWindowSize(Pointer windowHandle, double width, double height); + @Native, Pointer<_Sizing>)>(symbol: 'flutter_set_window_size') + external static void _setWindowContentSize(Pointer windowHandle, Pointer<_Sizing> size); } /// Request to initialize windowing system. @@ -243,13 +240,19 @@ final class _WindowingInitRequest extends Struct { external Pointer)>> onMessage; } -final class _WindowCreationRequest extends Struct { +final class _Sizing extends Struct { + @Bool() + external bool hasSize; + @Double() external double width; @Double() external double height; + @Bool() + external bool hasConstraints; + @Double() external double minWidth; @@ -261,6 +264,32 @@ final class _WindowCreationRequest extends Struct { @Double() external double maxHeight; + + void set(WindowSizing sizing) { + final Size? size = sizing.size; + if (size != null) { + hasSize = true; + width = size.width; + height = size.height; + } else { + hasSize = false; + } + + final BoxConstraints? constraints = sizing.constraints; + if (constraints != null) { + hasConstraints = true; + minWidth = constraints.minWidth; + minHeight = constraints.minHeight; + maxWidth = constraints.maxWidth; + maxHeight = constraints.maxHeight; + } else { + hasConstraints = false; + } + } +} + +final class _WindowCreationRequest extends Struct { + external _Sizing contentSize; } /// Windows message received for all top level windows (regardless whether From 26df53aea44bd59d6484746c9128fbcc4753d949 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 15 Apr 2025 15:40:28 +0200 Subject: [PATCH 36/52] rename --- examples/multi_window_ref_app/lib/app/main_window.dart | 4 ++-- .../multi_window_ref_app/lib/app/regular_window_content.dart | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/multi_window_ref_app/lib/app/main_window.dart b/examples/multi_window_ref_app/lib/app/main_window.dart index 3f8acdbc5af6d..ec8a670e23312 100644 --- a/examples/multi_window_ref_app/lib/app/main_window.dart +++ b/examples/multi_window_ref_app/lib/app/main_window.dart @@ -161,8 +161,8 @@ class _ActiveWindowsTable extends StatelessWidget { onPressed: () { if (controller.controller.type == WindowArchetype.regular) { showRegularWindowEditDialog(context, - initialWidth: controller.controller.size.width, - initialHeight: controller.controller.size.height, + initialWidth: controller.controller.contentSize.width, + initialHeight: controller.controller.contentSize.height, initialTitle: "", initialState: (controller.controller as RegularWindowController) diff --git a/examples/multi_window_ref_app/lib/app/regular_window_content.dart b/examples/multi_window_ref_app/lib/app/regular_window_content.dart index b7e4ea2159d32..e1ef972c50119 100644 --- a/examples/multi_window_ref_app/lib/app/regular_window_content.dart +++ b/examples/multi_window_ref_app/lib/app/regular_window_content.dart @@ -114,7 +114,7 @@ class _RegularWindowContentState extends State builder: (BuildContext context, Widget? _) { return Text( 'View #${widget.window.rootView.viewId}\n' - 'Size: ${(widget.window.size.width).toStringAsFixed(1)}\u00D7${(widget.window.size.height).toStringAsFixed(1)}\n' + 'Size: ${(widget.window.contentSize.width).toStringAsFixed(1)}\u00D7${(widget.window.contentSize.height).toStringAsFixed(1)}\n' 'Device Pixel Ratio: $dpr', textAlign: TextAlign.center, ); From 0894c21a9297b17e30ab57960f1c3308be5d2152 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 15 Apr 2025 18:27:28 +0200 Subject: [PATCH 37/52] Fix tests and build --- .../platform/windows/flutter_host_window.cc | 64 +++--- .../platform/windows/flutter_host_window.h | 15 +- .../windows/flutter_host_window_controller.cc | 197 +++++++----------- .../windows/flutter_host_window_controller.h | 61 +++++- ...lutter_host_window_controller_unittests.cc | 143 +++++-------- .../flutter/lib/src/widgets/window_win32.dart | 24 +-- 6 files changed, 239 insertions(+), 265 deletions(-) diff --git a/engine/src/flutter/shell/platform/windows/flutter_host_window.cc b/engine/src/flutter/shell/platform/windows/flutter_host_window.cc index 07be173e79b29..4ae52566e1584 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_host_window.cc +++ b/engine/src/flutter/shell/platform/windows/flutter_host_window.cc @@ -266,7 +266,8 @@ void UpdateTheme(HWND window) { namespace flutter { FlutterHostWindow::FlutterHostWindow(FlutterHostWindowController* controller, - WindowArchetype archetype) + WindowArchetype archetype, + const FlutterWindowSizing& content_size) : window_controller_(controller), archetype_(archetype) { // Check preconditions and set window styles based on window type. DWORD window_style = 0; @@ -279,24 +280,23 @@ FlutterHostWindow::FlutterHostWindow(FlutterHostWindowController* controller, FML_UNREACHABLE(); } - // Validate size constraints. - min_size_ = settings.min_size; - max_size_ = settings.max_size; - if (min_size_ && max_size_) { - if (min_size_->width() > max_size_->width() || - min_size_->height() > max_size_->height()) { - FML_LOG(ERROR) << "Invalid size constraints."; - return; + if (content_size.has_constraints) { + min_size_ = Size(content_size.min_width, content_size.min_height); + if (content_size.max_width > 0 && content_size.max_height > 0) { + max_size_ = Size(content_size.max_width, content_size.max_height); } } + // TODO(knopp): What about windows sized to content? + assert(content_size.has_size); + // Calculate the screen space window rectangle for the new window. // Default positioning values (CW_USEDEFAULT) are used // if the window has no owner. Rect const initial_window_rect = [&]() -> Rect { std::optional const window_size = GetWindowSizeForClientSize( - settings.size, min_size_, max_size_, window_style, - extended_window_style, nullptr); + Size(content_size.width, content_size.height), min_size_, max_size_, + window_style, extended_window_style, nullptr); return {{CW_USEDEFAULT, CW_USEDEFAULT}, window_size ? *window_size : Size{CW_USEDEFAULT, CW_USEDEFAULT}}; }(); @@ -354,12 +354,11 @@ FlutterHostWindow::FlutterHostWindow(FlutterHostWindowController* controller, } // Create the native window. - HWND hwnd = CreateWindowEx( - extended_window_style, kWindowClassName, - StringToWstring(settings.title.value_or("")).c_str(), window_style, - initial_window_rect.left(), initial_window_rect.top(), - initial_window_rect.width(), initial_window_rect.height(), nullptr, - nullptr, GetModuleHandle(nullptr), this); + HWND hwnd = + CreateWindowEx(extended_window_style, kWindowClassName, L"", window_style, + initial_window_rect.left(), initial_window_rect.top(), + initial_window_rect.width(), initial_window_rect.height(), + nullptr, nullptr, GetModuleHandle(nullptr), this); if (!hwnd) { FML_LOG(ERROR) << "Cannot create window: " << GetLastErrorAsString(); @@ -533,25 +532,28 @@ LRESULT FlutterHostWindow::HandleMessage(HWND hwnd, return DefWindowProc(hwnd, message, wparam, lparam); } -void FlutterHostWindow::SetClientSize(Size const& client_size) { +void FlutterHostWindow::SetContentSize(const FlutterWindowSizing& size) { WINDOWINFO window_info = {.cbSize = sizeof(WINDOWINFO)}; GetWindowInfo(window_handle_, &window_info); - std::optional const window_size = GetWindowSizeForClientSize( - client_size, min_size_, max_size_, window_info.dwStyle, - window_info.dwExStyle, nullptr); - - Size const size = window_size.value_or(client_size); - SetWindowPos(window_handle_, NULL, 0, 0, size.width(), size.height(), - SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); -} + if (size.has_constraints) { + min_size_ = Size(size.min_width, size.min_height); + if (size.max_width > 0 && size.max_height > 0) { + max_size_ = Size(size.max_width, size.max_height); + } + } -void FlutterHostWindow::SetMinClientSize(Size const& min_size) { - min_size_ = min_size; -} + if (size.has_size) { + std::optional const window_size = GetWindowSizeForClientSize( + Size(size.width, size.height), min_size_, max_size_, + window_info.dwStyle, window_info.dwExStyle, nullptr); -void FlutterHostWindow::SetMaxClientSize(Size const& max_size) { - max_size_ = max_size; + if (window_size) { + SetWindowPos(window_handle_, NULL, 0, 0, window_size->width(), + window_size->height(), + SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + } + } } void FlutterHostWindow::SetChildContent(HWND content) { diff --git a/engine/src/flutter/shell/platform/windows/flutter_host_window.h b/engine/src/flutter/shell/platform/windows/flutter_host_window.h index c7358f510e44b..db4ebd1a446a5 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_host_window.h +++ b/engine/src/flutter/shell/platform/windows/flutter_host_window.h @@ -6,13 +6,13 @@ #define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_HOST_WINDOW_H_ #include - #include #include #include "flutter/fml/macros.h" #include "flutter/shell/platform/common/geometry.h" #include "flutter/shell/platform/common/windowing.h" +#include "flutter/shell/platform/windows/flutter_host_window_controller.h" namespace flutter { @@ -28,7 +28,8 @@ class FlutterHostWindow { // |FlutterHostWindow|. On success, a valid window handle can be retrieved // via |FlutterHostWindow::GetWindowHandle|. FlutterHostWindow(FlutterHostWindowController* controller, - WindowArchetype archetype); + WindowArchetype archetype, + const FlutterWindowSizing& content_size); virtual ~FlutterHostWindow(); @@ -40,14 +41,8 @@ class FlutterHostWindow { HWND GetWindowHandle() const; // Resizes the window to accommodate a client area of the given - // |client_size|. - void SetClientSize(Size const& client_size); - - // Sets the minimum client size for the window. - void SetMinClientSize(Size const& min_size); - - // Sets the maximum client size for the window. - void SetMaxClientSize(Size const& max_size); + // |size|. + void SetContentSize(const FlutterWindowSizing& size); private: friend FlutterHostWindowController; diff --git a/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.cc b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.cc index 7a4274bec7c59..c922028350269 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.cc +++ b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.cc @@ -10,6 +10,7 @@ #include "embedder.h" #include "flutter/shell/platform/common/windowing.h" +#include "flutter/shell/platform/windows/flutter_host_window.h" #include "flutter/shell/platform/windows/flutter_windows_engine.h" #include "flutter/shell/platform/windows/flutter_windows_view_controller.h" #include "fml/logging.h" @@ -19,115 +20,6 @@ namespace flutter { -struct WindowingInitRequest { - void (*on_message)(WindowsMessage*); -}; - -struct WindowCreationRequest { - double width; - double height; - double min_width; - double min_height; - double max_width; - double max_height; -}; -} // namespace flutter - -extern "C" { - -FLUTTER_EXPORT -void flutter_windowing_initialize( - int64_t engine_id, - const flutter::WindowingInitRequest* request) { - flutter::FlutterWindowsEngine* engine = - flutter::FlutterWindowsEngine::GetEngineForId(engine_id); - engine->get_host_window_controller()->Initialize(request); -} - -FLUTTER_EXPORT -bool flutter_windowing_has_top_level_windows(int64_t engine_id) { - flutter::FlutterWindowsEngine* engine = - flutter::FlutterWindowsEngine::GetEngineForId(engine_id); - return engine->get_host_window_controller()->HasTopLevelWindows(); -} - -FLUTTER_EXPORT -int64_t flutter_create_regular_window( - int64_t engine_id, - const flutter::WindowCreationRequest* request) { - flutter::FlutterWindowsEngine* engine = - flutter::FlutterWindowsEngine::GetEngineForId(engine_id); - return engine->get_host_window_controller()->CreateRegularWindow(request); -} - -FLUTTER_EXPORT -HWND flutter_get_window_handle(int64_t engine_id, FlutterViewId view_id) { - flutter::FlutterWindowsEngine* engine = - flutter::FlutterWindowsEngine::GetEngineForId(engine_id); - flutter::FlutterWindowsView* view = engine->view(view_id); - if (view == nullptr) { - return nullptr; - } else { - return GetAncestor(view->GetWindowHandle(), GA_ROOT); - } -} - -struct Size { - double width; - double height; -}; - -FLUTTER_EXPORT -void flutter_get_window_size(HWND hwnd, Size* size) { - RECT rect; - GetClientRect(hwnd, &rect); - double const dpr = FlutterDesktopGetDpiForHWND(hwnd) / - static_cast(USER_DEFAULT_SCREEN_DPI); - double const width = rect.right / dpr; - double const height = rect.bottom / dpr; - size->width = width; - size->height = height; -} - -FLUTTER_EXPORT -int64_t flutter_get_window_state(HWND hwnd) { - if (IsIconic(hwnd)) { - return static_cast(flutter::WindowState::kMinimized); - } else if (IsZoomed(hwnd)) { - return static_cast(flutter::WindowState::kMaximized); - } else { - return static_cast(flutter::WindowState::kRestored); - } -} - -FLUTTER_EXPORT -void flutter_set_window_state(HWND hwnd, int64_t state) { - switch (static_cast(state)) { - case flutter::WindowState::kRestored: - ShowWindow(hwnd, SW_RESTORE); - break; - case flutter::WindowState::kMaximized: - ShowWindow(hwnd, SW_MAXIMIZE); - break; - case flutter::WindowState::kMinimized: - ShowWindow(hwnd, SW_MINIMIZE); - break; - } -} - -FLUTTER_EXPORT -void flutter_set_window_size(HWND hwnd, double width, double height) { - flutter::FlutterHostWindow* window = - flutter::FlutterHostWindow::GetThisFromHandle(hwnd); - if (window) { - window->SetClientSize(flutter::Size(width, height)); - } -} - -} // extern "C" - -namespace flutter { - FlutterHostWindowController::FlutterHostWindowController( FlutterWindowsEngine* engine) : engine_(engine) {} @@ -151,13 +43,8 @@ bool FlutterHostWindowController::HasTopLevelWindows() const { FlutterViewId FlutterHostWindowController::CreateRegularWindow( const WindowCreationRequest* request) { - WindowCreationSettings settings; - settings.size = Size(request->width, request->height); - settings.min_size = Size(request->min_width, request->min_height); - if (request->max_width != 0 && request->max_height != 0) { - settings.max_size = Size(request->max_width, request->max_height); - } - auto window = std::make_unique(this, settings); + auto window = std::make_unique( + this, WindowArchetype::kRegular, request->content_size); if (!window->GetWindowHandle()) { FML_LOG(ERROR) << "Failed to create host window"; return 0; @@ -226,3 +113,81 @@ FlutterWindowsEngine* FlutterHostWindowController::engine() const { } } // namespace flutter + +void FlutterWindowingInitialize(int64_t engine_id, + const flutter::WindowingInitRequest* request) { + flutter::FlutterWindowsEngine* engine = + flutter::FlutterWindowsEngine::GetEngineForId(engine_id); + engine->get_host_window_controller()->Initialize(request); +} + +bool FlutterWindowingHasTopLevelWindows(int64_t engine_id) { + flutter::FlutterWindowsEngine* engine = + flutter::FlutterWindowsEngine::GetEngineForId(engine_id); + return engine->get_host_window_controller()->HasTopLevelWindows(); +} + +int64_t FlutterCreateRegularWindow( + int64_t engine_id, + const flutter::WindowCreationRequest* request) { + flutter::FlutterWindowsEngine* engine = + flutter::FlutterWindowsEngine::GetEngineForId(engine_id); + return engine->get_host_window_controller()->CreateRegularWindow(request); +} + +HWND FlutterGetWindowHandle(int64_t engine_id, FlutterViewId view_id) { + flutter::FlutterWindowsEngine* engine = + flutter::FlutterWindowsEngine::GetEngineForId(engine_id); + flutter::FlutterWindowsView* view = engine->view(view_id); + if (view == nullptr) { + return nullptr; + } else { + return GetAncestor(view->GetWindowHandle(), GA_ROOT); + } +} + +FlutterWindowSize FlutterGetWindowContentSize(HWND hwnd) { + RECT rect; + GetClientRect(hwnd, &rect); + double const dpr = FlutterDesktopGetDpiForHWND(hwnd) / + static_cast(USER_DEFAULT_SCREEN_DPI); + double const width = rect.right / dpr; + double const height = rect.bottom / dpr; + return { + .width = rect.right / dpr, + .height = rect.bottom / dpr, + }; +} + +int64_t FlutterGetWindowState(HWND hwnd) { + if (IsIconic(hwnd)) { + return static_cast(flutter::WindowState::kMinimized); + } else if (IsZoomed(hwnd)) { + return static_cast(flutter::WindowState::kMaximized); + } else { + return static_cast(flutter::WindowState::kRestored); + } +} + +void FlutterSetWindowState(HWND hwnd, int64_t state) { + switch (static_cast(state)) { + case flutter::WindowState::kRestored: + ShowWindow(hwnd, SW_RESTORE); + break; + case flutter::WindowState::kMaximized: + ShowWindow(hwnd, SW_MAXIMIZE); + break; + case flutter::WindowState::kMinimized: + ShowWindow(hwnd, SW_MINIMIZE); + break; + } +} + +void FlutterSetWindowContentSize(HWND hwnd, + const flutter::FlutterWindowSizing* size) { + flutter::FlutterHostWindow* window = + flutter::FlutterHostWindow::GetThisFromHandle(hwnd); + if (window) { + window->SetContentSize(*size); + } +} diff --git a/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.h b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.h index df409deb442df..36127619a6638 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.h +++ b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller.h @@ -5,20 +5,22 @@ #ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_HOST_WINDOW_CONTROLLER_H_ #define FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_HOST_WINDOW_CONTROLLER_H_ +#include #include #include #include +#include "flutter/shell/platform/common/public/flutter_export.h" + #include "flutter/fml/macros.h" #include "flutter/shell/platform/common/isolate_scope.h" #include "flutter/shell/platform/embedder/embedder.h" -#include "flutter/shell/platform/windows/flutter_host_window.h" namespace flutter { class FlutterWindowsEngine; +class FlutterHostWindow; struct WindowingInitRequest; -struct WindowCreationRequest; struct WindowsMessage { int64_t view_id; @@ -30,6 +32,25 @@ struct WindowsMessage { bool handled; }; +struct FlutterWindowSizing { + bool has_size; + double width; + double height; + bool has_constraints; + double min_width; + double min_height; + double max_width; + double max_height; +}; + +struct WindowingInitRequest { + void (*on_message)(WindowsMessage*); +}; + +struct WindowCreationRequest { + FlutterWindowSizing content_size; +}; + // A controller class for managing |FlutterHostWindow| instances. // A unique instance of this class is owned by |FlutterWindowsEngine| and used // in |WindowingHandler| to handle methods and messages enabling multi-window @@ -82,4 +103,40 @@ class FlutterHostWindowController { } // namespace flutter +extern "C" { + +FLUTTER_EXPORT +void FlutterWindowingInitialize(int64_t engine_id, + const flutter::WindowingInitRequest* request); + +FLUTTER_EXPORT +bool FlutterWindowingHasTopLevelWindows(int64_t engine_id); + +FLUTTER_EXPORT +int64_t FlutterCreateRegularWindow( + int64_t engine_id, + const flutter::WindowCreationRequest* request); + +FLUTTER_EXPORT +HWND FlutterGetWindowHandle(int64_t engine_id, FlutterViewId view_id); + +struct FlutterWindowSize { + double width; + double height; +}; + +FLUTTER_EXPORT +FlutterWindowSize FlutterGetWindowContentSize(HWND hwnd); + +FLUTTER_EXPORT +int64_t FlutterGetWindowState(HWND hwnd); + +FLUTTER_EXPORT +void FlutterSetWindowState(HWND hwnd, int64_t state); + +FLUTTER_EXPORT +void FlutterSetWindowContentSize(HWND hwnd, + const flutter::FlutterWindowSizing* size); +} + #endif // FLUTTER_SHELL_PLATFORM_WINDOWS_FLUTTER_HOST_WINDOW_CONTROLLER_H_ diff --git a/engine/src/flutter/shell/platform/windows/flutter_host_window_controller_unittests.cc b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller_unittests.cc index 1dc37472a12cc..b854762b999c8 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_host_window_controller_unittests.cc +++ b/engine/src/flutter/shell/platform/windows/flutter_host_window_controller_unittests.cc @@ -8,49 +8,6 @@ #include "gtest/gtest.h" namespace flutter { - -struct WindowingInitRequest { - void (*on_message)(WindowsMessage*); -}; - -struct WindowCreationRequest { - double width; - double height; - double min_width; - double min_height; - double max_width; - double max_height; -}; - -extern "C" { -FLUTTER_EXPORT -void flutter_windowing_initialize(int64_t engine_id, - const flutter::WindowingInitRequest* request); - -FLUTTER_EXPORT -bool flutter_windowing_has_top_level_windows(int64_t engine_id); - -FLUTTER_EXPORT -int64_t flutter_create_regular_window( - int64_t engine_id, - const flutter::WindowCreationRequest* request); - -FLUTTER_EXPORT -HWND flutter_get_window_handle(int64_t engine_id, FlutterViewId view_id); - -FLUTTER_EXPORT -void flutter_get_window_size(HWND hwnd, Size* size); - -FLUTTER_EXPORT -int64_t flutter_get_window_state(HWND hwnd); - -FLUTTER_EXPORT -void flutter_set_window_state(HWND hwnd, int64_t state); - -FLUTTER_EXPORT -void flutter_set_window_size(HWND hwnd, double width, double height); -} - namespace testing { namespace { @@ -64,7 +21,7 @@ class FlutterHostWindowControllerTest : public WindowsTest { void SetUp() override { auto& context = GetContext(); FlutterWindowsEngineBuilder builder(context); - builder.SetSwitches({"--enable-multi-window=true"}); + builder.SetSwitches({"--enable-windowing=true"}); engine_ = builder.Build(); ASSERT_TRUE(engine_); @@ -75,7 +32,7 @@ class FlutterHostWindowControllerTest : public WindowsTest { bool signalled = false; context.AddNativeFunction( "Signal", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { - isolate_ = std::make_unique(); + isolate_ = flutter::Isolate::Current(); signalled = true; })); while (!signalled) { @@ -86,19 +43,19 @@ class FlutterHostWindowControllerTest : public WindowsTest { void TearDown() override { engine_->Stop(); } int64_t engine_id() { return reinterpret_cast(engine_.get()); } - flutter::Isolate* isolate() { return isolate_.get(); } + flutter::Isolate& isolate() { return *isolate_; } WindowCreationRequest* creation_request() { return &creation_request_; } private: std::unique_ptr engine_; - std::unique_ptr isolate_; + std::optional isolate_; WindowCreationRequest creation_request_{ - .width = 800, - .height = 600, - .min_width = 0, - .min_height = 0, - .max_width = 0, - .max_height = 0, + .content_size = + { + .has_size = true, + .width = 800, + .height = 600, + }, }; FML_DISALLOW_COPY_AND_ASSIGN(FlutterHostWindowControllerTest); @@ -107,79 +64,77 @@ class FlutterHostWindowControllerTest : public WindowsTest { } // namespace TEST_F(FlutterHostWindowControllerTest, WindowingInitialize) { - IsolateScope isolate_scope(*isolate()); + IsolateScope isolate_scope(isolate()); static bool received_message = false; WindowingInitRequest init_request{ .on_message = [](WindowsMessage* message) { received_message = true; }}; + FlutterWindowingInitialize(engine_id(), &init_request); const int64_t view_id = - flutter_create_regular_window(engine_id(), creation_request()); - flutter_windowing_initialize(engine_id(), &init_request); - DestroyWindow(flutter_get_window_handle(engine_id(), view_id)); + FlutterCreateRegularWindow(engine_id(), creation_request()); + DestroyWindow(FlutterGetWindowHandle(engine_id(), view_id)); EXPECT_TRUE(received_message); } TEST_F(FlutterHostWindowControllerTest, HasTopLevelWindows) { - IsolateScope isolate_scope(*isolate()); + IsolateScope isolate_scope(isolate()); - bool has_top_level_windows = - flutter_windowing_has_top_level_windows(engine_id()); + bool has_top_level_windows = FlutterWindowingHasTopLevelWindows(engine_id()); EXPECT_FALSE(has_top_level_windows); - flutter_create_regular_window(engine_id(), creation_request()); - has_top_level_windows = flutter_windowing_has_top_level_windows(engine_id()); + FlutterCreateRegularWindow(engine_id(), creation_request()); + has_top_level_windows = FlutterWindowingHasTopLevelWindows(engine_id()); EXPECT_TRUE(has_top_level_windows); } TEST_F(FlutterHostWindowControllerTest, CreateRegularWindow) { - IsolateScope isolate_scope(*isolate()); + IsolateScope isolate_scope(isolate()); const int64_t view_id = - flutter_create_regular_window(engine_id(), creation_request()); + FlutterCreateRegularWindow(engine_id(), creation_request()); EXPECT_EQ(view_id, 0); } TEST_F(FlutterHostWindowControllerTest, GetWindowHandle) { - IsolateScope isolate_scope(*isolate()); + IsolateScope isolate_scope(isolate()); const int64_t view_id = - flutter_create_regular_window(engine_id(), creation_request()); - const HWND window_handle = flutter_get_window_handle(engine_id(), view_id); + FlutterCreateRegularWindow(engine_id(), creation_request()); + const HWND window_handle = FlutterGetWindowHandle(engine_id(), view_id); EXPECT_NE(window_handle, nullptr); } TEST_F(FlutterHostWindowControllerTest, GetWindowSize) { - IsolateScope isolate_scope(*isolate()); + IsolateScope isolate_scope(isolate()); const int64_t view_id = - flutter_create_regular_window(engine_id(), creation_request()); - const HWND window_handle = flutter_get_window_handle(engine_id(), view_id); + FlutterCreateRegularWindow(engine_id(), creation_request()); + const HWND window_handle = FlutterGetWindowHandle(engine_id(), view_id); - Size size; - flutter_get_window_size(window_handle, &size); + FlutterWindowSize size = FlutterGetWindowContentSize(window_handle); - EXPECT_EQ(size.width(), creation_request()->width); - EXPECT_EQ(size.height(), creation_request()->height); + EXPECT_EQ(size.width, creation_request()->content_size.width); + EXPECT_EQ(size.height, creation_request()->content_size.height); } TEST_F(FlutterHostWindowControllerTest, GetWindowState) { - IsolateScope isolate_scope(*isolate()); + IsolateScope isolate_scope(isolate()); const int64_t view_id = - flutter_create_regular_window(engine_id(), creation_request()); - const HWND window_handle = flutter_get_window_handle(engine_id(), view_id); - const int64_t window_state = flutter_get_window_state(window_handle); + FlutterCreateRegularWindow(engine_id(), creation_request()); + const HWND window_handle = FlutterGetWindowHandle(engine_id(), view_id); + const int64_t window_state = FlutterGetWindowState(window_handle); EXPECT_EQ(window_state, static_cast(WindowState::kRestored)); } TEST_F(FlutterHostWindowControllerTest, SetWindowState) { - IsolateScope isolate_scope(*isolate()); + IsolateScope isolate_scope(isolate()); const int64_t view_id = - flutter_create_regular_window(engine_id(), creation_request()); - const HWND window_handle = flutter_get_window_handle(engine_id(), view_id); + FlutterCreateRegularWindow(engine_id(), creation_request()); + const HWND window_handle = FlutterGetWindowHandle(engine_id(), view_id); const std::array kWindowStates = { static_cast(WindowState::kRestored), @@ -188,27 +143,29 @@ TEST_F(FlutterHostWindowControllerTest, SetWindowState) { }; for (const auto requested_state : kWindowStates) { - flutter_set_window_state(window_handle, requested_state); - const int64_t actual_state = flutter_get_window_state(window_handle); + FlutterSetWindowState(window_handle, requested_state); + const int64_t actual_state = FlutterGetWindowState(window_handle); EXPECT_EQ(actual_state, requested_state); } } TEST_F(FlutterHostWindowControllerTest, SetWindowSize) { - IsolateScope isolate_scope(*isolate()); + IsolateScope isolate_scope(isolate()); const int64_t view_id = - flutter_create_regular_window(engine_id(), creation_request()); - const HWND window_handle = flutter_get_window_handle(engine_id(), view_id); + FlutterCreateRegularWindow(engine_id(), creation_request()); + const HWND window_handle = FlutterGetWindowHandle(engine_id(), view_id); - const Size requested_size{640, 480}; - flutter_set_window_size(window_handle, requested_size.width(), - requested_size.height()); + FlutterWindowSizing requestedSize{ + .has_size = true, + .width = 640, + .height = 480, + }; + FlutterSetWindowContentSize(window_handle, &requestedSize); - Size actual_size; - flutter_get_window_size(window_handle, &actual_size); - EXPECT_EQ(actual_size.width(), requested_size.width()); - EXPECT_EQ(actual_size.height(), requested_size.height()); + FlutterWindowSize actual_size = FlutterGetWindowContentSize(window_handle); + EXPECT_EQ(actual_size.width, 640); + EXPECT_EQ(actual_size.height, 480); } } // namespace testing diff --git a/packages/flutter/lib/src/widgets/window_win32.dart b/packages/flutter/lib/src/widgets/window_win32.dart index 17b971e80f028..c36275d7841ec 100644 --- a/packages/flutter/lib/src/widgets/window_win32.dart +++ b/packages/flutter/lib/src/widgets/window_win32.dart @@ -76,11 +76,11 @@ class WindowingOwnerWin32 extends WindowingOwner { return _hasTopLevelWindows(PlatformDispatcher.instance.engineId!); } - @Native(symbol: 'flutter_windowing_has_top_level_windows') + @Native(symbol: 'FlutterWindowingHasTopLevelWindows') external static bool _hasTopLevelWindows(int engineId); @Native)>( - symbol: 'flutter_windowing_initialize', + symbol: 'FlutterWindowingInitialize', ) external static void _initializeWindowing(int engineId, Pointer<_WindowingInitRequest> request); } @@ -108,10 +108,8 @@ class RegularWindowControllerWin32 extends RegularWindowController @override Size get contentSize { _ensureNotDestroyed(); - final Pointer<_Size> size = ffi.calloc<_Size>(); - _getWindowContentSize(getWindowHandle(), size); - final Size result = Size(size.ref.width, size.ref.height); - ffi.calloc.free(size); + final _Size size = _getWindowContentSize(getWindowHandle()); + final Size result = Size(size.width, size.height); return result; } @@ -209,29 +207,29 @@ class RegularWindowControllerWin32 extends RegularWindowController final WindowingOwnerWin32 _owner; @Native)>( - symbol: 'flutter_create_regular_window', + symbol: 'FlutterCreateRegularWindow', ) external static int _createWindow(int engineId, Pointer<_WindowCreationRequest> request); - @Native Function(Int64, Int64)>(symbol: 'flutter_get_window_handle') + @Native Function(Int64, Int64)>(symbol: 'FlutterGetWindowHandle') external static Pointer _getWindowHandle(int engineId, int viewId); @Native)>(symbol: 'DestroyWindow') external static void _destroyWindow(Pointer windowHandle); - @Native, Pointer<_Size>)>(symbol: 'flutter_get_window_size') - external static void _getWindowContentSize(Pointer windowHandle, Pointer<_Size> size); + @Native<_Size Function(Pointer)>(symbol: 'FlutterGetWindowContentSize') + external static _Size _getWindowContentSize(Pointer windowHandle); - @Native)>(symbol: 'flutter_get_window_state') + @Native)>(symbol: 'FlutterGetWindowState') external static int _getWindowState(Pointer windowHandle); - @Native, Int64)>(symbol: 'flutter_set_window_state') + @Native, Int64)>(symbol: 'FlutterSetWindowState') external static void _setWindowState(Pointer windowHandle, int state); @Native, Pointer)>(symbol: 'SetWindowTextW') external static void _setWindowTitle(Pointer windowHandle, Pointer title); - @Native, Pointer<_Sizing>)>(symbol: 'flutter_set_window_size') + @Native, Pointer<_Sizing>)>(symbol: 'FlutterSetWindowContentSize') external static void _setWindowContentSize(Pointer windowHandle, Pointer<_Sizing> size); } From 6b6e32b36d68b4ebc816fc5b7c2f522ff2b92449 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Wed, 16 Apr 2025 15:20:37 +0200 Subject: [PATCH 38/52] Break-up modify, rename methods --- .../Source/FlutterWindowController.h | 2 +- .../Source/FlutterWindowController.mm | 2 +- .../lib/app/main_window.dart | 19 ++++++++---- packages/flutter/lib/src/widgets/window.dart | 30 ++++++++++++------- .../flutter/lib/src/widgets/window_macos.dart | 19 ++---------- .../flutter/lib/src/widgets/window_win32.dart | 19 +++--------- 6 files changed, 42 insertions(+), 49 deletions(-) diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h index 9681a0d2c5de1..fce87090e3126 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.h @@ -57,7 +57,7 @@ FLUTTER_DARWIN_EXPORT void* FlutterGetWindowHandle(int64_t engine_id, FlutterViewIdentifier view_id); FLUTTER_DARWIN_EXPORT -FlutterWindowSize FlutterGetWindowSize(void* window); +FlutterWindowSize FlutterGetWindowContentSize(void* window); FLUTTER_DARWIN_EXPORT void FlutterSetWindowContentSize(void* window, const FlutterWindowSizing* size); diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm index ccf1a922e083d..3949c87a34472 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterWindowController.mm @@ -174,7 +174,7 @@ void FlutterDestroyWindow(int64_t engine_id, void* window) { return (__bridge void*)controller.view.window; } -FlutterWindowSize FlutterGetWindowSize(void* window) { +FlutterWindowSize FlutterGetWindowContentSize(void* window) { NSWindow* w = (__bridge NSWindow*)window; return { .width = w.frame.size.width, diff --git a/examples/multi_window_ref_app/lib/app/main_window.dart b/examples/multi_window_ref_app/lib/app/main_window.dart index ec8a670e23312..93bde69145102 100644 --- a/examples/multi_window_ref_app/lib/app/main_window.dart +++ b/examples/multi_window_ref_app/lib/app/main_window.dart @@ -168,12 +168,19 @@ class _ActiveWindowsTable extends StatelessWidget { (controller.controller as RegularWindowController) .state, onSave: (double? width, double? height, String? title, WindowState? state) { - (controller.controller as RegularWindowController).modify( - contentSize: width != null && height != null - ? WindowSizing(size: Size(width, height)) - : null, - title: title, - state: state); + final regularController = + controller.controller as RegularWindowController; + if (width != null && height != null) { + regularController.setContentSize( + WindowSizing(size: Size(width, height)), + ); + } + if (title != null) { + regularController.setTitle(title); + } + if (state != null) { + regularController.setState(state); + } }); } }, diff --git a/packages/flutter/lib/src/widgets/window.dart b/packages/flutter/lib/src/widgets/window.dart index 85e4706daff58..60f7718fcc8a7 100644 --- a/packages/flutter/lib/src/widgets/window.dart +++ b/packages/flutter/lib/src/widgets/window.dart @@ -145,8 +145,11 @@ abstract class RegularWindowController extends WindowController { contentSize: contentSize, delegate: delegate ?? RegularWindowControllerDelegate(), ); - if (title != null || state != null) { - controller.modify(title: title, state: state); + if (title != null) { + controller.setTitle(title); + } + if (state != null) { + controller.setState(state); } return controller; } @@ -161,16 +164,23 @@ abstract class RegularWindowController extends WindowController { /// The current state of the window. WindowState get state; - /// Modify the properties of the window. The window must be ready before - /// calling this method. If the window is not ready, an assertion will be - /// thrown. The caller must provide at least one of the following parameters: + /// Request change for the window content size. /// - /// [size] the new size of the window - /// [title] the new title of the window - /// [state] the new state of the window + /// [contentSize] describes the new requested window size. The properties + /// of this object are applied independently of each other. For example, + /// setting [WindowSizing.size] does not affect the [WindowSizing.constraints] + /// set previously. /// - /// If no parameters are provided, then an assertion will be thrown. - void modify({WindowSizing? contentSize, String? title, WindowState? state}); + /// System compositor is free to ignore the request. + void setContentSize(WindowSizing contentSize); + + /// Request change for the window title. + /// [title] new title of the window. + void setTitle(String title); + + /// Request change for the window state. + /// [state] new state of the window. + void setState(WindowState state); } /// [WindowingOwner] is responsible for creating and managing window controllers. diff --git a/packages/flutter/lib/src/widgets/window_macos.dart b/packages/flutter/lib/src/widgets/window_macos.dart index 8a4e4bff2e7e6..a392e7eafad46 100644 --- a/packages/flutter/lib/src/widgets/window_macos.dart +++ b/packages/flutter/lib/src/widgets/window_macos.dart @@ -99,7 +99,7 @@ class RegularWindowControllerMacOS extends RegularWindowController { notifyListeners(); } - /// Updates the window size. + @override void setContentSize(WindowSizing size) { final Pointer<_Sizing> sizing = ffi.calloc<_Sizing>(); sizing.ref.set(size); @@ -107,26 +107,13 @@ class RegularWindowControllerMacOS extends RegularWindowController { ffi.calloc.free(sizing); } - /// Updates the window title. + @override void setTitle(String title) { final Pointer titlePointer = title.toNativeUtf8(); _setWindowTitle(getWindowHandle(), titlePointer); ffi.calloc.free(titlePointer); } - @override - void modify({WindowSizing? contentSize, String? title, WindowState? state}) { - if (contentSize != null) { - setContentSize(contentSize); - } - if (title != null) { - setTitle(title); - } - if (state != null) { - setState(state); - } - } - final WindowingOwnerMacOS _owner; final RegularWindowControllerDelegate _delegate; late final NativeCallable _onClose; @@ -141,7 +128,7 @@ class RegularWindowControllerMacOS extends RegularWindowController { @override WindowState get state => WindowState.values[_getWindowState(getWindowHandle())]; - /// Updates window state. + @override void setState(WindowState state) { _setWindowState(getWindowHandle(), state.index); } diff --git a/packages/flutter/lib/src/widgets/window_win32.dart b/packages/flutter/lib/src/widgets/window_win32.dart index c36275d7841ec..d2292f2a0c98c 100644 --- a/packages/flutter/lib/src/widgets/window_win32.dart +++ b/packages/flutter/lib/src/widgets/window_win32.dart @@ -121,31 +121,20 @@ class RegularWindowControllerWin32 extends RegularWindowController } @override - void modify({WindowSizing? contentSize, String? title, WindowState? state}) { - _ensureNotDestroyed(); - if (state != null) { - setWindowState(state); - } - if (title != null) { - setWindowTitle(title); - } - if (contentSize != null) { - setContentSize(contentSize); - } - } - - void setWindowState(WindowState state) { + void setState(WindowState state) { _ensureNotDestroyed(); _setWindowState(getWindowHandle(), state.index); } - void setWindowTitle(String title) { + @override + void setTitle(String title) { _ensureNotDestroyed(); final Pointer titlePointer = title.toNativeUtf16(); _setWindowTitle(getWindowHandle(), titlePointer); ffi.calloc.free(titlePointer); } + @override void setContentSize(WindowSizing size) { _ensureNotDestroyed(); final Pointer<_Sizing> sizing = ffi.calloc<_Sizing>(); From ca898d13918f32929d488133940cb07905efdcf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sawicz?= Date: Thu, 17 Apr 2025 18:48:54 +0200 Subject: [PATCH 39/52] windows: remove windowing check when running on a single thread It will crash, but that's actually ok for now. --- .../flutter/shell/platform/windows/flutter_windows_engine.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc b/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc index 6c48e3271bc17..5d003b9ad3d37 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc +++ b/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc @@ -319,7 +319,7 @@ bool FlutterWindowsEngine::Run(std::string_view entrypoint) { &WindowsPlatformThreadPrioritySetter; if (project_->ui_thread_policy() != - FlutterUIThreadPolicy::RunOnSeparateThread && !enable_windowing_) { + FlutterUIThreadPolicy::RunOnSeparateThread) { custom_task_runners.ui_task_runner = &platform_task_runner; } else { FML_LOG(WARNING) << "Running with unmerged platform and UI threads. This " From ffe82aff0dc337f2ead6f5a84981ecc895950343 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 18 Apr 2025 10:01:25 +0200 Subject: [PATCH 40/52] Remove leftover linux code --- engine/src/flutter/shell/platform/linux/fl_engine.cc | 10 ---------- .../flutter/shell/platform/linux/fl_engine_private.h | 3 --- 2 files changed, 13 deletions(-) diff --git a/engine/src/flutter/shell/platform/linux/fl_engine.cc b/engine/src/flutter/shell/platform/linux/fl_engine.cc index a6612c70da3b2..fdb0d24c6b6cc 100644 --- a/engine/src/flutter/shell/platform/linux/fl_engine.cc +++ b/engine/src/flutter/shell/platform/linux/fl_engine.cc @@ -25,7 +25,6 @@ #include "flutter/shell/platform/linux/fl_settings_handler.h" #include "flutter/shell/platform/linux/fl_texture_gl_private.h" #include "flutter/shell/platform/linux/fl_texture_registrar_private.h" -#include "flutter/shell/platform/linux/fl_windowing.h" #include "flutter/shell/platform/linux/fl_windowing_handler.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h" @@ -67,8 +66,6 @@ struct _FlEngine { // Implements the flutter/windowing channel. FlWindowingHandler* windowing_handler; - FlWindowingController* windowing_controller; - // Process keyboard events. FlKeyboardManager* keyboard_manager; @@ -493,7 +490,6 @@ static void fl_engine_dispose(GObject* object) { g_clear_object(&self->settings_handler); g_clear_object(&self->platform_handler); g_clear_object(&self->windowing_handler); - g_clear_object(&self->windowing_controller); g_clear_object(&self->keyboard_manager); g_clear_object(&self->text_input_handler); g_clear_object(&self->keyboard_handler); @@ -575,7 +571,6 @@ static FlEngine* fl_engine_new_full(FlDartProject* project, self->mouse_cursor_handler = fl_mouse_cursor_handler_new(self->binary_messenger); self->windowing_handler = fl_windowing_handler_new(self); - self->windowing_controller = fl_windowing_controller_new(self); return self; } @@ -1327,11 +1322,6 @@ FlWindowingHandler* fl_engine_get_windowing_handler(FlEngine* self) { return self->windowing_handler; } -FlWindowingController* fl_engine_get_windowing_controller(FlEngine* self) { - g_return_val_if_fail(FL_IS_ENGINE(self), nullptr); - return self->windowing_controller; -} - FlKeyboardManager* fl_engine_get_keyboard_manager(FlEngine* self) { g_return_val_if_fail(FL_IS_ENGINE(self), nullptr); return self->keyboard_manager; diff --git a/engine/src/flutter/shell/platform/linux/fl_engine_private.h b/engine/src/flutter/shell/platform/linux/fl_engine_private.h index 33df2a943237a..6447ea96b44ef 100644 --- a/engine/src/flutter/shell/platform/linux/fl_engine_private.h +++ b/engine/src/flutter/shell/platform/linux/fl_engine_private.h @@ -16,7 +16,6 @@ #include "flutter/shell/platform/linux/fl_renderable.h" #include "flutter/shell/platform/linux/fl_task_runner.h" #include "flutter/shell/platform/linux/fl_text_input_handler.h" -#include "flutter/shell/platform/linux/fl_windowing.h" #include "flutter/shell/platform/linux/fl_windowing_handler.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h" @@ -584,8 +583,6 @@ void fl_engine_request_app_exit(FlEngine* engine); */ FlWindowingHandler* fl_engine_get_windowing_handler(FlEngine* engine); -FlWindowingController* fl_engine_get_windowing_controller(FlEngine* engine); - /** * fl_engine_get_keyboard_manager: * @engine: an #FlEngine. From 10695301242726bb3508bba28dd441327e4ed7d9 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 18 Apr 2025 12:13:43 +0200 Subject: [PATCH 41/52] force upgrade packages --- dev/a11y_assessments/pubspec.yaml | 3 +- dev/automated_tests/pubspec.yaml | 11 ++++--- dev/benchmarks/complex_layout/pubspec.yaml | 11 ++++--- .../imitation_game_flutter/pubspec.yaml | 3 +- dev/benchmarks/macrobenchmarks/pubspec.yaml | 11 ++++--- dev/benchmarks/microbenchmarks/pubspec.yaml | 11 ++++--- .../platform_channels_benchmarks/pubspec.yaml | 11 ++++--- .../platform_views_layout/pubspec.yaml | 15 ++++----- .../pubspec.yaml | 11 ++++--- dev/benchmarks/test_apps/stocks/pubspec.yaml | 11 ++++--- dev/bots/pubspec.yaml | 10 +++--- dev/conductor/core/pubspec.yaml | 10 +++--- dev/customer_testing/pubspec.yaml | 10 +++--- dev/devicelab/pubspec.yaml | 10 +++--- .../android_engine_test/pubspec.yaml | 11 ++++--- .../android_semantics_testing/pubspec.yaml | 11 ++++--- .../android_views/pubspec.yaml | 10 +++--- dev/integration_tests/channels/pubspec.yaml | 3 +- .../deferred_components_test/pubspec.yaml | 11 ++++--- .../display_cutout_rotation/pubspec.yaml | 3 +- .../external_textures/pubspec.yaml | 11 ++++--- dev/integration_tests/flavors/pubspec.yaml | 11 ++++--- .../flutter_gallery/pubspec.yaml | 13 ++++---- .../hook_user_defines/pubspec.yaml | 10 +++--- .../flutterapp/pubspec.yaml | 3 +- .../ios_app_with_extensions/pubspec.yaml | 3 +- .../ios_platform_view_tests/pubspec.yaml | 11 ++++--- dev/integration_tests/link_hook/pubspec.yaml | 10 +++--- .../new_gallery/pubspec.yaml | 10 +++--- .../platform_interaction/pubspec.yaml | 11 ++++--- .../release_smoke_test/pubspec.yaml | 3 +- .../spell_check/pubspec.yaml | 3 +- dev/integration_tests/ui/pubspec.yaml | 11 ++++--- dev/integration_tests/web/pubspec.yaml | 3 +- .../web_compile_tests/pubspec.yaml | 3 +- .../web_e2e_tests/pubspec.yaml | 11 ++++--- .../wide_gamut_test/pubspec.yaml | 3 +- .../windows_startup_test/pubspec.yaml | 11 ++++--- dev/manual_tests/pubspec.yaml | 3 +- dev/snippets/pubspec.yaml | 10 +++--- .../android_driver_extensions/pubspec.yaml | 11 ++++--- dev/tools/gen_defaults/pubspec.yaml | 10 +++--- dev/tools/gen_keycodes/pubspec.yaml | 10 +++--- dev/tools/pubspec.yaml | 10 +++--- dev/tools/vitool/pubspec.yaml | 3 +- dev/tracing_tests/pubspec.yaml | 3 +- examples/api/pubspec.yaml | 11 ++++--- examples/flutter_view/pubspec.yaml | 3 +- examples/hello_world/pubspec.yaml | 11 ++++--- examples/image_list/pubspec.yaml | 3 +- examples/layers/pubspec.yaml | 3 +- examples/multi_window_ref_app/pubspec.yaml | 31 +++++++++++++++++-- examples/platform_channel/pubspec.yaml | 11 ++++--- examples/platform_channel_swift/pubspec.yaml | 11 ++++--- examples/platform_view/pubspec.yaml | 3 +- examples/splash/pubspec.yaml | 3 +- examples/texture/pubspec.yaml | 11 ++++--- packages/flutter/pubspec.yaml | 2 +- .../flutter/test_private/test/pubspec.yaml | 3 +- packages/flutter_driver/pubspec.yaml | 11 ++++--- packages/flutter_goldens/pubspec.yaml | 3 +- packages/flutter_localizations/pubspec.yaml | 3 +- packages/flutter_test/pubspec.yaml | 3 +- packages/flutter_tools/pubspec.yaml | 14 ++++----- .../widget_preview_scaffold/pubspec.yaml | 11 ++++--- packages/flutter_web_plugins/pubspec.yaml | 3 +- .../pubspec.yaml | 10 +++--- .../integration_test/example/pubspec.yaml | 11 ++++--- .../integration_test_macos/pubspec.yaml | 3 +- packages/integration_test/pubspec.yaml | 3 +- 70 files changed, 324 insertions(+), 245 deletions(-) diff --git a/dev/a11y_assessments/pubspec.yaml b/dev/a11y_assessments/pubspec.yaml index 9ee5a9efc8e86..656a0552ecf19 100644 --- a/dev/a11y_assessments/pubspec.yaml +++ b/dev/a11y_assessments/pubspec.yaml @@ -11,6 +11,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -39,4 +40,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 5b23 +# PUBSPEC CHECKSUM: b9a7 diff --git a/dev/automated_tests/pubspec.yaml b/dev/automated_tests/pubspec.yaml index e99c66476a22c..5271bf545aebd 100644 --- a/dev/automated_tests/pubspec.yaml +++ b/dev/automated_tests/pubspec.yaml @@ -15,8 +15,8 @@ dependencies: platform: 3.1.6 test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -28,6 +28,7 @@ dependencies: coverage: 1.12.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" frontend_server_client: 4.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -67,8 +68,8 @@ dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -77,4 +78,4 @@ flutter: assets: - icon/test.png -# PUBSPEC CHECKSUM: d60f +# PUBSPEC CHECKSUM: 5692 diff --git a/dev/benchmarks/complex_layout/pubspec.yaml b/dev/benchmarks/complex_layout/pubspec.yaml index 4570376f726ea..9014c419c2156 100644 --- a/dev/benchmarks/complex_layout/pubspec.yaml +++ b/dev/benchmarks/complex_layout/pubspec.yaml @@ -20,6 +20,7 @@ dependencies: boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.17 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -43,8 +44,8 @@ dev_dependencies: integration_test: sdk: flutter - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -77,8 +78,8 @@ dev_dependencies: typed_data: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -88,4 +89,4 @@ flutter: - packages/flutter_gallery_assets/people/square/ali.png - packages/flutter_gallery_assets/places/india_chettinad_silk_maker.png -# PUBSPEC CHECKSUM: f9e2 +# PUBSPEC CHECKSUM: fa66 diff --git a/dev/benchmarks/imitation_game_flutter/pubspec.yaml b/dev/benchmarks/imitation_game_flutter/pubspec.yaml index e2f6fd434f92d..bf86e9bfa51aa 100644 --- a/dev/benchmarks/imitation_game_flutter/pubspec.yaml +++ b/dev/benchmarks/imitation_game_flutter/pubspec.yaml @@ -14,6 +14,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -44,4 +45,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 9b29 +# PUBSPEC CHECKSUM: 70ad diff --git a/dev/benchmarks/macrobenchmarks/pubspec.yaml b/dev/benchmarks/macrobenchmarks/pubspec.yaml index b0a9d9d43bf89..67c7e73d0ca6a 100644 --- a/dev/benchmarks/macrobenchmarks/pubspec.yaml +++ b/dev/benchmarks/macrobenchmarks/pubspec.yaml @@ -26,6 +26,7 @@ dependencies: clock: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker: 10.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -50,8 +51,8 @@ dev_dependencies: integration_test: sdk: flutter - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -78,8 +79,8 @@ dev_dependencies: test_core: 0.6.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" typed_data: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -215,4 +216,4 @@ flutter: fonts: - asset: packages/flutter_gallery_assets/fonts/GalleryIcons.ttf -# PUBSPEC CHECKSUM: f9e2 +# PUBSPEC CHECKSUM: fa66 diff --git a/dev/benchmarks/microbenchmarks/pubspec.yaml b/dev/benchmarks/microbenchmarks/pubspec.yaml index 28f0be5327559..f8abf2e67e3ee 100644 --- a/dev/benchmarks/microbenchmarks/pubspec.yaml +++ b/dev/benchmarks/microbenchmarks/pubspec.yaml @@ -15,8 +15,8 @@ dependencies: test: 1.25.15 flutter_gallery_assets: 1.0.2 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -28,6 +28,7 @@ dependencies: coverage: 1.12.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" frontend_server_client: 4.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -68,8 +69,8 @@ dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -142,4 +143,4 @@ flutter: - packages/flutter_gallery_assets/people/square/stella.png - packages/flutter_gallery_assets/people/square/trevor.png -# PUBSPEC CHECKSUM: 141f +# PUBSPEC CHECKSUM: 0ba2 diff --git a/dev/benchmarks/platform_channels_benchmarks/pubspec.yaml b/dev/benchmarks/platform_channels_benchmarks/pubspec.yaml index 9960b9ebe4e39..464aa7c979578 100644 --- a/dev/benchmarks/platform_channels_benchmarks/pubspec.yaml +++ b/dev/benchmarks/platform_channels_benchmarks/pubspec.yaml @@ -16,8 +16,8 @@ dependencies: path: ../microbenchmarks cupertino_icons: 1.0.8 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -29,6 +29,7 @@ dependencies: coverage: 1.12.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" flutter_gallery_assets: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" frontend_server_client: 4.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -69,8 +70,8 @@ dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -79,4 +80,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: a149 +# PUBSPEC CHECKSUM: 27cc diff --git a/dev/benchmarks/platform_views_layout/pubspec.yaml b/dev/benchmarks/platform_views_layout/pubspec.yaml index 55b61bc0d4fee..31a713904a384 100644 --- a/dev/benchmarks/platform_views_layout/pubspec.yaml +++ b/dev/benchmarks/platform_views_layout/pubspec.yaml @@ -20,6 +20,7 @@ dependencies: boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.17 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -38,16 +39,16 @@ dependencies: webdriver: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webview_flutter: 4.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webview_flutter_android: 3.16.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - webview_flutter_platform_interface: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - webview_flutter_wkwebview: 3.18.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + webview_flutter_platform_interface: 2.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + webview_flutter_wkwebview: 3.20.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" dev_dependencies: flutter_test: sdk: flutter test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -80,8 +81,8 @@ dev_dependencies: typed_data: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -91,4 +92,4 @@ flutter: - packages/flutter_gallery_assets/people/square/ali.png - packages/flutter_gallery_assets/places/india_chettinad_silk_maker.png -# PUBSPEC CHECKSUM: 6ab2 +# PUBSPEC CHECKSUM: d82b diff --git a/dev/benchmarks/platform_views_layout_hybrid_composition/pubspec.yaml b/dev/benchmarks/platform_views_layout_hybrid_composition/pubspec.yaml index 8e9d1c7f5f4d6..eb3735dc8b79f 100644 --- a/dev/benchmarks/platform_views_layout_hybrid_composition/pubspec.yaml +++ b/dev/benchmarks/platform_views_layout_hybrid_composition/pubspec.yaml @@ -20,6 +20,7 @@ dependencies: boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.17 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -41,8 +42,8 @@ dev_dependencies: sdk: flutter test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -75,8 +76,8 @@ dev_dependencies: typed_data: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -86,4 +87,4 @@ flutter: - packages/flutter_gallery_assets/people/square/ali.png - packages/flutter_gallery_assets/places/india_chettinad_silk_maker.png -# PUBSPEC CHECKSUM: f9e2 +# PUBSPEC CHECKSUM: fa66 diff --git a/dev/benchmarks/test_apps/stocks/pubspec.yaml b/dev/benchmarks/test_apps/stocks/pubspec.yaml index 491eb05b926c6..564bcf90412d5 100644 --- a/dev/benchmarks/test_apps/stocks/pubspec.yaml +++ b/dev/benchmarks/test_apps/stocks/pubspec.yaml @@ -16,6 +16,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -34,8 +35,8 @@ dev_dependencies: sdk: flutter test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -72,8 +73,8 @@ dev_dependencies: test_core: 0.6.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -82,4 +83,4 @@ flutter: generate: true uses-material-design: true -# PUBSPEC CHECKSUM: 65c8 +# PUBSPEC CHECKSUM: ee4c diff --git a/dev/bots/pubspec.yaml b/dev/bots/pubspec.yaml index 251d11c72bcec..935a84ac5979e 100644 --- a/dev/bots/pubspec.yaml +++ b/dev/bots/pubspec.yaml @@ -5,7 +5,7 @@ environment: sdk: ^3.7.0-0 dependencies: - analyzer: 7.3.0 + analyzer: 7.4.1 args: 2.7.0 crypto: 3.0.6 intl: 0.20.2 @@ -20,7 +20,7 @@ dependencies: test: 1.25.15 _discoveryapis_commons: 1.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" archive: 3.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -69,8 +69,8 @@ dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" xml: 6.5.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -78,4 +78,4 @@ dependencies: dev_dependencies: test_api: 0.7.4 -# PUBSPEC CHECKSUM: ddbf +# PUBSPEC CHECKSUM: a8be diff --git a/dev/conductor/core/pubspec.yaml b/dev/conductor/core/pubspec.yaml index cc4f0e8570574..23eb0dd1d5754 100644 --- a/dev/conductor/core/pubspec.yaml +++ b/dev/conductor/core/pubspec.yaml @@ -34,8 +34,8 @@ dev_dependencies: test: 1.25.15 test_api: 0.7.4 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -63,9 +63,9 @@ dev_dependencies: test_core: 0.6.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 5ac7 +# PUBSPEC CHECKSUM: cac6 diff --git a/dev/customer_testing/pubspec.yaml b/dev/customer_testing/pubspec.yaml index 3cab34367fa96..669e9c7cca1bf 100644 --- a/dev/customer_testing/pubspec.yaml +++ b/dev/customer_testing/pubspec.yaml @@ -20,8 +20,8 @@ dependencies: dev_dependencies: test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -53,9 +53,9 @@ dev_dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 8148 +# PUBSPEC CHECKSUM: 9e47 diff --git a/dev/devicelab/pubspec.yaml b/dev/devicelab/pubspec.yaml index 249be41838dbf..a44dd30b3771f 100644 --- a/dev/devicelab/pubspec.yaml +++ b/dev/devicelab/pubspec.yaml @@ -52,8 +52,8 @@ dependencies: dev_dependencies: test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.12.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -73,7 +73,7 @@ dev_dependencies: test_api: 0.7.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" test_core: 0.6.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 0932 +# PUBSPEC CHECKSUM: 1331 diff --git a/dev/integration_tests/android_engine_test/pubspec.yaml b/dev/integration_tests/android_engine_test/pubspec.yaml index 48bd84276cb7d..886421fd2d737 100644 --- a/dev/integration_tests/android_engine_test/pubspec.yaml +++ b/dev/integration_tests/android_engine_test/pubspec.yaml @@ -16,6 +16,7 @@ dependencies: boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.17 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -37,8 +38,8 @@ dependencies: dev_dependencies: test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -66,9 +67,9 @@ dev_dependencies: typed_data: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: fb83 +# PUBSPEC CHECKSUM: a107 diff --git a/dev/integration_tests/android_semantics_testing/pubspec.yaml b/dev/integration_tests/android_semantics_testing/pubspec.yaml index 1c317cfd393c5..d5f2d32a727cf 100644 --- a/dev/integration_tests/android_semantics_testing/pubspec.yaml +++ b/dev/integration_tests/android_semantics_testing/pubspec.yaml @@ -13,8 +13,8 @@ dependencies: pub_semver: 2.2.0 test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -26,6 +26,7 @@ dependencies: coverage: 1.12.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" frontend_server_client: 4.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -63,12 +64,12 @@ dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" flutter: uses-material-design: true -# PUBSPEC CHECKSUM: b504 +# PUBSPEC CHECKSUM: 4687 diff --git a/dev/integration_tests/android_views/pubspec.yaml b/dev/integration_tests/android_views/pubspec.yaml index e65cdf4a8b0c8..89b3bfae364ea 100644 --- a/dev/integration_tests/android_views/pubspec.yaml +++ b/dev/integration_tests/android_views/pubspec.yaml @@ -54,8 +54,8 @@ dev_dependencies: sdk: flutter test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -88,12 +88,12 @@ dev_dependencies: typed_data: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 617d +# PUBSPEC CHECKSUM: 7f7c diff --git a/dev/integration_tests/channels/pubspec.yaml b/dev/integration_tests/channels/pubspec.yaml index f5685a9c1a514..d380a14959eae 100644 --- a/dev/integration_tests/channels/pubspec.yaml +++ b/dev/integration_tests/channels/pubspec.yaml @@ -10,6 +10,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -45,4 +46,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: c058 +# PUBSPEC CHECKSUM: a3dc diff --git a/dev/integration_tests/deferred_components_test/pubspec.yaml b/dev/integration_tests/deferred_components_test/pubspec.yaml index 47cf26e912fd3..9fd888911e0c0 100644 --- a/dev/integration_tests/deferred_components_test/pubspec.yaml +++ b/dev/integration_tests/deferred_components_test/pubspec.yaml @@ -15,6 +15,7 @@ dependencies: boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.17 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -36,8 +37,8 @@ dev_dependencies: sdk: flutter test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -70,8 +71,8 @@ dev_dependencies: typed_data: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -85,4 +86,4 @@ flutter: assets: - customassets/flutter_logo.png -# PUBSPEC CHECKSUM: 7548 +# PUBSPEC CHECKSUM: 87cb diff --git a/dev/integration_tests/display_cutout_rotation/pubspec.yaml b/dev/integration_tests/display_cutout_rotation/pubspec.yaml index 55dd2929ff732..29657c9ff5455 100644 --- a/dev/integration_tests/display_cutout_rotation/pubspec.yaml +++ b/dev/integration_tests/display_cutout_rotation/pubspec.yaml @@ -37,6 +37,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -120,4 +121,4 @@ flutter: # For details regarding fonts from package dependencies, # see https://flutter.dev/to/font-from-package -# PUBSPEC CHECKSUM: 695e +# PUBSPEC CHECKSUM: c2e2 diff --git a/dev/integration_tests/external_textures/pubspec.yaml b/dev/integration_tests/external_textures/pubspec.yaml index 255378e894b38..bbf8116e9b6cc 100644 --- a/dev/integration_tests/external_textures/pubspec.yaml +++ b/dev/integration_tests/external_textures/pubspec.yaml @@ -11,8 +11,8 @@ dependencies: sdk: flutter test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -22,6 +22,7 @@ dependencies: convert: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.12.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" frontend_server_client: 4.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -58,8 +59,8 @@ dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -67,4 +68,4 @@ dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 2d78 +# PUBSPEC CHECKSUM: e3fb diff --git a/dev/integration_tests/flavors/pubspec.yaml b/dev/integration_tests/flavors/pubspec.yaml index 1545a7f4a07ba..2a5a4f6d615cb 100644 --- a/dev/integration_tests/flavors/pubspec.yaml +++ b/dev/integration_tests/flavors/pubspec.yaml @@ -13,8 +13,8 @@ dependencies: sdk: flutter test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -24,6 +24,7 @@ dependencies: convert: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.12.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" frontend_server_client: 4.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -60,8 +61,8 @@ dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -87,4 +88,4 @@ flutter: flavors: - free -# PUBSPEC CHECKSUM: 7548 +# PUBSPEC CHECKSUM: 87cb diff --git a/dev/integration_tests/flutter_gallery/pubspec.yaml b/dev/integration_tests/flutter_gallery/pubspec.yaml index 99917f1617550..de408a54856a9 100644 --- a/dev/integration_tests/flutter_gallery/pubspec.yaml +++ b/dev/integration_tests/flutter_gallery/pubspec.yaml @@ -22,6 +22,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" csslib: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" html: 0.15.5+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -38,7 +39,7 @@ dependencies: url_launcher_windows: 3.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" video_player_android: 2.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - video_player_avfoundation: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + video_player_avfoundation: 2.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" video_player_platform_interface: 6.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" video_player_web: 2.3.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -54,8 +55,8 @@ dev_dependencies: integration_test: sdk: flutter - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -97,8 +98,8 @@ dev_dependencies: typed_data: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -273,4 +274,4 @@ flutter: - asset: packages/flutter_gallery_assets/fonts/merriweather/Merriweather-Regular.ttf - asset: packages/flutter_gallery_assets/fonts/merriweather/Merriweather-Light.ttf -# PUBSPEC CHECKSUM: fea4 +# PUBSPEC CHECKSUM: 9329 diff --git a/dev/integration_tests/hook_user_defines/pubspec.yaml b/dev/integration_tests/hook_user_defines/pubspec.yaml index 3eb473125b24b..0f8e904643fe6 100644 --- a/dev/integration_tests/hook_user_defines/pubspec.yaml +++ b/dev/integration_tests/hook_user_defines/pubspec.yaml @@ -35,8 +35,8 @@ dev_dependencies: flutter_lints: 5.0.0 test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -69,9 +69,9 @@ dev_dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml_edit: 2.2.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 9dc2 +# PUBSPEC CHECKSUM: 5fc1 diff --git a/dev/integration_tests/ios_add2app_life_cycle/flutterapp/pubspec.yaml b/dev/integration_tests/ios_add2app_life_cycle/flutterapp/pubspec.yaml index 857fd8b9aaf7b..072262b912ac1 100644 --- a/dev/integration_tests/ios_add2app_life_cycle/flutterapp/pubspec.yaml +++ b/dev/integration_tests/ios_add2app_life_cycle/flutterapp/pubspec.yaml @@ -26,6 +26,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -104,4 +105,4 @@ flutter: androidPackage: com.example.iosadd2appflutter iosBundleIdentifier: com.example.iosAdd2appFlutter -# PUBSPEC CHECKSUM: 16cd +# PUBSPEC CHECKSUM: c952 diff --git a/dev/integration_tests/ios_app_with_extensions/pubspec.yaml b/dev/integration_tests/ios_app_with_extensions/pubspec.yaml index ebf7be8f366d5..82c761dda8768 100644 --- a/dev/integration_tests/ios_app_with_extensions/pubspec.yaml +++ b/dev/integration_tests/ios_app_with_extensions/pubspec.yaml @@ -25,6 +25,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" device_info_platform_interface: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" plugin_platform_interface: 2.1.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -94,4 +95,4 @@ flutter: # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages -# PUBSPEC CHECKSUM: 2b54 +# PUBSPEC CHECKSUM: 87d8 diff --git a/dev/integration_tests/ios_platform_view_tests/pubspec.yaml b/dev/integration_tests/ios_platform_view_tests/pubspec.yaml index c666bf7454ed1..ed66e702a663e 100644 --- a/dev/integration_tests/ios_platform_view_tests/pubspec.yaml +++ b/dev/integration_tests/ios_platform_view_tests/pubspec.yaml @@ -15,6 +15,7 @@ dependencies: boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.17 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -36,8 +37,8 @@ dev_dependencies: sdk: flutter test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -70,8 +71,8 @@ dev_dependencies: typed_data: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -82,4 +83,4 @@ flutter: # the material Icons class. uses-material-design: true -# PUBSPEC CHECKSUM: 7548 +# PUBSPEC CHECKSUM: 87cb diff --git a/dev/integration_tests/link_hook/pubspec.yaml b/dev/integration_tests/link_hook/pubspec.yaml index afa1cf09a60bb..b26163c63856a 100644 --- a/dev/integration_tests/link_hook/pubspec.yaml +++ b/dev/integration_tests/link_hook/pubspec.yaml @@ -30,8 +30,8 @@ dev_dependencies: flutter_lints: 5.0.0 test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -64,9 +64,9 @@ dev_dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml_edit: 2.2.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 9dc2 +# PUBSPEC CHECKSUM: 5fc1 diff --git a/dev/integration_tests/new_gallery/pubspec.yaml b/dev/integration_tests/new_gallery/pubspec.yaml index 0b1a0e7557232..4a5a25b4182fb 100644 --- a/dev/integration_tests/new_gallery/pubspec.yaml +++ b/dev/integration_tests/new_gallery/pubspec.yaml @@ -68,8 +68,8 @@ dev_dependencies: sdk: flutter test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -105,8 +105,8 @@ dev_dependencies: test_core: 0.6.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -312,4 +312,4 @@ flutter: fonts: - asset: packages/flutter_gallery_assets/fonts/GalleryIcons.ttf -# PUBSPEC CHECKSUM: 07b9 +# PUBSPEC CHECKSUM: 58b8 diff --git a/dev/integration_tests/platform_interaction/pubspec.yaml b/dev/integration_tests/platform_interaction/pubspec.yaml index 7b33d2270eaa9..1ba86be50e553 100644 --- a/dev/integration_tests/platform_interaction/pubspec.yaml +++ b/dev/integration_tests/platform_interaction/pubspec.yaml @@ -11,8 +11,8 @@ dependencies: sdk: flutter test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -22,6 +22,7 @@ dependencies: convert: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.12.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" frontend_server_client: 4.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -58,8 +59,8 @@ dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -67,4 +68,4 @@ dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 2d78 +# PUBSPEC CHECKSUM: e3fb diff --git a/dev/integration_tests/release_smoke_test/pubspec.yaml b/dev/integration_tests/release_smoke_test/pubspec.yaml index 7f6c50016ef8f..7747f4dede674 100644 --- a/dev/integration_tests/release_smoke_test/pubspec.yaml +++ b/dev/integration_tests/release_smoke_test/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -36,4 +37,4 @@ dev_dependencies: test_api: 0.7.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 5b23 +# PUBSPEC CHECKSUM: b9a7 diff --git a/dev/integration_tests/spell_check/pubspec.yaml b/dev/integration_tests/spell_check/pubspec.yaml index b953f2093430a..862e7a67e40ed 100644 --- a/dev/integration_tests/spell_check/pubspec.yaml +++ b/dev/integration_tests/spell_check/pubspec.yaml @@ -36,6 +36,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -108,4 +109,4 @@ flutter: # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages -# PUBSPEC CHECKSUM: 16cd +# PUBSPEC CHECKSUM: c952 diff --git a/dev/integration_tests/ui/pubspec.yaml b/dev/integration_tests/ui/pubspec.yaml index 7d8a0e3483038..598617736faf4 100644 --- a/dev/integration_tests/ui/pubspec.yaml +++ b/dev/integration_tests/ui/pubspec.yaml @@ -13,8 +13,8 @@ dependencies: sdk: flutter test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -24,6 +24,7 @@ dependencies: convert: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.12.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" frontend_server_client: 4.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -59,8 +60,8 @@ dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -81,4 +82,4 @@ flutter: assets: - assets/foo.png -# PUBSPEC CHECKSUM: 7548 +# PUBSPEC CHECKSUM: 87cb diff --git a/dev/integration_tests/web/pubspec.yaml b/dev/integration_tests/web/pubspec.yaml index 713cd30ee0cee..32628cab19f8c 100644 --- a/dev/integration_tests/web/pubspec.yaml +++ b/dev/integration_tests/web/pubspec.yaml @@ -18,8 +18,9 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: e351 +# PUBSPEC CHECKSUM: e6d5 diff --git a/dev/integration_tests/web_compile_tests/pubspec.yaml b/dev/integration_tests/web_compile_tests/pubspec.yaml index b5be54d6e95cc..8fb7360e20043 100644 --- a/dev/integration_tests/web_compile_tests/pubspec.yaml +++ b/dev/integration_tests/web_compile_tests/pubspec.yaml @@ -8,8 +8,9 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: e4c7 +# PUBSPEC CHECKSUM: 8d4c diff --git a/dev/integration_tests/web_e2e_tests/pubspec.yaml b/dev/integration_tests/web_e2e_tests/pubspec.yaml index 2eb5ae1f0d739..3b5a8e8ccac67 100644 --- a/dev/integration_tests/web_e2e_tests/pubspec.yaml +++ b/dev/integration_tests/web_e2e_tests/pubspec.yaml @@ -29,6 +29,7 @@ dependencies: clock: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker: 10.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -54,8 +55,8 @@ dev_dependencies: http: 1.3.0 test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -84,9 +85,9 @@ dev_dependencies: test_core: 0.6.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" typed_data: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: bbfa +# PUBSPEC CHECKSUM: 677e diff --git a/dev/integration_tests/wide_gamut_test/pubspec.yaml b/dev/integration_tests/wide_gamut_test/pubspec.yaml index a7e07c6c43a43..94e6576a6ce9c 100644 --- a/dev/integration_tests/wide_gamut_test/pubspec.yaml +++ b/dev/integration_tests/wide_gamut_test/pubspec.yaml @@ -13,6 +13,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -43,4 +44,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 5b23 +# PUBSPEC CHECKSUM: b9a7 diff --git a/dev/integration_tests/windows_startup_test/pubspec.yaml b/dev/integration_tests/windows_startup_test/pubspec.yaml index 9451d248e1202..3af09abed7110 100644 --- a/dev/integration_tests/windows_startup_test/pubspec.yaml +++ b/dev/integration_tests/windows_startup_test/pubspec.yaml @@ -11,8 +11,8 @@ dependencies: sdk: flutter test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -22,6 +22,7 @@ dependencies: convert: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.12.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" frontend_server_client: 4.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -58,10 +59,10 @@ dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 2d78 +# PUBSPEC CHECKSUM: e3fb diff --git a/dev/manual_tests/pubspec.yaml b/dev/manual_tests/pubspec.yaml index e31e056b503ae..ac8182a103350 100644 --- a/dev/manual_tests/pubspec.yaml +++ b/dev/manual_tests/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -37,4 +38,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 5b23 +# PUBSPEC CHECKSUM: b9a7 diff --git a/dev/snippets/pubspec.yaml b/dev/snippets/pubspec.yaml index 09198dee2a83d..a0f5f72f18fc1 100644 --- a/dev/snippets/pubspec.yaml +++ b/dev/snippets/pubspec.yaml @@ -5,7 +5,7 @@ environment: sdk: ^3.7.0-0 dependencies: - analyzer: 7.3.0 + analyzer: 7.4.1 args: 2.7.0 file: 7.0.1 meta: 1.16.0 @@ -13,7 +13,7 @@ dependencies: platform: 3.1.6 process: 5.0.3 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -56,11 +56,11 @@ dev_dependencies: test_core: 0.6.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" executables: snippets: -# PUBSPEC CHECKSUM: ae53 +# PUBSPEC CHECKSUM: b452 diff --git a/dev/tools/android_driver_extensions/pubspec.yaml b/dev/tools/android_driver_extensions/pubspec.yaml index dcf15cd083b3d..bb2bc42ed9a6f 100644 --- a/dev/tools/android_driver_extensions/pubspec.yaml +++ b/dev/tools/android_driver_extensions/pubspec.yaml @@ -26,6 +26,7 @@ dependencies: collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker: 10.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -45,8 +46,8 @@ dependencies: dev_dependencies: test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -72,9 +73,9 @@ dev_dependencies: test_core: 0.6.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 5953 +# PUBSPEC CHECKSUM: 5ad6 diff --git a/dev/tools/gen_defaults/pubspec.yaml b/dev/tools/gen_defaults/pubspec.yaml index 4af05a4984f79..c1840813e8f90 100644 --- a/dev/tools/gen_defaults/pubspec.yaml +++ b/dev/tools/gen_defaults/pubspec.yaml @@ -12,8 +12,8 @@ dev_dependencies: path: 1.9.1 test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -53,9 +53,9 @@ dev_dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 8148 +# PUBSPEC CHECKSUM: 9e47 diff --git a/dev/tools/gen_keycodes/pubspec.yaml b/dev/tools/gen_keycodes/pubspec.yaml index 1057a5ace3cd5..78febd3cc2102 100644 --- a/dev/tools/gen_keycodes/pubspec.yaml +++ b/dev/tools/gen_keycodes/pubspec.yaml @@ -24,8 +24,8 @@ dev_dependencies: test: 1.25.15 test_api: 0.7.4 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -55,9 +55,9 @@ dev_dependencies: test_core: 0.6.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 600f +# PUBSPEC CHECKSUM: 4e0e diff --git a/dev/tools/pubspec.yaml b/dev/tools/pubspec.yaml index f24e5bde56a34..9f3d7d83e5070 100644 --- a/dev/tools/pubspec.yaml +++ b/dev/tools/pubspec.yaml @@ -35,8 +35,8 @@ dev_dependencies: file_testing: 3.0.2 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -63,8 +63,8 @@ dev_dependencies: test_core: 0.6.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: f2bd +# PUBSPEC CHECKSUM: c3bc diff --git a/dev/tools/vitool/pubspec.yaml b/dev/tools/vitool/pubspec.yaml index 885b4b624bf3b..860c35b8fc1c4 100644 --- a/dev/tools/vitool/pubspec.yaml +++ b/dev/tools/vitool/pubspec.yaml @@ -15,6 +15,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" petitparser: 6.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -40,4 +41,4 @@ dev_dependencies: test_api: 0.7.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 2ccc +# PUBSPEC CHECKSUM: 7351 diff --git a/dev/tracing_tests/pubspec.yaml b/dev/tracing_tests/pubspec.yaml index e588242ca7b05..63b5d4708790f 100644 --- a/dev/tracing_tests/pubspec.yaml +++ b/dev/tracing_tests/pubspec.yaml @@ -12,6 +12,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -36,4 +37,4 @@ dev_dependencies: term_glyph: 1.2.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" test_api: 0.7.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 5b23 +# PUBSPEC CHECKSUM: b9a7 diff --git a/examples/api/pubspec.yaml b/examples/api/pubspec.yaml index 9c1717b0306d4..cf8e7cd9fbb90 100644 --- a/examples/api/pubspec.yaml +++ b/examples/api/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -36,8 +37,8 @@ dev_dependencies: sdk: flutter test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -86,8 +87,8 @@ dev_dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -95,4 +96,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 8f32 +# PUBSPEC CHECKSUM: 63b5 diff --git a/examples/flutter_view/pubspec.yaml b/examples/flutter_view/pubspec.yaml index 087c50083931d..d5d25d48a4553 100644 --- a/examples/flutter_view/pubspec.yaml +++ b/examples/flutter_view/pubspec.yaml @@ -10,6 +10,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -19,4 +20,4 @@ flutter: assets: - assets/flutter-mark-square-64.png -# PUBSPEC CHECKSUM: e4c7 +# PUBSPEC CHECKSUM: 8d4c diff --git a/examples/hello_world/pubspec.yaml b/examples/hello_world/pubspec.yaml index abaed105a28c8..a89dd43aabd0f 100644 --- a/examples/hello_world/pubspec.yaml +++ b/examples/hello_world/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -20,8 +21,8 @@ dev_dependencies: sdk: flutter test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -67,10 +68,10 @@ dev_dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 7548 +# PUBSPEC CHECKSUM: 87cb diff --git a/examples/image_list/pubspec.yaml b/examples/image_list/pubspec.yaml index f0e9dfc36d525..1c3842b39e070 100644 --- a/examples/image_list/pubspec.yaml +++ b/examples/image_list/pubspec.yaml @@ -16,6 +16,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -57,4 +58,4 @@ flutter: assets: - images/coast.jpg -# PUBSPEC CHECKSUM: 16cd +# PUBSPEC CHECKSUM: c952 diff --git a/examples/layers/pubspec.yaml b/examples/layers/pubspec.yaml index 27f390799db45..c06990e9a7aec 100644 --- a/examples/layers/pubspec.yaml +++ b/examples/layers/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -39,4 +40,4 @@ flutter: - services/data.json uses-material-design: true -# PUBSPEC CHECKSUM: 5b23 +# PUBSPEC CHECKSUM: b9a7 diff --git a/examples/multi_window_ref_app/pubspec.yaml b/examples/multi_window_ref_app/pubspec.yaml index 02e7bcaa02760..05b89c7afc57c 100644 --- a/examples/multi_window_ref_app/pubspec.yaml +++ b/examples/multi_window_ref_app/pubspec.yaml @@ -8,13 +8,38 @@ environment: dependencies: flutter: sdk: flutter - stack_trace: ^1.11.1 - vector_math: ^2.1.4 + stack_trace: 1.12.1 + vector_math: 2.1.4 + + characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^4.0.0 + flutter_lints: 5.0.0 + + async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + clock: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + fake_async: 1.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + lints: 5.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + matcher: 0.12.17 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + source_span: 1.10.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + stream_channel: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + string_scanner: 1.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + term_glyph: 1.2.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + test_api: 0.7.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" flutter: uses-material-design: true + +# PUBSPEC CHECKSUM: b503 diff --git a/examples/platform_channel/pubspec.yaml b/examples/platform_channel/pubspec.yaml index 3fe33f7d069e0..cf28aa97a2198 100644 --- a/examples/platform_channel/pubspec.yaml +++ b/examples/platform_channel/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -20,8 +21,8 @@ dev_dependencies: sdk: flutter test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -67,8 +68,8 @@ dev_dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -76,4 +77,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 7548 +# PUBSPEC CHECKSUM: 87cb diff --git a/examples/platform_channel_swift/pubspec.yaml b/examples/platform_channel_swift/pubspec.yaml index 1254c43c60418..bf51632114590 100644 --- a/examples/platform_channel_swift/pubspec.yaml +++ b/examples/platform_channel_swift/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -20,8 +21,8 @@ dev_dependencies: sdk: flutter test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -67,8 +68,8 @@ dev_dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -76,4 +77,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 7548 +# PUBSPEC CHECKSUM: 87cb diff --git a/examples/platform_view/pubspec.yaml b/examples/platform_view/pubspec.yaml index cdf6bac97332e..530f9483baa1c 100644 --- a/examples/platform_view/pubspec.yaml +++ b/examples/platform_view/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -19,4 +20,4 @@ flutter: assets: - assets/flutter-mark-square-64.png -# PUBSPEC CHECKSUM: e4c7 +# PUBSPEC CHECKSUM: 8d4c diff --git a/examples/splash/pubspec.yaml b/examples/splash/pubspec.yaml index e1b3abd78de8f..2e915e21e2a26 100644 --- a/examples/splash/pubspec.yaml +++ b/examples/splash/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -34,4 +35,4 @@ dev_dependencies: test_api: 0.7.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 5b23 +# PUBSPEC CHECKSUM: b9a7 diff --git a/examples/texture/pubspec.yaml b/examples/texture/pubspec.yaml index 594449d927336..030564159d310 100644 --- a/examples/texture/pubspec.yaml +++ b/examples/texture/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -18,8 +19,8 @@ dev_dependencies: sdk: flutter test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -64,9 +65,9 @@ dev_dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: b504 +# PUBSPEC CHECKSUM: 4687 diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml index dd275852f6968..c7711f7789c54 100644 --- a/packages/flutter/pubspec.yaml +++ b/packages/flutter/pubspec.yaml @@ -56,4 +56,4 @@ dev_dependencies: vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 7384 +# PUBSPEC CHECKSUM: cb09 diff --git a/packages/flutter/test_private/test/pubspec.yaml b/packages/flutter/test_private/test/pubspec.yaml index 8de754be6b521..02d1407cbca3d 100644 --- a/packages/flutter/test_private/test/pubspec.yaml +++ b/packages/flutter/test_private/test/pubspec.yaml @@ -22,6 +22,7 @@ dependencies: async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker: 10.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -47,4 +48,4 @@ dev_dependencies: process: 5.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" typed_data: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 7b82 +# PUBSPEC CHECKSUM: eb07 diff --git a/packages/flutter_driver/pubspec.yaml b/packages/flutter_driver/pubspec.yaml index a5fa4980b30ef..48ab4892b4077 100644 --- a/packages/flutter_driver/pubspec.yaml +++ b/packages/flutter_driver/pubspec.yaml @@ -23,6 +23,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker: 10.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -43,8 +44,8 @@ dev_dependencies: fake_async: 1.3.3 test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" cli_config: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -72,9 +73,9 @@ dev_dependencies: typed_data: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 5953 +# PUBSPEC CHECKSUM: 5ad6 diff --git a/packages/flutter_goldens/pubspec.yaml b/packages/flutter_goldens/pubspec.yaml index cdad4ce5e6e36..b617a907be437 100644 --- a/packages/flutter_goldens/pubspec.yaml +++ b/packages/flutter_goldens/pubspec.yaml @@ -24,6 +24,7 @@ dependencies: clock: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker: 10.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -40,4 +41,4 @@ dependencies: vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 7b82 +# PUBSPEC CHECKSUM: eb07 diff --git a/packages/flutter_localizations/pubspec.yaml b/packages/flutter_localizations/pubspec.yaml index 1eb908eba4112..f330d105b8d48 100644 --- a/packages/flutter_localizations/pubspec.yaml +++ b/packages/flutter_localizations/pubspec.yaml @@ -16,6 +16,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -40,4 +41,4 @@ dev_dependencies: test_api: 0.7.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 3257 +# PUBSPEC CHECKSUM: fcdb diff --git a/packages/flutter_test/pubspec.yaml b/packages/flutter_test/pubspec.yaml index 627d6847068dc..e7c1696e24f78 100644 --- a/packages/flutter_test/pubspec.yaml +++ b/packages/flutter_test/pubspec.yaml @@ -40,6 +40,7 @@ dependencies: boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker: 10.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -58,4 +59,4 @@ dev_dependencies: sync_http: 0.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: c058 +# PUBSPEC CHECKSUM: a3dc diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index b5942b1766a9e..abb77e513d388 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -40,7 +40,7 @@ dependencies: shelf: 1.4.2 vm_snapshot_analysis: 0.7.6 uuid: 4.5.1 - web_socket_channel: 3.0.2 + web_socket_channel: 3.0.3 stream_channel: 2.1.4 shelf_web_socket: 2.0.1 shelf_static: 1.1.3 @@ -52,7 +52,7 @@ dependencies: http_multi_server: 3.2.2 convert: 3.1.2 async: 2.13.0 - unified_analytics: 7.0.2 + unified_analytics: 8.0.1 graphs: 2.3.2 native_assets_builder: 0.16.0 @@ -68,8 +68,8 @@ dependencies: standard_message_codec: 0.0.1+4 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" browser_launcher: 1.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" built_collection: 5.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -80,7 +80,7 @@ dependencies: dap: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" dds_service_extensions: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" devtools_shared: 11.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - dtd: 2.5.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + dtd: 2.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" extension_discovery: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fixnum: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" frontend_server_client: 4.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -105,7 +105,7 @@ dependencies: vm_service_interface: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml_edit: 2.2.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" dev_dependencies: @@ -123,4 +123,4 @@ dartdoc: # Exclude this package from the hosted API docs. nodoc: true -# PUBSPEC CHECKSUM: f2f3 +# PUBSPEC CHECKSUM: 4df3 diff --git a/packages/flutter_tools/test/widget_preview_scaffold.shard/widget_preview_scaffold/pubspec.yaml b/packages/flutter_tools/test/widget_preview_scaffold.shard/widget_preview_scaffold/pubspec.yaml index 6fcaa97500c34..7f294c2810d2e 100644 --- a/packages/flutter_tools/test/widget_preview_scaffold.shard/widget_preview_scaffold/pubspec.yaml +++ b/packages/flutter_tools/test/widget_preview_scaffold.shard/widget_preview_scaffold/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: flutter_test: sdk: flutter # These will be replaced with proper constraints after the template is hydrated. - dtd: 2.5.0 + dtd: 2.5.1 flutter_lints: 5.0.0 stack_trace: 1.12.1 url_launcher: 6.3.1 @@ -25,6 +25,7 @@ dependencies: convert: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http: 1.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -44,7 +45,7 @@ dependencies: term_glyph: 1.2.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" test_api: 0.7.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" typed_data: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - unified_analytics: 7.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + unified_analytics: 8.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" url_launcher_android: 6.3.15 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" url_launcher_ios: 6.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" url_launcher_linux: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -55,10 +56,10 @@ dependencies: vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 72a8 +# PUBSPEC CHECKSUM: c229 diff --git a/packages/flutter_web_plugins/pubspec.yaml b/packages/flutter_web_plugins/pubspec.yaml index 6badb1f481ee2..7a8296838ecea 100644 --- a/packages/flutter_web_plugins/pubspec.yaml +++ b/packages/flutter_web_plugins/pubspec.yaml @@ -11,6 +11,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -36,4 +37,4 @@ dev_dependencies: test_api: 0.7.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 5b23 +# PUBSPEC CHECKSUM: b9a7 diff --git a/packages/fuchsia_remote_debug_protocol/pubspec.yaml b/packages/fuchsia_remote_debug_protocol/pubspec.yaml index 019d8e36cb01e..b100cb8b2d2d2 100644 --- a/packages/fuchsia_remote_debug_protocol/pubspec.yaml +++ b/packages/fuchsia_remote_debug_protocol/pubspec.yaml @@ -18,8 +18,8 @@ dependencies: dev_dependencies: test: 1.25.15 - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -57,8 +57,8 @@ dev_dependencies: typed_data: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -66,4 +66,4 @@ dartdoc: # Exclude this package from the hosted API docs. nodoc: true -# PUBSPEC CHECKSUM: ae53 +# PUBSPEC CHECKSUM: b452 diff --git a/packages/integration_test/example/pubspec.yaml b/packages/integration_test/example/pubspec.yaml index f2b1b8f6462d1..9598759b9a03e 100644 --- a/packages/integration_test/example/pubspec.yaml +++ b/packages/integration_test/example/pubspec.yaml @@ -15,6 +15,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -34,8 +35,8 @@ dev_dependencies: # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec - _fe_analyzer_shared: 80.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 7.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 82.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 7.4.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.13.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -80,8 +81,8 @@ dev_dependencies: typed_data: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 15.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" watcher: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket: 0.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - web_socket_channel: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 3.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -89,4 +90,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 30b9 +# PUBSPEC CHECKSUM: 593d diff --git a/packages/integration_test/integration_test_macos/pubspec.yaml b/packages/integration_test/integration_test_macos/pubspec.yaml index 7e932ea46b428..7ff0eca1c3d06 100644 --- a/packages/integration_test/integration_test_macos/pubspec.yaml +++ b/packages/integration_test/integration_test_macos/pubspec.yaml @@ -18,6 +18,7 @@ dependencies: characters: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.16.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -25,4 +26,4 @@ dependencies: dev_dependencies: pedantic: 1.11.1 -# PUBSPEC CHECKSUM: de8e +# PUBSPEC CHECKSUM: 1813 diff --git a/packages/integration_test/pubspec.yaml b/packages/integration_test/pubspec.yaml index b34d4656c901a..ad4244bacba8f 100644 --- a/packages/integration_test/pubspec.yaml +++ b/packages/integration_test/pubspec.yaml @@ -21,6 +21,7 @@ dependencies: clock: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + ffi: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker: 10.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -56,4 +57,4 @@ flutter: ios: pluginClass: IntegrationTestPlugin -# PUBSPEC CHECKSUM: 1dc6 +# PUBSPEC CHECKSUM: ae4b From fe2bf3b475a44a87012e3217ddd802b9edd95119 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 18 Apr 2025 13:13:25 +0200 Subject: [PATCH 42/52] Windowing implementation for tester --- packages/flutter_test/lib/src/binding.dart | 6 ++++++ packages/flutter_test/lib/src/windowing.dart | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 packages/flutter_test/lib/src/windowing.dart diff --git a/packages/flutter_test/lib/src/binding.dart b/packages/flutter_test/lib/src/binding.dart index 5808af6386397..07903161d26ec 100644 --- a/packages/flutter_test/lib/src/binding.dart +++ b/packages/flutter_test/lib/src/binding.dart @@ -35,6 +35,7 @@ import 'test_default_binary_messenger.dart'; import 'test_exception_reporter.dart'; import 'test_text_input.dart'; import 'window.dart'; +import 'windowing.dart'; /// Phases that can be reached by [WidgetTester.pumpWidget] and /// [TestWidgetsFlutterBinding.pump]. @@ -204,6 +205,11 @@ abstract class TestWidgetsFlutterBinding extends BindingBase debugDisableShadows = disableShadows; } + @override + WindowingOwner createWindowingOwner() { + return TestWindowingOwner(); + } + /// Deprecated. Will be removed in a future version of Flutter. /// /// This property has been deprecated to prepare for Flutter's upcoming diff --git a/packages/flutter_test/lib/src/windowing.dart b/packages/flutter_test/lib/src/windowing.dart new file mode 100644 index 0000000000000..4005cac913b5e --- /dev/null +++ b/packages/flutter_test/lib/src/windowing.dart @@ -0,0 +1,17 @@ +import 'package:flutter/widgets.dart'; + +/// WindowingOwner used in Flutter Tester. +class TestWindowingOwner extends WindowingOwner { + @override + RegularWindowController createRegularWindowController({ + required WindowSizing contentSize, + required RegularWindowControllerDelegate delegate, + }) { + throw UnsupportedError('Current platform does not support windowing.\n'); + } + + @override + bool hasTopLevelWindows() { + return false; + } +} From aa20f9317d4db168fb01ac3090f1c21c7c8d4450 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 18 Apr 2025 13:44:54 +0200 Subject: [PATCH 43/52] add ffi to allowlist --- dev/bots/allowlist.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/bots/allowlist.dart b/dev/bots/allowlist.dart index 91f5da871f202..dfc4f3a866a43 100644 --- a/dev/bots/allowlist.dart +++ b/dev/bots/allowlist.dart @@ -23,6 +23,7 @@ const Set kCorePackageAllowList = { 'clock', 'collection', 'fake_async', + "ffi", 'file', 'flutter', 'flutter_driver', From 43537bd3bd1345507cabdd7b7acd538a433d6b26 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 18 Apr 2025 14:35:54 +0200 Subject: [PATCH 44/52] Remove app_icon --- .../AppIcon.appiconset/Contents.json | 68 ------------------ .../AppIcon.appiconset/app_icon_1024.png | Bin 102994 -> 0 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 5680 -> 0 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 520 -> 0 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 14142 -> 0 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 1066 -> 0 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 36406 -> 0 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 2218 -> 0 bytes .../Runner/Assets.xcassets/Contents.json | 6 ++ 9 files changed, 6 insertions(+), 68 deletions(-) delete mode 100644 examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png delete mode 100644 examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png delete mode 100644 examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png delete mode 100644 examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png delete mode 100644 examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png delete mode 100644 examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png delete mode 100644 examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 examples/multi_window_ref_app/macos/Runner/Assets.xcassets/Contents.json diff --git a/examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index a2ec33f19f110..0000000000000 --- a/examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "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/examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png deleted file mode 100644 index 82b6f9d9a33e198f5747104729e1fcef999772a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? 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 z 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 Pe7HEdAEg|=c=LY&YVNkY diff --git a/examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png deleted file mode 100644 index 13b35eba55c6dabc3aac36f33d859266c18fa0d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5680 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 diff --git a/examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png deleted file mode 100644 index 0a3f5fa40fb3d1e0710331a48de5d256da3f275d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 520 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 diff --git a/examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/examples/multi_window_ref_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png deleted file mode 100644 index 2f1632cfddf3d9dade342351e627a0a75609fb46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 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 Date: Fri, 18 Apr 2025 14:37:37 +0200 Subject: [PATCH 45/52] Remove app icon --- .../windows/runner/Runner.rc | 10 ---------- .../windows/runner/resource.h | 1 - .../windows/runner/resources/app_icon.ico | Bin 33772 -> 0 bytes 3 files changed, 11 deletions(-) delete mode 100644 examples/multi_window_ref_app/windows/runner/resources/app_icon.ico diff --git a/examples/multi_window_ref_app/windows/runner/Runner.rc b/examples/multi_window_ref_app/windows/runner/Runner.rc index d81714ff69411..909820ff45c09 100644 --- a/examples/multi_window_ref_app/windows/runner/Runner.rc +++ b/examples/multi_window_ref_app/windows/runner/Runner.rc @@ -45,16 +45,6 @@ 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 diff --git a/examples/multi_window_ref_app/windows/runner/resource.h b/examples/multi_window_ref_app/windows/runner/resource.h index c245ff19cb580..69cacf3cead96 100644 --- a/examples/multi_window_ref_app/windows/runner/resource.h +++ b/examples/multi_window_ref_app/windows/runner/resource.h @@ -6,7 +6,6 @@ // Microsoft Visual C++ generated include file. // Used by Runner.rc // -#define IDI_APP_ICON 101 // Next default values for new objects // diff --git a/examples/multi_window_ref_app/windows/runner/resources/app_icon.ico b/examples/multi_window_ref_app/windows/runner/resources/app_icon.ico deleted file mode 100644 index c04e20caf6370ebb9253ad831cc31de4a9c965f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33772 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 From fd3556a9f8305d3e9dc1a1943094db9226dc0ab4 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 18 Apr 2025 14:51:31 +0200 Subject: [PATCH 46/52] Fix analyze 1 --- .../lib/app/main_window.dart | 4 +++ .../lib/app/regular_window_content.dart | 4 +++ .../lib/app/regular_window_edit_dialog.dart | 10 ++++--- .../lib/app/window_controller_render.dart | 7 +++-- .../lib/app/window_manager_model.dart | 9 +++--- .../lib/app/window_settings.dart | 7 +++-- .../lib/app/window_settings_dialog.dart | 28 +++++++++--------- examples/multi_window_ref_app/lib/main.dart | 12 ++++++-- .../macos/Runner/AppDelegate.swift | 4 +++ .../macos/Runner/MainFlutterWindow.swift | 4 +++ .../macos/RunnerTests/RunnerTests.swift | 4 +++ .../test/widget_test.dart | 29 +++---------------- .../windows/runner/main.cpp | 8 +++-- .../flutter/lib/src/widgets/_window_ffi.dart | 4 +++ .../flutter/lib/src/widgets/_window_web.dart | 4 +++ packages/flutter/lib/src/widgets/window.dart | 6 +++- .../flutter/lib/src/widgets/window_macos.dart | 9 +++++- .../flutter/lib/src/widgets/window_win32.dart | 26 +++++++++++++---- packages/flutter_test/lib/src/windowing.dart | 4 +++ 19 files changed, 119 insertions(+), 64 deletions(-) diff --git a/examples/multi_window_ref_app/lib/app/main_window.dart b/examples/multi_window_ref_app/lib/app/main_window.dart index 93bde69145102..b60e21dc73a62 100644 --- a/examples/multi_window_ref_app/lib/app/main_window.dart +++ b/examples/multi_window_ref_app/lib/app/main_window.dart @@ -1,3 +1,7 @@ +// Copyright 2014 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:multi_window_ref_app/app/window_controller_render.dart'; diff --git a/examples/multi_window_ref_app/lib/app/regular_window_content.dart b/examples/multi_window_ref_app/lib/app/regular_window_content.dart index e1ef972c50119..4657830c70dbe 100644 --- a/examples/multi_window_ref_app/lib/app/regular_window_content.dart +++ b/examples/multi_window_ref_app/lib/app/regular_window_content.dart @@ -1,3 +1,7 @@ +// Copyright 2014 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:multi_window_ref_app/app/window_controller_render.dart'; import 'package:multi_window_ref_app/app/window_manager_model.dart'; diff --git a/examples/multi_window_ref_app/lib/app/regular_window_edit_dialog.dart b/examples/multi_window_ref_app/lib/app/regular_window_edit_dialog.dart index f6a5b4b3dd025..71ade208a8100 100644 --- a/examples/multi_window_ref_app/lib/app/regular_window_edit_dialog.dart +++ b/examples/multi_window_ref_app/lib/app/regular_window_edit_dialog.dart @@ -1,3 +1,7 @@ +// Copyright 2014 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'; void showRegularWindowEditDialog(BuildContext context, @@ -10,8 +14,7 @@ void showRegularWindowEditDialog(BuildContext context, TextEditingController(text: initialWidth?.toString() ?? ''); final TextEditingController heightController = TextEditingController(text: initialHeight?.toString() ?? ''); - final TextEditingController titleController = - TextEditingController(text: initialTitle ?? ''); + final TextEditingController titleController = TextEditingController(text: initialTitle ?? ''); showDialog( context: context, @@ -71,8 +74,7 @@ void showRegularWindowEditDialog(BuildContext context, onPressed: () { double? width = double.tryParse(widthController.text); double? height = double.tryParse(heightController.text); - String? title = - titleController.text.isEmpty ? null : titleController.text; + String? title = titleController.text.isEmpty ? null : titleController.text; onSave?.call(width, height, title, selectedState); Navigator.of(context).pop(); diff --git a/examples/multi_window_ref_app/lib/app/window_controller_render.dart b/examples/multi_window_ref_app/lib/app/window_controller_render.dart index 1ad041b80be11..39706841899c6 100644 --- a/examples/multi_window_ref_app/lib/app/window_controller_render.dart +++ b/examples/multi_window_ref_app/lib/app/window_controller_render.dart @@ -1,3 +1,7 @@ +// Copyright 2014 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 'regular_window_content.dart'; import 'window_manager_model.dart'; @@ -30,8 +34,7 @@ class WindowControllerRender extends StatelessWidget { windowSettings: windowSettings, windowManagerModel: windowManagerModel)); default: - throw UnimplementedError( - "The provided window type does not have an implementation"); + throw UnimplementedError("The provided window type does not have an implementation"); } } } diff --git a/examples/multi_window_ref_app/lib/app/window_manager_model.dart b/examples/multi_window_ref_app/lib/app/window_manager_model.dart index 27927dd4e413c..cb7da73a4b23c 100644 --- a/examples/multi_window_ref_app/lib/app/window_manager_model.dart +++ b/examples/multi_window_ref_app/lib/app/window_manager_model.dart @@ -1,11 +1,12 @@ +// Copyright 2014 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/widgets.dart'; class KeyedWindowController { KeyedWindowController( - {this.parent, - this.isMainWindow = false, - required this.key, - required this.controller}); + {this.parent, this.isMainWindow = false, required this.key, required this.controller}); final WindowController? parent; final bool isMainWindow; diff --git a/examples/multi_window_ref_app/lib/app/window_settings.dart b/examples/multi_window_ref_app/lib/app/window_settings.dart index 8cb0d5ba13498..56669bb3b3f1d 100644 --- a/examples/multi_window_ref_app/lib/app/window_settings.dart +++ b/examples/multi_window_ref_app/lib/app/window_settings.dart @@ -1,8 +1,11 @@ +// Copyright 2014 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'; class WindowSettings extends ChangeNotifier { - WindowSettings({Size regularSize = const Size(400, 300)}) - : _regularSize = regularSize; + WindowSettings({Size regularSize = const Size(400, 300)}) : _regularSize = regularSize; Size _regularSize; Size get regularSize => _regularSize; diff --git a/examples/multi_window_ref_app/lib/app/window_settings_dialog.dart b/examples/multi_window_ref_app/lib/app/window_settings_dialog.dart index 2d8fe433b133f..a11ff5e282131 100644 --- a/examples/multi_window_ref_app/lib/app/window_settings_dialog.dart +++ b/examples/multi_window_ref_app/lib/app/window_settings_dialog.dart @@ -1,8 +1,11 @@ +// Copyright 2014 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:multi_window_ref_app/app/window_settings.dart'; -Future windowSettingsDialog( - BuildContext context, WindowSettings settings) async { +Future windowSettingsDialog(BuildContext context, WindowSettings settings) async { return await showDialog( barrierDismissible: true, context: context, @@ -31,15 +34,13 @@ Future windowSettingsDialog( children: [ Expanded( child: TextFormField( - initialValue: settings.regularSize.width - .toString(), + initialValue: settings.regularSize.width.toString(), decoration: const InputDecoration( labelText: 'Initial width', ), - onChanged: (String value) => - settings.regularSize = Size( - double.tryParse(value) ?? 0, - settings.regularSize.height), + onChanged: (String value) => settings.regularSize = Size( + double.tryParse(value) ?? 0, + settings.regularSize.height), ), ), const SizedBox( @@ -47,16 +48,13 @@ Future windowSettingsDialog( ), Expanded( child: TextFormField( - initialValue: settings - .regularSize.height - .toString(), + initialValue: settings.regularSize.height.toString(), decoration: const InputDecoration( labelText: 'Initial height', ), - onChanged: (String value) => - settings.regularSize = Size( - settings.regularSize.width, - double.tryParse(value) ?? 0), + onChanged: (String value) => settings.regularSize = Size( + settings.regularSize.width, + double.tryParse(value) ?? 0), ), ), ], diff --git a/examples/multi_window_ref_app/lib/main.dart b/examples/multi_window_ref_app/lib/main.dart index d9c989db712f3..2697d9baa1b15 100644 --- a/examples/multi_window_ref_app/lib/main.dart +++ b/examples/multi_window_ref_app/lib/main.dart @@ -1,3 +1,7 @@ +// Copyright 2014 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 'app/main_window.dart'; @@ -9,6 +13,10 @@ void main() { ), title: "Multi-Window Reference Application", ); - runWidget(RegularWindow( - controller: controller, child: MaterialApp(home: MainWindow(mainController: controller)))); + runWidget( + RegularWindow( + controller: controller, + child: MaterialApp(home: MainWindow(mainController: controller)), + ), + ); } diff --git a/examples/multi_window_ref_app/macos/Runner/AppDelegate.swift b/examples/multi_window_ref_app/macos/Runner/AppDelegate.swift index 840ecce51c98f..84cd1f9d64f7b 100644 --- a/examples/multi_window_ref_app/macos/Runner/AppDelegate.swift +++ b/examples/multi_window_ref_app/macos/Runner/AppDelegate.swift @@ -1,3 +1,7 @@ +// Copyright 2014 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 Cocoa import FlutterMacOS diff --git a/examples/multi_window_ref_app/macos/Runner/MainFlutterWindow.swift b/examples/multi_window_ref_app/macos/Runner/MainFlutterWindow.swift index 3cc05eb234916..a6a6b9af83072 100644 --- a/examples/multi_window_ref_app/macos/Runner/MainFlutterWindow.swift +++ b/examples/multi_window_ref_app/macos/Runner/MainFlutterWindow.swift @@ -1,3 +1,7 @@ +// Copyright 2014 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 Cocoa import FlutterMacOS diff --git a/examples/multi_window_ref_app/macos/RunnerTests/RunnerTests.swift b/examples/multi_window_ref_app/macos/RunnerTests/RunnerTests.swift index 61f3bd1fc504c..eeb5f29483661 100644 --- a/examples/multi_window_ref_app/macos/RunnerTests/RunnerTests.swift +++ b/examples/multi_window_ref_app/macos/RunnerTests/RunnerTests.swift @@ -1,3 +1,7 @@ +// Copyright 2014 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 Cocoa import FlutterMacOS import XCTest diff --git a/examples/multi_window_ref_app/test/widget_test.dart b/examples/multi_window_ref_app/test/widget_test.dart index 66c3df7577f1c..31030bc0daaa8 100644 --- a/examples/multi_window_ref_app/test/widget_test.dart +++ b/examples/multi_window_ref_app/test/widget_test.dart @@ -1,30 +1,9 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. +// Copyright 2014 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_test/flutter_test.dart'; -import 'package:multi_window_ref_app/main.dart'; - void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // // Build our app and trigger a frame. - // await tester.pumpWidget(const MyApp()); - - // // Verify that our counter starts at 0. - // expect(find.text('0'), findsOneWidget); - // expect(find.text('1'), findsNothing); - - // // Tap the '+' icon and trigger a frame. - // await tester.tap(find.byIcon(Icons.add)); - // await tester.pump(); - - // // Verify that our counter has incremented. - // expect(find.text('0'), findsNothing); - // expect(find.text('1'), findsOneWidget); - }); + testWidgets('Counter increments smoke test', (WidgetTester tester) async {}); } diff --git a/examples/multi_window_ref_app/windows/runner/main.cpp b/examples/multi_window_ref_app/windows/runner/main.cpp index 6f944fa05a109..11a76c8ceffa2 100644 --- a/examples/multi_window_ref_app/windows/runner/main.cpp +++ b/examples/multi_window_ref_app/windows/runner/main.cpp @@ -1,11 +1,15 @@ -#include +// Copyright 2014 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 #include +#include #include "utils.h" int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { + _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()) { diff --git a/packages/flutter/lib/src/widgets/_window_ffi.dart b/packages/flutter/lib/src/widgets/_window_ffi.dart index 99522033152c7..897ba1d321876 100644 --- a/packages/flutter/lib/src/widgets/_window_ffi.dart +++ b/packages/flutter/lib/src/widgets/_window_ffi.dart @@ -1,3 +1,7 @@ +// Copyright 2014 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 'window.dart'; diff --git a/packages/flutter/lib/src/widgets/_window_web.dart b/packages/flutter/lib/src/widgets/_window_web.dart index 40d9b638ff38b..160eaeb192f27 100644 --- a/packages/flutter/lib/src/widgets/_window_web.dart +++ b/packages/flutter/lib/src/widgets/_window_web.dart @@ -1,3 +1,7 @@ +// Copyright 2014 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 'window.dart'; /// Creates a default [WindowingOwner] for web. Returns `null` as web does not diff --git a/packages/flutter/lib/src/widgets/window.dart b/packages/flutter/lib/src/widgets/window.dart index 60f7718fcc8a7..1ddebfd1da52a 100644 --- a/packages/flutter/lib/src/widgets/window.dart +++ b/packages/flutter/lib/src/widgets/window.dart @@ -4,10 +4,14 @@ import 'dart:ui' show AppExitType, FlutterView; +import 'package:flutter/foundation.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import '_window_ffi.dart' if (dart.library.js_util) '_window_web.dart' as window_impl; +import 'binding.dart'; +import 'framework.dart'; +import 'view.dart'; /// Defines the possible archetypes for a window. enum WindowArchetype { diff --git a/packages/flutter/lib/src/widgets/window_macos.dart b/packages/flutter/lib/src/widgets/window_macos.dart index a392e7eafad46..3c878d9ec4ec1 100644 --- a/packages/flutter/lib/src/widgets/window_macos.dart +++ b/packages/flutter/lib/src/widgets/window_macos.dart @@ -1,10 +1,17 @@ +// Copyright 2014 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' hide Size; import 'dart:ui' show FlutterView; import 'package:ffi/ffi.dart' as ffi; -import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter/src/foundation/binding.dart'; +import 'binding.dart'; +import 'window.dart'; + /// The macOS implementation of the windowing API. class WindowingOwnerMacOS extends WindowingOwner { @override diff --git a/packages/flutter/lib/src/widgets/window_win32.dart b/packages/flutter/lib/src/widgets/window_win32.dart index d2292f2a0c98c..c2598cbbfca54 100644 --- a/packages/flutter/lib/src/widgets/window_win32.dart +++ b/packages/flutter/lib/src/widgets/window_win32.dart @@ -1,12 +1,17 @@ -// ignore_for_file: public_member_api_docs, avoid_unused_constructor_parameters +// Copyright 2014 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' hide Size; import 'dart:ui' show FlutterView; import 'package:ffi/ffi.dart' as ffi; import 'package:flutter/foundation.dart'; +import 'package:flutter/rendering.dart'; -import 'package:flutter/material.dart'; +import 'binding.dart'; +import 'window.dart'; +/// Handler for Win32 messages. abstract class WindowsMessageHandler { /// Handles a window message. Returned value, if not null will be /// returned to the system as LRESULT and will stop all other @@ -20,7 +25,9 @@ abstract class WindowsMessageHandler { ); } +/// Windowing owner implementation for Windows. class WindowingOwnerWin32 extends WindowingOwner { + /// Creates a new [WindowingOwnerWin32] instance. WindowingOwnerWin32() { final Pointer<_WindowingInitRequest> request = ffi.calloc<_WindowingInitRequest>() @@ -40,10 +47,13 @@ class WindowingOwnerWin32 extends WindowingOwner { return RegularWindowControllerWin32(owner: this, delegate: delegate, contentSize: contentSize); } + /// Register new message handler. The handler will be called for unhandled + /// messages for all top level windows. void addMessageHandler(WindowsMessageHandler handler) { _messageHandlers.add(handler); } + /// Unregister message handler. void removeMessageHandler(WindowsMessageHandler handler) { _messageHandlers.remove(handler); } @@ -85,8 +95,11 @@ class WindowingOwnerWin32 extends WindowingOwner { external static void _initializeWindowing(int engineId, Pointer<_WindowingInitRequest> request); } +/// The Win32 implementation of the regular window controller. class RegularWindowControllerWin32 extends RegularWindowController implements WindowsMessageHandler { + /// Creates a new regular window controller for Win32. When this constructor + /// completes the FlutterView is created and framework is aware of it. RegularWindowControllerWin32({ required WindowingOwnerWin32 owner, required RegularWindowControllerDelegate delegate, @@ -143,6 +156,7 @@ class RegularWindowControllerWin32 extends RegularWindowController ffi.calloc.free(sizing); } + /// Returns HWND pointer to the top level window. Pointer getWindowHandle() { _ensureNotDestroyed(); return _getWindowHandle(PlatformDispatcher.instance.engineId!, rootView.viewId); @@ -169,8 +183,8 @@ class RegularWindowControllerWin32 extends RegularWindowController _owner.removeMessageHandler(this); } - static const int WM_SIZE = 0x0005; - static const int WM_CLOSE = 0x0010; + static const int _WM_SIZE = 0x0005; + static const int _WM_CLOSE = 0x0010; @override int? handleWindowsMessage( @@ -184,10 +198,10 @@ class RegularWindowControllerWin32 extends RegularWindowController return null; } - if (message == WM_CLOSE) { + if (message == _WM_CLOSE) { _delegate.onWindowCloseRequested(this); return 0; - } else if (message == WM_SIZE) { + } else if (message == _WM_SIZE) { notifyListeners(); } return null; diff --git a/packages/flutter_test/lib/src/windowing.dart b/packages/flutter_test/lib/src/windowing.dart index 4005cac913b5e..b1b82cf112c8e 100644 --- a/packages/flutter_test/lib/src/windowing.dart +++ b/packages/flutter_test/lib/src/windowing.dart @@ -1,3 +1,7 @@ +// Copyright 2014 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/widgets.dart'; /// WindowingOwner used in Flutter Tester. From 46c97ac2bedd461cecd54c14a6f3555280e93c4d Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 18 Apr 2025 15:03:36 +0200 Subject: [PATCH 47/52] fix imports --- packages/flutter/.vscode/launch.json | 51 ++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 packages/flutter/.vscode/launch.json diff --git a/packages/flutter/.vscode/launch.json b/packages/flutter/.vscode/launch.json new file mode 100644 index 0000000000000..da9e2432de52d --- /dev/null +++ b/packages/flutter/.vscode/launch.json @@ -0,0 +1,51 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "flutter", + "request": "launch", + "type": "dart" + }, + { + "name": "flutter (profile mode)", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "flutter (release mode)", + "request": "launch", + "type": "dart", + "flutterMode": "release" + }, + { + "name": "test_private", + "cwd": "test_private", + "request": "launch", + "type": "dart" + }, + { + "name": "test", + "cwd": "test_private/test", + "request": "launch", + "type": "dart" + }, + { + "name": "test (profile mode)", + "cwd": "test_private/test", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "test (release mode)", + "cwd": "test_private/test", + "request": "launch", + "type": "dart", + "flutterMode": "release" + } + ] +} \ No newline at end of file From 19965edbae19aa9216e1cf21016333f8cc33be2e Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 18 Apr 2025 15:10:24 +0200 Subject: [PATCH 48/52] fix imports --- packages/flutter/lib/src/widgets/window_macos.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter/lib/src/widgets/window_macos.dart b/packages/flutter/lib/src/widgets/window_macos.dart index 3c878d9ec4ec1..a94323921ccc6 100644 --- a/packages/flutter/lib/src/widgets/window_macos.dart +++ b/packages/flutter/lib/src/widgets/window_macos.dart @@ -6,8 +6,8 @@ import 'dart:ffi' hide Size; import 'dart:ui' show FlutterView; import 'package:ffi/ffi.dart' as ffi; +import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; -import 'package:flutter/src/foundation/binding.dart'; import 'binding.dart'; import 'window.dart'; From 4e5304adfcac95114b2b11484676258e76220d36 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 18 Apr 2025 15:10:31 +0200 Subject: [PATCH 49/52] fix quotes --- dev/bots/allowlist.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/bots/allowlist.dart b/dev/bots/allowlist.dart index dfc4f3a866a43..24fc2cb6d62c2 100644 --- a/dev/bots/allowlist.dart +++ b/dev/bots/allowlist.dart @@ -23,7 +23,7 @@ const Set kCorePackageAllowList = { 'clock', 'collection', 'fake_async', - "ffi", + 'ffi', 'file', 'flutter', 'flutter_driver', From 1482a024e66d3f3fd5676d6313f3c5848f0ec4b1 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 18 Apr 2025 16:23:44 +0200 Subject: [PATCH 50/52] Reformat --- .../lib/app/main_window.dart | 60 ++++++++++++------- .../lib/app/regular_window_content.dart | 18 ++++-- .../lib/app/regular_window_edit_dialog.dart | 6 +- .../lib/app/window_controller_render.dart | 30 +++++----- .../lib/app/window_manager_model.dart | 5 +- .../lib/app/window_settings.dart | 3 +- .../lib/app/window_settings_dialog.dart | 24 +++++--- .../flutter/lib/src/widgets/window_win32.dart | 1 - .../lib/src/runner/flutter_command.dart | 12 ++-- .../test/general.shard/features_test.dart | 1 - 10 files changed, 96 insertions(+), 64 deletions(-) diff --git a/examples/multi_window_ref_app/lib/app/main_window.dart b/examples/multi_window_ref_app/lib/app/main_window.dart index b60e21dc73a62..f6988499f1f25 100644 --- a/examples/multi_window_ref_app/lib/app/main_window.dart +++ b/examples/multi_window_ref_app/lib/app/main_window.dart @@ -13,8 +13,8 @@ import 'regular_window_edit_dialog.dart'; class MainWindow extends StatefulWidget { MainWindow({super.key, required WindowController mainController}) { - _windowManagerModel.add( - KeyedWindowController(isMainWindow: true, key: UniqueKey(), controller: mainController)); + _windowManagerModel.add(KeyedWindowController( + isMainWindow: true, key: UniqueKey(), controller: mainController)); } final WindowManagerModel _windowManagerModel = WindowManagerModel(); @@ -38,7 +38,8 @@ class _MainWindowState extends State { flex: 60, child: SingleChildScrollView( scrollDirection: Axis.vertical, - child: _ActiveWindowsTable(windowManagerModel: widget._windowManagerModel), + child: _ActiveWindowsTable( + windowManagerModel: widget._windowManagerModel), ), ), Expanded( @@ -66,15 +67,18 @@ class _MainWindowState extends State { listenable: widget._windowManagerModel, builder: (BuildContext context, Widget? _) { final List childViews = []; - for (final KeyedWindowController controller in widget._windowManagerModel.windows) { + for (final KeyedWindowController controller + in widget._windowManagerModel.windows) { if (controller.parent == null && !controller.isMainWindow) { childViews.add(WindowControllerRender( controller: controller.controller, key: controller.key, windowSettings: widget._settings, windowManagerModel: widget._windowManagerModel, - onDestroyed: () => widget._windowManagerModel.remove(controller.key), - onError: () => widget._windowManagerModel.remove(controller.key), + onDestroyed: () => + widget._windowManagerModel.remove(controller.key), + onError: () => + widget._windowManagerModel.remove(controller.key), )); } } @@ -130,7 +134,8 @@ class _ActiveWindowsTable extends StatelessWidget { ), numeric: true), ], - rows: (windowManagerModel.windows).map((KeyedWindowController controller) { + rows: (windowManagerModel.windows) + .map((KeyedWindowController controller) { return DataRow( key: controller.key, color: WidgetStateColor.resolveWith((states) { @@ -142,8 +147,9 @@ class _ActiveWindowsTable extends StatelessWidget { selected: controller.controller == windowManagerModel.selected, onSelectChanged: (selected) { if (selected != null) { - windowManagerModel - .select(selected ? controller.controller.rootView.viewId : null); + windowManagerModel.select(selected + ? controller.controller.rootView.viewId + : null); } }, cells: [ @@ -151,32 +157,39 @@ class _ActiveWindowsTable extends StatelessWidget { DataCell( ListenableBuilder( listenable: controller.controller, - builder: (BuildContext context, Widget? _) => Text(controller - .controller.type - .toString() - .replaceFirst('WindowArchetype.', ''))), + builder: (BuildContext context, Widget? _) => Text( + controller.controller.type + .toString() + .replaceFirst('WindowArchetype.', ''))), ), DataCell( ListenableBuilder( listenable: controller.controller, - builder: (BuildContext context, Widget? _) => Row(children: [ + builder: (BuildContext context, Widget? _) => + Row(children: [ IconButton( icon: const Icon(Icons.edit_outlined), onPressed: () { - if (controller.controller.type == WindowArchetype.regular) { + if (controller.controller.type == + WindowArchetype.regular) { showRegularWindowEditDialog(context, - initialWidth: controller.controller.contentSize.width, - initialHeight: controller.controller.contentSize.height, + initialWidth: controller + .controller.contentSize.width, + initialHeight: controller + .controller.contentSize.height, initialTitle: "", - initialState: - (controller.controller as RegularWindowController) - .state, onSave: (double? width, double? height, + initialState: (controller.controller + as RegularWindowController) + .state, + onSave: (double? width, double? height, String? title, WindowState? state) { final regularController = - controller.controller as RegularWindowController; + controller.controller + as RegularWindowController; if (width != null && height != null) { regularController.setContentSize( - WindowSizing(size: Size(width, height)), + WindowSizing( + size: Size(width, height)), ); } if (title != null) { @@ -247,7 +260,8 @@ class _WindowCreatorCard extends StatelessWidget { onDestroyed: () => windowManagerModel.remove(key), ), title: "Regular", - contentSize: WindowSizing(size: windowSettings.regularSize), + contentSize: + WindowSizing(size: windowSettings.regularSize), ))); }, child: const Text('Regular'), diff --git a/examples/multi_window_ref_app/lib/app/regular_window_content.dart b/examples/multi_window_ref_app/lib/app/regular_window_content.dart index 4657830c70dbe..53f444caddedc 100644 --- a/examples/multi_window_ref_app/lib/app/regular_window_content.dart +++ b/examples/multi_window_ref_app/lib/app/regular_window_content.dart @@ -87,7 +87,8 @@ class _RegularWindowContentState extends State builder: (context, child) { return CustomPaint( size: const Size(200, 200), - painter: _RotatedWireCube(angle: _animation.value, color: cubeColor), + painter: _RotatedWireCube( + angle: _animation.value, color: cubeColor), ); }, ), @@ -103,9 +104,11 @@ class _RegularWindowContentState extends State widget.windowManagerModel.add(KeyedWindowController( key: key, controller: RegularWindowController( - contentSize: WindowSizing(size: widget.windowSettings.regularSize), + contentSize: WindowSizing( + size: widget.windowSettings.regularSize), delegate: WindowControllerDelegate( - onDestroyed: () => widget.windowManagerModel.remove(key), + onDestroyed: () => + widget.windowManagerModel.remove(key), ), title: "Regular", ))); @@ -134,15 +137,18 @@ class _RegularWindowContentState extends State listenable: widget.windowManagerModel, builder: (BuildContext context, Widget? _) { final List childViews = []; - for (final KeyedWindowController controller in widget.windowManagerModel.windows) { + for (final KeyedWindowController controller + in widget.windowManagerModel.windows) { if (controller.parent == widget.window) { childViews.add(WindowControllerRender( controller: controller.controller, key: controller.key, windowSettings: widget.windowSettings, windowManagerModel: widget.windowManagerModel, - onDestroyed: () => widget.windowManagerModel.remove(controller.key), - onError: () => widget.windowManagerModel.remove(controller.key), + onDestroyed: () => + widget.windowManagerModel.remove(controller.key), + onError: () => + widget.windowManagerModel.remove(controller.key), )); } } diff --git a/examples/multi_window_ref_app/lib/app/regular_window_edit_dialog.dart b/examples/multi_window_ref_app/lib/app/regular_window_edit_dialog.dart index 71ade208a8100..64a603c2809d0 100644 --- a/examples/multi_window_ref_app/lib/app/regular_window_edit_dialog.dart +++ b/examples/multi_window_ref_app/lib/app/regular_window_edit_dialog.dart @@ -14,7 +14,8 @@ void showRegularWindowEditDialog(BuildContext context, TextEditingController(text: initialWidth?.toString() ?? ''); final TextEditingController heightController = TextEditingController(text: initialHeight?.toString() ?? ''); - final TextEditingController titleController = TextEditingController(text: initialTitle ?? ''); + final TextEditingController titleController = + TextEditingController(text: initialTitle ?? ''); showDialog( context: context, @@ -74,7 +75,8 @@ void showRegularWindowEditDialog(BuildContext context, onPressed: () { double? width = double.tryParse(widthController.text); double? height = double.tryParse(heightController.text); - String? title = titleController.text.isEmpty ? null : titleController.text; + String? title = + titleController.text.isEmpty ? null : titleController.text; onSave?.call(width, height, title, selectedState); Navigator.of(context).pop(); diff --git a/examples/multi_window_ref_app/lib/app/window_controller_render.dart b/examples/multi_window_ref_app/lib/app/window_controller_render.dart index 39706841899c6..1e2ab569f8cb6 100644 --- a/examples/multi_window_ref_app/lib/app/window_controller_render.dart +++ b/examples/multi_window_ref_app/lib/app/window_controller_render.dart @@ -8,13 +8,14 @@ import 'window_manager_model.dart'; import 'window_settings.dart'; class WindowControllerRender extends StatelessWidget { - const WindowControllerRender( - {required this.controller, - required this.onDestroyed, - required this.onError, - required this.windowSettings, - required this.windowManagerModel, - required super.key}); + const WindowControllerRender({ + required this.controller, + required this.onDestroyed, + required this.onError, + required this.windowSettings, + required this.windowManagerModel, + required super.key, + }); final WindowController controller; final VoidCallback onDestroyed; @@ -27,14 +28,13 @@ class WindowControllerRender extends StatelessWidget { switch (controller.type) { case WindowArchetype.regular: return RegularWindow( - key: key, - controller: controller as RegularWindowController, - child: RegularWindowContent( - window: controller as RegularWindowController, - windowSettings: windowSettings, - windowManagerModel: windowManagerModel)); - default: - throw UnimplementedError("The provided window type does not have an implementation"); + key: key, + controller: controller as RegularWindowController, + child: RegularWindowContent( + window: controller as RegularWindowController, + windowSettings: windowSettings, + windowManagerModel: windowManagerModel), + ); } } } diff --git a/examples/multi_window_ref_app/lib/app/window_manager_model.dart b/examples/multi_window_ref_app/lib/app/window_manager_model.dart index cb7da73a4b23c..741f3a74ad94a 100644 --- a/examples/multi_window_ref_app/lib/app/window_manager_model.dart +++ b/examples/multi_window_ref_app/lib/app/window_manager_model.dart @@ -6,7 +6,10 @@ import 'package:flutter/widgets.dart'; class KeyedWindowController { KeyedWindowController( - {this.parent, this.isMainWindow = false, required this.key, required this.controller}); + {this.parent, + this.isMainWindow = false, + required this.key, + required this.controller}); final WindowController? parent; final bool isMainWindow; diff --git a/examples/multi_window_ref_app/lib/app/window_settings.dart b/examples/multi_window_ref_app/lib/app/window_settings.dart index 56669bb3b3f1d..70aa13db5fbaa 100644 --- a/examples/multi_window_ref_app/lib/app/window_settings.dart +++ b/examples/multi_window_ref_app/lib/app/window_settings.dart @@ -5,7 +5,8 @@ import 'package:flutter/material.dart'; class WindowSettings extends ChangeNotifier { - WindowSettings({Size regularSize = const Size(400, 300)}) : _regularSize = regularSize; + WindowSettings({Size regularSize = const Size(400, 300)}) + : _regularSize = regularSize; Size _regularSize; Size get regularSize => _regularSize; diff --git a/examples/multi_window_ref_app/lib/app/window_settings_dialog.dart b/examples/multi_window_ref_app/lib/app/window_settings_dialog.dart index a11ff5e282131..481f1a8c068ea 100644 --- a/examples/multi_window_ref_app/lib/app/window_settings_dialog.dart +++ b/examples/multi_window_ref_app/lib/app/window_settings_dialog.dart @@ -5,7 +5,8 @@ import 'package:flutter/material.dart'; import 'package:multi_window_ref_app/app/window_settings.dart'; -Future windowSettingsDialog(BuildContext context, WindowSettings settings) async { +Future windowSettingsDialog( + BuildContext context, WindowSettings settings) async { return await showDialog( barrierDismissible: true, context: context, @@ -34,13 +35,15 @@ Future windowSettingsDialog(BuildContext context, WindowSettings settings) children: [ Expanded( child: TextFormField( - initialValue: settings.regularSize.width.toString(), + initialValue: settings.regularSize.width + .toString(), decoration: const InputDecoration( labelText: 'Initial width', ), - onChanged: (String value) => settings.regularSize = Size( - double.tryParse(value) ?? 0, - settings.regularSize.height), + onChanged: (String value) => + settings.regularSize = Size( + double.tryParse(value) ?? 0, + settings.regularSize.height), ), ), const SizedBox( @@ -48,13 +51,16 @@ Future windowSettingsDialog(BuildContext context, WindowSettings settings) ), Expanded( child: TextFormField( - initialValue: settings.regularSize.height.toString(), + initialValue: settings + .regularSize.height + .toString(), decoration: const InputDecoration( labelText: 'Initial height', ), - onChanged: (String value) => settings.regularSize = Size( - settings.regularSize.width, - double.tryParse(value) ?? 0), + onChanged: (String value) => + settings.regularSize = Size( + settings.regularSize.width, + double.tryParse(value) ?? 0), ), ), ], diff --git a/packages/flutter/lib/src/widgets/window_win32.dart b/packages/flutter/lib/src/widgets/window_win32.dart index c2598cbbfca54..ca8b0eea387d6 100644 --- a/packages/flutter/lib/src/widgets/window_win32.dart +++ b/packages/flutter/lib/src/widgets/window_win32.dart @@ -177,7 +177,6 @@ class RegularWindowControllerWin32 extends RegularWindowController return; } _destroyWindow(getWindowHandle()); - ; _destroyed = true; _delegate.onWindowDestroyed(); _owner.removeMessageHandler(this); diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 7289473eba1e1..a98f5f0377313 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -1291,11 +1291,13 @@ abstract class FlutterCommand extends Command { } void addWindowingFlag({required bool verboseHelp}) { - argParser.addFlag('enable-windowing', - hide: !verboseHelp, - help: 'Whether to enable support for multiple windows. ' - 'Disabled by default. Ignored on platforms other than ' - 'macOS, Linux, and Windows.', + argParser.addFlag( + 'enable-windowing', + hide: !verboseHelp, + help: + 'Whether to enable support for multiple windows. ' + 'Disabled by default. Ignored on platforms other than ' + 'macOS, Linux, and Windows.', ); } diff --git a/packages/flutter_tools/test/general.shard/features_test.dart b/packages/flutter_tools/test/general.shard/features_test.dart index d5d585807292c..883b3e061b721 100644 --- a/packages/flutter_tools/test/general.shard/features_test.dart +++ b/packages/flutter_tools/test/general.shard/features_test.dart @@ -447,6 +447,5 @@ void main() { expect(featureFlags.isWindowingEnabled, true); }); } - }); } From 688b6050dedb756c57400709fe1f0cad119d6980 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 18 Apr 2025 16:59:01 +0200 Subject: [PATCH 51/52] remove --- .../flutter/test/widgets/window_test.dart | 276 ------------------ 1 file changed, 276 deletions(-) delete mode 100644 packages/flutter/test/widgets/window_test.dart diff --git a/packages/flutter/test/widgets/window_test.dart b/packages/flutter/test/widgets/window_test.dart deleted file mode 100644 index c4a087eb05b89..0000000000000 --- a/packages/flutter/test/widgets/window_test.dart +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright 2014 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/services.dart'; -import 'package:flutter/widgets.dart'; -import 'package:flutter_test/flutter_test.dart'; - -// Future? Function(MethodCall)? _createWindowMethodCallHandler({ -// required WidgetTester tester, -// void Function(MethodCall)? onMethodCall, -// }) { -// return (MethodCall call) async { -// onMethodCall?.call(call); -// final Map args = call.arguments as Map; -// if (call.method == 'createRegular') { -// final List size = args['size']! as List; -// final String state = args['state'] as String? ?? WindowState.restored.toString(); - -// return {'viewId': tester.view.viewId, 'size': size, 'state': state}; -// } else if (call.method == 'modifyRegular') { -// return null; -// } else if (call.method == 'destroyWindow') { -// await tester.binding.defaultBinaryMessenger.handlePlatformMessage( -// SystemChannels.windowing.name, -// SystemChannels.windowing.codec.encodeMethodCall( -// MethodCall('onWindowDestroyed', {'viewId': tester.view.viewId}), -// ), -// (ByteData? data) {}, -// ); - -// return null; -// } - -// throw Exception('Unsupported method call: ${call.method}'); -// }; -// } - -// void main() { -// testWidgets('RegularWindow widget populates the controller with proper values', ( -// WidgetTester tester, -// ) async { -// const Size windowSize = Size(800, 600); - -// tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( -// SystemChannels.windowing, -// _createWindowMethodCallHandler(tester: tester), -// ); - -// final RegularWindowController controller = RegularWindowController(size: windowSize); - -// await tester.pump(); - -// expect(controller.type, WindowArchetype.regular); -// expect(controller.size, windowSize); -// expect(controller.rootView.viewId, tester.view.viewId); -// }); - -// testWidgets('RegularWindow.onError is called when creation throws an error', ( -// WidgetTester tester, -// ) async { -// const Size windowSize = Size(800, 600); - -// tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.windowing, ( -// MethodCall call, -// ) async { -// throw Exception('Failed to create the window'); -// }); - -// bool receivedError = false; -// final RegularWindowController controller = RegularWindowController(size: windowSize); - -// await tester.pump(); - -// expect(receivedError, true); -// }); - -// testWidgets('RegularWindowController.destroy results in the RegularWindow.onDestroyed callback', ( -// WidgetTester tester, -// ) async { -// const Size windowSize = Size(800, 600); - -// tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( -// SystemChannels.windowing, -// _createWindowMethodCallHandler(tester: tester), -// ); - -// bool destroyed = false; -// final RegularWindowController controller = RegularWindowController( -// size: windowSize, -// onDestroyed: () { -// destroyed = true; -// }, -// ); -// await tester.pumpWidget( -// wrapWithView: false, -// Builder( -// builder: (BuildContext context) { -// return RegularWindow(controller: controller, child: Container()); -// }, -// ), -// ); - -// await tester.pump(); -// controller.destroy(); - -// await tester.pump(); -// expect(destroyed, true); -// }); - -// testWidgets( -// 'RegularWindowController.size is updated when an onWindowChanged event is triggered on the channel', -// (WidgetTester tester) async { -// const Size initialSize = Size(800, 600); -// const Size newSize = Size(400, 300); - -// tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( -// SystemChannels.windowing, -// _createWindowMethodCallHandler(tester: tester), -// ); - -// final RegularWindowController controller = RegularWindowController(size: initialSize); -// await tester.pump(); - -// await tester.binding.defaultBinaryMessenger.handlePlatformMessage( -// SystemChannels.windowing.name, -// SystemChannels.windowing.codec.encodeMethodCall( -// MethodCall('onWindowChanged', { -// 'viewId': tester.view.viewId, -// 'size': [newSize.width, newSize.height], -// }), -// ), -// (ByteData? data) {}, -// ); -// await tester.pump(); - -// expect(controller.size, newSize); -// }, -// ); - -// testWidgets('RegularWindowController.modify can be called when provided with a "size" argument', ( -// WidgetTester tester, -// ) async { -// const Size initialSize = Size(800, 600); -// const Size newSize = Size(400, 300); - -// bool wasCalled = false; -// tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( -// SystemChannels.windowing, -// _createWindowMethodCallHandler( -// tester: tester, -// onMethodCall: (MethodCall call) { -// if (call.method != 'modifyRegular') { -// return; -// } - -// final Map args = call.arguments as Map; -// final int viewId = args['viewId']! as int; -// final List? size = args['size'] as List?; -// final String? title = args['title'] as String?; -// final String? state = args['state'] as String?; -// expect(viewId, tester.view.viewId); -// expect(size, [newSize.width, newSize.height]); -// expect(title, null); -// expect(state, null); -// wasCalled = true; -// }, -// ), -// ); - -// final RegularWindowController controller = RegularWindowController(size: initialSize); -// await tester.pump(); - -// controller.modify(size: newSize); -// await tester.pump(); - -// expect(wasCalled, true); -// }); - -// testWidgets( -// 'RegularWindowController.modify can be called when provided with a "title" argument', -// (WidgetTester tester) async { -// const Size initialSize = Size(800, 600); -// const String newTitle = 'New Title'; - -// bool wasCalled = false; -// tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( -// SystemChannels.windowing, -// _createWindowMethodCallHandler( -// tester: tester, -// onMethodCall: (MethodCall call) { -// if (call.method != 'modifyRegular') { -// return; -// } - -// final Map args = call.arguments as Map; -// final int viewId = args['viewId']! as int; -// final List? size = args['size'] as List?; -// final String? title = args['title'] as String?; -// final String? state = args['state'] as String?; -// expect(viewId, tester.view.viewId); -// expect(size, null); -// expect(title, newTitle); -// expect(state, null); -// wasCalled = true; -// }, -// ), -// ); - -// final RegularWindowController controller = RegularWindowController(size: initialSize); -// await tester.pump(); - -// controller.modify(title: newTitle); -// await tester.pump(); - -// expect(wasCalled, true); -// }, -// ); - -// testWidgets( -// 'RegularWindowController.modify can be called when provided with a "state" argument', -// (WidgetTester tester) async { -// const Size initialSize = Size(800, 600); -// const WindowState newState = WindowState.minimized; - -// bool wasCalled = false; -// tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( -// SystemChannels.windowing, -// _createWindowMethodCallHandler( -// tester: tester, -// onMethodCall: (MethodCall call) { -// if (call.method != 'modifyRegular') { -// return; -// } - -// final Map args = call.arguments as Map; -// final int viewId = args['viewId']! as int; -// final List? size = args['size'] as List?; -// final String? title = args['title'] as String?; -// final String? state = args['state'] as String?; -// expect(viewId, tester.view.viewId); -// expect(size, null); -// expect(title, null); -// expect(state, newState.toString()); -// wasCalled = true; -// }, -// ), -// ); - -// final RegularWindowController controller = RegularWindowController(size: initialSize); -// await tester.pump(); - -// controller.modify(state: newState); -// await tester.pump(); - -// expect(wasCalled, true); -// }, -// ); - -// testWidgets('RegularWindowController.modify throws when no arguments are provided', ( -// WidgetTester tester, -// ) async { -// const Size initialSize = Size(800, 600); -// const WindowState newState = WindowState.minimized; - -// tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( -// SystemChannels.windowing, -// _createWindowMethodCallHandler(tester: tester), -// ); - -// final RegularWindowController controller = RegularWindowController(size: initialSize); -// await tester.pump(); - -// expect(() async => controller.modify(), throwsA(isA())); -// }); -// } From 980b36dba7111ea5999b9d0b1fa9142d18ff70e8 Mon Sep 17 00:00:00 2001 From: Harlen Batagelo Date: Thu, 24 Apr 2025 16:42:43 -0300 Subject: [PATCH 52/52] Fix some issues in the window controller and ref app --- .../multi_window_ref_app/.vscode/launch.json | 35 ------------------ .../lib/app/main_window.dart | 2 +- .../windows/runner/Runner.rc | 10 +++++ .../windows/runner/resource.h | 1 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../flutter/lib/src/widgets/window_win32.dart | 2 +- 6 files changed, 13 insertions(+), 37 deletions(-) delete mode 100644 examples/multi_window_ref_app/.vscode/launch.json create mode 100644 examples/multi_window_ref_app/windows/runner/resources/app_icon.ico diff --git a/examples/multi_window_ref_app/.vscode/launch.json b/examples/multi_window_ref_app/.vscode/launch.json deleted file mode 100644 index c846434f923e5..0000000000000 --- a/examples/multi_window_ref_app/.vscode/launch.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "multi_window_ref_app", - "request": "launch", - "type": "dart", - "program": "lib/main.dart", - "toolArgs": [ - "--local-engine=host_debug_unopt_arm64", - "--local-engine-host=host_debug_unopt_arm64", - ] - }, - { - "name": "multi_window_ref_app (profile mode)", - "request": "launch", - "type": "dart", - "flutterMode": "profile" - }, - { - "name": "multi_window_ref_app (release mode)", - "request": "launch", - "type": "dart", - "flutterMode": "release" - }, - { - "name": "(Windows) Attach", - "type": "cppvsdbg", - "request": "attach", - } - ] -} \ No newline at end of file diff --git a/examples/multi_window_ref_app/lib/app/main_window.dart b/examples/multi_window_ref_app/lib/app/main_window.dart index f6988499f1f25..d532d62e9e4da 100644 --- a/examples/multi_window_ref_app/lib/app/main_window.dart +++ b/examples/multi_window_ref_app/lib/app/main_window.dart @@ -153,7 +153,7 @@ class _ActiveWindowsTable extends StatelessWidget { } }, cells: [ - DataCell(Text('$controller.controller.rootView.viewId')), + DataCell(Text('${controller.controller.rootView.viewId}')), DataCell( ListenableBuilder( listenable: controller.controller, diff --git a/examples/multi_window_ref_app/windows/runner/Runner.rc b/examples/multi_window_ref_app/windows/runner/Runner.rc index 909820ff45c09..d81714ff69411 100644 --- a/examples/multi_window_ref_app/windows/runner/Runner.rc +++ b/examples/multi_window_ref_app/windows/runner/Runner.rc @@ -45,6 +45,16 @@ 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 diff --git a/examples/multi_window_ref_app/windows/runner/resource.h b/examples/multi_window_ref_app/windows/runner/resource.h index 69cacf3cead96..c245ff19cb580 100644 --- a/examples/multi_window_ref_app/windows/runner/resource.h +++ b/examples/multi_window_ref_app/windows/runner/resource.h @@ -6,6 +6,7 @@ // Microsoft Visual C++ generated include file. // Used by Runner.rc // +#define IDI_APP_ICON 101 // Next default values for new objects // diff --git a/examples/multi_window_ref_app/windows/runner/resources/app_icon.ico b/examples/multi_window_ref_app/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/packages/flutter/lib/src/widgets/window_win32.dart b/packages/flutter/lib/src/widgets/window_win32.dart index ca8b0eea387d6..6b3918afa96a1 100644 --- a/packages/flutter/lib/src/widgets/window_win32.dart +++ b/packages/flutter/lib/src/widgets/window_win32.dart @@ -107,7 +107,6 @@ class RegularWindowControllerWin32 extends RegularWindowController }) : _owner = owner, _delegate = delegate, super.empty() { - owner.addMessageHandler(this); final Pointer<_WindowCreationRequest> request = ffi.calloc<_WindowCreationRequest>()..ref.contentSize.set(contentSize); final int viewId = _createWindow(PlatformDispatcher.instance.engineId!, request); @@ -116,6 +115,7 @@ class RegularWindowControllerWin32 extends RegularWindowController (FlutterView view) => view.viewId == viewId, ); setView(flutterView); + owner.addMessageHandler(this); } @override