Repository: flutter
Issue: `SemanticsService.announce` gets interrupted by VoiceOver
Labels: platform-ios,engine,a: accessibility,customer: money (g3),P2,team-ios,triaged-ios

Internal: b/308890939

On iOS, `SemanticsService.announce` easily gets interrupted by VoiceOver when calling `SemanticsService.announce('This is an announcement', TextDirection.ltr);` in a button (any widget that has its own or child semantic hints for the VoiceOver to read).

This affects widgets that need to announce their states such as `ExpansionTile`.

Take this simple button that makes an announcement when pressing the button. On Android, the  Semantic announcement is read when pressing the button but on iOS, VoiceOver reads the "'Announce now!'" button text, which interrupts the Semantic announcement.

```dart
            FilledButton(
              onPressed: () async {
                SemanticsService.announce('This is an announcement', TextDirection.ltr);
              },
              child: const Text('Announce now!'),
            ),
```

On iOS, you can add delay to the announcement so it can be played after VoiceOver is done reading "'Announce now!'" button text.

```dart
            FilledButton(
              onPressed: () async {
                await Future<void>.delayed(const Duration(seconds: 2), () {
                  SemanticsService.announce(
                      'This is an announcement', TextDirection.ltr);
                });
              },
              child: const Text('Announce 2sec later!'),
            ),
```



<details> 
<summary>code sample</summary> 

```dart
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(useMaterial3: true),
      home: const Example(),
    );
  }
}

class Example extends StatelessWidget {
  const Example({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sample'),
      ),
      body: Align(
        alignment: Alignment.bottomCenter,
        child: Column(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            FilledButton(
              onPressed: () async {
                SemanticsService.announce('This is an announcement', TextDirection.ltr);
              },
              child: const Text('Announce now!'),
            ),
            FilledButton(
              onPressed: () async {
                await Future<void>.delayed(const Duration(seconds: 2), () {
                  SemanticsService.announce(
                      'This is an announcement', TextDirection.ltr);
                });
              },
              child: const Text('Announce 2sec later!'),
            ),
           const SizedBox(height: 150)
          ],
        ),
      ),
    );
  }
}
``` 
	
</details>

### iOS


https://user-images.githubusercontent.com/48603081/223467691-c677b6b3-2e4c-4726-a100-69b9696f4607.MP4



### Android



https://user-images.githubusercontent.com/48603081/223467714-42e11f96-d6f3-4a25-80f1-3443e1787312.mp4




Found this [explanation](https://stackoverflow.com/a/34017699) on SO.