Skip to content

Pinput on Web gains semantic focus but not input focus with screen reader navigation #208

@SebastianWaloszek

Description

@SebastianWaloszek

Describe the bug
When navigating with a screen reader on Flutter Web, a discrepancy occurs between the semantic focus and the input focus for the Pinput widget. The screen reader successfully navigates to and announces the Pinput widget (it gains semantic focus).

However, the actual keyboard input focus does not follow. It remains on the previously focused element. As a result, the user hears that they are on the pin input field but is unable to type into it.

While standard Tab key navigation or clicking on thePinput works correctly, navigation via the screen reader's virtual cursor does not.

To Reproduce
Steps to reproduce the behavior:

  1. Run the minimal reproduction code (provided below) on the web (flutter run -d chrome).
  2. Enable a screen reader (e.g., VoiceOver on macOS).
  3. Navigate from the "Regular TextField" using the screen reader's "next item" gesture.
  4. Observe: The screen reader's focus moves to the Pinput widget, and it is announced correctly. This is the semantic focus.
  5. Observe: The visual input cursor (the blinking caret) remains on the "Regular TextField". This is the input focus.
  6. Try to type. The characters will not appear in the Pinput, confirming the input focus was not transferred.

Video

bug_video.mov

Pinput version: 5.0.1

Result of: flutter doctor --verbose

[✓] Flutter (Channel stable, 3.32.0, on macOS 14.1 23B2073 darwin-arm64, locale en-PL)
    • Flutter version 3.32.0 on channel stable at
      /Users/adam/fvm/versions/3.32.0
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision be698c48a6 (9 weeks ago), 2025-05-19 12:59:14 -0700
    • Engine revision 1881800949
    • Dart version 3.8.0
    • DevTools version 2.45.1

[!] Android toolchain - develop for Android devices (Android SDK version 35.0.0)
• Android SDK at /Users/adam/Library/Android/sdk
✗ cmdline-tools component is missing.
Try installing or updating Android Studio.
Alternatively, download the tools from
https://developer.android.com/studio#command-line-tools-only and make sure to set
the ANDROID_HOME environment variable.
See https://developer.android.com/studio/command-line for more details.
✗ Android license status unknown.
Run flutter doctor --android-licenses to accept the SDK licenses.
See https://flutter.dev/to/macos-android-setup for more details.

[✓] Xcode - develop for iOS and macOS (Xcode 15.4)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 15F31d
• CocoaPods version 1.16.2

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

[✓] Android Studio (version 2024.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 21.0.5+-12932927-b750.29)

[✓] Android Studio (version 2025.1)
• Android Studio at /Users/adam/Documents/06/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 21.0.6+-13391695-b895.109)

[✓] VS Code (version 1.102.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.114.0

[✓] Connected device (4 available)
• sdk gphone64 arm64 (mobile) • emulator-5554 • android-arm64
• Android 14 (API 34) (emulator)
• Adam’s iPhone (wireless) (mobile) • 00008130-000449540208001C • ios
• iOS 18.5 22F76
• macOS (desktop) • macos • darwin-arm64
• macOS 14.1 23B2073 darwin-arm64
• Chrome (web) • chrome • web-javascript
• Google Chrome 138.0.7204.158
! Error: Browsing on the local area network for iPhone 13 von Manu. Ensure the device
is unlocked and attached with a cable or associated with the same local area network
as this Mac.
The device must be opted into Developer Mode to connect wirelessly. (code -27)

[✓] Network resources
• All expected network resources are available.

! Doctor found issues in 1 category.

Device:
This issue occurs on desktop web browsers.

  • Device: Desktop
  • OS: macOS,
  • Browser: Chrome

Additional context
Here is a minimal, self-contained example to reproduce the bug.

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:pinput/pinput.dart';

void main() {
  runApp(const PinputAccessibilityRepro());

    if (kIsWeb) {
    SemanticsBinding.instance.ensureSemantics();
  }
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Pinput Accessibility Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const ReproHomePage(),
    );
  }
}

class ReproHomePage extends StatefulWidget {
  const ReproHomePage({super.key});

  @override
  State<ReproHomePage> createState() => _ReproHomePageState();
}

class _ReproHomePageState extends State<ReproHomePage> {
  final FocusNode _pinputFocusNode = FocusNode();
  final TextEditingController _pinputController = TextEditingController();

  @override
  void dispose() {
    _pinputFocusNode.dispose();
    _pinputController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final defaultPinTheme = PinTheme(
      width: 56,
      height: 56,
      textStyle: const TextStyle(
        fontSize: 22,
        color: Color.fromRGBO(30, 60, 87, 1),
      ),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(8),
        border: Border.all(color: Colors.grey),
      ),
    );

    return Scaffold(
      appBar: AppBar(
        title: const Text('Pinput Accessibility Issue'),
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(20.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const TextField(
                decoration: InputDecoration(
                  labelText: 'Regular TextField (works)',
                  border: OutlineInputBorder(),
                ),
              ),
              const SizedBox(height: 40),
              Pinput(
                length: 6,
                controller: _pinputController,
                focusNode: _pinputFocusNode,
                defaultPinTheme: defaultPinTheme,
                focusedPinTheme: defaultPinTheme.copyWith(
                  decoration: defaultPinTheme.decoration!.copyWith(
                    border: Border.all(color: Colors.blue),
                  ),
                ),
              ),
              const SizedBox(height: 20),
              ElevatedButton(
                onPressed: () {},
                child: const Text('Another focusable element'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions