From 78bc813c0e690381cbc90719fda69efd3e75715c Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Tue, 6 Jun 2023 10:46:00 -0700 Subject: [PATCH 1/2] [macOS] Add platformview create parameter support Previously, when creating native platform views on macOS, we ignored any parameters passed via the framework side "params" argument in the "create" method call, and instead always passed a nil value to the FlutterPlatformViewFactory. This made it impossible for users of macOS platform views to pass constructor arguments to the NSView subclass implementing the platform view. We now decode the arguments data using the codec specified by the `FlutterPlatformViewFactory` and pass them through to the `[FlutterPlatformViewFactory createWithIdentifier:arguments:]` method where the platform view author can make use of them. Fixes: https://github.com/flutter/flutter/issues/124723 This is a part of the broader macOS platform view support effort: https://github.com/flutter/flutter/issues/41722 --- .../Source/FlutterPlatformViewController.h | 4 +- .../Source/FlutterPlatformViewController.mm | 15 ++++++- .../FlutterPlatformViewControllerTest.mm | 40 ++++++++++++++++++- .../Source/TestFlutterPlatformView.h | 4 ++ .../Source/TestFlutterPlatformView.mm | 5 ++- 5 files changed, 61 insertions(+), 7 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h index d0969e1f1a443..f135adc720ee3 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h @@ -17,12 +17,14 @@ @interface FlutterPlatformViewController () /** - * Creates a platform view of viewType with viewId. + * Creates a platform view of viewType with viewId and arguments passed from + * the framework's creationParams constructor parameter. * FlutterResult is updated to contain nil for success or to contain * a FlutterError if there is an error. */ - (void)onCreateWithViewID:(int64_t)viewId viewType:(nonnull NSString*)viewType + arguments:(id _Nullable)args result:(nonnull FlutterResult)result; /** diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm index ba2399ab9f88c..67a6f52b627b8 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm @@ -27,6 +27,7 @@ - (instancetype)init { - (void)onCreateWithViewID:(int64_t)viewId viewType:(nonnull NSString*)viewType + arguments:(id _Nullable)args result:(nonnull FlutterResult)result { if (_platformViews.count(viewId) != 0) { result([FlutterError errorWithCode:@"recreating_view" @@ -52,7 +53,7 @@ - (void)onCreateWithViewID:(int64_t)viewId return; } - NSView* platform_view = [factory createWithViewIdentifier:viewId arguments:nil]; + NSView* platform_view = [factory createWithViewIdentifier:viewId arguments:args]; // Flutter compositing requires CALayer-backed platform views. // Force the platform view to be backed by a CALayer. [platform_view setWantsLayer:YES]; @@ -92,7 +93,17 @@ - (void)handleMethodCall:(nonnull FlutterMethodCall*)call result:(nonnull Flutte if ([args objectForKey:@"id"]) { int64_t viewId = [args[@"id"] longLongValue]; NSString* viewType = [NSString stringWithUTF8String:([args[@"viewType"] UTF8String])]; - [self onCreateWithViewID:viewId viewType:viewType result:result]; + + id params = nil; + NSObject* factory = _platformViewFactories[viewType]; + if ([factory respondsToSelector:@selector(createArgsCodec)]) { + NSObject* codec = [factory createArgsCodec]; + if (codec != nil && args[@"params"] != nil) { + FlutterStandardTypedData* paramsData = args[@"params"]; + params = [codec decode:paramsData.data]; + } + } + [self onCreateWithViewID:viewId viewType:viewType arguments:params result:result]; } else { result([FlutterError errorWithCode:@"unknown_view" message:@"'id' argument must be passed to create a platform view." diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewControllerTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewControllerTest.mm index 03c435ec0b369..917db2f415220 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewControllerTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewControllerTest.mm @@ -43,11 +43,22 @@ [platformViewController registerViewFactory:factory withId:@"MockPlatformView"]; + NSDictionary* params = @{ + @"album" : @"スコットとリバース", + @"releaseYear" : @2013, + @"artists" : @[ @"Scott Murphy", @"Rivers Cuomo" ], + @"playlist" : @[ @"おかしいやつ", @"ほどけていたんだ" ], + }; + NSObject* codec = [factory createArgsCodec]; + FlutterStandardTypedData* paramData = + [FlutterStandardTypedData typedDataWithBytes:[codec encode:params]]; + FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"create" arguments:@{ @"id" : @2, - @"viewType" : @"MockPlatformView" + @"viewType" : @"MockPlatformView", + @"params" : paramData, }]; __block bool success = false; @@ -58,8 +69,33 @@ } }; [platformViewController handleMethodCall:methodCall result:result]; - EXPECT_TRUE(success); + + // Verify PlatformView parameters are decoded correctly. + TestFlutterPlatformView* view = + (TestFlutterPlatformView*)[platformViewController platformViewWithID:2]; + ASSERT_TRUE(view != nil); + ASSERT_TRUE(view.args != nil); + + // Verify string type. + NSString* album = [view.args objectForKey:@"album"]; + EXPECT_TRUE([album isEqualToString:@"スコットとリバース"]); + + // Verify int type. + NSNumber* releaseYear = [view.args objectForKey:@"releaseYear"]; + EXPECT_EQ(releaseYear.intValue, 2013); + + // Verify list/array types. + NSArray* artists = [view.args objectForKey:@"artists"]; + ASSERT_TRUE(artists != nil); + ASSERT_EQ(artists.count, 2ul); + EXPECT_TRUE([artists[0] isEqualToString:@"Scott Murphy"]); + EXPECT_TRUE([artists[1] isEqualToString:@"Rivers Cuomo"]); + + NSArray* playlist = [view.args objectForKey:@"playlist"]; + ASSERT_EQ(playlist.count, 2ul); + EXPECT_TRUE([playlist[0] isEqualToString:@"おかしいやつ"]); + EXPECT_TRUE([playlist[1] isEqualToString:@"ほどけていたんだ"]); } TEST(FlutterPlatformViewController, TestCreateAndDispose) { diff --git a/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.h b/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.h index 719000ae66f08..f6ba2cdfb2e07 100644 --- a/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.h +++ b/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.h @@ -7,6 +7,10 @@ #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" @interface TestFlutterPlatformView : NSView + +/// Arguments passed via the params value in the create method call. +@property(nonatomic, copy) id args; + @end @interface TestFlutterPlatformViewFactory : NSObject diff --git a/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.mm b/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.mm index 9c486e25650e8..b1bd763b25c4a 100644 --- a/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.mm +++ b/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.mm @@ -9,8 +9,9 @@ @implementation TestFlutterPlatformView -- (instancetype)initWithFrame:(CGRect)frame { +- (instancetype)initWithFrame:(CGRect)frame arguments:(nullable NSDictionary*)args { self = [super initWithFrame:frame]; + _args = args; return self; } @@ -18,7 +19,7 @@ - (instancetype)initWithFrame:(CGRect)frame { @implementation TestFlutterPlatformViewFactory - (NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable id)args { - return [[TestFlutterPlatformView alloc] initWithFrame:CGRectZero]; + return [[TestFlutterPlatformView alloc] initWithFrame:CGRectZero arguments:args]; } - (NSObject*)createArgsCodec { From 722c8fd93bc0ba6d4fab3e72ac4fe6972803b409 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Wed, 7 Jun 2023 09:37:49 -0700 Subject: [PATCH 2/2] Review feedback --- .../Source/FlutterPlatformViewController.h | 8 ++++---- .../Source/FlutterPlatformViewController.mm | 19 +++++++++++-------- .../FlutterPlatformViewControllerTest.mm | 8 ++++---- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h index f135adc720ee3..31f9c63cff3fc 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h @@ -22,10 +22,10 @@ * FlutterResult is updated to contain nil for success or to contain * a FlutterError if there is an error. */ -- (void)onCreateWithViewID:(int64_t)viewId - viewType:(nonnull NSString*)viewType - arguments:(id _Nullable)args - result:(nonnull FlutterResult)result; +- (void)onCreateWithViewIdentifier:(int64_t)viewId + viewType:(nonnull NSString*)viewType + arguments:(nullable id)args + result:(nonnull FlutterResult)result; /** * Disposes the platform view with `viewId`. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm index 67a6f52b627b8..917ee5def5a52 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm @@ -25,10 +25,10 @@ - (instancetype)init { return self; } -- (void)onCreateWithViewID:(int64_t)viewId - viewType:(nonnull NSString*)viewType - arguments:(id _Nullable)args - result:(nonnull FlutterResult)result { +- (void)onCreateWithViewIdentifier:(int64_t)viewId + viewType:(nonnull NSString*)viewType + arguments:(nullable id)args + result:(nonnull FlutterResult)result { if (_platformViews.count(viewId) != 0) { result([FlutterError errorWithCode:@"recreating_view" message:@"trying to create an already created view" @@ -94,16 +94,19 @@ - (void)handleMethodCall:(nonnull FlutterMethodCall*)call result:(nonnull Flutte int64_t viewId = [args[@"id"] longLongValue]; NSString* viewType = [NSString stringWithUTF8String:([args[@"viewType"] UTF8String])]; - id params = nil; + id creationArgs = nil; NSObject* factory = _platformViewFactories[viewType]; if ([factory respondsToSelector:@selector(createArgsCodec)]) { NSObject* codec = [factory createArgsCodec]; if (codec != nil && args[@"params"] != nil) { - FlutterStandardTypedData* paramsData = args[@"params"]; - params = [codec decode:paramsData.data]; + FlutterStandardTypedData* creationArgsData = args[@"params"]; + creationArgs = [codec decode:creationArgsData.data]; } } - [self onCreateWithViewID:viewId viewType:viewType arguments:params result:result]; + [self onCreateWithViewIdentifier:viewId + viewType:viewType + arguments:creationArgs + result:result]; } else { result([FlutterError errorWithCode:@"unknown_view" message:@"'id' argument must be passed to create a platform view." diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewControllerTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewControllerTest.mm index 917db2f415220..f8880ba69d3fb 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewControllerTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewControllerTest.mm @@ -43,22 +43,22 @@ [platformViewController registerViewFactory:factory withId:@"MockPlatformView"]; - NSDictionary* params = @{ + NSDictionary* creationArgs = @{ @"album" : @"スコットとリバース", @"releaseYear" : @2013, @"artists" : @[ @"Scott Murphy", @"Rivers Cuomo" ], @"playlist" : @[ @"おかしいやつ", @"ほどけていたんだ" ], }; NSObject* codec = [factory createArgsCodec]; - FlutterStandardTypedData* paramData = - [FlutterStandardTypedData typedDataWithBytes:[codec encode:params]]; + FlutterStandardTypedData* creationArgsData = + [FlutterStandardTypedData typedDataWithBytes:[codec encode:creationArgs]]; FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"create" arguments:@{ @"id" : @2, @"viewType" : @"MockPlatformView", - @"params" : paramData, + @"params" : creationArgsData, }]; __block bool success = false;