From 64a815656040aed57cdced6f8fbfb526826fcf6d Mon Sep 17 00:00:00 2001 From: sergiy-sc Date: Fri, 14 Oct 2022 16:04:36 +0300 Subject: [PATCH] [macos] expose runWithEngine constructor in FlutterViewController Removing [engine setViewController:self]; because view is not loaded, but in that function controller is saved. Later, when the view is loaded (loadView) passed as an argument controller is checked against the saved controller and since they match, the nil view is not replaced with a new view. Fixes: flutter/flutter#74735 --- .../framework/Headers/FlutterViewController.h | 13 +++++++++- .../framework/Source/FlutterViewController.mm | 19 ++++++++------ .../Source/FlutterViewControllerTest.mm | 26 +++++++++++++++++++ .../Source/FlutterViewController_Internal.h | 13 ---------- 4 files changed, 49 insertions(+), 22 deletions(-) diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h index 43c88215d3f..038db1f322e 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h @@ -53,7 +53,18 @@ FLUTTER_DARWIN_EXPORT bundle:(nullable NSBundle*)nibBundleOrNil NS_DESIGNATED_INITIALIZER; - (nonnull instancetype)initWithCoder:(nonnull NSCoder*)nibNameOrNil NS_DESIGNATED_INITIALIZER; - +/** + * Initializes this FlutterViewController with the specified `FlutterEngine`. + * + * The initialized viewcontroller will attach itself to the engine as part of this process. + * + * @param engine The `FlutterEngine` instance to attach to. Cannot be nil. + * @param nibName The NIB name to initialize this controller with. + * @param nibBundle The NIB bundle. + */ +- (nonnull instancetype)initWithEngine:(nonnull FlutterEngine*)engine + nibName:(nullable NSString*)nibName + bundle:(nullable NSBundle*)nibBundle NS_DESIGNATED_INITIALIZER; /** * Invoked by the engine right before the engine is restarted. * diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index bf13f616782..cb472018404 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -329,18 +329,21 @@ static void CommonInit(FlutterViewController* controller) { nibName:(nullable NSString*)nibName bundle:(nullable NSBundle*)nibBundle { NSAssert(engine != nil, @"Engine is required"); + NSAssert(engine.viewController == nil, + @"The supplied FlutterEngine is already used with FlutterViewController " + "instance. One instance of the FlutterEngine can only be attached to one " + "FlutterViewController at a time. Set FlutterEngine.viewController " + "to nil before attaching it to another FlutterViewController."); + self = [super initWithNibName:nibName bundle:nibBundle]; if (self) { - if (engine.viewController) { - NSLog(@"The supplied FlutterEngine %@ is already used with FlutterViewController " - "instance %@. One instance of the FlutterEngine can only be attached to one " - "FlutterViewController at a time. Set FlutterEngine.viewController " - "to nil before attaching it to another FlutterViewController.", - [engine description], [engine.viewController description]); - } _engine = engine; CommonInit(self); - [engine setViewController:self]; + if (engine.running) { + [self loadView]; + engine.viewController = self; + [self initializeKeyboard]; + } } return self; diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm index 5905600b6d1..eb373819a8f 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm @@ -23,6 +23,7 @@ - (bool)testKeyboardIsRestartedOnEngineRestart; - (bool)testTrackpadGesturesAreSentToFramework; - (bool)testViewWillAppearCalledMultipleTimes; +- (bool)testFlutterViewIsConfigured; + (void)respondFalseForSendEvent:(const FlutterKeyEvent&)event callback:(nullable FlutterKeyEventCallback)callback @@ -164,6 +165,10 @@ TEST(FlutterViewControllerTest, testViewWillAppearCalledMultipleTimes) { ASSERT_TRUE([[FlutterViewControllerTestObjC alloc] testViewWillAppearCalledMultipleTimes]); } +TEST(FlutterViewControllerTest, testFlutterViewIsConfigured) { + ASSERT_TRUE([[FlutterViewControllerTestObjC alloc] testFlutterViewIsConfigured]); +} + } // namespace flutter::testing @implementation FlutterViewControllerTestObjC @@ -261,6 +266,27 @@ TEST(FlutterViewControllerTest, testViewWillAppearCalledMultipleTimes) { return true; } +- (bool)testFlutterViewIsConfigured { + id engineMock = OCMClassMock([FlutterEngine class]); + + id renderer_ = [[FlutterMetalRenderer alloc] initWithFlutterEngine:engineMock]; + OCMStub([engineMock renderer]).andReturn(renderer_); + + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock + nibName:@"" + bundle:nil]; + [viewController loadView]; + + @try { + // Make sure "renderer" was called during "loadView", which means "flutterView" is created + OCMVerify([engineMock renderer]); + } @catch (...) { + return false; + } + + return true; +} + - (bool)testFlagsChangedEventsArePropagatedIfNotHandled { id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h index 04ed474602c..19870cf5b18 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h @@ -18,19 +18,6 @@ */ @property(nonatomic, readonly, nonnull) FlutterTextInputPlugin* textInputPlugin; -/** - * Initializes this FlutterViewController with the specified `FlutterEngine`. - * - * The initialized viewcontroller will attach itself to the engine as part of this process. - * - * @param engine The `FlutterEngine` instance to attach to. Cannot be nil. - * @param nibName The NIB name to initialize this controller with. - * @param nibBundle The NIB bundle. - */ -- (nonnull instancetype)initWithEngine:(nonnull FlutterEngine*)engine - nibName:(nullable NSString*)nibName - bundle:(nullable NSBundle*)nibBundle NS_DESIGNATED_INITIALIZER; - /** * Returns YES if provided event is being currently redispatched by keyboard manager. */