823 self = [
super initWithFrame:CGRectZero];
826 _textInputClient = 0;
828 _preventCursorDismissWhenResignFirstResponder = NO;
831 _text = [[NSMutableString alloc] init];
836 _pendingDeltas = [[NSMutableArray alloc] init];
842 _autocapitalizationType = UITextAutocapitalizationTypeSentences;
843 _autocorrectionType = UITextAutocorrectionTypeDefault;
844 _spellCheckingType = UITextSpellCheckingTypeDefault;
845 _enablesReturnKeyAutomatically = NO;
846 _keyboardAppearance = UIKeyboardAppearanceDefault;
847 _keyboardType = UIKeyboardTypeDefault;
848 _returnKeyType = UIReturnKeyDone;
849 _secureTextEntry = NO;
850 _enableDeltaModel = NO;
852 _accessibilityEnabled = NO;
853 _smartQuotesType = UITextSmartQuotesTypeYes;
854 _smartDashesType = UITextSmartDashesTypeYes;
855 _selectionRects = [[NSArray alloc] init];
857 if (@available(iOS 14.0, *)) {
858 UIScribbleInteraction* interaction = [[UIScribbleInteraction alloc] initWithDelegate:self];
859 [
self addInteraction:interaction];
863 if (@available(iOS 16.0, *)) {
864 _editMenuInteraction = [[UIEditMenuInteraction alloc] initWithDelegate:self];
865 [
self addInteraction:_editMenuInteraction];
871- (UIMenu*)editMenuInteraction:(UIEditMenuInteraction*)interaction
872 menuForConfiguration:(UIEditMenuConfiguration*)configuration
873 suggestedActions:(NSArray<UIMenuElement*>*)suggestedActions API_AVAILABLE(
ios(16.0)) {
874 return [UIMenu menuWithChildren:suggestedActions];
877- (void)editMenuInteraction:(UIEditMenuInteraction*)interaction
878 willDismissMenuForConfiguration:(UIEditMenuConfiguration*)configuration
879 animator:(
id<UIEditMenuInteractionAnimating>)animator
880 API_AVAILABLE(
ios(16.0)) {
881 [
self.textInputDelegate flutterTextInputView:self
882 willDismissEditMenuWithTextInputClient:_textInputClient];
885- (CGRect)editMenuInteraction:(UIEditMenuInteraction*)interaction
886 targetRectForConfiguration:(UIEditMenuConfiguration*)configuration API_AVAILABLE(
ios(16.0)) {
887 return _editMenuTargetRect;
890- (void)showEditMenuWithTargetRect:(CGRect)targetRect API_AVAILABLE(
ios(16.0)) {
891 _editMenuTargetRect = targetRect;
892 UIEditMenuConfiguration* config =
893 [UIEditMenuConfiguration configurationWithIdentifier:nil sourcePoint:CGPointZero];
894 [
self.editMenuInteraction presentEditMenuWithConfiguration:config];
898 [
self.editMenuInteraction dismissMenu];
901- (void)configureWithDictionary:(NSDictionary*)configuration {
902 NSDictionary* inputType = configuration[kKeyboardType];
904 NSDictionary* autofill = configuration[kAutofillProperties];
906 self.secureTextEntry = [configuration[kSecureTextEntry] boolValue];
907 self.enableDeltaModel = [configuration[kEnableDeltaModel] boolValue];
914 NSString* smartDashesType = configuration[kSmartDashesType];
916 bool smartDashesIsDisabled = smartDashesType && [smartDashesType isEqualToString:@"0"];
917 self.smartDashesType = smartDashesIsDisabled ? UITextSmartDashesTypeNo : UITextSmartDashesTypeYes;
918 NSString* smartQuotesType = configuration[kSmartQuotesType];
920 bool smartQuotesIsDisabled = smartQuotesType && [smartQuotesType isEqualToString:@"0"];
921 self.smartQuotesType = smartQuotesIsDisabled ? UITextSmartQuotesTypeNo : UITextSmartQuotesTypeYes;
923 self.keyboardAppearance = UIKeyboardAppearanceDark;
925 self.keyboardAppearance = UIKeyboardAppearanceLight;
927 self.keyboardAppearance = UIKeyboardAppearanceDefault;
929 NSString* autocorrect = configuration[kAutocorrectionType];
930 bool autocorrectIsDisabled = autocorrect && ![autocorrect boolValue];
931 self.autocorrectionType =
932 autocorrectIsDisabled ? UITextAutocorrectionTypeNo : UITextAutocorrectionTypeDefault;
933 self.spellCheckingType =
934 autocorrectIsDisabled ? UITextSpellCheckingTypeNo : UITextSpellCheckingTypeDefault;
936 if (autofill == nil) {
937 self.textContentType =
@"";
940 [
self setTextInputState:autofill[kAutofillEditingValue]];
941 NSAssert(_autofillId,
@"The autofill configuration must contain an autofill id");
945 self.isVisibleToAutofill = autofill || _secureTextEntry;
948- (UITextContentType)textContentType {
949 return _textContentType;
962- (UIColor*)insertionPointColor {
963 return [UIColor clearColor];
966- (UIColor*)selectionBarColor {
967 return [UIColor clearColor];
970- (UIColor*)selectionHighlightColor {
971 return [UIColor clearColor];
974- (UIInputViewController*)inputViewController {
985- (
id<FlutterTextInputDelegate>)textInputDelegate {
989- (
BOOL)respondsToSelector:(
SEL)selector {
990 if (@available(iOS 17.0, *)) {
992 if (selector ==
@selector(insertionPointColor)) {
996 return [
super respondsToSelector:selector];
999- (void)setTextInputClient:(
int)client {
1000 _textInputClient = client;
1005 if (!_textInteraction) {
1006 _textInteraction = [UITextInteraction textInteractionForMode:UITextInteractionModeEditable];
1007 _textInteraction.textInput =
self;
1009 return _textInteraction;
1012- (void)setTextInputState:(NSDictionary*)state {
1013 if (@available(iOS 13.0, *)) {
1019 if (!
self.inputDelegate &&
self.isFirstResponder) {
1020 [
self addInteraction:self.textInteraction];
1024 NSString* newText =
state[@"text"];
1025 BOOL textChanged = ![
self.text isEqualToString:newText];
1027 [
self.inputDelegate textWillChange:self];
1028 [
self.text setString:newText];
1030 NSInteger composingBase = [state[@"composingBase"] intValue];
1031 NSInteger composingExtent = [state[@"composingExtent"] intValue];
1032 NSRange composingRange = [
self clampSelection:NSMakeRange(MIN(composingBase, composingExtent),
1033 ABS(composingBase - composingExtent))
1036 self.markedTextRange =
1039 NSRange selectedRange = [
self clampSelectionFromBase:[state[@"selectionBase"] intValue]
1040 extent:[state[@"selectionExtent"] intValue]
1043 NSRange oldSelectedRange = [(
FlutterTextRange*)
self.selectedTextRange range];
1044 if (!NSEqualRanges(selectedRange, oldSelectedRange)) {
1045 [
self.inputDelegate selectionWillChange:self];
1053 [
self.inputDelegate selectionDidChange:self];
1057 [
self.inputDelegate textDidChange:self];
1060 if (@available(iOS 13.0, *)) {
1061 if (_textInteraction) {
1062 [
self removeInteraction:_textInteraction];
1068- (void)touchesBegan:(NSSet*)touches withEvent:(
UIEvent*)event {
1069 _scribbleFocusStatus = FlutterScribbleFocusStatusUnfocused;
1070 [
self resetScribbleInteractionStatusIfEnding];
1071 [
self.viewResponder touchesBegan:touches withEvent:event];
1074- (void)touchesMoved:(NSSet*)touches withEvent:(
UIEvent*)event {
1075 [
self.viewResponder touchesMoved:touches withEvent:event];
1078- (void)touchesEnded:(NSSet*)touches withEvent:(
UIEvent*)event {
1079 [
self.viewResponder touchesEnded:touches withEvent:event];
1082- (void)touchesCancelled:(NSSet*)touches withEvent:(
UIEvent*)event {
1083 [
self.viewResponder touchesCancelled:touches withEvent:event];
1086- (void)touchesEstimatedPropertiesUpdated:(NSSet*)touches {
1087 [
self.viewResponder touchesEstimatedPropertiesUpdated:touches];
1097- (NSRange)clampSelectionFromBase:(
int)selectionBase
1098 extent:(
int)selectionExtent
1099 forText:(NSString*)text {
1100 int loc =
MIN(selectionBase, selectionExtent);
1101 int len = ABS(selectionExtent - selectionBase);
1102 return loc < 0 ? NSMakeRange(0, 0)
1106- (NSRange)clampSelection:(NSRange)range forText:(NSString*)text {
1112- (
BOOL)isVisibleToAutofill {
1113 return self.frame.size.width > 0 &&
self.frame.size.height > 0;
1121- (void)setIsVisibleToAutofill:(
BOOL)isVisibleToAutofill {
1124 self.frame = isVisibleToAutofill ? CGRectMake(0, 0, 1, 1) : CGRectZero;
1127#pragma mark UIScribbleInteractionDelegate
1132 if (@available(iOS 14.0, *)) {
1133 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
1140- (void)scribbleInteractionWillBeginWriting:(UIScribbleInteraction*)interaction
1141 API_AVAILABLE(
ios(14.0)) {
1143 [
self.textInputDelegate flutterTextInputViewScribbleInteractionBegan:self];
1146- (void)scribbleInteractionDidFinishWriting:(UIScribbleInteraction*)interaction
1147 API_AVAILABLE(
ios(14.0)) {
1149 [
self.textInputDelegate flutterTextInputViewScribbleInteractionFinished:self];
1152- (
BOOL)scribbleInteraction:(UIScribbleInteraction*)interaction
1153 shouldBeginAtLocation:(CGPoint)location API_AVAILABLE(
ios(14.0)) {
1157- (
BOOL)scribbleInteractionShouldDelayFocus:(UIScribbleInteraction*)interaction
1158 API_AVAILABLE(
ios(14.0)) {
1162#pragma mark - UIResponder Overrides
1164- (
BOOL)canBecomeFirstResponder {
1169 return _textInputClient != 0;
1172- (
BOOL)resignFirstResponder {
1173 BOOL success = [
super resignFirstResponder];
1175 if (!_preventCursorDismissWhenResignFirstResponder) {
1176 [
self.textInputDelegate flutterTextInputView:self
1177 didResignFirstResponderWithTextInputClient:_textInputClient];
1183- (
BOOL)canPerformAction:(
SEL)action withSender:(
id)sender {
1184 if (
action ==
@selector(paste:)) {
1186 return [UIPasteboard generalPasteboard].hasStrings;
1189 return [
self textInRange:_selectedTextRange].length > 0;
1191 return [
super canPerformAction:action withSender:sender];
1194#pragma mark - UIResponderStandardEditActions Overrides
1196- (void)cut:(
id)sender {
1197 [UIPasteboard generalPasteboard].string = [
self textInRange:_selectedTextRange];
1198 [
self replaceRange:_selectedTextRange withText:@""];
1201- (void)
copy:(
id)sender {
1202 [UIPasteboard generalPasteboard].string = [
self textInRange:_selectedTextRange];
1205- (void)paste:(
id)sender {
1206 NSString* pasteboardString = [UIPasteboard generalPasteboard].string;
1207 if (pasteboardString != nil) {
1208 [
self insertText:pasteboardString];
1212- (void)
delete:(
id)sender {
1213 [
self replaceRange:_selectedTextRange withText:@""];
1216- (void)selectAll:(
id)sender {
1217 [
self setSelectedTextRange:[
self textRangeFromPosition:[
self beginningOfDocument]
1218 toPosition:[
self endOfDocument]]];
1221#pragma mark - UITextInput Overrides
1223- (
id<UITextInputTokenizer>)tokenizer {
1224 if (_tokenizer == nil) {
1231 return [_selectedTextRange copy];
1235- (void)setSelectedTextRangeLocal:(UITextRange*)selectedTextRange {
1240 rangeWithNSRange:fml::RangeForCharactersInRange(self.text, flutterTextRange.range)] copy];
1247- (void)setSelectedTextRange:(UITextRange*)selectedTextRange {
1252 [
self setSelectedTextRangeLocal:selectedTextRange];
1254 if (_enableDeltaModel) {
1255 [
self updateEditingStateWithDelta:flutter::TextEditingDelta([
self.text UTF8String])];
1257 [
self updateEditingState];
1261 _scribbleFocusStatus == FlutterScribbleFocusStatusFocused) {
1265 if (flutterTextRange.
range.length > 0) {
1266 [
self.textInputDelegate flutterTextInputView:self showToolbar:_textInputClient];
1270 [
self resetScribbleInteractionStatusIfEnding];
1273- (
id)insertDictationResultPlaceholder {
1277- (void)removeDictationResultPlaceholder:(
id)placeholder willInsertResult:(
BOOL)willInsertResult {
1280- (NSString*)textInRange:(UITextRange*)range {
1285 @"Expected a FlutterTextRange for range (got %@).", [range class]);
1287 NSAssert(textRange.location != NSNotFound,
@"Expected a valid text range.");
1289 NSUInteger location =
MIN(textRange.location,
self.text.length);
1290 NSUInteger
length =
MIN(
self.text.length - location, textRange.length);
1291 NSRange safeRange = NSMakeRange(location,
length);
1292 return [
self.text substringWithRange:safeRange];
1297- (void)replaceRangeLocal:(NSRange)range withText:(NSString*)text {
1298 [
self.text replaceCharactersInRange:[
self clampSelection:range forText:self.text]
1304 const NSRange newSelectionRange =
1305 [
self clampSelection:NSMakeRange(range.location + text.length, 0) forText:self.text];
1308 self.markedTextRange = nil;
1311- (void)replaceRange:(UITextRange*)range withText:(NSString*)text {
1312 NSString* textBeforeChange = [
self.text copy];
1314 [
self replaceRangeLocal:replaceRange withText:text];
1315 if (_enableDeltaModel) {
1316 NSRange nextReplaceRange = [
self clampSelection:replaceRange forText:textBeforeChange];
1317 [
self updateEditingStateWithDelta:flutter::TextEditingDelta(
1318 [textBeforeChange UTF8String],
1320 nextReplaceRange.location,
1321 nextReplaceRange.location + nextReplaceRange.length),
1322 [text UTF8String])];
1324 [
self updateEditingState];
1328- (
BOOL)shouldChangeTextInRange:(UITextRange*)range replacementText:(NSString*)text {
1331 self.temporarilyDeletedComposedCharacter = nil;
1333 if (
self.returnKeyType == UIReturnKeyDefault && [
text isEqualToString:
@"\n"]) {
1334 [
self.textInputDelegate flutterTextInputView:self
1335 performAction:FlutterTextInputActionNewline
1336 withClient:_textInputClient];
1340 if ([
text isEqualToString:
@"\n"]) {
1341 FlutterTextInputAction
action;
1342 switch (
self.returnKeyType) {
1343 case UIReturnKeyDefault:
1344 action = FlutterTextInputActionUnspecified;
1346 case UIReturnKeyDone:
1347 action = FlutterTextInputActionDone;
1350 action = FlutterTextInputActionGo;
1352 case UIReturnKeySend:
1353 action = FlutterTextInputActionSend;
1355 case UIReturnKeySearch:
1356 case UIReturnKeyGoogle:
1357 case UIReturnKeyYahoo:
1358 action = FlutterTextInputActionSearch;
1360 case UIReturnKeyNext:
1361 action = FlutterTextInputActionNext;
1363 case UIReturnKeyContinue:
1364 action = FlutterTextInputActionContinue;
1366 case UIReturnKeyJoin:
1367 action = FlutterTextInputActionJoin;
1369 case UIReturnKeyRoute:
1370 action = FlutterTextInputActionRoute;
1372 case UIReturnKeyEmergencyCall:
1373 action = FlutterTextInputActionEmergencyCall;
1377 [
self.textInputDelegate flutterTextInputView:self
1378 performAction:action
1379 withClient:_textInputClient];
1388- (void)setMarkedText:(NSString*)markedText selectedRange:(NSRange)markedSelectedRange {
1389 NSString* textBeforeChange = [
self.text copy];
1392 _scribbleFocusStatus != FlutterScribbleFocusStatusUnfocused) {
1396 if (markedText == nil) {
1401 const NSRange& actualReplacedRange = currentMarkedTextRange && !currentMarkedTextRange.isEmpty
1402 ? currentMarkedTextRange.range
1406 [
self.text replaceCharactersInRange:actualReplacedRange withString:markedText];
1408 const NSRange newMarkedRange = NSMakeRange(actualReplacedRange.location, markedText.length);
1409 self.markedTextRange =
1412 [
self setSelectedTextRangeLocal:
1414 rangeWithNSRange:[
self clampSelection:NSMakeRange(markedSelectedRange.location +
1415 newMarkedRange.location,
1416 markedSelectedRange.length)
1417 forText:self.text]]];
1418 if (_enableDeltaModel) {
1419 NSRange nextReplaceRange = [
self clampSelection:actualReplacedRange forText:textBeforeChange];
1420 [
self updateEditingStateWithDelta:flutter::TextEditingDelta(
1421 [textBeforeChange UTF8String],
1423 nextReplaceRange.location,
1424 nextReplaceRange.location + nextReplaceRange.length),
1425 [markedText UTF8String])];
1427 [
self updateEditingState];
1432 if (!
self.markedTextRange) {
1435 self.markedTextRange = nil;
1436 if (_enableDeltaModel) {
1437 [
self updateEditingStateWithDelta:flutter::TextEditingDelta([
self.text UTF8String])];
1439 [
self updateEditingState];
1443- (UITextRange*)textRangeFromPosition:(UITextPosition*)fromPosition
1444 toPosition:(UITextPosition*)toPosition {
1447 if (toIndex >= fromIndex) {
1460- (NSUInteger)decrementOffsetPosition:(NSUInteger)position {
1464- (NSUInteger)incrementOffsetPosition:(NSUInteger)position {
1466 return MIN(position + charRange.length,
self.text.length);
1469- (UITextPosition*)positionFromPosition:(UITextPosition*)position offset:(NSInteger)offset {
1472 NSInteger newLocation = (NSInteger)offsetPosition +
offset;
1473 if (newLocation < 0 || newLocation > (NSInteger)
self.text.length) {
1482 for (NSInteger i = 0; i <
offset && offsetPosition <
self.text.length; ++i) {
1483 offsetPosition = [
self incrementOffsetPosition:offsetPosition];
1486 for (NSInteger i = 0; i < ABS(
offset) && offsetPosition > 0; ++i) {
1487 offsetPosition = [
self decrementOffsetPosition:offsetPosition];
1493- (UITextPosition*)positionFromPosition:(UITextPosition*)position
1494 inDirection:(UITextLayoutDirection)direction
1495 offset:(NSInteger)offset {
1497 switch (direction) {
1498 case UITextLayoutDirectionLeft:
1499 case UITextLayoutDirectionUp:
1500 return [
self positionFromPosition:position offset:offset * -1];
1501 case UITextLayoutDirectionRight:
1502 case UITextLayoutDirectionDown:
1503 return [
self positionFromPosition:position offset:1];
1507- (UITextPosition*)beginningOfDocument {
1511- (UITextPosition*)endOfDocument {
1513 affinity:UITextStorageDirectionBackward];
1516- (NSComparisonResult)comparePosition:(UITextPosition*)position toPosition:(UITextPosition*)other {
1519 if (positionIndex < otherIndex) {
1520 return NSOrderedAscending;
1522 if (positionIndex > otherIndex) {
1523 return NSOrderedDescending;
1527 if (positionAffinity == otherAffinity) {
1528 return NSOrderedSame;
1530 if (positionAffinity == UITextStorageDirectionBackward) {
1532 return NSOrderedAscending;
1535 return NSOrderedDescending;
1538- (NSInteger)offsetFromPosition:(UITextPosition*)from toPosition:(UITextPosition*)toPosition {
1542- (UITextPosition*)positionWithinRange:(UITextRange*)range
1543 farthestInDirection:(UITextLayoutDirection)direction {
1545 UITextStorageDirection affinity;
1546 switch (direction) {
1547 case UITextLayoutDirectionLeft:
1548 case UITextLayoutDirectionUp:
1550 affinity = UITextStorageDirectionForward;
1552 case UITextLayoutDirectionRight:
1553 case UITextLayoutDirectionDown:
1555 affinity = UITextStorageDirectionBackward;
1561- (UITextRange*)characterRangeByExtendingPosition:(UITextPosition*)position
1562 inDirection:(UITextLayoutDirection)direction {
1564 NSUInteger startIndex;
1565 NSUInteger endIndex;
1566 switch (direction) {
1567 case UITextLayoutDirectionLeft:
1568 case UITextLayoutDirectionUp:
1569 startIndex = [
self decrementOffsetPosition:positionIndex];
1570 endIndex = positionIndex;
1572 case UITextLayoutDirectionRight:
1573 case UITextLayoutDirectionDown:
1574 startIndex = positionIndex;
1575 endIndex = [
self incrementOffsetPosition:positionIndex];
1581#pragma mark - UITextInput text direction handling
1583- (UITextWritingDirection)baseWritingDirectionForPosition:(UITextPosition*)position
1584 inDirection:(UITextStorageDirection)direction {
1586 return UITextWritingDirectionNatural;
1589- (void)setBaseWritingDirection:(UITextWritingDirection)writingDirection
1590 forRange:(UITextRange*)range {
1594#pragma mark - UITextInput cursor, selection rect handling
1596- (void)setMarkedRect:(CGRect)markedRect {
1597 _markedRect = markedRect;
1604- (void)setEditableTransform:(NSArray*)matrix {
1607 transform->m11 = [matrix[0] doubleValue];
1608 transform->m12 = [matrix[1] doubleValue];
1609 transform->m13 = [matrix[2] doubleValue];
1610 transform->m14 = [matrix[3] doubleValue];
1612 transform->m21 = [matrix[4] doubleValue];
1613 transform->m22 = [matrix[5] doubleValue];
1614 transform->m23 = [matrix[6] doubleValue];
1615 transform->m24 = [matrix[7] doubleValue];
1617 transform->m31 = [matrix[8] doubleValue];
1618 transform->m32 = [matrix[9] doubleValue];
1619 transform->m33 = [matrix[10] doubleValue];
1620 transform->m34 = [matrix[11] doubleValue];
1622 transform->m41 = [matrix[12] doubleValue];
1623 transform->m42 = [matrix[13] doubleValue];
1624 transform->m43 = [matrix[14] doubleValue];
1625 transform->m44 = [matrix[15] doubleValue];
1635 incomingRect.origin,
1636 CGPointMake(incomingRect.origin.x, incomingRect.origin.y + incomingRect.size.height),
1637 CGPointMake(incomingRect.origin.x + incomingRect.size.width, incomingRect.origin.y),
1638 CGPointMake(incomingRect.origin.x + incomingRect.size.width,
1639 incomingRect.origin.y + incomingRect.size.height)};
1641 CGPoint origin = CGPointMake(CGFLOAT_MAX, CGFLOAT_MAX);
1642 CGPoint farthest = CGPointMake(-CGFLOAT_MAX, -CGFLOAT_MAX);
1644 for (
int i = 0; i < 4; i++) {
1645 const CGPoint point =
points[i];
1657 }
else if (
w != 1.0) {
1662 origin.x =
MIN(origin.x,
x);
1663 origin.y =
MIN(origin.y,
y);
1664 farthest.x =
MAX(farthest.x,
x);
1665 farthest.y =
MAX(farthest.y,
y);
1667 return CGRectMake(origin.x, origin.y, farthest.x - origin.x, farthest.y - origin.y);
1676- (CGRect)firstRectForRange:(UITextRange*)range {
1678 @"Expected a FlutterTextPosition for range.start (got %@).", [range.
start class]);
1680 @"Expected a FlutterTextPosition for range.end (got %@).", [range.
end class]);
1683 if (_markedTextRange != nil) {
1694 CGRect
rect = _markedRect;
1695 if (CGRectIsEmpty(rect)) {
1696 rect = CGRectInset(rect, -0.1, 0);
1702 NSAssert(hostView == nil || [
self isDescendantOfView:hostView],
@"%@ is not a descendant of %@",
1704 return hostView ? [hostView convertRect:_cachedFirstRect toView:self] :
_cachedFirstRect;
1708 _scribbleFocusStatus == FlutterScribbleFocusStatusUnfocused) {
1709 if (@available(iOS 17.0, *)) {
1719 [
self.textInputDelegate flutterTextInputView:self
1720 showAutocorrectionPromptRectForStart:start
1722 withClient:_textInputClient];
1730 if (@available(iOS 17, *)) {
1736 NSUInteger first =
start;
1741 CGRect startSelectionRect = CGRectNull;
1742 CGRect endSelectionRect = CGRectNull;
1745 CGFloat minY = CGFLOAT_MAX;
1746 CGFloat maxY = CGFLOAT_MIN;
1749 rangeWithNSRange:fml::RangeForCharactersInRange(self.text, NSMakeRange(0, self.text.length))];
1750 for (NSUInteger i = 0; i < [_selectionRects count]; i++) {
1751 BOOL startsOnOrBeforeStartOfRange = _selectionRects[i].position <= first;
1752 BOOL isLastSelectionRect = i + 1 == [_selectionRects
count];
1753 BOOL endOfTextIsAfterStartOfRange = isLastSelectionRect && textRange.
range.length > first;
1754 BOOL nextSelectionRectIsAfterStartOfRange =
1755 !isLastSelectionRect && _selectionRects[i + 1].position > first;
1756 if (startsOnOrBeforeStartOfRange &&
1757 (endOfTextIsAfterStartOfRange || nextSelectionRectIsAfterStartOfRange)) {
1759 if (@available(iOS 17, *)) {
1760 startSelectionRect = _selectionRects[i].rect;
1762 return _selectionRects[i].rect;
1765 if (!CGRectIsNull(startSelectionRect)) {
1766 minY = fmin(minY, CGRectGetMinY(_selectionRects[i].rect));
1767 maxY = fmax(maxY, CGRectGetMaxY(_selectionRects[i].rect));
1768 BOOL endsOnOrAfterEndOfRange = _selectionRects[i].position >=
end - 1;
1769 BOOL nextSelectionRectIsOnNextLine =
1770 !isLastSelectionRect &&
1775 CGRectGetMidY(_selectionRects[i + 1].rect) > CGRectGetMaxY(_selectionRects[i].rect);
1776 if (endsOnOrAfterEndOfRange || isLastSelectionRect || nextSelectionRectIsOnNextLine) {
1777 endSelectionRect = _selectionRects[i].rect;
1782 if (CGRectIsNull(startSelectionRect) || CGRectIsNull(endSelectionRect)) {
1786 CGFloat minX = fmin(CGRectGetMinX(startSelectionRect), CGRectGetMinX(endSelectionRect));
1787 CGFloat maxX = fmax(CGRectGetMaxX(startSelectionRect), CGRectGetMaxX(endSelectionRect));
1788 return CGRectMake(minX, minY, maxX - minX, maxY - minY);
1796 NSArray<UITextSelectionRect*>* rects = [
self
1798 rangeWithNSRange:fml::RangeForCharactersInRange(
1802 (index >= (NSInteger)self.text.length)
1805 if (rects.count == 0) {
1811 CGRect characterAfterCaret = rects[0].rect;
1816 return CGRectMake(characterAfterCaret.origin.x + characterAfterCaret.size.width,
1817 characterAfterCaret.origin.y, 0, characterAfterCaret.size.height);
1819 return CGRectMake(characterAfterCaret.origin.x, characterAfterCaret.origin.y, 0,
1820 characterAfterCaret.size.height);
1822 }
else if (rects.count == 2 && affinity == UITextStorageDirectionForward) {
1825 CGRect characterAfterCaret = rects[1].rect;
1830 return CGRectMake(characterAfterCaret.origin.x + characterAfterCaret.size.width,
1831 characterAfterCaret.origin.y, 0, characterAfterCaret.size.height);
1833 return CGRectMake(characterAfterCaret.origin.x, characterAfterCaret.origin.y, 0,
1834 characterAfterCaret.size.height);
1843 CGRect characterBeforeCaret = rects[0].rect;
1846 return CGRectMake(characterBeforeCaret.origin.x, characterBeforeCaret.origin.y, 0,
1847 characterBeforeCaret.size.height);
1849 return CGRectMake(characterBeforeCaret.origin.x + characterBeforeCaret.size.width,
1850 characterBeforeCaret.origin.y, 0, characterBeforeCaret.size.height);
1854- (UITextPosition*)closestPositionToPoint:(CGPoint)point {
1855 if ([_selectionRects
count] == 0) {
1857 @"Expected a FlutterTextPosition for position (got %@).",
1860 UITextStorageDirection currentAffinity =
1866 rangeWithNSRange:fml::RangeForCharactersInRange(self.text, NSMakeRange(0, self.text.length))];
1867 return [
self closestPositionToPoint:point withinRange:range];
1870- (NSArray*)selectionRectsForRange:(UITextRange*)range {
1878 @"Expected a FlutterTextPosition for range.start (got %@).", [range.
start class]);
1880 @"Expected a FlutterTextPosition for range.end (got %@).", [range.
end class]);
1883 NSMutableArray* rects = [[NSMutableArray alloc] init];
1884 for (NSUInteger i = 0; i < [_selectionRects count]; i++) {
1885 if (_selectionRects[i].position >=
start &&
1886 (_selectionRects[i].position <
end ||
1887 (
start ==
end && _selectionRects[i].position <=
end))) {
1888 float width = _selectionRects[i].rect.size.width;
1892 CGRect
rect = CGRectMake(_selectionRects[i].
rect.origin.x, _selectionRects[i].rect.origin.y,
1893 width, _selectionRects[i].rect.size.height);
1896 position:_selectionRects[i].position
1900 self.text, NSMakeRange(0, self.text.length))
1903 [rects addObject:selectionRect];
1909- (UITextPosition*)closestPositionToPoint:(CGPoint)point withinRange:(UITextRange*)range {
1911 @"Expected a FlutterTextPosition for range.start (got %@).", [range.
start class]);
1913 @"Expected a FlutterTextPosition for range.end (got %@).", [range.
end class]);
1923 NSUInteger _closestRectIndex = 0;
1924 for (NSUInteger i = 0; i < [_selectionRects count]; i++) {
1925 NSUInteger position = _selectionRects[i].position;
1926 if (position >=
start && position <=
end) {
1929 point, _selectionRects[i].rect, _selectionRects[i].isRTL,
1930 NO, _selectionRects[_closestRectIndex].rect,
1931 _selectionRects[_closestRectIndex].isRTL, verticalPrecision)) {
1933 _closestRectIndex = i;
1940 affinity:UITextStorageDirectionForward];
1946 for (NSUInteger i =
MAX(0, _closestRectIndex - 1);
1947 i <
MIN(_closestRectIndex + 2, [_selectionRects
count]); i++) {
1948 NSUInteger position = _selectionRects[i].position + 1;
1949 if (position >=
start && position <=
end) {
1951 point, _selectionRects[i].rect, _selectionRects[i].isRTL,
1952 YES, _selectionRects[_closestRectIndex].rect,
1953 _selectionRects[_closestRectIndex].isRTL, verticalPrecision)) {
1956 affinity:UITextStorageDirectionBackward];
1961 return closestPosition;
1964- (UITextRange*)characterRangeAtPoint:(CGPoint)point {
1967 return [
FlutterTextRange rangeWithNSRange:fml::RangeForCharacterAtIndex(self.text, currentIndex)];
1998- (void)beginFloatingCursorAtPoint:(CGPoint)point {
2015 [
self.textInputDelegate flutterTextInputView:self
2016 updateFloatingCursor:FlutterFloatingCursorDragStateStart
2017 withClient:_textInputClient
2018 withPosition:@{@"X" : @0, @"Y" : @0}];
2021- (void)updateFloatingCursorAtPoint:(CGPoint)point {
2022 [
self.textInputDelegate flutterTextInputView:self
2023 updateFloatingCursor:FlutterFloatingCursorDragStateUpdate
2024 withClient:_textInputClient
2026 @"X" : @(point.x - _floatingCursorOffset.x),
2027 @"Y" : @(point.y - _floatingCursorOffset.y)
2031- (void)endFloatingCursor {
2033 [
self.textInputDelegate flutterTextInputView:self
2034 updateFloatingCursor:FlutterFloatingCursorDragStateEnd
2035 withClient:_textInputClient
2036 withPosition:@{@"X" : @0, @"Y" : @0}];
2039#pragma mark - UIKeyInput Overrides
2041- (void)updateEditingState {
2046 NSInteger composingBase = -1;
2047 NSInteger composingExtent = -1;
2048 if (
self.markedTextRange != nil) {
2052 NSDictionary*
state = @{
2053 @"selectionBase" : @(selectionBase),
2054 @"selectionExtent" : @(selectionExtent),
2056 @"selectionIsDirectional" : @(
false),
2057 @"composingBase" : @(composingBase),
2058 @"composingExtent" : @(composingExtent),
2059 @"text" : [NSString stringWithString:
self.
text],
2062 if (_textInputClient == 0 && _autofillId != nil) {
2063 [
self.textInputDelegate flutterTextInputView:self
2064 updateEditingClient:_textInputClient
2066 withTag:_autofillId];
2068 [
self.textInputDelegate flutterTextInputView:self
2069 updateEditingClient:_textInputClient
2074- (void)updateEditingStateWithDelta:(
flutter::TextEditingDelta)delta {
2079 NSInteger composingBase = -1;
2080 NSInteger composingExtent = -1;
2081 if (
self.markedTextRange != nil) {
2086 NSDictionary* deltaToFramework = @{
2087 @"oldText" : @(
delta.old_text().c_str()),
2088 @"deltaText" : @(
delta.delta_text().c_str()),
2089 @"deltaStart" : @(
delta.delta_start()),
2090 @"deltaEnd" : @(
delta.delta_end()),
2091 @"selectionBase" : @(selectionBase),
2092 @"selectionExtent" : @(selectionExtent),
2094 @"selectionIsDirectional" : @(
false),
2095 @"composingBase" : @(composingBase),
2096 @"composingExtent" : @(composingExtent),
2099 [_pendingDeltas addObject:deltaToFramework];
2101 if (_pendingDeltas.count == 1) {
2103 dispatch_async(dispatch_get_main_queue(), ^{
2105 if (strongSelf && strongSelf.pendingDeltas.count > 0) {
2106 NSDictionary* deltas = @{
2107 @"deltas" : strongSelf.pendingDeltas,
2110 [strongSelf.textInputDelegate flutterTextInputView:strongSelf
2111 updateEditingClient:strongSelf->_textInputClient
2113 [strongSelf.pendingDeltas removeAllObjects];
2120 return self.text.length > 0;
2123- (void)insertText:(NSString*)text {
2124 if (
self.temporarilyDeletedComposedCharacter.length > 0 &&
text.length == 1 && !
text.UTF8String &&
2125 [
text characterAtIndex:0] == [
self.temporarilyDeletedComposedCharacter characterAtIndex:0]) {
2130 self.temporarilyDeletedComposedCharacter = nil;
2133 NSMutableArray<FlutterTextSelectionRect*>* copiedRects =
2134 [[NSMutableArray alloc] initWithCapacity:[_selectionRects count]];
2136 @"Expected a FlutterTextPosition for position (got %@).",
2139 for (NSUInteger i = 0; i < [_selectionRects count]; i++) {
2140 NSUInteger rectPosition = _selectionRects[i].position;
2141 if (rectPosition == insertPosition) {
2142 for (NSUInteger j = 0; j <=
text.length; j++) {
2149 if (rectPosition > insertPosition) {
2159 _scribbleFocusStatus = FlutterScribbleFocusStatusUnfocused;
2160 [
self resetScribbleInteractionStatusIfEnding];
2161 self.selectionRects = copiedRects;
2163 [
self replaceRange:_selectedTextRange withText:text];
2166- (UITextPlaceholder*)insertTextPlaceholderWithSize:(CGSize)size API_AVAILABLE(
ios(13.0)) {
2167 [
self.textInputDelegate flutterTextInputView:self
2168 insertTextPlaceholderWithSize:size
2169 withClient:_textInputClient];
2174- (void)removeTextPlaceholder:(UITextPlaceholder*)textPlaceholder API_AVAILABLE(
ios(13.0)) {
2176 [
self.textInputDelegate flutterTextInputView:self removeTextPlaceholder:_textInputClient];
2179- (void)deleteBackward {
2181 _scribbleFocusStatus = FlutterScribbleFocusStatusUnfocused;
2182 [
self resetScribbleInteractionStatusIfEnding];
2199 if (oldRange.location > 0) {
2200 NSRange newRange = NSMakeRange(oldRange.location - 1, 1);
2206 newRange = NSMakeRange(charRange.location, oldRange.location - charRange.location);
2218 NSString* deletedText = [
self.text substringWithRange:_selectedTextRange.range];
2220 self.temporarilyDeletedComposedCharacter =
2221 [deletedText substringWithRange:deleteFirstCharacterRange];
2223 [
self replaceRange:_selectedTextRange withText:@""];
2227- (void)postAccessibilityNotification:(UIAccessibilityNotifications)notification target:(
id)target {
2228 UIAccessibilityPostNotification(notification,
target);
2231- (void)accessibilityElementDidBecomeFocused {
2232 if ([
self accessibilityElementIsFocused]) {
2236 FML_DCHECK(_backingTextInputAccessibilityObject);
2237 [
self postAccessibilityNotification:UIAccessibilityScreenChangedNotification
2238 target:_backingTextInputAccessibilityObject];
2342@property(nonatomic, assign) CGFloat pointerYVelocity;
2346 NSTimer* _enableFlutterTextInputViewAccessibilityTimer;
2349- (instancetype)initWithDelegate:(
id<FlutterTextInputDelegate>)textInputDelegate {
2350 self = [
super init];
2353 _textInputDelegate = textInputDelegate;
2354 _autofillContext = [[NSMutableDictionary alloc] init];
2356 _scribbleElements = [[NSMutableDictionary alloc] init];
2357 _keyboardViewContainer = [[UIView alloc] init];
2359 [[NSNotificationCenter defaultCenter] addObserver:self
2360 selector:@selector(handleKeyboardWillShow:)
2361 name:UIKeyboardWillShowNotification
2368- (void)handleKeyboardWillShow:(NSNotification*)notification {
2369 NSDictionary* keyboardInfo = [notification userInfo];
2370 NSValue* keyboardFrameEnd = [keyboardInfo valueForKey:UIKeyboardFrameEndUserInfoKey];
2371 _keyboardRect = [keyboardFrameEnd CGRectValue];
2375 [
self hideTextInput];
2378- (void)removeEnableFlutterTextInputViewAccessibilityTimer {
2379 if (_enableFlutterTextInputViewAccessibilityTimer) {
2380 [_enableFlutterTextInputViewAccessibilityTimer invalidate];
2381 _enableFlutterTextInputViewAccessibilityTimer = nil;
2385- (UIView<UITextInput>*)textInputView {
2390 NSString* method =
call.method;
2393 [
self showTextInput];
2395 }
else if ([method isEqualToString:
kHideMethod]) {
2396 [
self hideTextInput];
2399 [
self setTextInputClient:[args[0] intValue] withConfiguration:args[1]];
2403 [
self setPlatformViewTextInputClient];
2406 [
self setTextInputEditingState:args];
2409 [
self clearTextInputClient];
2412 [
self setEditableSizeAndTransform:args];
2415 [
self updateMarkedRect:args];
2418 [
self triggerAutofillSave:[args boolValue]];
2424 [
self setSelectionRects:args];
2427 [
self setSelectionRects:args];
2430 [
self startLiveTextInput];
2433 [
self updateConfig:args];
2436 CGFloat pointerY = (CGFloat)[
args[
@"pointerY"] doubleValue];
2437 [
self handlePointerMove:pointerY];
2440 CGFloat pointerY = (CGFloat)[
args[
@"pointerY"] doubleValue];
2441 [
self handlePointerUp:pointerY];
2448- (void)handlePointerUp:(CGFloat)pointerY {
2449 if (_keyboardView.superview != nil) {
2453 CGFloat screenHeight = screen.bounds.size.height;
2454 CGFloat keyboardHeight = _keyboardRect.size.height;
2456 BOOL shouldDismissKeyboardBasedOnVelocity = _pointerYVelocity < 0;
2457 [UIView animateWithDuration:kKeyboardAnimationTimeToCompleteion
2459 double keyboardDestination =
2460 shouldDismissKeyboardBasedOnVelocity ? screenHeight : screenHeight - keyboardHeight;
2461 _keyboardViewContainer.frame = CGRectMake(
2462 0, keyboardDestination, _viewController.flutterScreenIfViewLoaded.bounds.size.width,
2463 _keyboardViewContainer.frame.size.height);
2465 completion:^(BOOL finished) {
2466 if (shouldDismissKeyboardBasedOnVelocity) {
2467 [
self.textInputDelegate flutterTextInputView:self.activeView
2468 didResignFirstResponderWithTextInputClient:self.activeView.textInputClient];
2469 [
self dismissKeyboardScreenshot];
2471 [
self showKeyboardAndRemoveScreenshot];
2477- (void)dismissKeyboardScreenshot {
2478 for (UIView* subView in _keyboardViewContainer.subviews) {
2479 [subView removeFromSuperview];
2483- (void)showKeyboardAndRemoveScreenshot {
2484 [UIView setAnimationsEnabled:NO];
2485 [_cachedFirstResponder becomeFirstResponder];
2489 dispatch_get_main_queue(), ^{
2490 [UIView setAnimationsEnabled:YES];
2491 [
self dismissKeyboardScreenshot];
2495- (void)handlePointerMove:(CGFloat)pointerY {
2498 CGFloat screenHeight = screen.bounds.size.height;
2499 CGFloat keyboardHeight = _keyboardRect.size.height;
2500 if (screenHeight - keyboardHeight <= pointerY) {
2502 if (_keyboardView.superview == nil) {
2504 [
self takeKeyboardScreenshotAndDisplay];
2505 [
self hideKeyboardWithoutAnimationAndAvoidCursorDismissUpdate];
2507 [
self setKeyboardContainerHeight:pointerY];
2508 _pointerYVelocity = _previousPointerYPosition - pointerY;
2511 if (_keyboardView.superview != nil) {
2513 _keyboardViewContainer.frame = _keyboardRect;
2514 _pointerYVelocity = _previousPointerYPosition - pointerY;
2517 _previousPointerYPosition = pointerY;
2520- (void)setKeyboardContainerHeight:(CGFloat)pointerY {
2521 CGRect frameRect = _keyboardRect;
2522 frameRect.origin.y = pointerY;
2523 _keyboardViewContainer.frame = frameRect;
2526- (void)hideKeyboardWithoutAnimationAndAvoidCursorDismissUpdate {
2527 [UIView setAnimationsEnabled:NO];
2528 _cachedFirstResponder = UIApplication.sharedApplication.keyWindow.flutterFirstResponder;
2529 _activeView.preventCursorDismissWhenResignFirstResponder = YES;
2530 [_cachedFirstResponder resignFirstResponder];
2531 _activeView.preventCursorDismissWhenResignFirstResponder = NO;
2532 [UIView setAnimationsEnabled:YES];
2535- (void)takeKeyboardScreenshotAndDisplay {
2538 UIView* keyboardSnap = [screen snapshotViewAfterScreenUpdates:YES];
2539 keyboardSnap = [keyboardSnap resizableSnapshotViewFromRect:_keyboardRect
2540 afterScreenUpdates:YES
2541 withCapInsets:UIEdgeInsetsZero];
2542 _keyboardView = keyboardSnap;
2543 [_keyboardViewContainer addSubview:_keyboardView];
2544 if (_keyboardViewContainer.superview == nil) {
2545 [UIApplication.sharedApplication.delegate.window.rootViewController.view
2546 addSubview:_keyboardViewContainer];
2548 _keyboardViewContainer.layer.zPosition = NSIntegerMax;
2549 _keyboardViewContainer.frame = _keyboardRect;
2552- (
BOOL)showEditMenu:(NSDictionary*)args API_AVAILABLE(
ios(16.0)) {
2553 if (!
self.activeView.isFirstResponder) {
2556 NSDictionary<NSString*, NSNumber*>* encodedTargetRect =
args[@"targetRect"];
2557 CGRect globalTargetRect = CGRectMake(
2558 [encodedTargetRect[
@"x"] doubleValue], [encodedTargetRect[
@"y"] doubleValue],
2559 [encodedTargetRect[
@"width"] doubleValue], [encodedTargetRect[
@"height"] doubleValue]);
2560 CGRect localTargetRect = [
self.hostView convertRect:globalTargetRect toView:self.activeView];
2561 [
self.activeView showEditMenuWithTargetRect:localTargetRect];
2565- (void)hideEditMenu {
2566 [
self.activeView hideEditMenu];
2569- (void)setEditableSizeAndTransform:(NSDictionary*)dictionary {
2570 NSArray*
transform = dictionary[@"transform"];
2571 [_activeView setEditableTransform:transform];
2572 const int leftIndex = 12;
2573 const int topIndex = 13;
2578 [dictionary[
@"width"] intValue], [dictionary[
@"height"] intValue]);
2580 CGRectMake(0, 0, [dictionary[
@"width"] intValue], [dictionary[
@"height"] intValue]);
2581 _activeView.tintColor = [UIColor clearColor];
2586 if (@available(iOS 17, *)) {
2599- (void)updateMarkedRect:(NSDictionary*)dictionary {
2600 NSAssert(dictionary[
@"x"] != nil && dictionary[
@"y"] != nil && dictionary[
@"width"] != nil &&
2601 dictionary[
@"height"] != nil,
2602 @"Expected a dictionary representing a CGRect, got %@", dictionary);
2603 CGRect
rect = CGRectMake([dictionary[
@"x"] doubleValue], [dictionary[
@"y"] doubleValue],
2604 [dictionary[
@"width"] doubleValue], [dictionary[
@"height"] doubleValue]);
2608- (void)setSelectionRects:(NSArray*)encodedRects {
2609 NSMutableArray<FlutterTextSelectionRect*>* rectsAsRect =
2610 [[NSMutableArray alloc] initWithCapacity:[encodedRects count]];
2611 for (NSUInteger i = 0; i < [encodedRects count]; i++) {
2612 NSArray<NSNumber*>* encodedRect = encodedRects[i];
2614 selectionRectWithRect:CGRectMake([encodedRect[0] floatValue],
2615 [encodedRect[1] floatValue],
2616 [encodedRect[2] floatValue],
2617 [encodedRect[3] floatValue])
2618 position:[encodedRect[4] unsignedIntegerValue]
2619 writingDirection:[encodedRect[5] unsignedIntegerValue] == 1
2620 ? NSWritingDirectionLeftToRight
2621 : NSWritingDirectionRightToLeft]];
2627 _activeView.selectionRects = rectsAsRect;
2630- (void)startLiveTextInput {
2631 if (@available(iOS 15.0, *)) {
2632 if (_activeView == nil || !_activeView.isFirstResponder) {
2635 [_activeView captureTextFromCamera:nil];
2639- (void)showTextInput {
2640 _activeView.viewResponder = _viewResponder;
2641 [
self addToInputParentViewIfNeeded:_activeView];
2650 if (!_enableFlutterTextInputViewAccessibilityTimer) {
2651 _enableFlutterTextInputViewAccessibilityTimer =
2652 [NSTimer scheduledTimerWithTimeInterval:kUITextInputAccessibilityEnablingDelaySeconds
2654 selector:@selector(enableActiveViewAccessibility)
2658 [_activeView becomeFirstResponder];
2661- (void)enableActiveViewAccessibility {
2662 if (_activeView.isFirstResponder) {
2663 _activeView.accessibilityEnabled = YES;
2665 [
self removeEnableFlutterTextInputViewAccessibilityTimer];
2668- (void)hideTextInput {
2669 [
self removeEnableFlutterTextInputViewAccessibilityTimer];
2670 _activeView.accessibilityEnabled = NO;
2671 [_activeView resignFirstResponder];
2672 [_activeView removeFromSuperview];
2673 [_inputHider removeFromSuperview];
2676- (void)triggerAutofillSave:(
BOOL)saveEntries {
2677 [_activeView resignFirstResponder];
2682 [
self cleanUpViewHierarchy:YES clearText:YES delayRemoval:NO];
2683 [_autofillContext removeAllObjects];
2684 [
self changeInputViewsAutofillVisibility:YES];
2686 [_autofillContext removeAllObjects];
2689 [
self cleanUpViewHierarchy:YES clearText:!saveEntries delayRemoval:NO];
2690 [
self addToInputParentViewIfNeeded:_activeView];
2693- (void)setPlatformViewTextInputClient {
2697 [
self removeEnableFlutterTextInputViewAccessibilityTimer];
2698 _activeView.accessibilityEnabled = NO;
2699 [_activeView removeFromSuperview];
2700 [_inputHider removeFromSuperview];
2703- (void)setTextInputClient:(
int)client withConfiguration:(NSDictionary*)configuration {
2704 [
self resetAllClientIds];
2707 [
self changeInputViewsAutofillVisibility:NO];
2711 case kFlutterAutofillTypeNone:
2712 self.activeView = [
self createInputViewWith:configuration];
2714 case kFlutterAutofillTypeRegular:
2717 self.activeView = [
self updateAndShowAutofillViews:nil
2718 focusedField:configuration
2719 isPasswordRelated:NO];
2721 case kFlutterAutofillTypePassword:
2722 self.activeView = [
self updateAndShowAutofillViews:configuration[kAssociatedAutofillFields]
2723 focusedField:configuration
2724 isPasswordRelated:YES];
2727 [_activeView setTextInputClient:client];
2728 [_activeView reloadInputViews];
2740 [
self cleanUpViewHierarchy:NO clearText:YES delayRemoval:YES];
2751 [_autofillContext removeObjectForKey:autofillId];
2755 [
self addToInputParentViewIfNeeded:newView];
2759 if (autofillId &&
AutofillTypeOf(field) == kFlutterAutofillTypeNone) {
2760 [_autofillContext removeObjectForKey:autofillId];
2767 focusedField:(NSDictionary*)focusedField
2768 isPasswordRelated:(
BOOL)isPassword {
2771 NSAssert(focusedId,
@"autofillId must not be null for the focused field: %@", focusedField);
2776 focused = [
self getOrCreateAutofillableView:focusedField isPasswordAutofill:isPassword];
2777 [_autofillContext removeObjectForKey:focusedId];
2780 for (NSDictionary* field in fields) {
2782 NSAssert(autofillId,
@"autofillId must not be null for field: %@", field);
2785 BOOL isFocused = [focusedId isEqualToString:autofillId];
2788 focused = [
self getOrCreateAutofillableView:field isPasswordAutofill:isPassword];
2793 _autofillContext[autofillId] = isFocused ? focused
2794 : [
self getOrCreateAutofillableView:field
2795 isPasswordAutofill:isPassword];
2798 [_autofillContext removeObjectForKey:autofillId];
2802 NSAssert(focused,
@"The current focused input view must not be nil.");
2812 isPasswordAutofill:(
BOOL)needsPasswordAutofill {
2819 [
self addToInputParentViewIfNeeded:inputView];
2827- (UIView*)hostView {
2829 NSAssert(host !=
nullptr,
2830 @"The application must have a host view since the keyboard client "
2831 @"must be part of the responder chain to function. The host view controller is %@",
2837- (NSArray<UIView*>*)textInputViews {
2838 return _inputHider.subviews;
2851- (void)cleanUpViewHierarchy:(
BOOL)includeActiveView
2852 clearText:(
BOOL)clearText
2853 delayRemoval:(
BOOL)delayRemoval {
2854 for (UIView* view in
self.textInputViews) {
2856 (includeActiveView || view != _activeView)) {
2858 if (_autofillContext[inputView.
autofillId] != view) {
2863 [inputView performSelector:@selector(removeFromSuperview) withObject:nil afterDelay:0.1];
2865 [inputView removeFromSuperview];
2874- (void)changeInputViewsAutofillVisibility:(
BOOL)newVisibility {
2875 for (UIView* view in
self.textInputViews) {
2890- (void)resetAllClientIds {
2891 for (UIView* view in
self.textInputViews) {
2900 if (![inputView isDescendantOfView:_inputHider]) {
2901 [_inputHider addSubview:inputView];
2910 UIView* parentView =
self.hostView;
2911 if (_inputHider.superview != parentView) {
2912 [parentView addSubview:_inputHider];