diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index acefd7e4a9672..217ad698cafed 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -521,6 +521,8 @@ FILE: ../../../flutter/shell/platform/android/platform_message_response_android. FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Internal.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm FILE: ../../../flutter/shell/platform/embedder/embedder_surface.cc @@ -590,6 +592,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/Virtual FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterCallbackInformation.java FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterRunArguments.java FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index f340f73e0eb6a..d33519ce0b712 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -29,6 +29,7 @@ _flutter_framework_headers = [ "framework/Headers/FlutterHeadlessDartRunner.h", "framework/Headers/FlutterMacros.h", "framework/Headers/FlutterNavigationController.h", + "framework/Headers/FlutterPlatformViews.h", "framework/Headers/FlutterPlugin.h", "framework/Headers/FlutterPluginAppLifeCycleDelegate.h", "framework/Headers/FlutterTexture.h", @@ -57,6 +58,8 @@ shared_library("create_flutter_framework_dylib") { "framework/Source/FlutterObservatoryPublisher.mm", "framework/Source/FlutterPlatformPlugin.h", "framework/Source/FlutterPlatformPlugin.mm", + "framework/Source/FlutterPlatformViews_Internal.h", + "framework/Source/FlutterPlatformViews.mm", "framework/Source/FlutterPluginAppLifeCycleDelegate.mm", "framework/Source/FlutterStandardCodec.mm", "framework/Source/FlutterStandardCodec_Internal.h", diff --git a/shell/platform/darwin/ios/framework/Headers/Flutter.h b/shell/platform/darwin/ios/framework/Headers/Flutter.h index 49464e40dc06b..458db5ed3637b 100644 --- a/shell/platform/darwin/ios/framework/Headers/Flutter.h +++ b/shell/platform/darwin/ios/framework/Headers/Flutter.h @@ -50,6 +50,7 @@ #include "FlutterHeadlessDartRunner.h" #include "FlutterMacros.h" #include "FlutterNavigationController.h" +#include "FlutterPlatformViews.h" #include "FlutterPlugin.h" #include "FlutterPluginAppLifeCycleDelegate.h" #include "FlutterTexture.h" diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h b/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h new file mode 100644 index 0000000000000..c077d52cd2a00 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h @@ -0,0 +1,46 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FLUTTERPLATFORMVIEWS_H_ +#define FLUTTER_FLUTTERPLATFORMVIEWS_H_ + +#import + +#import "FlutterCodecs.h" +#import "FlutterMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +FLUTTER_EXPORT +@protocol FlutterPlatformViewFactory +/** + * Create a `FlutterPlatformView`. + * + * Implemented by iOS code that expose a `UIView` for embedding in a Flutter app. + * + * The implementation of this method should create a new `UIView` and return it. + * + * @param frame The rectangle for the newly created `UIView` measued in points. + * @param viewIdentifier A unique identifier for this `UIView`. + * @param arguments Parameters for creating the `UIView` sent from the Dart side of the Flutter app. + * If `createArgsCodec` is not implemented, or if no creation arguments were sent from the Dart + * code, this will be null. Otherwise this will be the value sent from the Dart code as decoded by + * `createArgsCodec`. + */ +- (UIView*)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args; + +/** + * Returns the `FlutterMessageCodec` for decoding the args parameter of `createWithFrame`. + * + * Only needs to be implemented if `createWithFrame` needs an arguments parameter. + */ +@optional +- (NSObject*)createArgsCodec; +@end + +NS_ASSUME_NONNULL_END + +#endif // FLUTTER_FLUTTERPLATFORMVIEWS_H_ diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h index 799aa90fa0b30..4f6497712b619 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h @@ -10,6 +10,7 @@ #include "FlutterBinaryMessenger.h" #include "FlutterChannels.h" #include "FlutterCodecs.h" +#include "FlutterPlatformViews.h" #include "FlutterTexture.h" NS_ASSUME_NONNULL_BEGIN @@ -203,6 +204,18 @@ NS_ASSUME_NONNULL_BEGIN */ - (NSObject*)textures; +/** + * Registers a `FlutterPlatformViewFactory` for creation of platfrom views. + * + * Plugins expose `UIView` for embedding in Flutter apps by registering a view factory. + * + * @param factory The view factory that will be registered. + * @param factoryId:: A unique identifier for the factory, the Dart code of the Flutter app can use + * this identifier to request creation of a `UIView` by the registered factory. + */ +- (void)registerViewFactory:(NSObject*)factory + withId:(NSString*)factoryId; + /** Publishes a value for external use of the plugin. diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm new file mode 100644 index 0000000000000..9da977da8c993 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -0,0 +1,88 @@ +// Copyright 2018 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include + +#include "FlutterPlatformViews_Internal.h" +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterChannels.h" + +namespace shell { + +FlutterPlatformViewsController::FlutterPlatformViewsController( + NSObject* messenger) { + channel_.reset([[FlutterMethodChannel alloc] + initWithName:@"flutter/platform_views" + binaryMessenger:messenger + codec:[FlutterStandardMethodCodec sharedInstance]]); + [channel_.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { + OnMethodCall(call, result); + }]; +} + +void FlutterPlatformViewsController::OnMethodCall(FlutterMethodCall* call, FlutterResult& result) { + if ([[call method] isEqualToString:@"create"]) { + OnCreate(call, result); + } else if ([[call method] isEqualToString:@"dispose"]) { + OnDispose(call, result); + } else { + result(FlutterMethodNotImplemented); + } +} + +void FlutterPlatformViewsController::OnCreate(FlutterMethodCall* call, FlutterResult& result) { + NSDictionary* args = [call arguments]; + + long viewId = [args[@"id"] longValue]; + std::string viewType([args[@"viewType"] UTF8String]); + + if (views_[viewId] != nil) { + result([FlutterError errorWithCode:@"recreating_view" + message:@"trying to create an already created view" + details:[NSString stringWithFormat:@"view id: '%ld'", viewId]]); + } + + NSObject* factory = factories_[viewType].get(); + if (factory == nil) { + result([FlutterError errorWithCode:@"unregistered_view_type" + message:@"trying to create a view with an unregistered type" + details:[NSString stringWithFormat:@"unregistered view type: '%@'", + args[@"viewType"]]]); + return; + } + + // TODO(amirh): decode and pass the creation args. + views_[viewId] = fml::scoped_nsobject([[factory createWithFrame:CGRectZero + viewIdentifier:viewId + arguments:nil] retain]); + result(nil); +} + +void FlutterPlatformViewsController::OnDispose(FlutterMethodCall* call, FlutterResult& result) { + NSDictionary* args = [call arguments]; + int64_t viewId = [args[@"id"] longLongValue]; + + if (views_[viewId] == nil) { + result([FlutterError errorWithCode:@"unknown_view" + message:@"trying to dispose an unknown" + details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]); + return; + } + + views_.erase(viewId); + result(nil); +} + +void FlutterPlatformViewsController::RegisterViewFactory( + NSObject* factory, + NSString* factoryId) { + std::string idString([factoryId UTF8String]); + FML_CHECK(factories_.count(idString) == 0); + factories_[idString] = + fml::scoped_nsobject>([factory retain]); +} + +} // namespace shell diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h new file mode 100644 index 0000000000000..620bec26d3ee5 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -0,0 +1,36 @@ +// Copyright 2018 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ + +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/common/shell.h" +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterBinaryMessenger.h" +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterChannels.h" +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" + +namespace shell { + +class FlutterPlatformViewsController { + public: + FlutterPlatformViewsController(NSObject* messenger); + + void RegisterViewFactory(NSObject* factory, NSString* factoryId); + + private: + fml::scoped_nsobject channel_; + std::map>> factories_; + std::map> views_; + + void OnMethodCall(FlutterMethodCall* call, FlutterResult& result); + void OnCreate(FlutterMethodCall* call, FlutterResult& result); + void OnDispose(FlutterMethodCall* call, FlutterResult& result); + + FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index eeab410c37a0a..1575f2a4a8592 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -4,6 +4,7 @@ #define FML_USED_ON_EMBEDDER +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #include @@ -53,6 +54,7 @@ @implementation FlutterViewController { fml::scoped_nsobject _flutterView; fml::scoped_nsobject _splashScreenView; fml::ScopedBlock _flutterViewRenderedCallback; + std::unique_ptr _platformViewsController; UIInterfaceOrientationMask _orientationPreferences; UIStatusBarStyle _statusBarStyle; blink::ViewportMetrics _viewportMetrics; @@ -113,6 +115,7 @@ - (void)performCommonViewControllerInitialization { [self setupChannels]; [self setupNotificationCenterObservers]; + _platformViewsController.reset(new shell::FlutterPlatformViewsController(self)); _pluginPublications = [NSMutableDictionary new]; } } @@ -1091,6 +1094,12 @@ - (void)onPreferredStatusBarStyleUpdated:(NSNotification*)notification { }); } +#pragma mark - Platform views + +- (shell::FlutterPlatformViewsController*)platformViewsController { + return _platformViewsController.get(); +} + #pragma mark - FlutterBinaryMessenger - (void)sendOnChannel:(NSString*)channel message:(NSData*)message { @@ -1197,6 +1206,11 @@ - (void)dealloc { return _flutterViewController; } +- (void)registerViewFactory:(NSObject*)factory + withId:(NSString*)factoryId { + [_flutterViewController platformViewsController] -> RegisterViewFactory(factory, factoryId); +} + - (void)publish:(NSObject*)value { _flutterViewController.pluginPublications[_pluginKey] = value; }