diff --git a/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h b/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h index 038db1f322eed..53e26fbad4da6 100644 --- a/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h +++ b/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h @@ -73,4 +73,48 @@ FLUTTER_DARWIN_EXPORT */ - (void)onPreEngineRestart; +/** + * The contentView (FlutterView)'s background color is set to black during + * its instantiation. + * + * The containing layer's color can be set to the NSColor provided to this method. + * + * For example, the background may be set after the FlutterViewController + * is instantiated in MainFlutterWindow.swift in the Flutter project. + * ```swift + * import Cocoa + * import FlutterMacOS + * + * class MainFlutterWindow: NSWindow { + * override func awakeFromNib() { + * let flutterViewController = FlutterViewController.init() + * + * // The background color of the window and `FlutterViewController` + * // are retained separately. + * // + * // In this example, both the MainFlutterWindow and FlutterViewController's + * // FlutterView's backgroundColor are set to clear to achieve a fully + * // transparent effect. + * // + * // If the window's background color is not set, it will use the system + * // default. + * // + * // If the `FlutterView`'s color is not set via `FlutterViewController.setBackgroundColor` + * // it's default will be black. + * self.backgroundColor = NSColor.clear + * flutterViewController.backgroundColor = NSColor.clear + * + * let windowFrame = self.frame + * self.contentViewController = flutterViewController + * self.setFrame(windowFrame, display: true) + * + * RegisterGeneratedPlugins(registry: flutterViewController) + * + * super.awakeFromNib() + * } + * } + * ``` + */ +@property(readwrite, nonatomic, nullable) NSColor* backgroundColor; + @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm index 439519575e578..f6ac4a37a181a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm @@ -101,6 +101,67 @@ @interface FlutterEngine (Test) EXPECT_TRUE(logs.find("Hello logging") != std::string::npos); } +TEST_F(FlutterEngineTest, BackgroundIsBlack) { + // Launch the test entrypoint. + FlutterEngine* engine = GetFlutterEngine(); + EXPECT_TRUE([engine runWithEntrypoint:@"backgroundTest"]); + EXPECT_TRUE(engine.running); + + NSString* fixtures = @(flutter::testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; + [viewController loadView]; + viewController.flutterView.frame = CGRectMake(0, 0, 800, 600); + [engine setViewController:viewController]; + + // Latch to ensure the entire layer tree has been generated and presented. + fml::AutoResetWaitableEvent latch; + AddNativeCallback("SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { + CALayer* rootLayer = engine.viewController.flutterView.layer; + EXPECT_TRUE(rootLayer.backgroundColor != nil); + if (rootLayer.backgroundColor != nil) { + NSColor* actualBackgroundColor = + [NSColor colorWithCGColor:rootLayer.backgroundColor]; + EXPECT_EQ(actualBackgroundColor, [NSColor blackColor]); + } + latch.Signal(); + })); + latch.Wait(); +} + +TEST_F(FlutterEngineTest, CanOverrideBackgroundColor) { + // Launch the test entrypoint. + FlutterEngine* engine = GetFlutterEngine(); + EXPECT_TRUE([engine runWithEntrypoint:@"backgroundTest"]); + EXPECT_TRUE(engine.running); + + NSString* fixtures = @(flutter::testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; + [viewController loadView]; + viewController.flutterView.frame = CGRectMake(0, 0, 800, 600); + [engine setViewController:viewController]; + viewController.flutterView.backgroundColor = [NSColor whiteColor]; + + // Latch to ensure the entire layer tree has been generated and presented. + fml::AutoResetWaitableEvent latch; + AddNativeCallback("SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { + CALayer* rootLayer = engine.viewController.flutterView.layer; + EXPECT_TRUE(rootLayer.backgroundColor != nil); + if (rootLayer.backgroundColor != nil) { + NSColor* actualBackgroundColor = + [NSColor colorWithCGColor:rootLayer.backgroundColor]; + EXPECT_EQ(actualBackgroundColor, [NSColor whiteColor]); + } + latch.Signal(); + })); + latch.Wait(); +} + TEST_F(FlutterEngineTest, CanToggleAccessibility) { FlutterEngine* engine = GetFlutterEngine(); // Capture the update callbacks before the embedder API initializes. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.h b/shell/platform/darwin/macos/framework/Source/FlutterView.h index 8aed110e70adb..6dd21830b2c8d 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.h @@ -68,4 +68,13 @@ */ - (void)shutdown; +/** + * By default, the `FlutterSurfaceManager` creates two layers to manage Flutter + * content, the content layer and containing layer. To set the native background + * color, onto which the Flutter content is drawn, call this method with the + * NSColor which you would like to override the default, black background color + * with. + */ +- (void)setBackgroundColor:(nonnull NSColor*)color; + @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.mm b/shell/platform/darwin/macos/framework/Source/FlutterView.mm index 30c0a4495ac1a..b2c85efba3cd4 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.mm @@ -28,6 +28,7 @@ - (instancetype)initWithMTLDevice:(id)device self = [super initWithFrame:NSZeroRect]; if (self) { [self setWantsLayer:YES]; + [self setBackgroundColor:[NSColor blackColor]]; [self setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawDuringViewResize]; _reshapeListener = reshapeListener; _resizableBackingStoreProvider = @@ -51,6 +52,7 @@ - (instancetype)initWithFrame:(NSRect)frame self = [super initWithFrame:frame]; if (self) { [self setWantsLayer:YES]; + [self setBackgroundColor:[NSColor blackColor]]; _reshapeListener = reshapeListener; _resizableBackingStoreProvider = [[FlutterOpenGLResizableBackingStoreProvider alloc] initWithMainContext:mainContext @@ -84,6 +86,10 @@ - (void)reshaped { }]; } +- (void)setBackgroundColor:(NSColor*)color { + self.layer.backgroundColor = color.CGColor; +} + #pragma mark - NSView overrides - (void)setFrameSize:(NSSize)newSize { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index cb472018404c7..d78c3941aa275 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -159,6 +159,8 @@ void Reset() { */ @interface FlutterViewWrapper : NSView +- (void)setBackgroundColor:(NSColor*)color; + @end /** @@ -266,6 +268,10 @@ - (instancetype)initWithFlutterView:(FlutterView*)view { return self; } +- (void)setBackgroundColor:(NSColor*)color { + [_flutterView setBackgroundColor:color]; +} + - (NSArray*)accessibilityChildren { return @[ _flutterView ]; } @@ -376,6 +382,9 @@ - (void)loadView { } flutterView = [[FlutterView alloc] initWithMainContext:mainContext reshapeListener:self]; } + if (_backgroundColor != nil) { + [flutterView setBackgroundColor:_backgroundColor]; + } FlutterViewWrapper* wrapperView = [[FlutterViewWrapper alloc] initWithFlutterView:flutterView]; self.view = wrapperView; _flutterView = flutterView; @@ -418,6 +427,11 @@ - (void)setMouseTrackingMode:(FlutterMouseTrackingMode)mode { [self configureTrackingArea]; } +- (void)setBackgroundColor:(NSColor*)color { + _backgroundColor = color; + [_flutterView setBackgroundColor:_backgroundColor]; +} + - (void)onPreEngineRestart { [self initializeKeyboard]; } diff --git a/shell/platform/darwin/macos/framework/Source/fixtures/flutter_desktop_test.dart b/shell/platform/darwin/macos/framework/Source/fixtures/flutter_desktop_test.dart index 50c31f3adcee7..afdc3275e9618 100644 --- a/shell/platform/darwin/macos/framework/Source/fixtures/flutter_desktop_test.dart +++ b/shell/platform/darwin/macos/framework/Source/fixtures/flutter_desktop_test.dart @@ -57,3 +57,9 @@ Picture _createSimplePicture() { void nativeCallback() { signalNativeTest(); } + +@pragma('vm:entry-point') +void backgroundTest() { + PlatformDispatcher.instance.views.first.render(SceneBuilder().build()); + signalNativeTest(); // should look black +}