Skip to content

How to manage accessibility focus  #59594

Closed

Description

@KrystinaH

Use case

First, I have to thank you for your great work, Flutter gave us the developing speed we needed and even more. But there is still some accessibility focus edge cases that I can't find satisfying workarounds for, like these ones:

  • When a modal is closed, the focus normally return to the button that opens it, whatever I do, the accessibility focus return to the first element of the screen;
  • When an option is selected in a dropdown, the focus should return on the dropdown field itself, but again, the focus return to the first element of the screen;
  • When a save button from a form is clicked but there is still errors in it, the focus should go on the first field with error. The best I have done is to manage focus with requestFocus() and ensure it's visibility when focused with a scroll animation, but the accessibility focus just go on the element in the middle of the screen and I'm not sure why;

Proposal

I don't know if this is possible, but it would be great to be able to manage accessibility focus just like the input one, or even better: that the accessibility focus follows the input focus.

Activity

  1. added
    in triagePresently being triaged by the triage team
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Jun 17, 2020
  2. KrystinaH commented on Jun 17, 2020

    @KrystinaH
    Author

    Hi @KrystinaH
    Could this be helpful?
    https://api.flutter.dev/flutter/semantics/SemanticsProperties/focusable.html https://api.flutter.dev/flutter/semantics/SemanticsProperties/focused.html

    Hi @TahaTesser, I would like to say yes, but the lack of examples on how to use it in the documentation (and also on the Web) can't help me figure out if I am not using it well or if it's just not working.

    I know that we are not on Stackoverflow, but is it suppose to be used like this ? If so, it seems to work only the first time the raised button is tapped with Talkback on Android and it doesn't work at all with Voice Over on iOS.

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final appTitle = 'Focus demo';
    
        return MaterialApp(
          title: appTitle,
          home: Scaffold(
            appBar: AppBar(
              title: Text(appTitle),
            ),
            body: FocusTest(),
          ),
        );
      }
    }
    
    class FocusTest extends StatefulWidget {
      @override
      FocusTestState createState() {
        return FocusTestState();
      }
    }
    
    class FocusTestState extends State<FocusTest> {
      final focusNode = FocusNode();
    
      @override
      Widget build(BuildContext context) {
        return Padding(
          padding: EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Semantics(
                focusable: true,
                focused: focusNode.hasFocus,
                child:
                  FlatButton(
                    focusNode: focusNode,
                    child:Text("Focusable widget with focus node"),
                    onPressed: () => print("test")
                  )
              ),
              Padding(
                padding: const EdgeInsets.symmetric(vertical: 16.0),
                child: RaisedButton(
                  onPressed: () {
                    focusNode.requestFocus();
                    print(focusNode.hasFocus);
                    print(focusNode.hasPrimaryFocus);
                  },
                  child: Text('Focus'),
                ),
              ),
            ],
          ),
        );
      }
    }
    
  3. removed
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Jun 17, 2020
  4. removed
    in triagePresently being triaged by the triage team
    on Jun 18, 2020
  5. TahaTesser commented on Jun 18, 2020

    @TahaTesser
    Contributor

    Hi @nathanael-h
    Please enable VoiceControl and VoiceOver #59471

    and try this code sample and say tap focus

    Code Sample
    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final appTitle = 'Focus demo';
    
        return MaterialApp(
          title: appTitle,
          home: Scaffold(
            appBar: AppBar(
              title: Text(appTitle),
            ),
            body: FocusTest(),
          ),
        );
      }
    }
    
    class FocusTest extends StatefulWidget {
      @override
      FocusTestState createState() {
        return FocusTestState();
      }
    }
    
    class FocusTestState extends State<FocusTest> {
      final focusNode = FocusNode();
    
      @override
      Widget build(BuildContext context) {
        return Padding(
          padding: EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Semantics(
                  focusable: true,
                  focused: focusNode.hasFocus,
                  child: FlatButton(
                      focusNode: focusNode,
                      child: Text("Focusable widget with focus node"),
                      onPressed: () => print("test"))),
              Padding(
                padding: const EdgeInsets.symmetric(vertical: 16.0),
                child: Semantics(
                  focusable: true,
                  label: 'Focus',
                  child: RaisedButton(
                    onPressed: () {
                      focusNode.requestFocus();
                      print(focusNode.hasFocus);
                      print(focusNode.hasPrimaryFocus);
                    },
                    child: Text('Focus'),
                  ),
                ),
              ),
            ],
          ),
        );
      }
    }
    
    
  6. added
    in triagePresently being triaged by the triage team
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Jun 18, 2020
  7. nathanael-h commented on Jun 18, 2020

    @nathanael-h

    Hello @KrystinaH I think @TahaTesser wanted to ping you 😉

  8. KrystinaH commented on Jun 18, 2020

    @KrystinaH
    Author

    Hi @TahaTesser, I just tried your code on both Android and iOS (with VoiceOver on only and with VoiceControl) and the exact same thing happens: it works the first time the screen is loaded for Android and it never works on iOS.

    Is it really working for you?

  9. removed
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Jun 18, 2020
  10. removed
    in triagePresently being triaged by the triage team
    on Jun 19, 2020
  11. changed the title [-]Possibility to manage accessibility focus[/-] [+]How to manage manage accessibility focus [/+] on Jun 19, 2020
  12. 18 remaining items

  13. csells commented on Jul 22, 2021

    @csells
    Contributor

    @chunhtai I've reached out to the customer for their input.

  14. lorenzOliveto commented on Jul 26, 2021

    @lorenzOliveto

    We had a similar problem connected to this issue, we wanted to have the ability to change the focus of VO/TB to a custom element, so for example if we enter the screen we wanted to focus on a label or on the back button instead of the title.
    Is it possible?

  15. chunhtai commented on Jul 26, 2021

    @chunhtai
    Contributor

    @lorenzOliveto It is currently not possible since the flutter does not expose api to programmatically change the a11y focus. However, switching the a11y focus to widget other than first element when screen change may cause confusing to VO/TB user and is often against the a11y guideline.

  16. lorenzOliveto commented on Jul 28, 2021

    @lorenzOliveto

    @chunhtai I agree but sometimes it's needed. We ran in situations where the accessibility focus was placed on a button at the bottom of the screen instead of the first element. Is this the expected behavior?
    You can check out this example, tried on Nexus 5X:

    Code
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key? key, required this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Expanded(
                child: Container(),
              ),
              MaterialButton(
                child: Text('Go'),
                color: Colors.lightBlue,
                onPressed: () => _goToSecondScreen(),
              ),
            ],
          ),
        );
      }
    
      void _goToSecondScreen() {
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (_) => SecondScaffold(),
          ),
        );
      }
    }
    
    class SecondScaffold extends StatelessWidget {
      const SecondScaffold({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('AAAA'),
          ),
          body: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Expanded(
                child: Container(),
              ),
              MaterialButton(
                child: Text('Go'),
                color: Colors.lightBlue,
                onPressed: () {},
              ),
            ],
          ),
        );
      }
    }
    
    flutter doctor -v

    [✓] Flutter (Channel stable, 2.2.3, on macOS 11.5 20G71 darwin-x64, locale
    it-IT)
    • Flutter version 2.2.3 at /Users/lorenzooliveto/Flutter/flutter
    • Framework revision f4abaa0 (4 weeks ago), 2021-07-01 12:46:11 -0700
    • Engine revision 241c87ad80
    • Dart version 2.13.4

    [✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
    • Android SDK at /Users/lorenzooliveto/Library/Android/sdk
    • Platform android-30, build-tools 30.0.2
    • ANDROID_HOME = /Users/lorenzooliveto/Library/Android/sdk
    • Java binary at: /Applications/Android
    Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.8+10-b944.6916264)
    • All Android licenses accepted.

    [✓] Xcode - develop for iOS and macOS
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 12.5.1, Build version 12E507
    • CocoaPods version 1.10.1

    [✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

    [✓] Android Studio (version 4.2)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
    🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
    🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.8+10-b944.6916264)

    [✓] Connected device (1 available)
    • Chrome (web) • chrome • web-javascript • Google Chrome 92.0.4515.107

    • No issues found!

  17. chunhtai commented on Aug 2, 2021

    @chunhtai
    Contributor

    @lorenzOliveto
    The behavior is controlled by android, it seems to skip the appbar when you push the new page. Android does not provide a API to manage focus, they proactively discourage people from doing it. Although you may be able to fire custom Type_View_Focus notification to force it to change focus, it is not an official API and is not maintained. I don't think we can surface this API to flutter.

    Closing this issue since there is nothing we can do from flutter side

  18. dnfield commented on Aug 16, 2021

    @dnfield
    Contributor

    I'm late to the game here, but perhaps you'd have luck using some custom sort keys in these cases?

  19. chunhtai commented on Aug 16, 2021

    @chunhtai
    Contributor

    @dnfield Talkback seems to purposely skip node with setHeading = true. which causes the talkback to focus the first focusable element of the scaffold body. In customer's case, it is the go button. I verified with native android and it does the similar thing that ignores the native android app bar.

  20. github-actions commented on Aug 30, 2021

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  21. locked as resolved and limited conversation to collaborators on Aug 30, 2021
  22. added
    P0Critical issues such as a build break or regression
    and removed on Jun 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P0Critical issues such as a build break or regressiona: accessibilityAccessibility, e.g. VoiceOver or TalkBack. (aka a11y)customer: castawaycustomer: soldierd: examplesSample code and demosframeworkflutter/packages/flutter repository. See also f: labels.platform-androidAndroid applications specificallyplatform-iosiOS applications specificallywaiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions