Skip to content

Fix[ios]: Correct capitalization when replacing selected text #174234

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

okorohelijah
Copy link
Contributor

@okorohelijah okorohelijah commented Aug 21, 2025

This commit resolves an issue on iOS where TextCapitalization.words would incorrectly capitalize new text when it replaced a selected portion of an existing word.

Problem:

When using a TextFormField with textCapitalization: TextCapitalization.words, the following steps would lead to incorrect behavior:

1. Type a word, e.g., "Example".
2. Select a portion of the word that does not include the first letter, e.g., "ample".
3. Begin typing a replacement word, e.g., "pect".
4. The actual result would be "ExPect", with the first letter of the replacement text incorrectly capitalized.

Root Cause:

  • The root cause was a behavioral discrepancy between a native UITextField and Flutter's custom FlutterTextInputView. On a native text field, the iOS keyboard observes selection changes and intelligently updates its capitalization state (e.g., switching to lowercase when a selection is adjusted away from a word boundary).
  • However, the keyboard was not correctly reacting to these selection changes in Flutter's custom view, causing it to remain in an "uppercase" state after the initial selection. The automatic, declarative system of setting autocapitalizationType once was insufficient because the keyboard's internal observer logic was failing.

Solution:

This fix addresses the issue by taking manual control of the keyboard's capitalization state. The logic is now placed in setSelectedTextRangeLocal:, which acts as a reliable chokepoint for all selection changes, including typing, programmatic changes, and user-driven dragging of selection handles.

On each change, the code determines if the selection is at a word boundary, dynamically toggles the view's autocapitalizationType between Words and None, and then calls [self reloadInputViews] to reload the iOS keyboard to immediately discard its old state and reload its configuration based on the property we just changed.
This ensures that the keyboard's capitalization state is always in sync with the user's selection, correctly mimicking native behavior and resolving the bug.

Screen.Recording.2025-08-21.at.12.58.04.PM.mov

Fixes #127922

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

@okorohelijah okorohelijah requested a review from a team as a code owner August 21, 2025 19:56
@flutter-dashboard
Copy link

It looks like this pull request may not have tests. Please make sure to add tests or get an explicit test exemption before merging.

If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix?

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. If you believe this PR qualifies for a test exemption, contact "@test-exemption-reviewer" in the #hackers channel in Discord (don't just cc them here, they won't see it!). The test exemption team is a small volunteer group, so all reviewers should feel empowered to ask for tests, without delegating that responsibility entirely to the test exemption group.

@github-actions github-actions bot added platform-ios iOS applications specifically engine flutter/engine repository. See also e: labels. team-ios Owned by iOS platform team labels Aug 21, 2025
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request addresses an incorrect capitalization issue on iOS when replacing selected text. The fix introduces a property to store the original capitalization setting and dynamically adjusts autocapitalizationType based on the cursor's position, ensuring the keyboard behaves as expected. The changes are logical and effectively solve the problem. I have included one suggestion to improve the conciseness of the new logic.

Copy link
Contributor

@hellohuanlin hellohuanlin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overall makes sense. we will need to add some tests

@@ -150,6 +150,7 @@ FLUTTER_DARWIN_EXPORT

// UITextInputTraits
@property(nonatomic) UITextAutocapitalizationType autocapitalizationType;
@property(nonatomic) UITextAutocapitalizationType originalAutocapitalizationType;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can add a comment explaining why adding this? (why not just use autocapitalizationType?)

BOOL shouldCapitalize = NO;
if (selection.location == 0) {
shouldCapitalize = YES;
} else if (selection.location <= self.text.length) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can location be equal to the length? (im asking cuz i don't know. @LongCatIsLooong probably knows)

@@ -1045,6 +1045,7 @@ - (void)configureWithDictionary:(NSDictionary*)configuration {
_isSystemKeyboardEnabled = ShouldShowSystemKeyboard(inputType);
self.keyboardType = ToUIKeyboardType(inputType);
self.returnKeyType = ToUIReturnKeyType(configuration[kInputAction]);
_originalAutocapitalizationType = ToUITextAutoCapitalizationType(configuration);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: self.originalAutocapitalizationType = ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
engine flutter/engine repository. See also e: labels. platform-ios iOS applications specifically team-ios Owned by iOS platform team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[iOS] TextFormField TextCapitalization.words is wrong when characters is selected
2 participants