Skip to content

Flutter Web VoicerOver breaks on TextField when a Theme extension is set #182122

Description

@MaElaRo

Steps to reproduce

Run the provided code sample in Chrome flutter run -d chrome on MacOS.

Enable VoiceOver on MacOS with Command + F5.
On Chrome, enable the semantics tree using Control + Option + Left Arrow followed by Control + Option + Space. Then step into the Flutter content by pressing Control + Option + Shift + Arrow Down.

Navigate to the TextField by pressing Control + Option + Right Arrow a couple of times.

This only happens if a Theme exists with an extension:

     Theme(
        data: Theme.of(context).copyWith(
          extensions: [ProductColorExtension(color: Colors.purple)],
        ),
        child: child!,
      );

Expected results

TextField should be focused, and typing text should fill up the TextField.

Actual results

TextField is focused, but typing doesn't fill the TextField.

Code sample

Code sample
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'VoiceOver Bug Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      builder: (context, child) {
        // Simulating the structure from simpleclub app:
        // Material -> Overlay -> OverlayEntry -> AsyncThemeWrapper -> child
        return Material(
          child: Overlay(
            initialEntries: [
              OverlayEntry(
                builder: (context) {
                  return Theme(
                    data: Theme.of(context).copyWith(
                      extensions: [ProductColorExtension(color: Colors.purple)],
                    ),
                    child: child!,
                  );
                },
              ),
            ],
          ),
        );
      },
      home: ContentPage(),
    );
  }
}

/// Simple theme extension to simulate ProductColorThemeExtension
class ProductColorExtension extends ThemeExtension<ProductColorExtension> {
  const ProductColorExtension({required this.color});

  final Color color;

  @override
  ProductColorExtension copyWith({Color? color}) {
    return ProductColorExtension(color: color ?? this.color);
  }

  @override
  ProductColorExtension lerp(ProductColorExtension? other, double t) {
    if (other is! ProductColorExtension) return this;
    return ProductColorExtension(
      color: Color.lerp(color, other.color, t) ?? color,
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.red.shade100,

        title: Text(
          'With Theme extension (BROKEN!)',
          style: TextStyle(
            fontWeight: FontWeight.bold,
            color: Colors.red.shade900,
          ),
        ),
      ),
      body: CustomScrollView(
        slivers: [
          SliverToBoxAdapter(
            child: Padding(
              padding: const EdgeInsets.all(24.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const Text(
                    'This text should be readable by VoiceOver. '
                    'If you cannot hear this text when navigating with VoiceOver, '
                    'the bug is present.',
                    style: TextStyle(fontSize: 16),
                  ),
                  const SizedBox(height: 16),
                  TextField(
                    decoration: InputDecoration(
                      hintText: 'This text field should be accessible...',
                      border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(12),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration
bug.mp4

Logs

Logs
fvm flutter run -d chrome
Resolving dependencies... 
Downloading packages... 
  characters 1.4.0 (1.4.1 available)
  matcher 0.12.17 (0.12.18 available)
  material_color_utilities 0.11.1 (0.13.0 available)
  meta 1.17.0 (1.18.1 available)
  test_api 0.7.7 (0.7.9 available)
Got dependencies!
5 packages have newer versions incompatible with dependency constraints.
Try `flutter pub outdated` for more information.
Launching lib/main.dart on Chrome in debug mode...
Waiting for connection from debug service on Chrome...                 ⣽^    5.3s

Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h List all available interactive commands.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).


This app is linked to the debug service: ws://127.0.0.1:62250/LJs_TsYvXUM=/ws
Debug service listening on ws://127.0.0.1:62250/LJs_TsYvXUM=/ws
A Dart VM Service on Chrome is available at: http://127.0.0.1:62250/LJs_TsYvXUM=
The Flutter DevTools debugger and profiler on Chrome is available at:
http://127.0.0.1:62250/LJs_TsYvXUM=/devtools/?uri=ws://127.0.0.1:62250/LJs_TsYvXUM=/ws
Starting application from main method in: org-dartlang-app:/web_entrypoint.dart.

Flutter Doctor output

Doctor output
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.38.4, on macOS 15.6.1 24G90 darwin-arm64, locale en-US)
[!] Android toolchain - develop for Android devices (Android SDK version 35.0.1)
    ! Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses
[✓] Xcode - develop for iOS and macOS (Xcode 16.4)
[✓] Chrome - develop for the web
[✓] Connected device (3 available)
    ! 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)
    ! Error: Browsing on the local area network for iPad von Manuela. 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

! Doctor found issues in 1 category.

Metadata

Metadata

Assignees

Labels

Bot is counting down the days until it unassigns the issueP1High-priority issues at the top of the work lista: accessibilityAccessibility, e.g. VoiceOver or TalkBack. (aka a11y)has reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-webWeb applications specificallyteam-webOwned by Web platform teamtriaged-webTriaged by Web platform team

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions