diff --git a/.ci.yaml b/.ci.yaml index cd1ba1016f5..2feb5937ebf 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -7005,6 +7005,16 @@ targets: ["devicelab", "hostonly", "linux"] task_name: windowing_test_linux + - name: MacOS windowing_test + recipe: devicelab/devicelab_drone + presubmit: true + bringup: true + timeout: 60 + properties: + tags: > + ["devicelab", "hostonly", "macos"] + task_name: windowing_test_macos + - name: Windows flutter_tool_startup__windows recipe: devicelab/devicelab_drone diff --git a/TESTOWNERS b/TESTOWNERS index 4e619146f85..1eb191ac0f8 100644 --- a/TESTOWNERS +++ b/TESTOWNERS @@ -306,6 +306,7 @@ /dev/devicelab/bin/tasks/windows_home_scroll_perf__timeline_summary.dart @jonahwilliams @flutter/engine /dev/devicelab/bin/tasks/windows_startup_test.dart @loic-sharma @flutter/desktop /dev/devicelab/bin/tasks/windowing_test_linux.dart @robert-ancell @flutter/desktop +/dev/devicelab/bin/tasks/windowing_test_macos.dart @knopp @flutter/desktop /dev/devicelab/bin/tasks/windowing_test_windows.dart @mattkae @flutter/desktop /dev/devicelab/bin/tasks/mac_desktop_impeller.dart @jonahwilliams @flutter/engine /dev/devicelab/bin/tasks/linux_desktop_impeller.dart @jonahwilliams @flutter/engine diff --git a/dev/devicelab/bin/tasks/windowing_test_macos.dart b/dev/devicelab/bin/tasks/windowing_test_macos.dart new file mode 100644 index 00000000000..a52c8cf9d3b --- /dev/null +++ b/dev/devicelab/bin/tasks/windowing_test_macos.dart @@ -0,0 +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_devicelab/framework/devices.dart'; +import 'package:flutter_devicelab/framework/framework.dart'; +import 'package:flutter_devicelab/tasks/integration_tests.dart'; + +Future main() async { + deviceOperatingSystem = DeviceOperatingSystem.macos; + await task(createWindowingDriverTest()); +} diff --git a/dev/integration_tests/windowing_test/lib/main.dart b/dev/integration_tests/windowing_test/lib/main.dart index c40a4564ba5..e20c08d096d 100644 --- a/dev/integration_tests/windowing_test/lib/main.dart +++ b/dev/integration_tests/windowing_test/lib/main.dart @@ -8,6 +8,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/_window.dart'; import 'package:flutter_driver/driver_extension.dart'; @@ -56,6 +57,14 @@ void main() { controller.addListener(notificationHandler); act(); + + // On macOS, the predicate is immediately true, even though + // the animation is in progress and next request for state change + // will be ignored. Easiest way to handle this is to just wait. + if (defaultTargetPlatform == TargetPlatform.macOS) { + await Future.delayed(Duration(seconds: 1)); + } + await for (final _ in streamController.stream) { if (predicate()) { break; diff --git a/dev/integration_tests/windowing_test/macos/.gitignore b/dev/integration_tests/windowing_test/macos/.gitignore new file mode 100644 index 00000000000..746adbb6b9e --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/dev/integration_tests/windowing_test/macos/Flutter/Flutter-Debug.xcconfig b/dev/integration_tests/windowing_test/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 00000000000..4b81f9b2d20 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/dev/integration_tests/windowing_test/macos/Flutter/Flutter-Release.xcconfig b/dev/integration_tests/windowing_test/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 00000000000..5caa9d1579e --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/dev/integration_tests/windowing_test/macos/Podfile b/dev/integration_tests/windowing_test/macos/Podfile new file mode 100644 index 00000000000..66f6172bbb3 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Podfile @@ -0,0 +1,39 @@ +platform :osx, '10.15' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/project.pbxproj b/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000000..f45fd6898b1 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,628 @@ +// !$*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 */ + 18B7BE7286AA73E640D4546D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 23A79C077946AA5B54F579AF /* Pods_Runner.framework */; }; + 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 */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 23A79C077946AA5B54F579AF /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 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 = ""; }; + 337720BD6C32FB7FE93BBCF6 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* windowing_test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = windowing_test.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 = ""; }; + 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 = ""; }; + 47A0D2D6FF1E3F3FAC11013A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 51DCB034E431A572E7D3465E /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 8B4F1AA600E43ABB5D7F8B25 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 8BEBF1BF7D7BB8413407C4F0 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + A667FFD803107778FA1A5CF1 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + EE1DB3CE88B688DF762EB59B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 18B7BE7286AA73E640D4546D /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + D73B54801185B7255BC68518 /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* windowing_test.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 23A79C077946AA5B54F579AF /* Pods_Runner.framework */, + 51DCB034E431A572E7D3465E /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + D73B54801185B7255BC68518 /* Pods */ = { + isa = PBXGroup; + children = ( + EE1DB3CE88B688DF762EB59B /* Pods-Runner.debug.xcconfig */, + 8BEBF1BF7D7BB8413407C4F0 /* Pods-Runner.release.xcconfig */, + A667FFD803107778FA1A5CF1 /* Pods-Runner.profile.xcconfig */, + 47A0D2D6FF1E3F3FAC11013A /* Pods-RunnerTests.debug.xcconfig */, + 337720BD6C32FB7FE93BBCF6 /* Pods-RunnerTests.release.xcconfig */, + 8B4F1AA600E43ABB5D7F8B25 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 490E25A356C91E01B35C3A9E /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* windowing_test.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 = { + 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 */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + 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"; + }; + 490E25A356C91E01B35C3A9E /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + 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.15; + 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.15; + 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.15; + 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 */ + 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/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000000..18d981003d6 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000000..c811dbf0514 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/integration_tests/windowing_test/macos/Runner.xcworkspace/contents.xcworkspacedata b/dev/integration_tests/windowing_test/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000000..21a3cc14c74 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/dev/integration_tests/windowing_test/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/dev/integration_tests/windowing_test/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000000..18d981003d6 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/dev/integration_tests/windowing_test/macos/Runner/AppDelegate.swift b/dev/integration_tests/windowing_test/macos/Runner/AppDelegate.swift new file mode 100644 index 00000000000..ef951bbb999 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner/AppDelegate.swift @@ -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. + +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } + + var engine: FlutterEngine? + + override func applicationDidFinishLaunching(_ notification: Notification) { + engine = FlutterEngine(name: "project", project: nil) + engine?.run(withEntrypoint:nil) + } +} diff --git a/dev/integration_tests/windowing_test/macos/Runner/Assets.xcassets/Contents.json b/dev/integration_tests/windowing_test/macos/Runner/Assets.xcassets/Contents.json new file mode 100644 index 00000000000..73c00596a7f --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/dev/integration_tests/windowing_test/macos/Runner/Base.lproj/MainMenu.xib b/dev/integration_tests/windowing_test/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 00000000000..b60f00429f5 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/integration_tests/windowing_test/macos/Runner/Configs/AppInfo.xcconfig b/dev/integration_tests/windowing_test/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 00000000000..b20d4566925 --- /dev/null +++ b/dev/integration_tests/windowing_test/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 = windowing_test + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.windowingTest + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2025 com.example. All rights reserved. diff --git a/dev/integration_tests/windowing_test/macos/Runner/Configs/Debug.xcconfig b/dev/integration_tests/windowing_test/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 00000000000..36b0fd9464f --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/dev/integration_tests/windowing_test/macos/Runner/Configs/Release.xcconfig b/dev/integration_tests/windowing_test/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 00000000000..dff4f49561c --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/dev/integration_tests/windowing_test/macos/Runner/Configs/Warnings.xcconfig b/dev/integration_tests/windowing_test/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 00000000000..42bcbf4780b --- /dev/null +++ b/dev/integration_tests/windowing_test/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/dev/integration_tests/windowing_test/macos/Runner/DebugProfile.entitlements b/dev/integration_tests/windowing_test/macos/Runner/DebugProfile.entitlements new file mode 100644 index 00000000000..dddb8a30c85 --- /dev/null +++ b/dev/integration_tests/windowing_test/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/dev/integration_tests/windowing_test/macos/Runner/Info.plist b/dev/integration_tests/windowing_test/macos/Runner/Info.plist new file mode 100644 index 00000000000..4789daa6a44 --- /dev/null +++ b/dev/integration_tests/windowing_test/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/dev/integration_tests/windowing_test/macos/Runner/Release.entitlements b/dev/integration_tests/windowing_test/macos/Runner/Release.entitlements new file mode 100644 index 00000000000..852fa1a4728 --- /dev/null +++ b/dev/integration_tests/windowing_test/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/dev/integration_tests/windowing_test/test_driver/main_test.dart b/dev/integration_tests/windowing_test/test_driver/main_test.dart index 0a7ab93dbc3..5a0d27aab54 100644 --- a/dev/integration_tests/windowing_test/test_driver/main_test.dart +++ b/dev/integration_tests/windowing_test/test_driver/main_test.dart @@ -52,54 +52,6 @@ void main() { expect(data["height"], 600); }, timeout: Timeout.none); - test( - 'Can set constraints and see the resize', - () async { - await driver.requestData( - jsonEncode({ - 'type': 'set_constraints', - 'min_width': 0, - 'min_height': 0, - 'max_width': 500, - 'max_height': 501, - }), - ); - final response = await driver.requestData( - jsonEncode({'type': 'get_size'}), - ); - final data = jsonDecode(response); - expect(data["width"], 500); - expect(data["height"], 501); - }, - timeout: Timeout.none, - onPlatform: {'linux': Skip('Unable to exactly set dimensions on Linux')}, - ); - - test( - 'Can set constraints and see the resize (Linux)', - () async { - await driver.requestData( - jsonEncode({ - 'type': 'set_constraints', - 'min_width': 0, - 'min_height': 0, - 'max_width': 500, - 'max_height': 501, - }), - ); - final response = await driver.requestData( - jsonEncode({'type': 'get_size'}), - ); - final data = jsonDecode(response); - // On Linux setting the constraints limits the window including the decorations, - // but the returned size is the usable area and always smaller. - expect(data["width"], lessThanOrEqualTo(500)); - expect(data["height"], lessThanOrEqualTo(501)); - }, - timeout: Timeout.none, - testOn: "linux", - ); - test('Can set and get fullscreen', () async { await driver.requestData(jsonEncode({'type': 'set_fullscreen'})); var response = await driver.requestData( @@ -180,5 +132,53 @@ void main() { timeout: Timeout.none, onPlatform: {'linux': Skip('Dialogs are not yet supported on Wayland')}, ); + + test( + 'Can set constraints and see the resize', + () async { + await driver.requestData( + jsonEncode({ + 'type': 'set_constraints', + 'min_width': 0, + 'min_height': 0, + 'max_width': 500, + 'max_height': 501, + }), + ); + final response = await driver.requestData( + jsonEncode({'type': 'get_size'}), + ); + final data = jsonDecode(response); + expect(data["width"], 500); + expect(data["height"], 501); + }, + timeout: Timeout.none, + onPlatform: {'linux': Skip('Unable to exactly set dimensions on Linux')}, + ); + + test( + 'Can set constraints and see the resize (Linux)', + () async { + await driver.requestData( + jsonEncode({ + 'type': 'set_constraints', + 'min_width': 0, + 'min_height': 0, + 'max_width': 500, + 'max_height': 501, + }), + ); + final response = await driver.requestData( + jsonEncode({'type': 'get_size'}), + ); + final data = jsonDecode(response); + // On Linux setting the constraints limits the window including the decorations, + // but the returned size is the usable area and always smaller. + expect(data["width"], lessThanOrEqualTo(500)); + expect(data["height"], lessThanOrEqualTo(501)); + }, + timeout: Timeout.none, + testOn: "linux", + ); }); } 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 39681175cec..ee170d912b4 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 @@ -24,11 +24,12 @@ @end -struct FlutterWindowSizing { - bool has_size; +struct FlutterWindowSize { double width; double height; - bool has_constraints; +}; + +struct FlutterWindowConstraints { double min_width; double min_height; double max_width; @@ -36,14 +37,12 @@ struct FlutterWindowSizing { }; struct FlutterWindowCreationRequest { - FlutterWindowSizing contentSize; + bool has_size; + struct FlutterWindowSize size; + bool has_constraints; + struct FlutterWindowConstraints constraints; void (*on_close)(); - void (*on_size_change)(); -}; - -struct FlutterWindowSize { - double width; - double height; + void (*notify_listeners)(); }; extern "C" { @@ -65,7 +64,11 @@ FLUTTER_DARWIN_EXPORT FlutterWindowSize InternalFlutter_Window_GetContentSize(void* window); FLUTTER_DARWIN_EXPORT -void InternalFlutter_Window_SetContentSize(void* window, const FlutterWindowSizing* size); +void InternalFlutter_Window_SetContentSize(void* window, const FlutterWindowSize* size); + +FLUTTER_DARWIN_EXPORT +void InternalFlutter_Window_SetConstraints(void* window, + const FlutterWindowConstraints* constraints); FLUTTER_DARWIN_EXPORT void InternalFlutter_Window_SetTitle(void* window, const char* title); @@ -94,6 +97,12 @@ bool InternalFlutter_Window_IsFullScreen(void* window); FLUTTER_DARWIN_EXPORT void InternalFlutter_Window_Activate(void* window); +FLUTTER_DARWIN_EXPORT +char* InternalFlutter_Window_GetTitle(void* window); + +FLUTTER_DARWIN_EXPORT +bool InternalFlutter_Window_IsActivated(void* window); + // NOLINTEND(google-objc-function-naming) } 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 95aa3548ea5..a4e66babd9b 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 @@ -33,22 +33,31 @@ @interface NSWindow (FlutterWindowSizing) -- (void)flutterSetContentSize:(FlutterWindowSizing)contentSize; +- (void)flutterSetContentSize:(FlutterWindowSize)contentSize; +- (void)flutterSetConstraints:(FlutterWindowConstraints)constraints; @end @implementation NSWindow (FlutterWindowSizing) -- (void)flutterSetContentSize:(FlutterWindowSizing)contentSize { - if (contentSize.has_size) { - [self setContentSize:NSMakeSize(contentSize.width, contentSize.height)]; +- (void)flutterSetContentSize:(FlutterWindowSize)contentSize { + [self setContentSize:NSMakeSize(contentSize.width, contentSize.height)]; +} + +- (void)flutterSetConstraints:(FlutterWindowConstraints)constraints { + NSSize size = [self frameRectForContentRect:self.frame].size; + NSSize originalSize = size; + [self setContentMinSize:NSMakeSize(constraints.min_width, constraints.min_height)]; + size.width = std::max(size.width, constraints.min_width); + size.height = std::max(size.height, constraints.min_height); + if (constraints.max_width > 0 && constraints.max_height > 0) { + [self setContentMaxSize:NSMakeSize(constraints.max_width, constraints.max_height)]; + size.width = std::min(size.width, constraints.max_width); + size.height = std::min(size.height, constraints.max_height); + } else { + [self setContentMaxSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)]; } - if (contentSize.has_constraints) { - [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)]; - } + if (!NSEqualSizes(originalSize, size)) { + [self setContentSize:size]; } } @@ -87,21 +96,31 @@ - (void)windowDidResize:(NSNotification*)notification { flutter::IsolateScope isolate_scope(*_isolate); - _creationRequest.on_size_change(); + _creationRequest.notify_listeners(); } // Miniaturize does not trigger resize event, but for now there // is no other way to get notification about the state change. - (void)windowDidMiniaturize:(NSNotification*)notification { flutter::IsolateScope isolate_scope(*_isolate); - _creationRequest.on_size_change(); + _creationRequest.notify_listeners(); } // Deminiaturize does not trigger resize event, but for now there // is no other way to get notification about the state change. - (void)windowDidDeminiaturize:(NSNotification*)notification { flutter::IsolateScope isolate_scope(*_isolate); - _creationRequest.on_size_change(); + _creationRequest.notify_listeners(); +} + +- (void)windowWillEnterFullScreen:(NSNotification*)notification { + flutter::IsolateScope isolate_scope(*_isolate); + _creationRequest.notify_listeners(); +} + +- (void)windowWillExitFullScreen:(NSNotification*)notification { + flutter::IsolateScope isolate_scope(*_isolate); + _creationRequest.notify_listeners(); } @end @@ -135,7 +154,12 @@ window.contentViewController = c; window.styleMask = NSWindowStyleMaskResizable | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable; - [window flutterSetContentSize:request->contentSize]; + if (request->has_size) { + [window flutterSetContentSize:request->size]; + } + if (request->has_constraints) { + [window flutterSetConstraints:request->constraints]; + } [window setIsVisible:YES]; [window makeKeyAndOrderFront:nil]; @@ -200,17 +224,25 @@ void* InternalFlutter_Window_GetHandle(int64_t engine_id, FlutterViewIdentifier FlutterWindowSize InternalFlutter_Window_GetContentSize(void* window) { NSWindow* w = (__bridge NSWindow*)window; + NSRect contentRect = [w contentRectForFrameRect:w.frame]; return { - .width = w.frame.size.width, - .height = w.frame.size.height, + .width = contentRect.size.width, + .height = contentRect.size.height, }; } -void InternalFlutter_Window_SetContentSize(void* window, const FlutterWindowSizing* size) { +void InternalFlutter_Window_SetContentSize(void* window, const FlutterWindowSize* size) { NSWindow* w = (__bridge NSWindow*)window; [w flutterSetContentSize:*size]; } +FLUTTER_DARWIN_EXPORT +void InternalFlutter_Window_SetConstraints(void* window, + const FlutterWindowConstraints* constraints) { + NSWindow* w = (__bridge NSWindow*)window; + [w flutterSetConstraints:*constraints]; +} + void InternalFlutter_Window_SetTitle(void* window, const char* title) { NSWindow* w = (__bridge NSWindow*)window; w.title = [NSString stringWithUTF8String:title]; @@ -266,4 +298,14 @@ void InternalFlutter_Window_Activate(void* window) { [w makeKeyAndOrderFront:nil]; } +char* InternalFlutter_Window_GetTitle(void* window) { + NSWindow* w = (__bridge NSWindow*)window; + return strdup(w.title.UTF8String); +} + +bool InternalFlutter_Window_IsActivated(void* window) { + NSWindow* w = (__bridge NSWindow*)window; + return w.isKeyWindow; +} + // NOLINTEND(google-objc-function-naming) 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 36068f950f8..ab10904092d 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 @@ -57,9 +57,10 @@ class FlutterWindowControllerRetainTest : public ::testing::Test {}; TEST_F(FlutterWindowControllerTest, CreateRegularWindow) { FlutterWindowCreationRequest request{ - .contentSize = {.has_size = true, .width = 800, .height = 600}, + .has_size = true, + .size = {.width = 800, .height = 600}, .on_close = [] {}, - .on_size_change = [] {}, + .notify_listeners = [] {}, }; FlutterEngine* engine = GetFlutterEngine(); @@ -80,9 +81,10 @@ TEST_F(FlutterWindowControllerTest, CreateRegularWindow) { TEST_F(FlutterWindowControllerRetainTest, WindowControllerDoesNotRetainEngine) { FlutterWindowCreationRequest request{ - .contentSize = {.has_size = true, .width = 800, .height = 600}, + .has_size = true, + .size = {.width = 800, .height = 600}, .on_close = [] {}, - .on_size_change = [] {}, + .notify_listeners = [] {}, }; __weak FlutterEngine* weakEngine = nil; @@ -121,9 +123,10 @@ TEST_F(FlutterWindowControllerRetainTest, WindowControllerDoesNotRetainEngine) { TEST_F(FlutterWindowControllerTest, DestroyRegularWindow) { FlutterWindowCreationRequest request{ - .contentSize = {.has_size = true, .width = 800, .height = 600}, + .has_size = true, + .size = {.width = 800, .height = 600}, .on_close = [] {}, - .on_size_change = [] {}, + .notify_listeners = [] {}, }; FlutterEngine* engine = GetFlutterEngine(); @@ -140,9 +143,10 @@ TEST_F(FlutterWindowControllerTest, DestroyRegularWindow) { TEST_F(FlutterWindowControllerTest, InternalFlutter_Window_GetHandle) { FlutterWindowCreationRequest request{ - .contentSize = {.has_size = true, .width = 800, .height = 600}, + .has_size = true, + .size = {.width = 800, .height = 600}, .on_close = [] {}, - .on_size_change = [] {}, + .notify_listeners = [] {}, }; FlutterEngine* engine = GetFlutterEngine(); @@ -158,9 +162,10 @@ TEST_F(FlutterWindowControllerTest, InternalFlutter_Window_GetHandle) { TEST_F(FlutterWindowControllerTest, WindowStates) { FlutterWindowCreationRequest request{ - .contentSize = {.has_size = true, .width = 800, .height = 600}, + .has_size = true, + .size = {.width = 800, .height = 600}, .on_close = [] {}, - .on_size_change = [] {}, + .notify_listeners = [] {}, }; FlutterEngine* engine = GetFlutterEngine(); diff --git a/examples/multiple_windows/macos/Flutter/Flutter-Debug.xcconfig b/examples/multiple_windows/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 00000000000..c2efd0b608b --- /dev/null +++ b/examples/multiple_windows/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/examples/multiple_windows/macos/Flutter/Flutter-Release.xcconfig b/examples/multiple_windows/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 00000000000..c2efd0b608b --- /dev/null +++ b/examples/multiple_windows/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/examples/multiple_windows/macos/Runner.xcodeproj/project.pbxproj b/examples/multiple_windows/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000000..ded159e9d84 --- /dev/null +++ b/examples/multiple_windows/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,589 @@ +// !$*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 */ + 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 */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 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 /* multiple_windows.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = multiple_windows.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 = ""; }; + 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 */ + 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 /* multiple_windows.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 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 */ + 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 /* multiple_windows.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 = { + 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 */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + 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 */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + 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.15; + 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.15; + 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.15; + 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 */ + 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/multiple_windows/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/multiple_windows/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000000..18d981003d6 --- /dev/null +++ b/examples/multiple_windows/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/examples/multiple_windows/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/examples/multiple_windows/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000000..a2c5980daec --- /dev/null +++ b/examples/multiple_windows/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/multiple_windows/macos/Runner.xcworkspace/contents.xcworkspacedata b/examples/multiple_windows/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000000..1d526a16ed0 --- /dev/null +++ b/examples/multiple_windows/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/multiple_windows/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/multiple_windows/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000000..18d981003d6 --- /dev/null +++ b/examples/multiple_windows/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/examples/multiple_windows/macos/Runner/AppDelegate.swift b/examples/multiple_windows/macos/Runner/AppDelegate.swift new file mode 100644 index 00000000000..ef951bbb999 --- /dev/null +++ b/examples/multiple_windows/macos/Runner/AppDelegate.swift @@ -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. + +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } + + var engine: FlutterEngine? + + override func applicationDidFinishLaunching(_ notification: Notification) { + engine = FlutterEngine(name: "project", project: nil) + engine?.run(withEntrypoint:nil) + } +} diff --git a/examples/multiple_windows/macos/Runner/Assets.xcassets/Contents.json b/examples/multiple_windows/macos/Runner/Assets.xcassets/Contents.json new file mode 100644 index 00000000000..73c00596a7f --- /dev/null +++ b/examples/multiple_windows/macos/Runner/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/examples/multiple_windows/macos/Runner/Base.lproj/MainMenu.xib b/examples/multiple_windows/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 00000000000..d374b6b86f6 --- /dev/null +++ b/examples/multiple_windows/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/multiple_windows/macos/Runner/Configs/AppInfo.xcconfig b/examples/multiple_windows/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 00000000000..7a4115b2371 --- /dev/null +++ b/examples/multiple_windows/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 = multiple_windows + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.multipleWindows + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2025 com.example. All rights reserved. diff --git a/examples/multiple_windows/macos/Runner/Configs/Debug.xcconfig b/examples/multiple_windows/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 00000000000..36b0fd9464f --- /dev/null +++ b/examples/multiple_windows/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/examples/multiple_windows/macos/Runner/Configs/Release.xcconfig b/examples/multiple_windows/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 00000000000..dff4f49561c --- /dev/null +++ b/examples/multiple_windows/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/examples/multiple_windows/macos/Runner/Configs/Warnings.xcconfig b/examples/multiple_windows/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 00000000000..42bcbf4780b --- /dev/null +++ b/examples/multiple_windows/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/multiple_windows/macos/Runner/DebugProfile.entitlements b/examples/multiple_windows/macos/Runner/DebugProfile.entitlements new file mode 100644 index 00000000000..dddb8a30c85 --- /dev/null +++ b/examples/multiple_windows/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/multiple_windows/macos/Runner/Info.plist b/examples/multiple_windows/macos/Runner/Info.plist new file mode 100644 index 00000000000..4789daa6a44 --- /dev/null +++ b/examples/multiple_windows/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/multiple_windows/macos/Runner/Release.entitlements b/examples/multiple_windows/macos/Runner/Release.entitlements new file mode 100644 index 00000000000..852fa1a4728 --- /dev/null +++ b/examples/multiple_windows/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/packages/flutter/lib/src/widgets/_window_io.dart b/packages/flutter/lib/src/widgets/_window_io.dart index 424f88c106e..e86f084bd91 100644 --- a/packages/flutter/lib/src/widgets/_window_io.dart +++ b/packages/flutter/lib/src/widgets/_window_io.dart @@ -20,6 +20,7 @@ import 'package:flutter/foundation.dart'; import '_window.dart'; import '_window_linux.dart'; +import '_window_macos.dart'; import '_window_win32.dart'; /// Creates a default [WindowingOwner] for the current platform. @@ -34,6 +35,8 @@ WindowingOwner? createDefaultOwner() { return WindowingOwnerWin32(); } else if (Platform.isLinux) { return WindowingOwnerLinux(); + } else if (Platform.isMacOS) { + return WindowingOwnerMacOS(); } else { return null; } 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 00000000000..16c4dac015b --- /dev/null +++ b/packages/flutter/lib/src/widgets/_window_macos.dart @@ -0,0 +1,566 @@ +// 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:convert' show utf8; +import 'dart:ffi' hide Size; +import 'dart:io'; +import 'dart:ui' show Display, FlutterView; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/rendering.dart'; + +import '../foundation/_features.dart'; +import '_window.dart'; +import 'binding.dart'; + +// Do not import this file in production applications or packages published +// to pub.dev. Flutter will make breaking changes to this file, even in patch +// versions. +// +// All APIs in this file must be private or must: +// +// 1. Have the `@internal` attribute. +// 2. Throw an `UnsupportedError` if `isWindowingEnabled` +// is `false`. +// +// See: https://github.com/flutter/flutter/issues/30701. + +const String _kWindowingDisabledErrorMessage = ''' +Windowing APIs are not enabled. + +Windowing APIs are currently experimental. Do not use windowing APIs in +production applications or plugins published to pub.dev. + +To try experimental windowing APIs: +1. Switch to Flutter's main release channel. +2. Turn on the windowing feature flag. + +See: https://github.com/flutter/flutter/issues/30701. +'''; + +/// [WindowingOwner] implementation for macOS. +/// +/// If [Platform.isMacOS] is false, then the constructor will throw an +/// [UnsupportedError]. +/// +/// {@macro flutter.widgets.windowing.experimental} +/// +/// See also: +/// +/// * [WindowingOwner], the abstract class that manages native windows. +class WindowingOwnerMacOS extends WindowingOwner { + /// Creates a new [WindowingOwnerMacOS] instance. + /// + /// {@macro flutter.widgets.windowing.experimental} + /// + /// See also: + /// + /// * [WindowingOwner], the abstract class that manages native windows. + @internal + WindowingOwnerMacOS() { + if (!isWindowingEnabled) { + throw UnsupportedError(_kWindowingDisabledErrorMessage); + } + + if (!Platform.isMacOS) { + throw UnsupportedError('Only available on the macOS platform'); + } + + assert( + PlatformDispatcher.instance.engineId != null, + 'WindowingOwnerMacOS must be created after the engine has been initialized.', + ); + } + + @override + RegularWindowController createRegularWindowController({ + required RegularWindowControllerDelegate delegate, + Size? preferredSize, + BoxConstraints? preferredConstraints, + String? title, + }) { + final RegularWindowControllerMacOS res = RegularWindowControllerMacOS( + owner: this, + delegate: delegate, + preferredSize: preferredSize, + title: title, + ); + _activeControllers.add(res); + return res; + } + + @override + bool hasTopLevelWindows() { + return _activeControllers.isNotEmpty; + } + + 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 the NSWindow instance. + static Pointer getWindowHandle(FlutterView view) { + return _MacOSPlatformInterface.getWindowHandle( + PlatformDispatcher.instance.engineId!, + view.viewId, + ); + } + + @override + DialogWindowController createDialogWindowController({ + required DialogWindowControllerDelegate delegate, + Size? preferredSize, + BoxConstraints? preferredConstraints, + BaseWindowController? parent, + String? title, + }) { + throw UnimplementedError(); + } +} + +/// Implementation of [RegularWindowController] for the macOS platform. +/// +/// {@macro flutter.widgets.windowing.experimental} +/// +/// See also: +/// +/// * [RegularWindowController], the base class for regular windows. +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, + required Size? preferredSize, + BoxConstraints? preferredConstraints, + String? title, + }) : _owner = owner, + _delegate = delegate, + super.empty() { + if (!isWindowingEnabled) { + throw UnsupportedError(_kWindowingDisabledErrorMessage); + } + + _onClose = NativeCallable.isolateLocal(_handleOnClose); + _onResize = NativeCallable.isolateLocal(_handleOnResize); + final int viewId = _MacOSPlatformInterface.createWindow( + preferredSize: preferredSize, + preferredConstraints: preferredConstraints, + onClose: _onClose.nativeFunction, + onNotifyListeners: _onResize.nativeFunction, + ); + final FlutterView flutterView = WidgetsBinding.instance.platformDispatcher.views.firstWhere( + (FlutterView view) => view.viewId == viewId, + ); + rootView = flutterView; + if (title != null) { + setTitle(title); + } + } + + /// Returns window handle for the current window. + /// + /// The handle is a pointer to an NSWindow instance. + Pointer getWindowHandle() { + _ensureNotDestroyed(); + return WindowingOwnerMacOS.getWindowHandle(rootView); + } + + bool _destroyed = false; + + @override + void destroy() { + if (_destroyed) { + return; + } + final Pointer handle = getWindowHandle(); + _destroyed = true; + _owner._activeControllers.remove(this); + _MacOSPlatformInterface.destroyWindow(handle); + _delegate.onWindowDestroyed(); + _onClose.close(); + _onResize.close(); + } + + void _handleOnClose() { + _delegate.onWindowCloseRequested(this); + } + + void _handleOnResize() { + notifyListeners(); + } + + @override + @internal + void setSize(Size size) { + _ensureNotDestroyed(); + _MacOSPlatformInterface.setWindowContentSize(getWindowHandle(), size); + } + + @override + @internal + void setConstraints(BoxConstraints constraints) { + _ensureNotDestroyed(); + _MacOSPlatformInterface.setWindowConstraints(getWindowHandle(), constraints); + } + + @override + void setTitle(String title) { + _ensureNotDestroyed(); + _MacOSPlatformInterface.setWindowTitle(getWindowHandle(), title); + notifyListeners(); + } + + final WindowingOwnerMacOS _owner; + final RegularWindowControllerDelegate _delegate; + late final NativeCallable _onClose; + late final NativeCallable _onResize; + + @override + Size get contentSize { + _ensureNotDestroyed(); + return _MacOSPlatformInterface.getWindowContentSize(getWindowHandle()); + } + + @override + void activate() { + _ensureNotDestroyed(); + _MacOSPlatformInterface.activate(getWindowHandle()); + } + + @override + void setMaximized(bool maximized) { + _ensureNotDestroyed(); + _MacOSPlatformInterface.setMaximized(getWindowHandle(), maximized); + } + + @override + bool get isMaximized { + _ensureNotDestroyed(); + return _MacOSPlatformInterface.isMaximized(getWindowHandle()); + } + + @override + void setMinimized(bool minimized) { + _ensureNotDestroyed(); + if (minimized) { + _MacOSPlatformInterface.minimize(getWindowHandle()); + } else { + _MacOSPlatformInterface.unminimize(getWindowHandle()); + } + } + + @override + bool get isMinimized { + _ensureNotDestroyed(); + return _MacOSPlatformInterface.isMinimized(getWindowHandle()); + } + + @override + void setFullscreen(bool fullscreen, {Display? display}) { + _ensureNotDestroyed(); + _MacOSPlatformInterface.setFullscreen(getWindowHandle(), fullscreen); + } + + @override + bool get isFullscreen { + _ensureNotDestroyed(); + return _MacOSPlatformInterface.isFullscreen(getWindowHandle()); + } + + void _ensureNotDestroyed() { + if (_destroyed) { + throw StateError('Window has been destroyed.'); + } + } + + @override + bool get isActivated => _MacOSPlatformInterface.isActivated(getWindowHandle()); + + @override + String get title => _MacOSPlatformInterface.getTitle(getWindowHandle()); +} + +final class _WindowCreationRequest extends Struct { + @Bool() + external bool hasSize; + external _Size contentSize; + + @Bool() + external bool hasConstraints; + external _Constraints constraints; + + external Pointer> onClose; + external Pointer> onNotifyListeners; +} + +final class _Size extends Struct { + @Double() + external double width; + + @Double() + external double height; +} + +final class _Constraints extends Struct { + @Double() + external double minWidth; + + @Double() + external double minHeight; + + @Double() + external double maxWidth; + + @Double() + external double maxHeight; +} + +class _MacOSPlatformInterface { + @Native Function(Int64, Int64)>(symbol: 'InternalFlutter_Window_GetHandle') + external static Pointer getWindowHandle(int engineId, int viewId); + + @Native, Pointer<_Size>)>( + symbol: 'InternalFlutter_Window_SetContentSize', + ) + external static void _setWindowContentSize(Pointer windowHandle, Pointer<_Size> size); + + static void setWindowContentSize(Pointer windowHandle, Size size) { + final Pointer<_Size> ffiSize = _allocator<_Size>(); + ffiSize.ref + ..width = size.width + ..height = size.height; + _setWindowContentSize(windowHandle, ffiSize); + _allocator.free(ffiSize); + } + + @Native, Pointer<_Constraints>)>( + symbol: 'InternalFlutter_Window_SetConstraints', + ) + external static void _setWindowConstraints( + Pointer windowHandle, + Pointer<_Constraints> size, + ); + + static void setWindowConstraints(Pointer windowHandle, BoxConstraints constraints) { + final Pointer<_Constraints> ffiConstraints = _allocator<_Constraints>(); + ffiConstraints.ref + ..minWidth = constraints.minWidth + ..minHeight = constraints.minHeight + ..maxWidth = constraints.maxWidth + ..maxHeight = constraints.maxHeight; + + _setWindowConstraints(windowHandle, ffiConstraints); + _allocator.free(ffiConstraints); + } + + @Native)>( + symbol: 'InternalFlutter_WindowController_CreateRegularWindow', + ) + external static int _createWindow(int engineId, Pointer<_WindowCreationRequest> request); + + /// Creates a new window and returns the viewId of the created FlutterView. + static int createWindow({ + required Size? preferredSize, + BoxConstraints? preferredConstraints, + required Pointer> onClose, + required Pointer> onNotifyListeners, + }) { + final Pointer<_WindowCreationRequest> request = _allocator<_WindowCreationRequest>() + ..ref.onClose = onClose + ..ref.onNotifyListeners = onNotifyListeners; + + if (preferredSize != null) { + request.ref + ..hasSize = true + ..contentSize.width = preferredSize.width + ..contentSize.height = preferredSize.height; + } + + if (preferredConstraints != null) { + request.ref + ..hasConstraints = true + ..constraints.minWidth = preferredConstraints.minWidth + ..constraints.minHeight = preferredConstraints.minHeight + ..constraints.maxWidth = preferredConstraints.maxWidth + ..constraints.maxHeight = preferredConstraints.maxHeight; + } + final int viewId = _createWindow(PlatformDispatcher.instance.engineId!, request); + _allocator.free(request); + return viewId; + } + + @Native)>(symbol: 'InternalFlutter_Window_Destroy') + external static void _destroyWindow(int engineId, Pointer handle); + + static void destroyWindow(Pointer windowHandle) { + _destroyWindow(PlatformDispatcher.instance.engineId!, windowHandle); + } + + @Native<_Size Function(Pointer)>(symbol: 'InternalFlutter_Window_GetContentSize') + external static _Size _getWindowContentSize(Pointer windowHandle); + + static Size getWindowContentSize(Pointer windowHandle) { + final _Size size = _getWindowContentSize(windowHandle); + return Size(size.width, size.height); + } + + @Native, Pointer<_Utf8>)>(symbol: 'InternalFlutter_Window_SetTitle') + external static void _setWindowTitle(Pointer windowHandle, Pointer<_Utf8> title); + + static void setWindowTitle(Pointer windowHandle, String title) { + final Pointer<_Utf8> titlePointer = title.toNativeUtf8(); + _setWindowTitle(windowHandle, titlePointer); + _allocator.free(titlePointer); + } + + @Native, Bool)>(symbol: 'InternalFlutter_Window_SetMaximized') + external static void setMaximized(Pointer windowHandle, bool maximized); + + @Native)>(symbol: 'InternalFlutter_Window_IsMaximized') + external static bool isMaximized(Pointer windowHandle); + + @Native)>(symbol: 'InternalFlutter_Window_Minimize') + external static void minimize(Pointer windowHandle); + + @Native)>(symbol: 'InternalFlutter_Window_Unminimize') + external static void unminimize(Pointer windowHandle); + + @Native)>(symbol: 'InternalFlutter_Window_IsMinimized') + external static bool isMinimized(Pointer windowHandle); + + @Native, Bool)>(symbol: 'InternalFlutter_Window_SetFullScreen') + external static void setFullscreen(Pointer windowHandle, bool fullscreen); + + @Native)>(symbol: 'InternalFlutter_Window_IsFullScreen') + external static bool isFullscreen(Pointer windowHandle); + + @Native)>(symbol: 'InternalFlutter_Window_Activate') + external static void activate(Pointer windowHandle); + + @Native Function(Pointer)>(symbol: 'InternalFlutter_Window_GetTitle') + external static Pointer<_Utf8> _getTitle(Pointer windowHandle); + + static String getTitle(Pointer windowHandle) { + final Pointer<_Utf8> title = _getTitle(windowHandle); + final String result = title.toDartString(); + _allocator.free(title); + return result; + } + + @Native)>(symbol: 'InternalFlutter_Window_IsActivated') + external static bool isActivated(Pointer windowHandle); +} + +// FFI utilities. + +typedef _PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); + +@Native<_PosixCallocNative>(symbol: 'calloc') +external Pointer _posixCalloc(int num, int size); + +typedef _PosixFreeNative = Void Function(Pointer); + +@Native)>(symbol: 'free') +external void _posixFree(Pointer ptr); + +final Pointer> _posixFreePointer = + Native.addressOf>(_posixFree); + +const _CallocAllocator _allocator = _CallocAllocator._(); + +final class _CallocAllocator implements Allocator { + const _CallocAllocator._(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + @override + Pointer allocate(int byteCount, {int? alignment}) { + final Pointer result = _posixCalloc(byteCount, 1).cast(); + + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + @override + void free(Pointer pointer) { + _posixFree(pointer); + } + + /// Returns a pointer to a native free function. + Pointer get nativeFree => _posixFreePointer; +} + +/// The contents of a native zero-terminated array of UTF-8 code units. +/// +/// The Utf8 type itself has no functionality, it's only intended to be used +/// through a `Pointer` representing the entire array. This pointer is +/// the equivalent of a char pointer (`const char*`) in C code. +final class _Utf8 extends Opaque {} + +/// Extension method for converting a`Pointer` to a [String]. +extension _Utf8Pointer on Pointer<_Utf8> { + /// Converts this UTF-8 encoded string to a Dart string. + /// + /// Decodes the UTF-8 code units of this zero-terminated byte array as + /// Unicode code points and creates a Dart string containing those code + /// points. + /// + /// If [length] is provided, zero-termination is ignored and the result can + /// contain NUL characters. + /// + /// If [length] is not provided, the returned string is the string up til + /// but not including the first NUL character. + String toDartString({int? length}) { + _ensureNotNullptr('toDartString'); + final Pointer codeUnits = cast(); + if (length != null) { + RangeError.checkNotNegative(length, 'length'); + } else { + length = _length(codeUnits); + } + return utf8.decode(codeUnits.asTypedList(length)); + } + + static int _length(Pointer codeUnits) { + int length = 0; + while (codeUnits[length] != 0) { + length++; + } + return length; + } + + void _ensureNotNullptr(String operation) { + if (this == nullptr) { + throw UnsupportedError("Operation '$operation' not allowed on a 'nullptr'."); + } + } +} + +/// Extension method for converting a [String] to a `Pointer`. +extension _StringUtf8Pointer on String { + /// Creates a zero-terminated [_Utf8] code-unit array from this String. + /// + /// If this [String] contains NUL characters, converting it back to a string + /// using [_Utf8Pointer.toDartString] will truncate the result if a length is + /// not passed. + /// + /// Unpaired surrogate code points in this [String] will be encoded as + /// replacement characters (U+FFFD, encoded as the bytes 0xEF 0xBF 0xBD) in + /// the UTF-8 encoded result. See [Utf8Encoder] for details on encoding. + /// + /// Returns an [allocator]-allocated pointer to the result. + Pointer<_Utf8> toNativeUtf8({Allocator allocator = _allocator}) { + final Uint8List units = utf8.encode(this); + final Pointer result = allocator(units.length + 1); + final Uint8List nativeString = result.asTypedList(units.length + 1); + nativeString.setAll(0, units); + nativeString[units.length] = 0; + return result.cast(); + } +}