diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index ff94acaa2797b..422a6155e40c1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -1095,7 +1095,14 @@ - (void)flutterTextInputView:(FlutterTextInputView*)textInputView arguments:@[ @(client) ]]; } -- (void)flutterTextInputViewDidResignFirstResponder:(FlutterTextInputView*)textInputView { +- (void)flutterTextInputView:(FlutterTextInputView*)textInputView + didResignFirstResponderWithTextInputClient:(int)client { + // When flutter text input view resign first responder, send a message to + // framework to ensure the focus state is correct. This is useful when close + // keyboard from platform side. + [_textInputChannel.get() invokeMethod:@"TextInputClient.onConnectionClosed" + arguments:@[ @(client) ]]; + // Platform view's first responder detection logic: // // All text input widgets (e.g. EditableText) are backed by a dummy UITextInput view diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm index 7bd90a6a5a597..b4dfb9502f511 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm @@ -13,9 +13,14 @@ #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryMessengerRelay.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" FLUTTER_ASSERT_ARC +@interface FlutterEngine () + +@end + @interface FlutterEngineTest : XCTestCase @end @@ -313,4 +318,17 @@ - (void)testCanEnableDisableEmbedderAPIThroughInfoPlist { } } +- (void)testFlutterTextInputViewDidResignFirstResponderWillCallTextInputClientConnectionClosed { + id mockBinaryMessenger = OCMClassMock([FlutterBinaryMessengerRelay class]); + FlutterEngine* engine = [[FlutterEngine alloc] init]; + [engine setBinaryMessenger:mockBinaryMessenger]; + [engine runWithEntrypoint:FlutterDefaultDartEntrypoint initialRoute:@"test"]; + [engine flutterTextInputView:nil didResignFirstResponderWithTextInputClient:1]; + FlutterMethodCall* methodCall = + [FlutterMethodCall methodCallWithMethodName:@"TextInputClient.onConnectionClosed" + arguments:@[ @(1) ]]; + NSData* encodedMethodCall = [[FlutterJSONMethodCodec sharedInstance] encodeMethodCall:methodCall]; + OCMVerify([mockBinaryMessenger sendOnChannel:@"flutter/textinput" message:encodedMethodCall]); +} + @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h b/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h index 7ecda6a23bb93..cea4dd88daed6 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h @@ -59,8 +59,8 @@ typedef NS_ENUM(NSInteger, FlutterFloatingCursorDragState) { insertTextPlaceholderWithSize:(CGSize)size withClient:(int)client; - (void)flutterTextInputView:(FlutterTextInputView*)textInputView removeTextPlaceholder:(int)client; -- (void)flutterTextInputViewDidResignFirstResponder:(FlutterTextInputView*)textInputView; - +- (void)flutterTextInputView:(FlutterTextInputView*)textInputView + didResignFirstResponderWithTextInputClient:(int)client; @end #endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERTEXTINPUTDELEGATE_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm index 1ac316e819ab4..19aebe126a690 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm @@ -1043,7 +1043,8 @@ - (BOOL)canBecomeFirstResponder { - (BOOL)resignFirstResponder { BOOL success = [super resignFirstResponder]; if (success) { - [self.textInputDelegate flutterTextInputViewDidResignFirstResponder:self]; + [self.textInputDelegate flutterTextInputView:self + didResignFirstResponderWithTextInputClient:_textInputClient]; } return success; }