Skip to content
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

handleSelectWord in MultiSelectableSelectionContainerDelegate should handle rects inside of rects #127478

Merged
merged 15 commits into from Jun 6, 2023

Conversation

Renzo-Olivares
Copy link
Contributor

@Renzo-Olivares Renzo-Olivares commented May 24, 2023

Fixes #127076

Sometimes a Selectables rect may contain another Selectables rect within it. In the case of handleSelectWord when choosing which Selectable to dispatch the SelectionEvent to, the event would be dispatched to the wrong Selectable causing an assertion error to be thrown.

Screenshot 2023-05-24 at 2 46 13 AM

In the picture above the red outline shows the rect of a two-line piece of text. And the blue rect shows the rect of a piece of text that is on the second line of the two-line piece of text, but has been separated into its own rect for some case, for example when TextSpans are separated by a WidgetSpan.

We should check if the text layout of the selectable that has been dispatched the SelectionEvent contains the word, if not then return SelectionResult.next, and continue to look through the list of selectables.

Pre-launch Checklist

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

@flutter-dashboard flutter-dashboard bot added the framework flutter/packages/flutter repository. See also f: labels. label May 24, 2023
@github-actions github-actions bot removed the framework flutter/packages/flutter repository. See also f: labels. label May 24, 2023
@flutter-dashboard flutter-dashboard bot added the framework flutter/packages/flutter repository. See also f: labels. label May 24, 2023
@github-actions github-actions bot removed the framework flutter/packages/flutter repository. See also f: labels. label May 24, 2023
Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

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

LGTM but maybe give @chunhtai a chance to take a look too.

@@ -1892,7 +1923,15 @@ abstract class MultiSelectableSelectionContainerDelegate extends SelectionContai
final Rect localRect = Rect.fromLTWH(0, 0, selectables[index].size.width, selectables[index].size.height);
final Matrix4 transform = selectables[index].getTransformTo(null);
final Rect globalRect = MatrixUtils.transformRect(transform, localRect);
if (globalRect.contains(event.globalPosition)) {
final List<Rect> rectsInside = _selectableContains(selectables[index], selectables);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this making this O(n^2) instead of O(n) before? Not sure if this is a bottle neck at all, just pointing it out.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah this would be O(n^2) now. I couldn't think of a re-factor for the current solution that would make this O(n). @chunhtai do you have any thoughts on this?

Copy link
Contributor

@chunhtai chunhtai May 31, 2023

Choose a reason for hiding this comment

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

I am not sure if this is the right fix. I thought we may run into the same problem even if the inner selectable is not completely contained by the outer one?

In the example
image
It seems to me the right behavior is that if the selectword at the inside of inner selectable, the outer box should return SelectionResult.next because it should have known the coordinate is outside its actual text layout.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That makes a lot of sense! That makes the solution a lot simpler and keeps this O(n). Thanks for the input!

@Renzo-Olivares Renzo-Olivares force-pushed the fix-select-word-assert branch 3 times, most recently from 5183712 to 3efb197 Compare May 31, 2023 18:13
@flutter-dashboard flutter-dashboard bot added the framework flutter/packages/flutter repository. See also f: labels. label May 31, 2023
@github-actions github-actions bot removed the framework flutter/packages/flutter repository. See also f: labels. label May 31, 2023
Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

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

LGTM 👍

Good call @chunhtai, this way seems a lot better.

@@ -1478,7 +1478,10 @@ class _SelectableFragment with Selectable, ChangeNotifier implements TextLayoutM
assert(word.isNormalized);
// Fragments are separated by placeholder span, the word boundary shouldn't
// expand across fragments.
assert(word.start >= range.start && word.end <= range.end);
final bool wordInsideSelectableBounds = word.start >= range.start && word.end <= range.end;
Copy link
Contributor

Choose a reason for hiding this comment

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

There should only be three situation

  1. both word.end and word.start is smaller than range.start
  2. both word.end and word.start is larger than range.end
  3. range completely cover the word.

(1) should return prev.
(2) should return next.
(3) should return end.

if not in above three case it should throw.

@@ -1894,7 +1894,10 @@ abstract class MultiSelectableSelectionContainerDelegate extends SelectionContai
final Rect globalRect = MatrixUtils.transformRect(transform, localRect);
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we change the selectword contract that it should return next/prev we also need to update handleSelectWord to return pre/next. i.e. if all selectable return next. or if the selectable return prev.

Also should probably assert if a selectable return prev, it must be the first in the selectables.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I understand why this is needed but for clarification purposes can you confirm my assumptions. We need to update handleSelectWord to return next/prev because before this change it only return none/end. none if the word was not selected given the list of selectables managed by the container delegate. And end if the word was selected selected given the list of selectables.

Now since select word can return next/prev, handleSelectWord should be updated to also do this in the following cases:
(1) return next when the last SelectionResult is next
(2) return prev when the last SelectionResult is prev

Is this correct?

Also should probably assert if a selectable return prev, it must be the first in the selectables.

  • Not sure I completely understand why for this.

Copy link
Contributor

@chunhtai chunhtai May 31, 2023

Choose a reason for hiding this comment

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

none if the word was not selected given the list of selectables managed by the container delegate. And end if the word was selected selected given the list of selectables.

This is correct prior to this change, though the return value was never used so it doesn't really matter what value is return.

Now since select word can return next/prev, handleSelectWord should be updated to also do this in the following cases:
(1) return next when the last SelectionResult is next
(2) return prev when the last SelectionResult is prev

(2) is incorrect, handleSelectWord should only return prev if the selectionResult of first selectable return prev.

Is this correct?

yes this is correct and it is necessary because this selectionContainer can be a child selectable of another container, and it can also have the overlap issue with another selectable.

Not sure I completely understand why for this.

Nvm I think i was wrong, it is possible a selectable that is not the first selectable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for the clarification, I think I got it!

for (int index = 0; index < selectables.length; index += 1) {
final Rect localRect = Rect.fromLTWH(0, 0, selectables[index].size.width, selectables[index].size.height);
final Matrix4 transform = selectables[index].getTransformTo(null);
final Rect globalRect = MatrixUtils.transformRect(transform, localRect);
if (globalRect.contains(event.globalPosition)) {
final SelectionGeometry existingGeometry = selectables[index].value;
dispatchSelectionEventToChild(selectables[index], event);
lastSelectionResult = dispatchSelectionEventToChild(selectables[index], event);
if (lastSelectionResult == SelectionResult.previous || lastSelectionResult == SelectionResult.next) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems weird, it should stop as soon as dispatchSelectionEventToChild does not return next.

imagine this case

in sequential order

selectable1 -> next (continue)
selectable2 -> next (continue)
selectable3 -> prev (should stop)

this meant the selectword land in between selectable 2 and 3, one of them should be set to the currentSelectionStartIndex and currentSelectionEndIndex, I don't think it matter which one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That makes sense, but is this assuming that the selectables are in order of how the text is visually laid out?

Trying something like the following code, and the first selectable in the list is Hello world this is some long text that goes\non two lines. The second selectable is Word, and the third selectable is Word Word2.

If I change it to stop as soon as dispatchSelectionEventToChild does not return next then I get the following.

In sequential order this happens.

selectable1 -> prev (should stop)
currentSelectionStartIndex/currentSelectionEndIndex : null
should return SelectionResult.prev

Is this expected? Trying to right click Word at the beginning of the text fails since we never reach it.

import 'package:flutter/material.dart';

void main() {
  runApp(const MainApp());
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(
        body: Center(
          child: SelectionArea(
            child: Text.rich(
              TextSpan(
                children: <InlineSpan>[
                  TextSpan(text: 'Word'),
                  WidgetSpan(child: SizedBox.shrink()),
                  TextSpan(text: ' Hello world this is some long text that goes\non two lines.'),
                  WidgetSpan(child: SizedBox.shrink()),
                  TextSpan(text: 'Word'),
                  TextSpan(text: ' Word2'),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Copy link
Contributor

Choose a reason for hiding this comment

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

the selectables are in order of how the text is visually laid out?

they should be. I think the issue you encounter was a bug that the function to calculate screen order does not do well when the rect overlaps.

int _compareScreenOrder(Selectable a, Selectable b) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh okay I have tried to fix that, and have updated the handleSelectWord logic as well.

if (lastSelectionResult == SelectionResult.next) {
return SelectionResult.next;
} else if (lastSelectionResult == SelectionResult.previous) {
return SelectionResult.previous;
Copy link
Contributor

@chunhtai chunhtai May 31, 2023

Choose a reason for hiding this comment

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

This doesn't look right. consider these cases, assuming 5 child, skip means the selectword point is not in the rect

selectable 1 -> skipped (continue)
selectable 2 -> next (continue)
selectable 3 -> next (continue)
selectable 4 -> prev (stop here)

currentSelectionStartIndex/currentSelectionEndIndex : 3 or 4
should return SelectionResult.end

selectable 1 -> skipped (continue)
selectable 2 -> prev (stop here)

currentSelectionStartIndex/currentSelectionEndIndex : 2
should return SelectionResult.end

selectable 1 -> prev (stop here)

currentSelectionStartIndex/currentSelectionEndIndex : null
should return SelectionResult.prev

selectable 1 -> skip (continue)
selectable 2 -> next (continue)
selectable 3 -> end (stop here)

currentSelectionStartIndex/currentSelectionEndIndex : 3
should return SelectionResult.end

selectable 1 -> skip (continue)
selectable 2 -> next (continue)
selectable 3 -> skip (stop here)

currentSelectionStartIndex/currentSelectionEndIndex : 2
should return SelectionResult.end

selectable 1 -> skip (continue)
selectable 2 -> skip (continue)
selectable 3 -> skip (continue)
selectable 4 -> next (continue)
selectable 5 -> next (at the end)

currentSelectionStartIndex/currentSelectionEndIndex : null
should return SelectionResult.next

selectable 1 -> skip (continue)
selectable 2 -> skip (continue)
selectable 3 -> skip (continue)
selectable 4 -> skip (continue)
selectable 5 -> skip (at the end)

currentSelectionStartIndex/currentSelectionEndIndex : null
should probably return SelectionResult.next

@github-actions github-actions bot added the framework flutter/packages/flutter repository. See also f: labels. label Jun 1, 2023
@@ -1903,9 +1916,12 @@ abstract class MultiSelectableSelectionContainerDelegate extends SelectionContai
.forEach((Selectable target) => dispatchSelectionEventToChild(target, const ClearSelectionEvent()));
currentSelectionStartIndex = currentSelectionEndIndex = index;
}
return SelectionResult.end;
return lastSelectionResult;
Copy link
Contributor

@chunhtai chunhtai Jun 5, 2023

Choose a reason for hiding this comment

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

It seems this is still not addressed #127478 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, think it should be fixed now. But I'm a bit unsure about this case

5.
selectable 1 -> skip (continue)
selectable 2 -> next (continue)
selectable 3 -> skip (stop here)

currentSelectionStartIndex/currentSelectionEndIndex : 2
should return SelectionResult.end

Why does it stop at the last skip?

Copy link
Contributor

Choose a reason for hiding this comment

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

This assume all selectable after 3 are skipped. If not, it is likely a bug in sorting screen order. I am actually not sure whether this should return next or end, but I can't think of a case returning end would be a problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see, so what this case means is that in a given list of selectables a next was reported but it was not followed by a prev/or end that would indicate the word is inside of the list of selectables? Why in this case then do we still have to set the currentSelectionStartIndex/currentSelectionEndIndex : 2, when that might not be where the word is? Thank you for explaining.

Copy link
Contributor

@chunhtai chunhtai Jun 5, 2023

Choose a reason for hiding this comment

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

After I thought about it more, I think we should return end to short-circuit the logic. Otherwise the select world logic would need to go through every selectable in the subtree. to figure out whether the position has hit any selectables.

There are two mechanism in play here: the rect and selection result. In theory, we don't need rect, and we can completely rely on selection result. The rect is here so that we can trim the search tree. If we were to return next in this kind of un-decided case, we might as well remove the rect check completely.

That also reminding me I was wrong on a previous case. These two case should be the following

selectable 1 -> skip (continue)
selectable 2 -> next (continue)
selectable 3 -> skip (stop here)

currentSelectionStartIndex/currentSelectionEndIndex : 2
should return SelectionResult.end

7
selectable 1 -> skip (continue)
selectable 2 -> skip (continue)
selectable 3 -> skip (continue)
selectable 4 -> skip (continue)
selectable 5 -> skip (at the end)

currentSelectionStartIndex/currentSelectionEndIndex : null
should return SelectionResult.end

Copy link
Contributor

Choose a reason for hiding this comment

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

basically,

if the first selection return prev, this method return prev
if the [n...length-1] return next, this method return next
else return SelectionResult.end.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for the very in depth explanation. I understand now.

}
}
if (lastSelectionResult == SelectionResult.next) {
return SelectionResult.next;
}
return SelectionResult.none;
Copy link
Contributor

Choose a reason for hiding this comment

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

This method should never return none

Copy link
Contributor

@chunhtai chunhtai left a comment

Choose a reason for hiding this comment

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

LGTM, just one concern about the assert being to strict

}
}
return SelectionResult.none;
assert(lastSelectionResult == null);
Copy link
Contributor

Choose a reason for hiding this comment

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

This could be null if every selectable is skipped?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This should be okay right? We should always return inside of the loop, if we are not that means every selectable was skipped, and in that case lastSelectionResult should always be null. Can't really think of a case where we don't return inside of the loop besides the screen order not being correct.

If lastSelectionResult == prev is inside the loop, it will always return, same for end, and also next followed by a skip, and next as the final selectable.

Copy link
Contributor

Choose a reason for hiding this comment

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

oh sorry I read the assert wrong. Yes this is correct.

@Renzo-Olivares Renzo-Olivares added autosubmit Merge PR when tree becomes green via auto submit App and removed autosubmit Merge PR when tree becomes green via auto submit App labels Jun 6, 2023
@auto-submit auto-submit bot merged commit 0e1056f into flutter:master Jun 6, 2023
72 checks passed
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jun 7, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jun 7, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jun 7, 2023
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Jun 7, 2023
Roll Flutter from 0b7415356e07 to 8a5c22e282db (46 revisions)

flutter/flutter@0b74153...8a5c22e

2023-06-07 5236035+fzyzcjy@users.noreply.github.com Super tiny MediaQuery doc update (flutter/flutter#127904)
2023-06-07 jacksongardner@google.com Revert "Make inspector weakly referencing the inspected objects." (flutter/flutter#128436)
2023-06-07 leigha.jarett@gmail.com Update menu API docs to help developers migrate to m3 (flutter/flutter#128351)
2023-06-07 andrewrkolos@gmail.com [tools] allow explicitly specifying the JDK to use via a new config setting (flutter/flutter#128264)
2023-06-07 engine-flutter-autoroll@skia.org Roll Flutter Engine from 6f9df0f988c1 to 59d5444cf06c (3 revisions) (flutter/flutter#128376)
2023-06-07 andrewrkolos@gmail.com Do not try to load main/default asset image if only higher-res variants exist (flutter/flutter#128143)
2023-06-07 92602467+99spark@users.noreply.github.com Addressed Ambiguity in transform.scale constructor docs (flutter/flutter#128182)
2023-06-07 5236035+fzyzcjy@users.noreply.github.com Super tiny fix of dead link (flutter/flutter#128160)
2023-06-07 goderbauer@google.com Refactor tests (flutter/flutter#128371)
2023-06-07 polinach@google.com Make inspector weakly referencing the inspected objects. (flutter/flutter#128095)
2023-06-07 goderbauer@google.com Add viewId to PointerEvents (flutter/flutter#128287)
2023-06-07 5236035+fzyzcjy@users.noreply.github.com Show error message in release mode when box is not laid out without losing performance (flutter/flutter#126302)
2023-06-06 engine-flutter-autoroll@skia.org Roll Flutter Engine from ca499463ec2e to 6f9df0f988c1 (1 revision) (flutter/flutter#128363)
2023-06-06 khanhnwin@gmail.com Update Draggable YouTube video link (flutter/flutter#128078)
2023-06-06 31859944+LongCatIsLooong@users.noreply.github.com Remove more rounding hacks from TextPainter (flutter/flutter#127826)
2023-06-06 engine-flutter-autoroll@skia.org Roll Flutter Engine from 4571695f9e76 to ca499463ec2e (1 revision) (flutter/flutter#128356)
2023-06-06 43054281+camsim99@users.noreply.github.com [Android] Update plugin and module templates to use Flutter constant for `compileSdkVersion` (flutter/flutter#128073)
2023-06-06 rmolivares@renzo-olivares.dev handleSelectWord in MultiSelectableSelectionContainerDelegate should handle rects inside of rects (flutter/flutter#127478)
2023-06-06 christopherfujino@gmail.com [flutter_tools] never tree shake 0x20 (space) font codepoints on web (flutter/flutter#128302)
2023-06-06 31859944+LongCatIsLooong@users.noreply.github.com Remove `textScaleFactor` dependent logic from `AppBar` (flutter/flutter#128112)
2023-06-06 engine-flutter-autoroll@skia.org Roll Flutter Engine from b6d37f8f74ad to 4571695f9e76 (6 revisions) (flutter/flutter#128350)
2023-06-06 5236035+fzyzcjy@users.noreply.github.com Fix `Null check operator used on a null value` on TextField with contextMenuBuilder (flutter/flutter#128114)
2023-06-06 engine-flutter-autoroll@skia.org Roll Packages from db4e5c2 to da72219 (10 revisions) (flutter/flutter#128348)
2023-06-06 91688203+yusuf-goog@users.noreply.github.com Updating cirrus docker image to ubuntu focal. (flutter/flutter#128291)
2023-06-06 leigha.jarett@gmail.com Adding example for migrating to navigation drawer (flutter/flutter#128295)
2023-06-06 engine-flutter-autoroll@skia.org Roll Flutter Engine from 722aad83e5fe to b6d37f8f74ad (2 revisions) (flutter/flutter#128341)
2023-06-06 goderbauer@google.com Clean-up viewId casts in flutter_test (flutter/flutter#128256)
2023-06-06 kevinjchisholm@google.com Update cherry-pick issue template to more uniform labels. (flutter/flutter#128333)
2023-06-06 hans.muller@gmail.com Use Material3 in the 2D viewport tests (flutter/flutter#128155)
2023-06-06 nbosch@google.com Use a `show` over a `hide` for `test_api` exports (flutter/flutter#128298)
2023-06-06 engine-flutter-autoroll@skia.org Roll Flutter Engine from aaa7574375a6 to 722aad83e5fe (1 revision) (flutter/flutter#128307)
2023-06-06 leigha.jarett@gmail.com Migration guide for moving from BottomNavigationBar to NavigationBar (flutter/flutter#128263)
2023-06-06 mdebbar@google.com [web] Use 'Uri' instead of 'dart:html' to extract pathname (flutter/flutter#127983)
2023-06-06 engine-flutter-autoroll@skia.org Roll Flutter Engine from 2b353ae90731 to aaa7574375a6 (4 revisions) (flutter/flutter#128301)
2023-06-05 engine-flutter-autoroll@skia.org Roll Flutter Engine from 220ece4d9faa to 2b353ae90731 (1 revision) (flutter/flutter#128293)
2023-06-05 49699333+dependabot[bot]@users.noreply.github.com Bump actions/labeler from 4.0.4 to 4.1.0 (flutter/flutter#128290)
2023-06-05 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7f12e3497428 to 220ece4d9faa (6 revisions) (flutter/flutter#128282)
2023-06-05 jonahwilliams@google.com [framework] attempt non-key solution (flutter/flutter#128273)
2023-06-05 chillers@google.com [labeler] Fix adding labels when name is directory (flutter/flutter#128243)
2023-06-05 katelovett@google.com Remove scrollbar deprecations isAlwaysShown and hoverThickness (flutter/flutter#127351)
2023-06-05 katelovett@google.com Fix update drag error that made NestedScrollView un-scrollable (flutter/flutter#127718)
2023-06-05 engine-flutter-autoroll@skia.org Roll Flutter Engine from f9f72388a4da to 7f12e3497428 (4 revisions) (flutter/flutter#128271)
2023-06-05 goderbauer@google.com Migrate SemanticsBinding to onSemanticsActionEvent (flutter/flutter#128254)
2023-06-05 engine-flutter-autoroll@skia.org Roll Flutter Engine from c838a1b05924 to f9f72388a4da (19 revisions) (flutter/flutter#128252)
2023-06-05 jonahwilliams@google.com [framework] force flexible space background to rebuild. (flutter/flutter#128138)
2023-06-05 engine-flutter-autoroll@skia.org Roll Packages from 75085ed to db4e5c2 (4 revisions) (flutter/flutter#128246)
...
liangxianzhe added a commit to chooly/flutter that referenced this pull request Jul 15, 2023
This is a bug introduced by flutter#127478. The "else" logic that changed by
this PR is supposed to run even if the last child returns "next".
Without the fix, there would be an assert error.
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Aug 16, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Aug 17, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Aug 17, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Aug 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
autosubmit Merge PR when tree becomes green via auto submit App framework flutter/packages/flutter repository. See also f: labels.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

_SelectableFragment _handleSelectWord assert failed
3 participants