Sitelet https://github.com/flutter/flutter/pull/179928
Skip to content

fix: text selection two handles directionality. - #179928

Merged
auto-submit[bot] merged 39 commits into
flutter:masterfrom
muhammadkamel:fix/text-selection-mixed-directionality
Jul 31, 2026
Merged

auto-submit[bot] merged 39 commits into
flutter:masterfrom
muhammadkamel:fix/text-selection-mixed-directionality

Conversation

@muhammadkamel

@muhammadkamel muhammadkamel commented Dec 16, 2025 •

Copy link
Copy Markdown
Contributor

Fixes #187799

This PR fixes an issue where text selection handles would render facing the wrong direction when the text directionality differed from the ambient directionality (e.g., selecting English (LTR) text within an application set to Arabic (RTL)), and also when a single selection spans both LTR and RTL runs.

Previously, TextSelectionOverlay determined the start and end handle types based primarily on the ambient TextDirection and the logical selection range. This caused handles to swap (pointing away from the selection) in mixed-directionality scenarios.

This change updates the logic in _updateSelectionOverlay to derive each handle's type from the per-endpoint TextDirection reported by TextSelectionPoint.direction, falling back to the render object's textDirection when an endpoint does not provide one:

  • Start handle: TextDirection.ltr → TextSelectionHandleType.left, TextDirection.rtl → TextSelectionHandleType.right.
  • End handle: TextDirection.ltr → TextSelectionHandleType.right, TextDirection.rtl → TextSelectionHandleType.left.

Because each endpoint is evaluated independently, selections that cross a directionality boundary (e.g. "abc مرحبا") get handle types that match the local direction at each end, so both handles continue to "hug" the selection.

On iOS, UIKit conventionally keeps selection handles aligned with the field's own direction rather than the per-endpoint direction. To preserve platform behavior, when defaultTargetPlatform == TargetPlatform.iOS both handles use the render object's textDirection instead of the endpoint directions.

When a non-collapsed selection yields fewer than two endpoints (e.g. the selection is scrolled or clipped out of view), handle direction falls back to the field's textDirection instead of asserting.

Collapsed selections continue to use TextSelectionHandleType.collapsed for both handles and are unaffected by this change.

Tests added

Added new tests in text_selection_test.dart:

Android (TargetPlatformVariant.only(TargetPlatform.android)), driven by a table of _DirectionalityTestCases that assert the endpoint directions reported by RenderEditable and the handle types received by a spy TextSelectionControls:

  • ambient LTR, English text
  • ambient RTL, English text
  • ambient RTL, Arabic text
  • ambient LTR, Arabic text
  • ambient LTR, mixed "abc مرحبا" (English then Arabic)
  • ambient RTL, mixed "abc مرحبا" (English then Arabic)

iOS (TargetPlatformVariant.only(TargetPlatform.iOS)):

  • Verifies that for the mixed "abc مرحبا" selection under ambient LTR, both handles follow the field's textDirection (i.e. left / right) rather than the per-endpoint directions — exercising the UIKit-compatibility branch.

The new directionality tests set selectAllOnFocus: false explicitly (with an inline comment) because web CI defaults EditableText.selectAllOnFocus to true, which can select all text on focus and prevent handle rebuilds in these tests.

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.

image_issue

@github-actions github-actions Bot added a: text input Entering text in a text field or keyboard related problems framework flutter/packages/flutter repository. See also f: labels. labels Dec 16, 2025
@muhammadkamel muhammadkamel changed the title fix: text selection directionality. fix: text selection two handles directionality. Dec 16, 2025

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

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.

Code Review

This pull request effectively addresses an issue with text selection handle directionality in mixed LTR/RTL scenarios. The logic is updated to base handle types on the visual position of selection endpoints, which is a solid approach. The new tests are comprehensive and cover the relevant edge cases, ensuring the fix is robust. I have a few suggestions to improve the new test code for better readability and maintainability.

Comment thread packages/flutter/test/widgets/text_selection_test.dart Outdated
Comment thread packages/flutter/test/widgets/text_selection_test.dart Outdated
Comment thread packages/flutter/test/widgets/text_selection_test.dart Outdated
@muhammadkamel

Copy link
Copy Markdown
Contributor Author

Hi @loic-sharma,
Could you please assign another reviewer? It seems that @Renzo-Olivares might be on vacation.

@loic-sharma

Copy link
Copy Markdown
Member

@muhammadkamel It's the holidays in the US, a good chunk of the team is on vacation. Reviews might be a bit slower until January :)

@Renzo-Olivares

Copy link
Copy Markdown
Contributor

Hi @muhammadkamel thank you for your patience during the holidays. I'll be taking a look this week.

@Renzo-Olivares Renzo-Olivares left a comment

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.

Thank you for the contribution @muhammadkamel, this change definitely helps improve the text selection experience.

I verified that this is also the behavior on native mobile platforms.

Jetpack Compose (Android) with RTL set for application.

55

SwiftUI (iOS) with RTL set at application level

Screenshot 2026-01-06 at 5 32 45 PM

WRT to RTL text in general I think this also improves the experience slightly but text selection is still a bit buggy in RTL when using the text selection handles. The magnifier also seems to be broken when the application level text directionality is RTL and the textfield is LTR. That can be improved in a separate PR.

Comment thread packages/flutter/lib/src/widgets/text_selection.dart Outdated
Comment thread packages/flutter/test/widgets/text_selection_test.dart Outdated
Comment thread packages/flutter/test/widgets/text_selection_test.dart Outdated
@Renzo-Olivares

Copy link
Copy Markdown
Contributor

Hi @muhammadkamel, just wanted to follow up if there is any updates on this PR I should review? During my review last week I was still observing issues #179928 (comment). Let me know if I should take a look again.

@muhammadkamel

Copy link
Copy Markdown
Contributor Author

Hi @muhammadkamel, just wanted to follow up if there is any updates on this PR I should review? During my review last week I was still observing issues #179928 (comment). Let me know if I should take a look again.

Hi @Renzo-Olivares
I will work on fixing this behavior issue. But I have a question, should the handle (Left) or (Right) direction changed base on the text or on the app locale (Directionality)?

@muhammadkamel

Copy link
Copy Markdown
Contributor Author

Hi @Renzo-Olivares

Could you please take a look at the latest changes?
I believe the new fixes now adhere to the Android native approach.

@muhammadkamel

Copy link
Copy Markdown
Contributor Author

@Renzo-Olivares

Could you please add another reviewer so this PR can be merged as soon as possible?

@Renzo-Olivares

Copy link
Copy Markdown
Contributor

Hi @muhammadkamel, thank you for your patience on this one. After confirming some of the native behavior on Jetpack compose and Android View API's, I think we are able to closely match the native behavior with your previous change in 7ff9971 . Checking the TextDirection of the TextSelectionPoint end points should be enough as you did in that commit.

I was mistaken about the multi-line behavior before in #179928 (comment). Though I still think there may be a bug when moving the selection across multiple lines in the scenario where the App's directionality is RTL but the text is LTR, in the linked image the selection originally started on the first line on the word "world" but moving that selection to the line below, adds the "?" on the first line to the selection. This can be solved in a different PR though as the issue existed before this change.

I think some of the recent additional changes like passing the directionality to the TextSelectionOverlay, and _SelectionHandleOverlay are not needed. Can you explain why the changes for getEndPointsForSelection are needed and EditableText?

@muhammadkamel

Copy link
Copy Markdown
Contributor Author

Hi @muhammadkamel, thank you for your patience on this one. After confirming some of the native behavior on Jetpack compose and Android View API's, I think we are able to closely match the native behavior with your previous change in 7ff9971 . Checking the TextDirection of the TextSelectionPoint end points should be enough as you did in that commit.

I was mistaken about the multi-line behavior before in #179928 (comment). Though I still think there may be a bug when moving the selection across multiple lines in the scenario where the App's directionality is RTL but the text is LTR, in the linked image the selection originally started on the first line on the word "world" but moving that selection to the line below, adds the "?" on the first line to the selection. This can be solved in a different PR though as the issue existed before this change.

I think some of the recent additional changes like passing the directionality to the TextSelectionOverlay, and _SelectionHandleOverlay are not needed. Can you explain why the changes for getEndPointsForSelection are needed and EditableText?

Hi @Renzo-Olivares

I've updated the PR based on your feedback. The changes now match Android native and Jetpack Compose behavior:

✅ Removed the Directionality wrapper from handle builders
✅ Removed textDirection from TextSelectionOverlay and _SelectionHandleOverlay
✅ Removed startHandleDirection/endHandleDirection properties
✅ Reverted changes to getEndPointsForSelection and EditableText
The fix now simply checks the TextDirection of the TextSelectionPoint endpoints as you suggested.

I also kept a small addition in didChangeDependencies() to handle dynamic layout direction switching, ensuring handles reposition correctly when users switch between LTR and RTL layouts.

@Renzo-Olivares Renzo-Olivares left a comment

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.

Hi @muhammadkamel, thank you for the quick changes, I left a few comments. I'm also still seeing the changes to getEndPointsForSelection and EditableText.

Comment thread packages/flutter/lib/src/widgets/text_selection.dart Outdated
Comment thread packages/flutter/lib/src/widgets/text_selection.dart Outdated
Comment thread packages/flutter/lib/src/widgets/text_selection.dart
@Renzo-Olivares

Copy link
Copy Markdown
Contributor

Hi @muhammadkamel, just checking in if this needs a re-review. I'm still seeing the changes to getEndPointsForSelection and EditableText that we discussed removing.

Before merging this PR should also pass all existing tests.

@Renzo-Olivares
Renzo-Olivares removed their request for review February 17, 2026 20:57
@justinmc

justinmc commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

@muhammadkamel I'm going to close this PR as inactive, but if you end up deciding to come back to it just let me know and I'll reopen.

@justinmc justinmc closed this Mar 3, 2026
@muhammadkamel

Copy link
Copy Markdown
Contributor Author

Hi @justinmc

Sorry for late, I was busy. Could you please re-open it?

@fluttergithubbot

Copy link
Copy Markdown
Contributor

An existing Git SHA, a7bf309216b0a1ab5fca2a4afed21a8d44cf0eca, was detected, and no actions were taken.

To re-trigger presubmits after closing or re-opeing a PR, or pushing a HEAD commit (i.e. with --force) that already was pushed before, push a blank commit (git commit --allow-empty -m "Trigger Build") or rebase to continue.

@muhammadkamel

Copy link
Copy Markdown
Contributor Author

@Renzo-Olivares Could you take another look when you have a chance? I addressed the latest comments and narrowed the remaining changes.

Comment thread packages/flutter/lib/src/rendering/editable.dart Outdated
Comment thread packages/flutter/lib/src/widgets/editable_text.dart Outdated
Comment thread packages/flutter/lib/src/widgets/text_selection.dart Outdated
Comment thread packages/flutter/lib/src/widgets/text_selection.dart Outdated
Comment thread packages/flutter/lib/src/widgets/text_selection.dart
Comment thread packages/flutter/lib/src/widgets/text_selection.dart Outdated
@justinmc

Copy link
Copy Markdown
Contributor

@muhammadkamel FYI you have a branch conflict to resolve here.

@muhammadkamel

Copy link
Copy Markdown
Contributor Author

@muhammadkamel FYI you have a branch conflict to resolve here.

Hi @justinmc

Thanks!
I have resolved the conflict.

@Renzo-Olivares Renzo-Olivares added the CICD Run CI/CD label Jul 16, 2026
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Jul 16, 2026
@Renzo-Olivares Renzo-Olivares added the CICD Run CI/CD label Jul 20, 2026
@Renzo-Olivares

Renzo-Olivares commented Jul 20, 2026 •

Copy link
Copy Markdown
Contributor

@muhammadkamel can you try to rebase this to the latest master. Tests seem to be stuck at the moment.

Comment thread packages/flutter/test/widgets/text_selection_test.dart Outdated
Comment thread packages/flutter/lib/src/widgets/text_selection.dart Outdated
Comment thread packages/flutter/lib/src/widgets/text_selection.dart Outdated
@muhammadkamel

Copy link
Copy Markdown
Contributor Author

Hi @Renzo-Olivares — addressed your latest comments:

Removed the unused ValueKey from DirectionalitySpyTextSelectionControls (the spy already asserts via builtHandleTypes).
Reverted the unrelated toolbarIsVisible doc change.
Updated the < 2 endpoints fallback comment to your suggested wording (render lag / split graphemes / degenerate layout).
Please take another look when you have a chance.

Renzo-Olivares
Renzo-Olivares previously approved these changes Jul 29, 2026

@Renzo-Olivares Renzo-Olivares left a comment

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.

LGTM w/ one small nit. Thank you for your patience on this one!

Comment thread packages/flutter/test/widgets/text_selection_test.dart Outdated

@Renzo-Olivares Renzo-Olivares left a comment

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.

RE-LGTM, thank you for the contribution @muhammadkamel!

@muhammadkamel

Copy link
Copy Markdown
Contributor Author

RE-LGTM, thank you for the contribution @muhammadkamel!

You're welcome! :)

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 framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Text selection handles face wrong direction in RTL app with LTR text

7 participants