diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm index 68b57ae22f68f..1ac316e819ab4 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm @@ -2440,6 +2440,13 @@ - (void)addToInputParentViewIfNeeded:(FlutterTextInputView*)inputView { if (![inputView isDescendantOfView:_inputHider]) { [_inputHider addSubview:inputView]; } + + if (_viewController.view == nil) { + // If view controller's view has detached from flutter engine, we don't add _inputHider + // in parent view to fallback and avoid crash. + // https://github.com/flutter/flutter/issues/106404. + return; + } UIView* parentView = self.hostView; if (_inputHider.superview != parentView) { [parentView addSubview:_inputHider]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm index 459a74335bcfa..84827c4722e87 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm @@ -166,6 +166,24 @@ - (FlutterTextRange*)getLineRangeFromTokenizer:(id)tokeniz #pragma mark - Tests +- (void)testWillNotCrashWhenViewControllerIsNil { + FlutterEngine* flutterEngine = [[FlutterEngine alloc] init]; + FlutterTextInputPlugin* inputPlugin = + [[FlutterTextInputPlugin alloc] initWithDelegate:(id)flutterEngine]; + XCTAssertNil(inputPlugin.viewController); + FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"TextInput.show" + arguments:nil]; + XCTestExpectation* expectation = [[XCTestExpectation alloc] initWithDescription:@"result called"]; + + [inputPlugin handleMethodCall:methodCall + result:^(id _Nullable result) { + XCTAssertNil(result); + [expectation fulfill]; + }]; + XCTAssertNil(inputPlugin.activeView); + [self waitForExpectations:@[ expectation ] timeout:1.0]; +} + - (void)testInvokeStartLiveTextInput { FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"TextInput.startLiveTextInput" arguments:nil];