diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm index 1ac316e819ab4..66f753ab0fd94 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm @@ -56,6 +56,7 @@ static NSString* const kDeprecatedSetSelectionRectsMethod = @"TextInput.setSelectionRects"; static NSString* const kSetSelectionRectsMethod = @"Scribble.setSelectionRects"; static NSString* const kStartLiveTextInputMethod = @"TextInput.startLiveTextInput"; +static NSString* const kUpdateConfigMethod = @"TextInput.updateConfig"; #pragma mark - TextInputConfiguration Field Names static NSString* const kSecureTextEntry = @"obscureText"; @@ -2123,6 +2124,9 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } else if ([method isEqualToString:kStartLiveTextInputMethod]) { [self startLiveTextInput]; result(nil); + } else if ([method isEqualToString:kUpdateConfigMethod]) { + [self updateConfig:args]; + result(nil); } else { result(FlutterMethodNotImplemented); } @@ -2462,6 +2466,23 @@ - (void)clearTextInputClient { _activeView.frame = CGRectZero; } +- (void)updateConfig:(NSDictionary*)dictionary { + BOOL isSecureTextEntry = [dictionary[kSecureTextEntry] boolValue]; + for (UIView* view in self.textInputViews) { + if ([view isKindOfClass:[FlutterTextInputView class]]) { + FlutterTextInputView* inputView = (FlutterTextInputView*)view; + // The feature of holding and draging spacebar to move cursor is affected by + // secureTextEntry, so when obscureText is updated, we need to update secureTextEntry + // and call reloadInputViews. + // https://github.com/flutter/flutter/issues/122139 + if (inputView.isSecureTextEntry != isSecureTextEntry) { + inputView.secureTextEntry = isSecureTextEntry; + [inputView reloadInputViews]; + } + } + } +} + #pragma mark UIIndirectScribbleInteractionDelegate - (BOOL)indirectScribbleInteraction:(UIIndirectScribbleInteraction*)interaction diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm index 84827c4722e87..c0317a289c14d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm @@ -164,6 +164,14 @@ - (FlutterTextRange*)getLineRangeFromTokenizer:(id)tokeniz return (FlutterTextRange*)range; } +- (void)updateConfig:(NSDictionary*)config { + FlutterMethodCall* updateConfigCall = + [FlutterMethodCall methodCallWithMethodName:@"TextInput.updateConfig" arguments:config]; + [textInputPlugin handleMethodCall:updateConfigCall + result:^(id _Nullable result){ + }]; +} + #pragma mark - Tests - (void)testWillNotCrashWhenViewControllerIsNil { @@ -647,6 +655,29 @@ - (void)testPropagatePressEventsToViewController2 { withEvent:[OCMArg isNotNil]]); } +- (void)testUpdateSecureTextEntry { + NSDictionary* config = self.mutableTemplateCopy; + [config setValue:@"YES" forKey:@"obscureText"]; + [self setClientId:123 configuration:config]; + + NSArray* inputFields = self.installedInputViews; + FlutterTextInputView* inputView = OCMPartialMock(inputFields[0]); + + __block int callCount = 0; + OCMStub([inputView reloadInputViews]).andDo(^(NSInvocation* invocation) { + callCount++; + }); + + XCTAssertTrue(inputView.isSecureTextEntry); + + config = self.mutableTemplateCopy; + [config setValue:@"NO" forKey:@"obscureText"]; + [self updateConfig:config]; + + XCTAssertEqual(callCount, 1); + XCTAssertFalse(inputView.isSecureTextEntry); +} + #pragma mark - TextEditingDelta tests - (void)testTextEditingDeltasAreGeneratedOnTextInput { FlutterTextInputView* inputView = [[FlutterTextInputView alloc] initWithOwner:textInputPlugin];