From 2825131b34c74537d5a779040a4fc66bd0cbaad5 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Mon, 8 Feb 2021 13:01:54 -0800 Subject: [PATCH] Added sample code for multiple flutters on iOS (#670) --- add_to_app/README.md | 3 + add_to_app/multiple_flutters/README.md | 80 +++ .../multiple_flutters_ios/.gitignore | 6 + .../project.pbxproj | 457 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../MultipleFluttersIos/AppDelegate.swift | 47 ++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 98 ++++ .../Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 25 + .../Base.lproj/Main.storyboard | 116 +++++ .../MultipleFluttersIos/DataModel.swift | 56 +++ .../DoubleFlutterViewController.swift | 29 ++ .../HostViewController.swift | 43 ++ .../MultipleFluttersIos/Info.plist | 66 +++ .../MultipleFluttersIos/SceneDelegate.swift | 32 ++ .../SingleFlutterViewController.swift | 57 +++ .../multiple_flutters_ios/Podfile | 11 + .../multiple_flutters_ios/Podfile.lock | 28 ++ .../multiple_flutters_ios/README.md | 17 + .../multiple_flutters_module/.gitignore | 48 ++ .../multiple_flutters_module/.metadata | 10 + .../multiple_flutters_module/README.md | 5 + .../multiple_flutters_module/lib/main.dart | 98 ++++ .../multiple_flutters_module/pubspec.lock | 153 ++++++ .../multiple_flutters_module/pubspec.yaml | 25 + tool/flutter_ci_script_dev.sh | 6 +- tool/ios_ci_script.sh | 23 + 31 files changed, 1588 insertions(+), 1 deletion(-) create mode 100644 add_to_app/multiple_flutters/README.md create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/.gitignore create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcodeproj/project.pbxproj create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcworkspace/contents.xcworkspacedata create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/AppDelegate.swift create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Assets.xcassets/Contents.json create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Base.lproj/LaunchScreen.storyboard create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Base.lproj/Main.storyboard create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/DataModel.swift create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/DoubleFlutterViewController.swift create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/HostViewController.swift create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Info.plist create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/SceneDelegate.swift create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/SingleFlutterViewController.swift create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/Podfile create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/Podfile.lock create mode 100644 add_to_app/multiple_flutters/multiple_flutters_ios/README.md create mode 100644 add_to_app/multiple_flutters/multiple_flutters_module/.gitignore create mode 100644 add_to_app/multiple_flutters/multiple_flutters_module/.metadata create mode 100644 add_to_app/multiple_flutters/multiple_flutters_module/README.md create mode 100644 add_to_app/multiple_flutters/multiple_flutters_module/lib/main.dart create mode 100644 add_to_app/multiple_flutters/multiple_flutters_module/pubspec.lock create mode 100644 add_to_app/multiple_flutters/multiple_flutters_module/pubspec.yaml diff --git a/add_to_app/README.md b/add_to_app/README.md index bc5dc9763..4ec9bd0f1 100644 --- a/add_to_app/README.md +++ b/add_to_app/README.md @@ -17,6 +17,9 @@ existing Android and iOS apps. existing Android app and demonstrates using [Pigeon](https://pub.dev/packages/pigeon) to communicate between Flutter and the host application. +* ['multiple_flutters'](./multiple_flutters) — Shows the usage of the Flutter + Engine Group APIs to embed multiple instances of Flutter into an existing app + with low memory cost. ## Goals for these samples diff --git a/add_to_app/multiple_flutters/README.md b/add_to_app/multiple_flutters/README.md new file mode 100644 index 000000000..82a478758 --- /dev/null +++ b/add_to_app/multiple_flutters/README.md @@ -0,0 +1,80 @@ +# multiple_flutters + +This is a sample that shows how to use the Flutter Engine Groups API to embed +multiple instances of Flutter into an existing Android or iOS project. + +## Getting Started + +For iOS instructions, see: +[multiple_flutters_ios](./multiple_flutters_ios/README.md). + +For Android instructions, see: +[multiple_flutters_android](./multiple_flutters_android/README.md). + +## Requirements + +* Flutter -- after version 1991216543eb74e07c2de3746ca07c92071b19ac +* Android + * Android Studio +* iOS + * Xcode + * Cocoapods + +## Flutter Engine Group + +These examples use the Flutter Engine Group APIs on the host platform which +allows engines to share memory and CPU intensive resources. This leads to easier +embedding of Flutter into an existing app since multiple entrypoints can be +maintained via a FlutterFragment on Android or a UIViewController on iOS. +Before FlutterEngineGroup, users had to juggle the usage of a small number of +engines judiciously. + +More info on those API's can be found in the source +code: + +- iOS - + [FlutterEngineGroup.h](https://github.com/flutter/engine/blob/master/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h) +- Android - + [FlutterEngineGroup.java](https://github.com/flutter/engine/blob/master/shell/platform/android/io/flutter/embedding/engine/FlutterEngineGroup.java) + +## Important Files + +### iOS + +- [DataModel.swift](./multiple_flutters_ios/MultipleFluttersIos/HostViewController.swift) + — Observable data model that is the source of truth for the host platform and Flutter. +- [HostViewController.swift](./multiple_flutters_ios/MultipleFluttersIos/HostViewController.swift) + — A UIViewController that synchronizes to the DataModel. +- [main.dart](./multiple_flutters_module/lib/main.dart) — The Flutter source + code. +- [SingleFlutterViewController.swift](./multiple_flutters_ios/MultipleFluttersIos/SingleFlutterViewController.swift) + — A subclass of FlutterViewController that synchronizes with the DataModel. +- [DoubleFlutterViewController.swift](./multiple_flutters_ios/MultipleFluttersIos/DoubleFlutterViewController.swift) + — A UIViewController that embeds multiple Flutter instances. + +### Android + +## Data Synchronization Description + +This sample code performs data synchronization to share data between the host +platform and multiple instances of Flutter by combining the +[Observer design pattern](https://en.wikipedia.org/wiki/Observer_pattern) and +[Flutter platform channels](https://flutter.dev/docs/development/platform-integration/platform-channels). +Here is how it works: + +- The definitive source of truth for the data lives in the host platform data + model. +- Every host view displaying Flutter content maintains: a Flutter engine, a + bidirectional platform channel, and a subscription to the host data model. +- Flutter instances maintain a copy of the data they are interested in reading, + and this data is seeded by the host when the instance is first displayed. +- Mutations from Flutter code are sent to the host platform via the channel. The + host platform performs the mutations, and then notifies all host view + controllers and Flutter engines of the new value. +- Mutations from host code happen directly on the data model who notifies host + view controllers and Flutter engines of the new value. + +This is just one possible way to synchronize the data between the host platform +and multiple Flutter instances. A more complete implementation is proposed in +the work of +[flutter/issues/72030](https://github.com/flutter/flutter/issues/72030). diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/.gitignore b/add_to_app/multiple_flutters/multiple_flutters_ios/.gitignore new file mode 100644 index 000000000..10e3debe6 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/.gitignore @@ -0,0 +1,6 @@ +MultipleFluttersIos.xcworkspace/xcuserdata +MultipleFluttersIos.xcodeproj/project.xcworkspace/xcuserdata +Pods/Pods.xcodeproj/xcuserdata +MultipleFluttersIos.xcodeproj/xcuserdata +Pods/ +MultipleFluttersIos.xcodeproj/xcshareddata/ \ No newline at end of file diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcodeproj/project.pbxproj b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcodeproj/project.pbxproj new file mode 100644 index 000000000..ac1f8084c --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcodeproj/project.pbxproj @@ -0,0 +1,457 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 0D61B1E225BB8A2300A1A590 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D61B1E125BB8A2300A1A590 /* AppDelegate.swift */; }; + 0D61B1E425BB8A2300A1A590 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D61B1E325BB8A2300A1A590 /* SceneDelegate.swift */; }; + 0D61B1E625BB8A2300A1A590 /* HostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D61B1E525BB8A2300A1A590 /* HostViewController.swift */; }; + 0D61B1E925BB8A2300A1A590 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0D61B1E725BB8A2300A1A590 /* Main.storyboard */; }; + 0D61B1EB25BB8A2400A1A590 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0D61B1EA25BB8A2400A1A590 /* Assets.xcassets */; }; + 0D61B1EE25BB8A2400A1A590 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0D61B1EC25BB8A2400A1A590 /* LaunchScreen.storyboard */; }; + 0D61B20125BB8FDF00A1A590 /* DataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D61B20025BB8FDF00A1A590 /* DataModel.swift */; }; + 0D61B20825BBEA9E00A1A590 /* SingleFlutterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D61B20725BBEA9E00A1A590 /* SingleFlutterViewController.swift */; }; + 0D61B20B25BBF88700A1A590 /* DoubleFlutterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D61B20A25BBF88700A1A590 /* DoubleFlutterViewController.swift */; }; + DA84F1C17CFA00606F7B401C /* Pods_MultipleFluttersIos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E03DC90CED28B2143B2CFB1 /* Pods_MultipleFluttersIos.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0D61B1DE25BB8A2300A1A590 /* MultipleFluttersIos.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MultipleFluttersIos.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 0D61B1E125BB8A2300A1A590 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 0D61B1E325BB8A2300A1A590 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 0D61B1E525BB8A2300A1A590 /* HostViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostViewController.swift; sourceTree = ""; }; + 0D61B1E825BB8A2300A1A590 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 0D61B1EA25BB8A2400A1A590 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 0D61B1ED25BB8A2400A1A590 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 0D61B1EF25BB8A2400A1A590 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0D61B20025BB8FDF00A1A590 /* DataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataModel.swift; sourceTree = ""; }; + 0D61B20725BBEA9E00A1A590 /* SingleFlutterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleFlutterViewController.swift; sourceTree = ""; }; + 0D61B20A25BBF88700A1A590 /* DoubleFlutterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoubleFlutterViewController.swift; sourceTree = ""; }; + 4E03DC90CED28B2143B2CFB1 /* Pods_MultipleFluttersIos.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MultipleFluttersIos.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4F02FEA4EA8C75406BC5FFC5 /* Pods-MultipleFluttersIos.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MultipleFluttersIos.release.xcconfig"; path = "Target Support Files/Pods-MultipleFluttersIos/Pods-MultipleFluttersIos.release.xcconfig"; sourceTree = ""; }; + 5EE6997543EAE7F85C674231 /* Pods-MultipleFluttersIos.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MultipleFluttersIos.debug.xcconfig"; path = "Target Support Files/Pods-MultipleFluttersIos/Pods-MultipleFluttersIos.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0D61B1DB25BB8A2300A1A590 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DA84F1C17CFA00606F7B401C /* Pods_MultipleFluttersIos.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0D61B1D525BB8A2300A1A590 = { + isa = PBXGroup; + children = ( + 0D61B1E025BB8A2300A1A590 /* MultipleFluttersIos */, + 0D61B1DF25BB8A2300A1A590 /* Products */, + 2AB119CC8909334B007F8D1F /* Pods */, + A2F4F0BD46437369E87BA62F /* Frameworks */, + ); + sourceTree = ""; + }; + 0D61B1DF25BB8A2300A1A590 /* Products */ = { + isa = PBXGroup; + children = ( + 0D61B1DE25BB8A2300A1A590 /* MultipleFluttersIos.app */, + ); + name = Products; + sourceTree = ""; + }; + 0D61B1E025BB8A2300A1A590 /* MultipleFluttersIos */ = { + isa = PBXGroup; + children = ( + 0D61B1E125BB8A2300A1A590 /* AppDelegate.swift */, + 0D61B1E325BB8A2300A1A590 /* SceneDelegate.swift */, + 0D61B1E525BB8A2300A1A590 /* HostViewController.swift */, + 0D61B20025BB8FDF00A1A590 /* DataModel.swift */, + 0D61B1E725BB8A2300A1A590 /* Main.storyboard */, + 0D61B1EA25BB8A2400A1A590 /* Assets.xcassets */, + 0D61B1EC25BB8A2400A1A590 /* LaunchScreen.storyboard */, + 0D61B1EF25BB8A2400A1A590 /* Info.plist */, + 0D61B20725BBEA9E00A1A590 /* SingleFlutterViewController.swift */, + 0D61B20A25BBF88700A1A590 /* DoubleFlutterViewController.swift */, + ); + path = MultipleFluttersIos; + sourceTree = ""; + }; + 2AB119CC8909334B007F8D1F /* Pods */ = { + isa = PBXGroup; + children = ( + 5EE6997543EAE7F85C674231 /* Pods-MultipleFluttersIos.debug.xcconfig */, + 4F02FEA4EA8C75406BC5FFC5 /* Pods-MultipleFluttersIos.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + A2F4F0BD46437369E87BA62F /* Frameworks */ = { + isa = PBXGroup; + children = ( + 4E03DC90CED28B2143B2CFB1 /* Pods_MultipleFluttersIos.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 0D61B1DD25BB8A2300A1A590 /* MultipleFluttersIos */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0D61B1F225BB8A2400A1A590 /* Build configuration list for PBXNativeTarget "MultipleFluttersIos" */; + buildPhases = ( + 3BC074A3AF94322EF6E73F8B /* [CP] Check Pods Manifest.lock */, + C63B2D75D5C822F12D344293 /* [CP] Prepare Artifacts */, + 9A714FC9CCEF3D4EFCAF0E2F /* [CP-User] Run Flutter Build multiple_flutters_module Script */, + 0D61B1DA25BB8A2300A1A590 /* Sources */, + 0D61B1DB25BB8A2300A1A590 /* Frameworks */, + 0D61B1DC25BB8A2300A1A590 /* Resources */, + D93C82A75960A3ABB9D69453 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MultipleFluttersIos; + productName = MultipleFluttersIos; + productReference = 0D61B1DE25BB8A2300A1A590 /* MultipleFluttersIos.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0D61B1D625BB8A2300A1A590 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1220; + LastUpgradeCheck = 1220; + TargetAttributes = { + 0D61B1DD25BB8A2300A1A590 = { + CreatedOnToolsVersion = 12.2; + }; + }; + }; + buildConfigurationList = 0D61B1D925BB8A2300A1A590 /* Build configuration list for PBXProject "MultipleFluttersIos" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 0D61B1D525BB8A2300A1A590; + productRefGroup = 0D61B1DF25BB8A2300A1A590 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 0D61B1DD25BB8A2300A1A590 /* MultipleFluttersIos */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 0D61B1DC25BB8A2300A1A590 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0D61B1EE25BB8A2400A1A590 /* LaunchScreen.storyboard in Resources */, + 0D61B1EB25BB8A2400A1A590 /* Assets.xcassets in Resources */, + 0D61B1E925BB8A2300A1A590 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3BC074A3AF94322EF6E73F8B /* [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-MultipleFluttersIos-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; + }; + 9A714FC9CCEF3D4EFCAF0E2F /* [CP-User] Run Flutter Build multiple_flutters_module Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + name = "[CP-User] Run Flutter Build multiple_flutters_module Script"; + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\nset -u\nsource \"${SRCROOT}/../multiple_flutters_module/.ios/Flutter/flutter_export_environment.sh\"\nexport VERBOSE_SCRIPT_LOGGING=1 && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/xcode_backend.sh build"; + }; + C63B2D75D5C822F12D344293 /* [CP] Prepare Artifacts */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-MultipleFluttersIos/Pods-MultipleFluttersIos-artifacts-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Prepare Artifacts"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-MultipleFluttersIos/Pods-MultipleFluttersIos-artifacts-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MultipleFluttersIos/Pods-MultipleFluttersIos-artifacts.sh\"\n"; + showEnvVarsInLog = 0; + }; + D93C82A75960A3ABB9D69453 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-MultipleFluttersIos/Pods-MultipleFluttersIos-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-MultipleFluttersIos/Pods-MultipleFluttersIos-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MultipleFluttersIos/Pods-MultipleFluttersIos-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 0D61B1DA25BB8A2300A1A590 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0D61B20B25BBF88700A1A590 /* DoubleFlutterViewController.swift in Sources */, + 0D61B1E625BB8A2300A1A590 /* HostViewController.swift in Sources */, + 0D61B20125BB8FDF00A1A590 /* DataModel.swift in Sources */, + 0D61B1E225BB8A2300A1A590 /* AppDelegate.swift in Sources */, + 0D61B1E425BB8A2300A1A590 /* SceneDelegate.swift in Sources */, + 0D61B20825BBEA9E00A1A590 /* SingleFlutterViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 0D61B1E725BB8A2300A1A590 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 0D61B1E825BB8A2300A1A590 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 0D61B1EC25BB8A2400A1A590 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 0D61B1ED25BB8A2400A1A590 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 0D61B1F025BB8A2400A1A590 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.2; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 0D61B1F125BB8A2400A1A590 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.2; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 0D61B1F325BB8A2400A1A590 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5EE6997543EAE7F85C674231 /* Pods-MultipleFluttersIos.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = MultipleFluttersIos/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.MultipleFluttersIos; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 0D61B1F425BB8A2400A1A590 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4F02FEA4EA8C75406BC5FFC5 /* Pods-MultipleFluttersIos.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = MultipleFluttersIos/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.MultipleFluttersIos; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0D61B1D925BB8A2300A1A590 /* Build configuration list for PBXProject "MultipleFluttersIos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0D61B1F025BB8A2400A1A590 /* Debug */, + 0D61B1F125BB8A2400A1A590 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0D61B1F225BB8A2400A1A590 /* Build configuration list for PBXNativeTarget "MultipleFluttersIos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0D61B1F325BB8A2400A1A590 /* Debug */, + 0D61B1F425BB8A2400A1A590 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0D61B1D625BB8A2300A1A590 /* Project object */; +} diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcworkspace/contents.xcworkspacedata b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..bf84e1f33 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/AppDelegate.swift b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/AppDelegate.swift new file mode 100644 index 000000000..7d7c3c9d0 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/AppDelegate.swift @@ -0,0 +1,47 @@ +// Copyright 2021 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Flutter +import UIKit + +/// The app's UIApplicationDelegate. +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + let engines = FlutterEngineGroup(name: "multiple-flutters", project: nil) + + func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + #if DEBUG + let isDebug = true + #else + let isDebug = false + #endif + if isDebug { + NSLog( + "📣 NOTICE: the memory and CPU costs for Flutter engine groups are significantly greater in debug builds. See also: https://github.com/dart-lang/sdk/issues/36097" + ) + } else { + NSLog( + "📣 NOTICE: the memory and CPU costs for Flutter engine groups are significantly less here than in debug builds. See also: https://github.com/dart-lang/sdk/issues/36097" + ) + } + + return true + } + + func application( + _ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, + options: UIScene.ConnectionOptions + ) -> UISceneConfiguration { + return UISceneConfiguration( + name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application( + _ application: UIApplication, didDiscardSceneSessions sceneSessions: Set + ) { + } +} diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Assets.xcassets/AccentColor.colorset/Contents.json b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Assets.xcassets/AppIcon.appiconset/Contents.json b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..9221b9bb1 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Assets.xcassets/Contents.json b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Base.lproj/LaunchScreen.storyboard b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000..865e9329f --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Base.lproj/Main.storyboard b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Base.lproj/Main.storyboard new file mode 100644 index 000000000..785acff57 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Base.lproj/Main.storyboard @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/DataModel.swift b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/DataModel.swift new file mode 100644 index 000000000..26ad3cb15 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/DataModel.swift @@ -0,0 +1,56 @@ +// Copyright 2021 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation + +/// A protocol that receives updates when the datamodel is changed. +protocol DataModelObserver: AnyObject { + func onCountUpdate(newCount: Int64) +} + +/// A wrapper object around a weak reference to an object that implements DataModelObserver. +/// +/// This is required since you can't directly hold weak references to protocols in data structures. +private struct DataModelObserverWrapper { + weak var wrapped: DataModelObserver? + + init(_ wrapped: DataModelObserver) { + self.wrapped = wrapped + } +} + +/// A singleton data model that is observable. +class DataModel { + private var _count: Int64 = 0 + var count: Int64 { + get { + return self._count + } + set { + self._count = newValue + for observer in observers { + if let wrapped = observer.wrapped { + wrapped.onCountUpdate(newCount: self._count) + } + } + } + } + private var observers: [DataModelObserverWrapper] = [] + static let shared = DataModel() + + func addObserver(observer: DataModelObserver) { + observers.append(DataModelObserverWrapper(observer)) + } + + func removeObserver(observer: DataModelObserver) { + observers.removeAll { (element: DataModelObserverWrapper) -> Bool in + if let wrapped = element.wrapped { + return wrapped === observer + } else { + // Handle observers who dealloc'd without removing themselves. + return true + } + } + } +} diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/DoubleFlutterViewController.swift b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/DoubleFlutterViewController.swift new file mode 100644 index 000000000..8d354bc5a --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/DoubleFlutterViewController.swift @@ -0,0 +1,29 @@ +// Copyright 2021 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import UIKit + +/// A UIViewController that hosts two instances of SingleFlutterViewController, splitting the screen +/// between them. +class DoubleFlutterViewController: UIViewController { + private let topFlutter: SingleFlutterViewController = SingleFlutterViewController( + withEntrypoint: "topMain") + private let bottomFlutter: SingleFlutterViewController = SingleFlutterViewController( + withEntrypoint: "bottomMain") + + override func viewDidLoad() { + addChild(topFlutter) + addChild(bottomFlutter) + let safeFrame = self.view.safeAreaLayoutGuide.layoutFrame + let halfHeight = safeFrame.height / 2.0 + topFlutter.view.frame = CGRect( + x: safeFrame.minX, y: safeFrame.minY, width: safeFrame.width, height: halfHeight) + bottomFlutter.view.frame = CGRect( + x: safeFrame.minX, y: topFlutter.view.frame.maxY, width: safeFrame.width, height: halfHeight) + self.view.addSubview(topFlutter.view) + self.view.addSubview(bottomFlutter.view) + topFlutter.didMove(toParent: self) + bottomFlutter.didMove(toParent: self) + } +} diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/HostViewController.swift b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/HostViewController.swift new file mode 100644 index 000000000..060c2430c --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/HostViewController.swift @@ -0,0 +1,43 @@ +// Copyright 2021 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Flutter +import UIKit + +/// UIViewController associated with the NativeViewCount storyboard scene. +class HostViewController: UIViewController, DataModelObserver { + @IBOutlet weak var countView: UILabel! + + deinit { + DataModel.shared.removeObserver(observer: self) + } + + func onCountUpdate(newCount: Int64) { + self.countView.text = String(format: "%d", newCount) + } + + override func viewDidLoad() { + super.viewDidLoad() + DataModel.shared.addObserver(observer: self) + onCountUpdate(newCount: DataModel.shared.count) + } + + @IBAction func onAddCount() { + DataModel.shared.count = DataModel.shared.count + 1 + } + + @IBAction func onNext() { + let navController = self.navigationController! + // Based on the number of view controllers in the stack we push a + // SingleFlutterViewController or DoubleFlutterViewController to alternatve + // between the two. + if navController.viewControllers.count % 4 == 1 { + let vc = SingleFlutterViewController(withEntrypoint: nil) + navController.pushViewController(vc, animated: true) + } else { + let vc = DoubleFlutterViewController() + navController.pushViewController(vc, animated: true) + } + } +} diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Info.plist b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Info.plist new file mode 100644 index 000000000..5b531f7b2 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/Info.plist @@ -0,0 +1,66 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/SceneDelegate.swift b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/SceneDelegate.swift new file mode 100644 index 000000000..ae755e8c3 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/SceneDelegate.swift @@ -0,0 +1,32 @@ +// Copyright 2021 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import UIKit + +/// The UIWindowSceneDelegate for the app. +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + var window: UIWindow? + + func scene( + _ scene: UIScene, willConnectTo session: UISceneSession, + options connectionOptions: UIScene.ConnectionOptions + ) { + guard let _ = (scene as? UIWindowScene) else { return } + } + + func sceneDidDisconnect(_ scene: UIScene) { + } + + func sceneDidBecomeActive(_ scene: UIScene) { + } + + func sceneWillResignActive(_ scene: UIScene) { + } + + func sceneWillEnterForeground(_ scene: UIScene) { + } + + func sceneDidEnterBackground(_ scene: UIScene) { + } +} diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/SingleFlutterViewController.swift b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/SingleFlutterViewController.swift new file mode 100644 index 000000000..9f4af13f3 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/MultipleFluttersIos/SingleFlutterViewController.swift @@ -0,0 +1,57 @@ +// Copyright 2021 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Flutter +import Foundation + +/// A FlutterViewController intended for the MyApp widget in the Flutter module. +/// +/// This view controller maintains a connection to the Flutter instance and syncs it with the +/// datamodel. In practice you should override the other init methods or switch to composition +/// instead of inheritence. +class SingleFlutterViewController: FlutterViewController, DataModelObserver { + private var channel: FlutterMethodChannel? + + init(withEntrypoint entryPoint: String?) { + let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate + let newEngine = appDelegate.engines.makeEngine(withEntrypoint: entryPoint, libraryURI: nil) + super.init(engine: newEngine, nibName: nil, bundle: nil) + DataModel.shared.addObserver(observer: self) + } + + deinit { + DataModel.shared.removeObserver(observer: self) + } + + required init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func onCountUpdate(newCount: Int64) { + if let channel = channel { + channel.invokeMethod("setCount", arguments: newCount) + } + } + + override func viewDidLoad() { + super.viewDidLoad() + channel = FlutterMethodChannel( + name: "multiple-flutters", binaryMessenger: self.engine!.binaryMessenger) + channel!.invokeMethod("setCount", arguments: DataModel.shared.count) + let navController = self.navigationController! + channel!.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) in + if call.method == "incrementCount" { + DataModel.shared.count = DataModel.shared.count + 1 + result(nil) + } else if call.method == "next" { + let storyboard = UIStoryboard(name: "Main", bundle: nil) + let vc = storyboard.instantiateViewController(withIdentifier: "NativeViewCount") + navController.pushViewController(vc, animated: true) + result(nil) + } else { + result(FlutterMethodNotImplemented) + } + } + } +} diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/Podfile b/add_to_app/multiple_flutters/multiple_flutters_ios/Podfile new file mode 100644 index 000000000..97d788a69 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/Podfile @@ -0,0 +1,11 @@ +# Uncomment the next line to define a global platform for your project +# platform :ios, '9.0' + +flutter_application_path = '../multiple_flutters_module' +load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb') + +target 'MultipleFluttersIos' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + install_all_flutter_pods(flutter_application_path) +end diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/Podfile.lock b/add_to_app/multiple_flutters/multiple_flutters_ios/Podfile.lock new file mode 100644 index 000000000..3d5dc5886 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/Podfile.lock @@ -0,0 +1,28 @@ +PODS: + - Flutter (1.0.0) + - FlutterPluginRegistrant (0.0.1): + - Flutter + - multiple_flutters_module (0.0.1): + - Flutter + +DEPENDENCIES: + - Flutter (from `../multiple_flutters_module/.ios/Flutter/engine`) + - FlutterPluginRegistrant (from `../multiple_flutters_module/.ios/Flutter/FlutterPluginRegistrant`) + - multiple_flutters_module (from `../multiple_flutters_module/.ios/Flutter`) + +EXTERNAL SOURCES: + Flutter: + :path: "../multiple_flutters_module/.ios/Flutter/engine" + FlutterPluginRegistrant: + :path: "../multiple_flutters_module/.ios/Flutter/FlutterPluginRegistrant" + multiple_flutters_module: + :path: "../multiple_flutters_module/.ios/Flutter" + +SPEC CHECKSUMS: + Flutter: ac41d61a47ae5bf8195a5598d2d63754888ec0d5 + FlutterPluginRegistrant: 2afd5ea46d3a949472c9b7da6462d8fbf7d8b16e + multiple_flutters_module: fdf461a0e4225614d475110d85db6fd6de5aeff1 + +PODFILE CHECKSUM: d7f39981f450db398859f05de2475ec53909a487 + +COCOAPODS: 1.9.3 diff --git a/add_to_app/multiple_flutters/multiple_flutters_ios/README.md b/add_to_app/multiple_flutters/multiple_flutters_ios/README.md new file mode 100644 index 000000000..767969b2e --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_ios/README.md @@ -0,0 +1,17 @@ +# multiple_flutters_ios + +This is an add-to-app sample that uses the Flutter Engine Group API to host +multiple instances of Flutter in an iOS app. + +## Getting Started + +```sh +cd ../multiple_flutters_module +flutter pub get +cd - +pod install +open MultipleFluttersIos.xcworkspace +# (build and run) +``` + +For more information see: [multiple_flutters/README.md](../README.md) diff --git a/add_to_app/multiple_flutters/multiple_flutters_module/.gitignore b/add_to_app/multiple_flutters/multiple_flutters_module/.gitignore new file mode 100644 index 000000000..ff612b3be --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_module/.gitignore @@ -0,0 +1,48 @@ +.DS_Store +.dart_tool/ + +.packages +.pub/ + +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +*.swp +profile + +DerivedData/ + +.generated/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +build/ +.android/ +.ios/ +.flutter-plugins +.flutter-plugins-dependencies + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json diff --git a/add_to_app/multiple_flutters/multiple_flutters_module/.metadata b/add_to_app/multiple_flutters/multiple_flutters_module/.metadata new file mode 100644 index 000000000..bdc27cb22 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_module/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 1d3f6971600f6e3fb144a30fab2b889e34af0c22 + channel: master + +project_type: module diff --git a/add_to_app/multiple_flutters/multiple_flutters_module/README.md b/add_to_app/multiple_flutters/multiple_flutters_module/README.md new file mode 100644 index 000000000..819761cc4 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_module/README.md @@ -0,0 +1,5 @@ +# multiple_flutters_module + +This is the Flutter module that is embedded in the `multiple_flutters` projects. + +See also: [multiple_flutters/README.md](../README.md) \ No newline at end of file diff --git a/add_to_app/multiple_flutters/multiple_flutters_module/lib/main.dart b/add_to_app/multiple_flutters/multiple_flutters_module/lib/main.dart new file mode 100644 index 000000000..46fa37b36 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_module/lib/main.dart @@ -0,0 +1,98 @@ +// Copyright 2021 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +void main() => runApp(MyApp(Colors.blue)); + +@pragma('vm:entry-point') +void topMain() => runApp(MyApp(Colors.green)); + +@pragma('vm:entry-point') +void bottomMain() => runApp(MyApp(Colors.purple)); + +class MyApp extends StatelessWidget { + MyApp(this.color); + + final Color color; + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + primarySwatch: this.color, + ), + home: MyHomePage(title: 'Flutter Demo Home Page'), + ); + } +} + +class MyHomePage extends StatefulWidget { + MyHomePage({Key key, this.title}) : super(key: key); + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + int _counter = 0; + MethodChannel _channel; + + @override + void initState() { + super.initState(); + _channel = MethodChannel('multiple-flutters'); + _channel.setMethodCallHandler((MethodCall call) async { + if (call.method == "setCount") { + // A notification that the host platform's data model has been updated. + setState(() { + _counter = call.arguments as int; + }); + } else { + throw Exception('not implemented ${call.method}'); + } + }); + } + + void _incrementCounter() { + // Mutations to the data model are forwarded to the host platform. + _channel.invokeMethod("incrementCount", _counter); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'You have pushed the button this many times:', + ), + Text( + '$_counter', + style: Theme.of(context).textTheme.headline4, + ), + TextButton( + onPressed: _incrementCounter, + child: Text('Add'), + ), + TextButton( + onPressed: () { + _channel.invokeMethod("next", _counter); + }, + child: Text('Next'), + ), + ], + ), + ), + ); + } +} diff --git a/add_to_app/multiple_flutters/multiple_flutters_module/pubspec.lock b/add_to_app/multiple_flutters/multiple_flutters_module/pubspec.lock new file mode 100644 index 000000000..763ea98b4 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_module/pubspec.lock @@ -0,0 +1,153 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.19" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" +sdks: + dart: ">=2.12.0-0.0 <3.0.0" diff --git a/add_to_app/multiple_flutters/multiple_flutters_module/pubspec.yaml b/add_to_app/multiple_flutters/multiple_flutters_module/pubspec.yaml new file mode 100644 index 000000000..836ca66b6 --- /dev/null +++ b/add_to_app/multiple_flutters/multiple_flutters_module/pubspec.yaml @@ -0,0 +1,25 @@ +name: multiple_flutters_module +description: A module that is embedded in the multiple_flutters_ios and multiple_flutters_android sample code. + +version: 1.0.0+1 + +environment: + sdk: ">=2.8.1 <3.0.0" + +dependencies: + flutter: + sdk: flutter + + cupertino_icons: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + +flutter: + uses-material-design: true + + module: + androidX: true + androidPackage: com.example.multiple_flutters_module + iosBundleIdentifier: com.example.multipleFluttersModule diff --git a/tool/flutter_ci_script_dev.sh b/tool/flutter_ci_script_dev.sh index c735ed246..d2a02559d 100755 --- a/tool/flutter_ci_script_dev.sh +++ b/tool/flutter_ci_script_dev.sh @@ -7,6 +7,7 @@ declare -ar PROJECT_NAMES=( "add_to_app/prebuilt_module/flutter_module" \ "add_to_app/plugin/flutter_module_using_plugin" \ "add_to_app/books/flutter_module_books" \ + "add_to_app/multiple_flutters/multiple_flutters_module" \ "animations" \ # Tracking issue: https://github.com/flutter/samples/issues/652 # "flutter_maps_firestore" \ @@ -57,7 +58,10 @@ do flutter format -n --set-exit-if-changed . # Run the actual tests. - flutter test + if [ -d "test"] + then + flutter test + fi popd done diff --git a/tool/ios_ci_script.sh b/tool/ios_ci_script.sh index 22683a842..cd750c250 100755 --- a/tool/ios_ci_script.sh +++ b/tool/ios_ci_script.sh @@ -21,6 +21,11 @@ pushd add_to_app/fullscreen/flutter_module flutter packages get popd +echo "Fetching dependencies and building 'multiple_flutters/multiple_flutters_module'." +pushd add_to_app/multiple_flutters/multiple_flutters_module +flutter packages get +popd + echo "== Testing 'add_to_app/fullscreen/ios_fullscreen' on Flutter's $FLUTTER_VERSION channel ==" pushd "add_to_app/fullscreen/ios_fullscreen" @@ -71,5 +76,23 @@ COMPILER_INDEX_STORE_ENABLE=NO CONFIGURATION=Release \ popd +echo "== Testing 'add_to_app/multiple_flutters/multiple_flutters_ios' on Flutter's $FLUTTER_VERSION channel ==" +pushd "add_to_app/multiple_flutters/multiple_flutters_ios" + +pod install + +xcodebuild -workspace "MultipleFluttersIos.xcworkspace" \ +-scheme "MultipleFluttersIos" CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO \ +CODE_SIGN_IDENTITY=- EXPANDED_CODE_SIGN_IDENTITY=- \ +COMPILER_INDEX_STORE_ENABLE=NO CONFIGURATION=Debug | xcpretty + +xcodebuild -workspace "MultipleFluttersIos.xcworkspace" \ +-scheme "MultipleFluttersIos" CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO \ +CODE_SIGN_IDENTITY=- EXPANDED_CODE_SIGN_IDENTITY=- \ +COMPILER_INDEX_STORE_ENABLE=NO CONFIGURATION=Release \ +-destination generic/platform=iOS | xcpretty + +popd + echo "-- Success --"