Skip to content

Enable inline text prediction on iOS (issue #135221)#182728

Draft
nikb7 wants to merge 22 commits into
flutter:masterfrom
nikb7:ios-inline-text-prediction-135221
Draft

Enable inline text prediction on iOS (issue #135221)#182728
nikb7 wants to merge 22 commits into
flutter:masterfrom
nikb7:ios-inline-text-prediction-135221

Conversation

@nikb7
Copy link
Copy Markdown
Contributor

@nikb7 nikb7 commented Feb 22, 2026

Description

This PR adds support for inline predictive text on iOS 17+ (the gray suggestion that appears after the cursor as you type). It does two things:

  1. Enable/disable inline prediction – Apps can turn inline prediction on or off per field via a new enableInlinePrediction parameter on TextField and CupertinoTextField (default remains true). The setting is sent through TextInputConfiguration to the engine, which sets UITextInlinePredictionType on the iOS text input view so the system shows or hides inline suggestions.

  2. Style the prediction/composing region – A new optional composingStyle parameter lets apps control how the composing range (IME and inline prediction) is drawn. When set (e.g. gray with no underline), the suggestion can match the native iOS look instead of the default underline.

Why: On iOS 17+, the system keyboard can show inline predictions; Flutter had no way to enable/disable this or style the suggestion text. This change adds that control and styling so Flutter apps can match platform behavior and design.

Technical notes:

  • Framework: TextInputConfiguration.enableInlinePrediction, EditableText/TextField/CupertinoTextField pass-through, and TextEditingController.buildTextSpan(composingStyle) for the composing region.
  • Engine (iOS only): read enableInlinePrediction in configureWithDictionary:, set FlutterTextInputView.inlinePredictionType, and implement setAttributedMarkedText:selectedRange: so the system can deliver inline prediction. Semantics objects updated for the same protocol.
  • Other platforms are unchanged: they ignore the new config; composingStyle only affects how the composing range is drawn.

Consider adding before/after screenshots: one with inline prediction off or default underline, one with enableInlinePrediction: true and a custom composingStyle (e.g. gray, no underline) to show the new behavior.


Fixes #135221

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.

@nikb7 nikb7 requested a review from a team as a code owner February 22, 2026 14:17
@github-actions github-actions Bot added a: text input Entering text in a text field or keyboard related problems platform-ios iOS applications specifically engine flutter/engine related. See also e: labels. a: desktop Running on desktop team-ios Owned by iOS platform team platform-macos labels Feb 22, 2026
@google-cla
Copy link
Copy Markdown

google-cla Bot commented Feb 22, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Copy link
Copy Markdown
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 enables inline text prediction on iOS 17 and later by implementing the setAttributedMarkedText:selectedRange: method in FlutterTextInputPlugin and TextInputSemanticsObject. The implementation forwards the call to the existing setMarkedText method after extracting the plain string. A new unit test is added to verify this functionality. The PR also includes a timing adjustment in vsync_waiter_ios to prevent a potential assertion failure, and unrelated Swift concurrency improvements for ResizeSynchronizer on macOS. Additionally, a new markdown file is added to document how to run the iOS text input tests.

@github-actions github-actions Bot added framework flutter/packages/flutter repository. See also f: labels. f: material design flutter/packages/flutter/material repository. f: cupertino flutter/packages/flutter/cupertino repository and removed a: desktop Running on desktop platform-macos labels Feb 22, 2026
- FlutterTextInputPlugin: support predictive text / inline predictions
- TextInputSemanticsObject: semantics for inline prediction
- vsync_waiter_ios: related timing adjustments
- Add run_ios_text_input_tests.md
- ResizeSynchronizer (macOS): unrelated sync fixes
- Framework: add enableInlinePrediction and composingStyle to TextInputConfiguration,
  EditableText, TextField, and CupertinoTextField for iOS 17+ inline suggestions.
- Engine: set inlinePredictionType from config; implement setAttributedMarkedText
  for FlutterTextInputView and TextInputSemanticsObject (iOS 17+).
- Fix FlutterTextInputPluginTest: assert marked range length 17 for 'inline prediction'.
- Revert unrelated ResizeSynchronizer/ResizeSynchronizerTest (macOS) changes.
@nikb7 nikb7 force-pushed the ios-inline-text-prediction-135221 branch from 93f48ba to b289c9e Compare February 22, 2026 17:14
Copy link
Copy Markdown
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 enables inline text prediction on iOS 17+ by adding enableInlinePrediction and composingStyle properties to TextField and CupertinoTextField. The changes are well-implemented across the engine and framework, including necessary tests. Additionally, a fix in vsync_waiter_ios.mm addresses a potential crash related to CADisplayLink timestamps, which improves overall stability. I have one suggestion to improve code readability.

Comment thread packages/flutter/lib/src/widgets/editable_text.dart Outdated
@nikb7
Copy link
Copy Markdown
Contributor Author

nikb7 commented Feb 23, 2026

/gemeni review

self.isVisibleToAutofill = autofill || _secureTextEntry;

if (@available(iOS 17.0, *)) {
NSNumber* enableInlinePrediction = configuration[kEnableInlinePrediction];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is it supposed to be NSValue here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

the config value is a boolean from the channel, hence NSNumber. Tested this flow locally as well to validate this behaviour

NSNumber* enableInlinePrediction = configuration[kEnableInlinePrediction];
BOOL enabled = enableInlinePrediction == nil || [enableInlinePrediction boolValue];
self.inlinePredictionType =
enabled ? UITextInlinePredictionTypeYes : UITextInlinePredictionTypeNo;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What about default?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

platform default behaviour will be default - and null/no value will be considered as default

required BuildContext context,
TextStyle? style,
required bool withComposing,
TextStyle? composingStyle,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This will likely be a breaking change (based on my past attempts to add new parameters to this method).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If that's so we should solve this once and for all: Create a new data class class BuildTextSpanInfo that contains all these parameters, and use this class as the parameter instead. This way adding new parameters no longer breaks.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah it looks like you're trying to expose the configuration on TextField/CupertinoTextField. The widget-level API makes sense but I'm still worried this change (on TextEditingController) will be breaking.

Since this is a separate / potentially breaking feature from inline prediction (and people can subclass TextEditingController to achieve the same thing, and interestingly this is going to be a breaking change for them), I feel we should hold off adding this argument if it's indeed breaking.

Copy link
Copy Markdown
Member

@loic-sharma loic-sharma Mar 11, 2026

Choose a reason for hiding this comment

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

I agree with @LongCatIsLooong's comment. Also I'm concerned that:

  • Inline predictive text will have incorrect styling but will be on by default on iOS 17+.
  • We're coupling the styling for inline predictive text and the composition string, even though these are styled differently on iOS.

I'd suggest that in this PR:

  1. We remove the APIs to customize the composingStyle.
  2. We make inline text prediction off by default on all platforms. iOS apps with enablePredictiveText: null have inline predictive text disabled. iOS apps must use enablePredictiveText: true to enable it.

Then in one or more subsequent PRs:

  1. Add APIs to customize the composingStyle and/or the inlinePredictionStyle
  2. Update CupertinoTextField to customize these styles to match iOS's styling for inline predictive text
  3. Make enablePredictiveText: null use the platform default for inline text prediction on iOS 17+.

What do y'all think?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

makes sense @loic-sharma , I will split this PR and share

Comment thread packages/flutter/lib/src/widgets/editable_text.dart Outdated
Comment thread packages/flutter/lib/src/widgets/editable_text.dart Outdated
Comment thread engine/src/flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm Outdated
@github-actions github-actions Bot added d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos labels Feb 24, 2026
@nikb7
Copy link
Copy Markdown
Contributor Author

nikb7 commented Feb 25, 2026

I will be adding these attributes to ThemeData class as well - so it can easily controlled centrally.
Also it will better to keep default value for inline as false - so there is no change in behaviour of textfield for existing users.

Copy link
Copy Markdown
Contributor

@dkwingsmt dkwingsmt left a comment

Choose a reason for hiding this comment

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

Generally LGTM with minor comments.

/// Optional style for the composing (and inline prediction) region.
///
/// When set, applied to the composing range (IME and inline predictive text).
/// When null, the default is used ([TextDecoration.underline]).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How come you can use one style to represent both IME and predictive text? AFAI understand, IME text uses regular color with underline, while predictive text uses grey color and no underline.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The iOS text input implementation does not distinguish these two. UX-wise I don't think it's too big of a deal since to users they both represent uncommitted changes suggested by iOS.

Comment thread packages/flutter/lib/src/services/text_input.dart Outdated
required BuildContext context,
TextStyle? style,
required bool withComposing,
TextStyle? composingStyle,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If that's so we should solve this once and for all: Create a new data class class BuildTextSpanInfo that contains all these parameters, and use this class as the parameter instead. This way adding new parameters no longer breaks.

@LongCatIsLooong
Copy link
Copy Markdown
Contributor

FYI the analyzer is complaining:

   info • Redundant initialization to 'null' • packages/flutter/lib/src/cupertino/text_field.dart:312:5 • avoid_init_to_null
   info • Redundant initialization to 'null' • packages/flutter/lib/src/cupertino/text_field.dart:453:5 • avoid_init_to_null
   info • Redundant initialization to 'null' • packages/flutter/lib/src/material/text_field.dart:330:5 • avoid_init_to_null
   info • Redundant initialization to 'null' • packages/flutter/lib/src/services/text_input.dart:534:5 • avoid_init_to_null
   info • Redundant initialization to 'null' • packages/flutter/lib/src/widgets/editable_text.dart:921:5 • avoid_init_to_null
   info • The value of the argument is redundant because it matches the default value • packages/flutter/test/services/text_input_test.dart:420:35 • avoid_redundant_argument_values
6 issues found. (6 new) • analyzed 5479 files (ran in 12.92s)

Defaults to null (use platform default).

From my experience implementing the same feature, I was never able to trigger inline prediction on a custom UITextInput implementation when the flag is set to .default (but it works if the text field is a UITextField or UITextView). Have you been able to get inline predictions in a Flutter text field without changing the flag from null to true?

Overall the approach LGTM, I have just a question regarding the current default and not sure about the (likely breaking) change.

@loic-sharma loic-sharma self-requested a review February 26, 2026 22:09
Color? suffixIconColor,
BoxConstraints? suffixIconConstraints,
TextStyle? counterStyle,
TextStyle? composingStyle,
Copy link
Copy Markdown
Contributor

@QuncCccccc QuncCccccc Mar 10, 2026

Choose a reason for hiding this comment

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

New properties should be added in InputDecorationThemeData only. Check #168981 for more context:) Eventually the xxxTheme class should only contain data and child fields and any other properties should only exist in xxxThemeData:)

Comment thread packages/flutter/lib/src/services/text_input.dart Outdated
Comment thread packages/flutter/lib/src/services/text_input.dart Outdated
- (void)setAttributedMarkedText:(NSAttributedString*)attributedString
selectedRange:(NSRange)selectedRange API_AVAILABLE(ios(17.0)) {
NSString* markedText = attributedString ? [attributedString string] : @"";
[self setMarkedText:markedText selectedRange:selectedRange];
Copy link
Copy Markdown
Member

@loic-sharma loic-sharma Mar 11, 2026

Choose a reason for hiding this comment

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

From my understanding, this works because currently the iOS text input plugin considers any marked text as the composition string:

// Empty compositing range is represented by the framework's TextRange.empty.
NSInteger composingBase = -1;
NSInteger composingExtent = -1;
if (self.markedTextRange != nil) {
composingBase = ((FlutterTextPosition*)self.markedTextRange.start).index;
composingExtent = ((FlutterTextPosition*)self.markedTextRange.end).index;
}
NSDictionary* state = @{
@"selectionBase" : @(selectionBase),
@"selectionExtent" : @(selectionExtent),
@"selectionAffinity" : @(_selectionAffinity),
@"selectionIsDirectional" : @(false),
@"composingBase" : @(composingBase),
@"composingExtent" : @(composingExtent),
@"text" : [NSString stringWithString:self.text],
};

However, this forces the framework to use the same style for inline predictive text and the composition string. That is not desirable since these strings have different styling on iOS.

@hellohuanlin @LongCatIsLooong What do y'all think of introducing new values to the text input editing state to capture the inline predictive text's base / extent? Something like inlinePredictionBase and inlinePredictionExtent. This would let track the composition string and the inline predictive text separately, which would let us style these separately.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yes, reusing the composition text feels a bit too magical for me (#182728 (comment))

I prefer having a more explicit setup as you described, since they are semantically different concepts.

nikb7 and others added 3 commits March 12, 2026 20:46
Co-authored-by: Loïc Sharma <737941+loic-sharma@users.noreply.github.com>
Co-authored-by: Loïc Sharma <737941+loic-sharma@users.noreply.github.com>
@LongCatIsLooong LongCatIsLooong added the CICD Run CI/CD label Mar 12, 2026
@Renzo-Olivares Renzo-Olivares self-requested a review March 12, 2026 20:38
@nikb7
Copy link
Copy Markdown
Contributor Author

nikb7 commented Mar 13, 2026

Thanks everyone for reviewing the PR
i have added a minimal version of this here - #183650
This will only add the enableInlinePrediction field, no changes to any styling as of now.
I will adding those changes in separate PRs

@loic-sharma
Copy link
Copy Markdown
Member

loic-sharma commented Mar 16, 2026

Thanks! I'll convert this PR to a draft since it contains styling changes that aren't ready to be reviewed until after this lands.

Please feel free to mark this as ready for review when ready!

@loic-sharma loic-sharma marked this pull request as draft March 16, 2026 21:15
@flutter-dashboard
Copy link
Copy Markdown

This pull request has been changed to a draft. The currently pending flutter-gold status will not be able to resolve until a new commit is pushed or the change is marked ready for review again.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

github-merge-queue Bot pushed a commit that referenced this pull request Apr 1, 2026
Expose inline prediction configuration through Flutter text fields so
apps can explicitly control iOS 17 inline predictive text behavior and
ensure the engine handles attributed marked text correctly.

This PR adds an `enableInlinePrediction` option to
`TextInputConfiguration`, `EditableText`, `TextField`, and
`CupertinoTextField`, and forwards that setting to the iOS text input
plugin.

On the engine side, the iOS text input implementation now:
- maps `enableInlinePrediction` to `UITextInlinePredictionType` on iOS
17+
- defaults inline prediction to disabled unless the framework explicitly
enables it
- handles `setAttributedMarkedText:selectedRange:` by forwarding the
plain string to the existing marked text flow so inline predictive text
updates are processed correctly

This keeps the feature opt-in, preserves existing behavior by default,
and gives apps explicit control over inline predictive text on supported
iOS versions.

Fixes: #135221

Previous PR: #182728

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

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](https://developers.google.com/gemini-code-assist/docs/review-github-code).
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.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines

---------

Co-authored-by: Loïc Sharma <737941+loic-sharma@users.noreply.github.com>
mbcorona pushed a commit to mbcorona/flutter that referenced this pull request Apr 15, 2026
Expose inline prediction configuration through Flutter text fields so
apps can explicitly control iOS 17 inline predictive text behavior and
ensure the engine handles attributed marked text correctly.

This PR adds an `enableInlinePrediction` option to
`TextInputConfiguration`, `EditableText`, `TextField`, and
`CupertinoTextField`, and forwards that setting to the iOS text input
plugin.

On the engine side, the iOS text input implementation now:
- maps `enableInlinePrediction` to `UITextInlinePredictionType` on iOS
17+
- defaults inline prediction to disabled unless the framework explicitly
enables it
- handles `setAttributedMarkedText:selectedRange:` by forwarding the
plain string to the existing marked text flow so inline predictive text
updates are processed correctly

This keeps the feature opt-in, preserves existing behavior by default,
and gives apps explicit control over inline predictive text on supported
iOS versions.

Fixes: flutter#135221

Previous PR: flutter#182728

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

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](https://developers.google.com/gemini-code-assist/docs/review-github-code).
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.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines

---------

Co-authored-by: Loïc Sharma <737941+loic-sharma@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: text input Entering text in a text field or keyboard related problems CICD Run CI/CD d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos engine flutter/engine related. See also e: labels. f: cupertino flutter/packages/flutter/cupertino repository f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: 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.

6 participants