Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[web] autofocus in new routes #47727

Merged
merged 7 commits into from
Dec 21, 2023
Merged

Conversation

yjbanov
Copy link
Contributor

@yjbanov yjbanov commented Nov 7, 2023

Fixes flutter/flutter#138371

When a new route pops up the expectation is that the screen reader focuses on something in the new route. Since routes typically result in DOM nodes being replaced, the current effect is that the screen reader simply unfocuses from the page, causing the user to have to refocus on back on the page and look for elements to interact with, which is a poor user experience. The current workaround is to use autofocus, but that doesn't scale as it's easy to forget, and if the route in question is maintained by a different person you may not even have enough control over it to set autofocus on anything. For example, this is the case with Flutter's default date picker. All you have is showDatePicker and there's no way to control the focus.

With this change the route (managed by the Dialog primary role) will check if a widget requested explicit focus (perhaps using autofocus), and if not, looks for the first descendant that a screen reader can focus on, and requests focus on it. The auto-focused element does not have to be literally focusable. For example, plain Text nodes do not have input focus (i.e. they are not isFocusable) but screen readers can still focus on them. If such an element is found, the web engine requests that the browser move focus to it programmatically (element.focus()), which causes the screen reader to move the a11y focus to it as well, but it sets tabindex=-1 so the element is not focusable via keyboard or mouse.

@github-actions github-actions bot added the platform-web Code specifically for the web engine label Nov 7, 2023
// focus on something inside it. There could be two possibilities:
//
// 1. The framework explicitly marked a node inside the dialog as focused
// via the `isFocusabe` and `isFocused` flags. In this case, the node
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: typo isFocusabe

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch! Done.

@yjbanov yjbanov marked this pull request as ready for review November 13, 2023 20:46
@flutter-dashboard
Copy link

It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption to this rule, contact Hixie or stuartmorgan on the #hackers channel in Chat (don't just cc them here, they won't see it! Use Discord!).

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.

@Hixie
Copy link
Contributor

Hixie commented Nov 13, 2023

test-exempt: has tests
bonus points for figuring out why the bot didn't notice...

@yjbanov yjbanov requested a review from marcianx November 13, 2023 22:24
@christopherfujino
Copy link
Contributor

test-exempt: has tests bonus points for figuring out why the bot didn't notice...

spent 10 minutes looking, and I have no idea. Seems like lib/web_ui/test/engine/semantics/semantics_test.dart should have matched the regex at https://github.com/flutter/cocoon/blob/main/app_dart/lib/src/request_handlers/github/webhook_subscription.dart#L37.

if (semanticsObject.isFocusable) {
final Focusable? focusable = this.focusable;
if (focusable != null) {
return focusable.focusAsRouteDefault();
Copy link
Contributor

Choose a reason for hiding this comment

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

If this returns false, should it not continue further below to make more attempts at focusing?

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 it would be confusing if a node is explicitly focusable but when its focus management logic rejects focus we look for other ways for it to take it. It's probably better to look for another node that's more willing to take focus. But maybe I'm missing something. Do you have an example where we'd want to insist that a node take focus even if its focus management doesn't want it?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't have any particular example here. Just wondering in the abstract whether the logic applied to non-focusable items below should be applied to focusable items that don't take focus because in both cases, the object is not able to take direct focus. It's all up to you.

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. Let's take a disabled button, for example. The button does not take input focus because it's disabled. However, a screen reader can still find it via traversal, so it could theoretically be as much of an auto-focus candidate as static text is. Is this what you mean?

I'll need to test what effect calling .focus() on disabled controls has. I imagine it may be a noop, or it may depend on whether an element carries intrinsic semantics/focusability (e.g. <button>, <input>), or whether it's just a "div" with ARIA attributes on it.

Copy link
Contributor

Choose a reason for hiding this comment

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

.focus() is a noop on disabled native controls unless they have any valid tabindex on it (including -1), thereby allowing programmatic focus, as you're already aware below.
The disabled button is an interesting example. I think with that you've sold me.
But to confirm: are focusable entities that don't get primary focus considered focusable? If so, then I wonder whether one should recurse.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks. What I'll do is keep it as is for now, and wait for real world feedback to see how this logic could be improved. Even with the corner cases, I think the new approach already improves the situation considerably.

@@ -2270,6 +2352,23 @@ class EngineSemanticsOwner {
return true;
}());
}

/// True, if any semantics node requested focus explicitly.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm unclear on what the duration of the "requested focus" part is. Is this true if any semantics node has ever requested focus? Or in the last animation frame (and this it's intended to be turned to false at the end of the animation frame)? Or is this referring to autofocus (I imagine not since the DFS traversal above sets it to false)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. I expanded the docs. The relevant time frame for this to be true is during a semantics update (in the middle of updateSemantics invocation). Otherwise, this field is almost always false. Simply put, if any node in the semantics update requests focus explicitly, then there's no need to apply default focus. So the flag is flipped to true to shut off the default. At the end of the update the flag is reset back to false allowing the default logic to be used on future updates.

@@ -2873,6 +2894,210 @@ void _testDialog() {

semantics().semanticsEnabled = false;
});

// Test the simple scenario of a dialog coming up and containing focusable
// descentants that are not initially focused. The expectation is that the
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: "descentants"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

});

// Test the scenario of a dialog coming up and containing non-focusable
// descentants that can have a11y focus. The expectation is that the first
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: "descentants"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.


// Test the scenario of a dialog coming up and containing non-focusable
// descentants that can have a11y focus. The expectation is that the first
// descendant will be auto-focused.
Copy link
Contributor

Choose a reason for hiding this comment

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

"...even if it's not focusable."
(surprising to me, as I expected it to go to the first focusable descendant!)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are there any guidelines around this? My logic to focus on the first accessibility-focusable element is as follows:

For a screen reader user, nearly everything is focusable. The screen reader can focus on things that keyboard and mouse cannot, such as text (headings, paragraph), images, and other read-only elements. What the framework considers "focusable" is widgets that have input focus. You can interact with those widgets using keyboard and/or mouse. So let's say we auto-focused on the first widget from that subset. Consider a dialog with some headings, text, and buttons. Your screen reader starts in some random widget in the middle. To figure out what's on the dialog, you have to go back and forth to find the boundaries of the dialog and read available information. But if we focus on the first thing that can have accessibility focus, then all you need is to traverse the dialog forward, read through the text, then find the button you are interested in pressing. Defaulting to the first button may not even be the correct button to focus on by default. If it is desired that focus goes to an input-focusable widget, the app can still focus on it explicitly. The logic in this PR only kicks in if the framework did not communicate explicit focus to any descendant.

But it's totally possible that I'm not thinking about this correctly.

@@ -236,4 +236,7 @@ class Scrollable extends PrimaryRoleManager {
semanticsObject.owner.removeGestureModeListener(_gestureModeListener);
_gestureModeListener = null;
}

@override
bool focusAsRouteDefault() => false;
Copy link
Contributor

Choose a reason for hiding this comment

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

What if it's focusable, though? E.g. we've had to make some scrollable child containers focusable so that a keyboard-only user can scroll it with left/right arrow keys.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment on lines +35 to +36
// Case 2: nothing requested explicit focus. Focus on the first descendant.
_setDefaultFocus();
Copy link
Contributor

Choose a reason for hiding this comment

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

We may need to add a 3rd case:

  1. Nothing was focused before the semantics update, and nothing requested focus during this update, so leave everything as is.

This 3rd case will be common when Flutter is embedded inside another app, where the screen reader might be focusing somewhere else outside of the Flutter app. We don't want to steal that focus, do we?

Copy link
Contributor

Choose a reason for hiding this comment

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

If something in the embedded flutter view triggered a route change inside, we should bring the focus here I think.

Copy link
Contributor

Choose a reason for hiding this comment

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

In other platform we send screen change accessibilityEvent and let OS decide whether to move the accessibility focus. so we are in an uncharted territory here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When we have focusability at the FlutterView level, one possible middle ground could be:

/// Case 3: the route popped up inside a FlutterView that's not currently
            focused. Do not auto-focus inside the route because that could
            "steal" focus from an element outside the current FlutterView.

In the meantime, I agree with @chunhtai that if a route change happened, it is likely because the user took some action to push that route, and the most common outcome should be that a widget inside the new route takes focus. We can revisit when we have FlutterView focusability (currently worked on by @tugorez).

// focus on something inside it. There could be two possibilities:
//
// 1. The framework explicitly marked a node inside the dialog as focused
// via the `isFocusable` and `isFocused` flags. In this case, the node
Copy link
Contributor

Choose a reason for hiding this comment

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

isFocusable/isFocused are refering to keyboard focus. For a sighted user it is ok if some element in the middle of the page request keyboard focus and the system bring focus to there. For a visually impaired user, this will make them confused.

Should we always do (2) if screen reader is turned on.

Copy link
Contributor

Choose a reason for hiding this comment

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

It really depends on the type of dialog. For example for a role="alertdialog", it's very common to move focus into the Ok button since when the focus first moves into the alert dialog, the screen reader vocalizes the entire contents of the dialog. Of course, the choice to focus on the "Ok" dialog would ideally by done via an explicit autofocus rather than implicit focus. So maybe you may decide that that consideration might be less relevant for fallback behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

On the web we do not know if the screen reader is turned on, so we have to pick one option. I'm not sure if focusing on the first keyboard-focusable will produce good result, as there's no guarantee that the widget will be correct one, and landing in the middle of the dialog, the user will have to traverse backwards then forwards to find the widget they are interested in.

I think keeping the logic simple (i.e. what it is in the PR right now) while leaving the option to use autofocus on the framework should be flexible enough.

@@ -16,6 +16,39 @@ class Dialog extends PrimaryRoleManager {
// names its own route an `aria-label` is used instead of `aria-describedby`.
addFocusManagement();
addLiveRegion();

Copy link
Contributor

Choose a reason for hiding this comment

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

should this class be named Route instead? It took me a while to understand a dialog is corresponding to scopesRoute = true

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The PrimaryRoleManager is called Dialog because it sets the ARIA role="dialog", which is the unique role identifier. I can rename it, but I'd prefer doing it in a separate PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

If every route is a role="dialog", wouldn't the screen reader announce every MaterialPageRoute a dialog when focused? Moving this to a separate pr sgtm

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently "dialog" is the only available ARIA role. A route expressed as a transition to a different screen is expressed on the web by changing the URL (which we already do via UrlStrategy) and updating the title of the page (which is expressed via the Title widget). So I think for routes-that-are-not-dialogs we could improve the situation if the framework communicated the difference somehow. Happy to collaborate on improving this.

// 2. No node inside the route takes focus explicitly. In this case, the
// expectation is to look through all nodes in traversal order and focus
// on the first one.
semanticsObject.owner.addOneTimePostUpdateCallback(() {
Copy link
Contributor

Choose a reason for hiding this comment

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

how does the debouncing work if there are multiple dialogs created in a update

Copy link
Contributor Author

@yjbanov yjbanov Dec 18, 2023

Choose a reason for hiding this comment

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

I have not considered it. I hope apps do not create multiple dialogs in a single update. It seems like a messy UX implementation. I think the framework removes semantics from all background routes anyway, so the engine would only observe the top-most one, no?

Copy link
Contributor

Choose a reason for hiding this comment

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

if there are nested navigators or a pop up route like dropdownButtons that somehow open simultaneous , the former is probably more common than the latter.

Another case is that I do know customer money use scopeRoute + namesRoute combo when they are animating page change effect within one page, but they are targeting mobile.

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. Looking at the code, what will happen right now is that the last dialog to show up will "win" the focus because addOneTimePostUpdateCallback callbacks are processed in FIFO order. So whatever order the framework communicates dialogs, the last one to be initialized will win. So I think it should work, but there could be corner-cases. I'm willing to look into this as a follow-up. The current implementation covers all use-cases that we have in the a11y_assessments app.

Comment on lines +35 to +36
// Case 2: nothing requested explicit focus. Focus on the first descendant.
_setDefaultFocus();
Copy link
Contributor

Choose a reason for hiding this comment

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

If something in the embedded flutter view triggered a route change inside, we should bring the focus here I think.

Comment on lines +35 to +36
// Case 2: nothing requested explicit focus. Focus on the first descendant.
_setDefaultFocus();
Copy link
Contributor

Choose a reason for hiding this comment

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

In other platform we send screen change accessibilityEvent and let OS decide whether to move the accessibility focus. so we are in an uncharted territory here.

Copy link
Contributor

@marcianx marcianx left a comment

Choose a reason for hiding this comment

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

Overall, the strategy SGTM. mdebbar@'s comment seems like a particularly interesting edge case to think through carefully, however.

if (semanticsObject.isFocusable) {
final Focusable? focusable = this.focusable;
if (focusable != null) {
return focusable.focusAsRouteDefault();
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't have any particular example here. Just wondering in the abstract whether the logic applied to non-focusable items below should be applied to focusable items that don't take focus because in both cases, the object is not able to take direct focus. It's all up to you.

// focus on something inside it. There could be two possibilities:
//
// 1. The framework explicitly marked a node inside the dialog as focused
// via the `isFocusable` and `isFocused` flags. In this case, the node
Copy link
Contributor

Choose a reason for hiding this comment

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

It really depends on the type of dialog. For example for a role="alertdialog", it's very common to move focus into the Ok button since when the focus first moves into the alert dialog, the screen reader vocalizes the entire contents of the dialog. Of course, the choice to focus on the "Ok" dialog would ideally by done via an explicit autofocus rather than implicit focus. So maybe you may decide that that consideration might be less relevant for fallback behavior.

Copy link
Contributor

@marcianx marcianx left a comment

Choose a reason for hiding this comment

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

Thanks for all the changes! Things LGTM. But I imagine you'd want someone else's LG on this PR.

if (semanticsObject.isFocusable) {
final Focusable? focusable = this.focusable;
if (focusable != null) {
return focusable.focusAsRouteDefault();
Copy link
Contributor

Choose a reason for hiding this comment

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

.focus() is a noop on disabled native controls unless they have any valid tabindex on it (including -1), thereby allowing programmatic focus, as you're already aware below.
The disabled button is an interesting example. I think with that you've sold me.
But to confirm: are focusable entities that don't get primary focus considered focusable? If so, then I wonder whether one should recurse.

Copy link
Contributor

@mdebbar mdebbar left a comment

Choose a reason for hiding this comment

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

LGTM!

Comment on lines +44 to +45
/// programmatically, simulating the screen reader choosing a default element
/// to focus on.
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we inform the framework about the element we chose here? That way, when the user hits tab, the framework would continue the traversal correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We do inform the framework via a didGainAccessibilityFocus event, which happen by relevant nodes receiving the DOM focus event.

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.

The framework now will send focus event when route changes
See https://github.com/flutter/flutter/blob/9003f138033726a275b524539670ef3ae74e8ddf/packages/flutter/lib/src/widgets/navigator.dart#L3048

It currently only send the event for mobile, if we include web here, I think this may just work if the web engine focus the div based on this even

@@ -16,6 +16,39 @@ class Dialog extends PrimaryRoleManager {
// names its own route an `aria-label` is used instead of `aria-describedby`.
addFocusManagement();
addLiveRegion();

Copy link
Contributor

Choose a reason for hiding this comment

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

If every route is a role="dialog", wouldn't the screen reader announce every MaterialPageRoute a dialog when focused? Moving this to a separate pr sgtm

// 2. No node inside the route takes focus explicitly. In this case, the
// expectation is to look through all nodes in traversal order and focus
// on the first one.
semanticsObject.owner.addOneTimePostUpdateCallback(() {
Copy link
Contributor

Choose a reason for hiding this comment

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

if there are nested navigators or a pop up route like dropdownButtons that somehow open simultaneous , the former is probably more common than the latter.

Another case is that I do know customer money use scopeRoute + namesRoute combo when they are animating page change effect within one page, but they are targeting mobile.

@chunhtai
Copy link
Contributor

chunhtai commented Dec 20, 2023

actually this will only send event when the page has been visited before. When the page first pushed onto screen it won't send any event. You will still need a mechanism to determine first focus.

// 1. The framework explicitly marked a node inside the dialog as focused
// via the `isFocusable` and `isFocused` flags. In this case, the node
// will request focus directly and there's nothing to do on top of that.
// 2. No node inside the route takes focus explicitly. In this case, the
Copy link
Contributor

Choose a reason for hiding this comment

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

In case this route is reveal when the previous top-most routes are popped, you can listen to the send focus event if we start sending event for web in framework.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep. I've been getting updates from @Hangyujin about it. I'm planning to implementing for web, if/when it's ready on the framework side.

Copy link
Member

@hannah-hyj hannah-hyj Dec 20, 2023

Choose a reason for hiding this comment

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

it has been implemented on ios and android now.
related PR: ios: #48252
framework: flutter/flutter#135771

callback(this);
_currentChildrenInRenderOrder?.forEach((SemanticsObject child) {
child.visitDepthFirst(callback);
child.visitDepthFirstInRenderOrder(callback);
Copy link
Contributor

Choose a reason for hiding this comment

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

I looked at the code the _currentChildrenInRenderOrder is based on childrenInTraversalOrder, what is the difference between this and visitDepthFirstInTraversalOrder

Copy link
Contributor Author

Choose a reason for hiding this comment

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

childrenInRenderOrder is the order the child elements are rendered into the DOM. IOW, if you do containerElement.elements you will get children listed in childrenInRenderOrder. Separately each child may have a z-index, which dictates its hit test order relative to siblings (also paint order, but our semantics DOM is invisible so that doesn't matter).

Effectively, childrenInRenderOrder dictates the traversal order in the DOM. This is the order screen readers will observe. The logic that translates childrenInTraversalOrder/childrenInHitTestOrder to childrenInRenderOrder/z-index is in updateChildren(). We use a different name because it does not guarantee every possible traversal order the framework could use. The web limitations are similar to iOS though, so I think in practice they are the same.

The reason this function uses childrenInRenderOrder is to be in sync with the order that the screen reader observes.

Copy link
Contributor

Choose a reason for hiding this comment

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

I understand the difference between childrenInHitTestOrder and visitDepthFirstInRenderOrder. I was confused at the difference between visitDepthFirstInRenderOrder and visitDepthFirstInTraversalOrder, they seem to be the same.

Copy link
Contributor Author

@yjbanov yjbanov Dec 20, 2023

Choose a reason for hiding this comment

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

Ah, I see. You are correct. If the tree is fully consistent the two should effectively return the same result. Currently, the only useful difference is that visitDepthFirstInTraversalOrder ensures that the tree is finalized and is in consistent state before you traverse it. There are some asserts that enforce it. However, visitDepthFirstInRenderOrder can operate on unfinalized/inconsistent tree; it just returns partial results, which is useful for debugging and assertion messages. For example, _computeNodeMapConsistencyMessage uses it and it generates an error that fully explains where things are amiss. If it tried using visitDepthFirstInTraversalOrder it wouldn't be able to print the partial message because visitDepthFirstInTraversalOrder would crash on any missing node in the tree.

However, this difference doesn't seem to be too interesting. Let me see if I can clean it up.

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 couldn't find a way to fully remove visitDepthFirstInRenderOrder because it's still needed to be able to walk a potentially broken tree (for asserts). But it's a private method only, so I made it private and debug-only, added a doc about its difference from the other method, and stopped using it in a non-debug context.

Co-authored-by: Mouad Debbar <mouad.debbar@gmail.com>
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, my last concern is visitDepthFirstInRenderOrder and visitDepthFirstInTraversalOrder, but it is minor.

@yjbanov yjbanov added the autosubmit Merge PR when tree becomes green via auto submit App label Dec 21, 2023
@auto-submit auto-submit bot merged commit b5ba500 into flutter:main Dec 21, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Dec 21, 2023
auto-submit bot pushed a commit to flutter/flutter that referenced this pull request Dec 22, 2023
…140527)

flutter/engine@1b1b2a1...737e6f8

2023-12-21 skia-flutter-autoroll@skia.org Roll Dart SDK from bcf68d22f0fa to fe94d9b88531 (1 revision) (flutter/engine#49343)
2023-12-21 1961493+harryterkelsen@users.noreply.github.com Reland "[web] Enforce onDrawFrame/onBeginFrame render rule" (flutter/engine#49336)
2023-12-21 magder@google.com Turn on scenario app screenshots (flutter/engine#49066)
2023-12-21 chris@bracken.jp Revert onrender change (flutter/engine#49333)
2023-12-21 yjbanov@google.com [web] autofocus in new routes (flutter/engine#47727)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC jimgraham@google.com,rmistry@google.com,zra@google.com on the revert to ensure that a human
is aware of the problem.

To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
CoderDake pushed a commit to CoderDake/flutter that referenced this pull request Dec 28, 2023
…lutter#140527)

flutter/engine@1b1b2a1...737e6f8

2023-12-21 skia-flutter-autoroll@skia.org Roll Dart SDK from bcf68d22f0fa to fe94d9b88531 (1 revision) (flutter/engine#49343)
2023-12-21 1961493+harryterkelsen@users.noreply.github.com Reland "[web] Enforce onDrawFrame/onBeginFrame render rule" (flutter/engine#49336)
2023-12-21 magder@google.com Turn on scenario app screenshots (flutter/engine#49066)
2023-12-21 chris@bracken.jp Revert onrender change (flutter/engine#49333)
2023-12-21 yjbanov@google.com [web] autofocus in new routes (flutter/engine#47727)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC jimgraham@google.com,rmistry@google.com,zra@google.com on the revert to ensure that a human
is aware of the problem.

To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
auto-submit bot pushed a commit to flutter/flutter that referenced this pull request Mar 14, 2024
As of flutter/engine#47727 the web engine is able to find a default widget to focus on when a new route it pushed. The mobile engine already did that for some time. So `autofocus` is no longer necessary.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
autosubmit Merge PR when tree becomes green via auto submit App platform-web Code specifically for the web engine
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[web] when dialog pops up screen reader loses focus instead of focusing on something inside the dialog
7 participants