Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 99a48ad

Browse files
committed
Add MacOS platform view support to FlutterViewController
Add platform view support in FlutterGLCompositor and FlutterViewController
1 parent 494b66a commit 99a48ad

File tree

7 files changed

+196
-3
lines changed

7 files changed

+196
-3
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,7 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSur
10721072
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.mm
10731073
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h
10741074
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm
1075+
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViews.h
10751076
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h
10761077
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm
10771078
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h

shell/platform/darwin/macos/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ source_set("flutter_framework_source") {
6262
"framework/Source/FlutterIOSurfaceHolder.mm",
6363
"framework/Source/FlutterMouseCursorPlugin.h",
6464
"framework/Source/FlutterMouseCursorPlugin.mm",
65+
"framework/Source/FlutterPlatformViews.h",
6566
"framework/Source/FlutterResizeSynchronizer.h",
6667
"framework/Source/FlutterResizeSynchronizer.mm",
6768
"framework/Source/FlutterSurfaceManager.h",

shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ class FlutterGLCompositor {
6969
// Set frame_started_ to true and reset all layer state.
7070
void StartFrame();
7171

72+
// Remove platform views that are specified for deletion.
73+
void DisposePlatformViews();
74+
7275
// Creates a CALayer and adds it to ca_layer_map_ and increments
7376
// ca_layer_count_; Returns the key value (size_t) for the layer in
7477
// ca_layer_map_.

shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
FML_CHECK(content_layer) << "Unable to find a content layer with layer id " << layer_id;
8989

9090
content_layer.frame = content_layer.superlayer.bounds;
91+
content_layer.zPosition = i;
9192

9293
// The surface is an OpenGL texture, which means it has origin in bottom left corner
9394
// and needs to be flipped vertically
@@ -98,14 +99,22 @@
9899
break;
99100
}
100101
case kFlutterLayerContentTypePlatformView:
101-
// Add functionality in follow up PR.
102-
FML_LOG(WARNING) << "Presenting PlatformViews not yet supported";
102+
NSView* platform_view = view_controller_.platformViews[layer->platform_view->identifier];
103+
CGFloat scale = [[NSScreen mainScreen] backingScaleFactor];
104+
platform_view.frame = CGRectMake(layer->offset.x / scale, layer->offset.y / scale,
105+
layer->size.width / scale, layer->size.height / scale);
106+
if (platform_view.superview == nil) {
107+
[view_controller_.flutterView addSubview:platform_view];
108+
} else {
109+
platform_view.layer.zPosition = i;
110+
}
103111
break;
104112
};
105113
}
106114
// The frame has been presented, prepare FlutterGLCompositor to
107115
// render a new frame.
108116
frame_started_ = false;
117+
DisposePlatformViews();
109118
return present_callback_();
110119
}
111120

@@ -139,4 +148,18 @@
139148
return ca_layer_count_++;
140149
}
141150

151+
void FlutterGLCompositor::DisposePlatformViews() {
152+
auto views_to_dispose = view_controller_.platformViewsToDispose;
153+
if (views_to_dispose.empty()) {
154+
return;
155+
}
156+
157+
for (int64_t viewId : views_to_dispose) {
158+
NSView* view = view_controller_.platformViews[viewId];
159+
[view removeFromSuperview];
160+
view_controller_.platformViews.erase(viewId);
161+
}
162+
views_to_dispose.clear();
163+
}
164+
142165
} // namespace flutter
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef FLUTTER_FLUTTERPLATFORMVIEWS_H_
6+
#define FLUTTER_FLUTTERPLATFORMVIEWS_H_
7+
8+
#import <AppKit/AppKit.h>
9+
10+
#import "FlutterCodecs.h"
11+
#import "FlutterMacros.h"
12+
13+
NS_ASSUME_NONNULL_BEGIN
14+
15+
/**
16+
* Wraps a `NSView` for embedding in the Flutter hierarchy
17+
*/
18+
@protocol FlutterPlatformView <NSObject>
19+
/**
20+
* Returns a reference to the `NSView` that is wrapped by this `FlutterPlatformView`.
21+
*/
22+
- (NSView*)view;
23+
@end
24+
25+
FLUTTER_EXPORT
26+
@protocol FlutterPlatformViewFactory <NSObject>
27+
/**
28+
* Create a `FlutterPlatformView`.
29+
*
30+
* Implemented by MacOS code that expose a `NSView` for embedding in a Flutter app.
31+
*
32+
* The implementation of this method should create a new `NSView` and return it.
33+
*
34+
* @param frame The rectangle for the newly created `NSView` measured in points.
35+
* @param viewId A unique identifier for this `NSView`.
36+
* @param args Parameters for creating the `NSView` sent from the Dart side of the Flutter app.
37+
* If `createArgsCodec` is not implemented, or if no creation arguments were sent from the Dart
38+
* code, this will be null. Otherwise this will be the value sent from the Dart code as decoded by
39+
* `createArgsCodec`.
40+
*/
41+
- (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
42+
viewIdentifier:(int64_t)viewId
43+
arguments:(id _Nullable)args;
44+
45+
/**
46+
* Returns the `FlutterMessageCodec` for decoding the args parameter of `createWithFrame`.
47+
*
48+
* Only needs to be implemented if `createWithFrame` needs an arguments parameter.
49+
*/
50+
@optional
51+
- (NSObject<FlutterMessageCodec>*)createArgsCodec;
52+
@end
53+
54+
NS_ASSUME_NONNULL_END
55+
56+
#endif // FLUTTER_FLUTTERPLATFORMVIEWS_H_

shell/platform/darwin/macos/framework/Source/FlutterViewController.mm

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,9 @@ @implementation FlutterViewController {
198198

199199
// A method channel for miscellaneous platform functionality.
200200
FlutterMethodChannel* _platformChannel;
201+
202+
// A method channel for platform view functionality.
203+
FlutterMethodChannel* _platformViewsChannel;
201204
}
202205

203206
@dynamic view;
@@ -211,6 +214,7 @@ static void CommonInit(FlutterViewController* controller) {
211214
allowHeadlessExecution:NO];
212215
controller->_additionalKeyResponders = [[NSMutableOrderedSet alloc] init];
213216
controller->_mouseTrackingMode = FlutterMouseTrackingModeInKeyWindow;
217+
controller->_factories = [[NSMutableDictionary alloc] init];
214218
}
215219

216220
- (instancetype)initWithCoder:(NSCoder*)coder {
@@ -377,10 +381,74 @@ - (void)addInternalPlugins {
377381
[FlutterMethodChannel methodChannelWithName:@"flutter/platform"
378382
binaryMessenger:_engine.binaryMessenger
379383
codec:[FlutterJSONMethodCodec sharedInstance]];
384+
385+
_platformViewsChannel =
386+
[FlutterMethodChannel methodChannelWithName:@"flutter/platform_views"
387+
binaryMessenger:_engine.binaryMessenger
388+
codec:[FlutterStandardMethodCodec sharedInstance]];
389+
380390
__weak FlutterViewController* weakSelf = self;
381391
[_platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
382392
[weakSelf handleMethodCall:call result:result];
383393
}];
394+
395+
[_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
396+
if ([[call method] isEqualToString:@"create"]) {
397+
[weakSelf onCreate:call result:result];
398+
} else if ([[call method] isEqualToString:@"dispose"]) {
399+
[weakSelf onDispose:call result:result];
400+
}
401+
}];
402+
}
403+
404+
- (void)onCreate:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result {
405+
NSMutableDictionary<NSString*, id>* args = [call arguments];
406+
int64_t viewId = [args[@"id"] longValue];
407+
NSString* viewType = [NSString stringWithUTF8String:([args[@"viewType"] UTF8String])];
408+
409+
if (self->_platformViews.count(viewId) != 0) {
410+
result([FlutterError errorWithCode:@"recreating_view"
411+
message:@"trying to create an already created view"
412+
details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
413+
}
414+
415+
NSObject<FlutterPlatformViewFactory>* factory = _factories[viewType];
416+
if (factory == nil) {
417+
result([FlutterError errorWithCode:@"unregistered_view_type"
418+
message:@"trying to create a view with an unregistered type"
419+
details:[NSString stringWithFormat:@"unregistered view type: '%@'",
420+
args[@"viewType"]]]);
421+
return;
422+
}
423+
424+
NSObject<FlutterPlatformView>* platform_view = [factory createWithFrame:CGRectZero
425+
viewIdentifier:viewId
426+
arguments:nil];
427+
428+
self->_platformViews[viewId] = [platform_view view];
429+
result(nil);
430+
}
431+
432+
- (void)onDispose:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result {
433+
NSNumber* arg = [call arguments];
434+
int64_t viewId = [arg longLongValue];
435+
NSLog(@"onDispose ViewId: %lld", viewId);
436+
437+
if (self->_platformViews.count(viewId) == 0) {
438+
result([FlutterError errorWithCode:@"unknown_view"
439+
message:@"trying to dispose an unknown"
440+
details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
441+
return;
442+
}
443+
444+
// The following FlutterGLCompositor::Present call will dispose the views.
445+
self->_platformViewsToDispose.insert(viewId);
446+
result(nil);
447+
}
448+
449+
- (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
450+
withId:(nonnull NSString*)factoryId {
451+
_factories[factoryId] = factory;
384452
}
385453

386454
- (void)dispatchMouseEvent:(nonnull NSEvent*)event {

shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,61 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h"
5+
#include <map>
6+
#include <unordered_set>
67

8+
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h"
9+
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViews.h"
710
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h"
811

912
@interface FlutterViewController ()
1013

1114
// The FlutterView for this view controller.
1215
@property(nonatomic, readonly, nullable) FlutterView* flutterView;
1316

17+
// NSDictionary maps strings to FlutterPlatformViewFactorys.
18+
@property(nonnull) NSMutableDictionary<NSString*, NSObject<FlutterPlatformViewFactory>*>* factories;
19+
20+
// A map of view ids to views.
21+
@property() std::map<int, NSView*> platformViews;
22+
// View ids that are going to be disposed on the next present call.
23+
@property() std::unordered_set<int64_t> platformViewsToDispose;
24+
1425
/**
1526
* This just returns the NSPasteboard so that it can be mocked in the tests.
1627
*/
1728
@property(nonatomic, readonly, nonnull) NSPasteboard* pasteboard;
1829

30+
/**
31+
* Platform View Methods.
32+
*/
33+
34+
/**
35+
* Creates a platform view using the arguments from the provided call.
36+
* The call's arguments should be castable to an NSMutableDictionary<NSString*, id>*
37+
* and the dictionary should at least hold one key for "id" that maps to the view id and
38+
* one key for "viewType" which maps to the view type (string) that was used to register
39+
* the factory.
40+
* FlutterResult is updated to contain nil for success or to contain
41+
* a FlutterError if there is an error.
42+
*/
43+
- (void)onCreate:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result;
44+
45+
/**
46+
* Disposes a platform view using the arguments from the provided call.
47+
* The call's arguments should be the Id (castable to NSNumber*) of the platform view
48+
* that should be disposed.
49+
* FlutterResult is updated to contain nil for success or a FlutterError if there is an error.
50+
*/
51+
- (void)onDispose:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result;
52+
53+
/**
54+
* Register a view factory by adding an entry into the factories_ map with key factoryId
55+
* and value factory.
56+
*/
57+
- (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
58+
withId:(nonnull NSString*)factoryId;
59+
1960
/**
2061
* Adds a responder for keyboard events. Key up and key down events are forwarded to all added
2162
* responders.

0 commit comments

Comments
 (0)