-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Closed
Labels
P1High-priority issues at the top of the work listHigh-priority issues at the top of the work listcustomer: samehereframeworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.team-frameworkOwned by Framework teamOwned by Framework team
Description
Is there an existing issue for this?
- I have searched the existing issues
- I have read the guide to filing a bug
Steps to reproduce
This is toward making non-modal floating widgets accessible. Use-cases:
- A widget opens a tooltip with focusable content within it. I'd like screen reader navigation to end up within the content.
- A math widget opens a custom floating on-screen keyboard. I'd like the screen reader's virtual cursor to navigate directly from the widget onto this on-screen keyboard.
For both of these, the SemanticsNode tree needs to interleave the overlay right after the widget on which it's anchored.
OverlayPortal seems perfect for this and does automatically set the focus traversal order correctly, but it seems to not order the semantics tree to match. Here's a repro on DartPad (see Console for semantics output).
Expected semantics order: "button1", "floating-pause", "button2"
Observed semantics order: "floating-pause", "button1", "button2"
Code sample
Code sample
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() async {
runApp(
MaterialApp(
theme: ThemeData(useMaterial3: true),
title: 'Test',
home: const Scaffold(
body: MyWidget(),
),
),
);
SemanticsBinding.instance.ensureSemantics();
WidgetsBinding.instance.addPostFrameCallback((_) {
debugDumpSemanticsTree();
});
}
class MyWidget extends StatefulWidget {
const MyWidget();
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
final OverlayPortalController _controller = OverlayPortalController();
@override
Widget build(BuildContext context) {
_controller.show();
return FocusTraversalGroup(
// Necessary to ensure that floating-pause is between button1 and button2
// in the focus traversal order. Does not help semantics order one bit.
policy: WidgetOrderTraversalPolicy(),
child: Row(
children: [
OverlayPortal(
controller: _controller,
overlayChildBuilder: (context) => Positioned(
left: 100,
top: 20,
child: button(Icons.pause, "floating-pause")),
child: button(Icons.play_arrow, "button1"),
),
button(Icons.stop, "button2"),
],
),
);
}
}
Widget button(IconData icon, String label, [VoidCallback? onPressed]) {
return IconButton(
icon: Semantics(
label: label,
child: Icon(icon),
),
onPressed: onPressed ?? () {},
);
}Metadata
Metadata
Assignees
Labels
P1High-priority issues at the top of the work listHigh-priority issues at the top of the work listcustomer: samehereframeworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.team-frameworkOwned by Framework teamOwned by Framework team