No SystemContextMenu when readOnly is true (#171242)

When readOnly is true, there is no TextInputConnection, so the
SystemContextMenu can't be shown. Instead, this shows the Flutter-drawn
context menu.

This video shows the change. You can slightly tell that the system
context menu is shown when readOnly is false and the Flutter-drawn
context menu is shown when it is true.



https://github.com/user-attachments/assets/91480fa4-cce6-4d63-ae11-df72a229da73


This is the same root cause as
https://github.com/flutter/flutter/pull/169238.

Fixes https://github.com/flutter/flutter/issues/170521
This commit is contained in:
Justin McCandless 2025-07-17 11:40:50 -07:00 committed by GitHub
parent 0d999f39e9
commit f16f6d7f99
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 607 additions and 8 deletions

View File

@ -815,7 +815,7 @@ class CupertinoTextField extends StatefulWidget {
BuildContext context,
EditableTextState editableTextState,
) {
if (defaultTargetPlatform == TargetPlatform.iOS && SystemContextMenu.isSupported(context)) {
if (SystemContextMenu.isSupportedByField(editableTextState)) {
return SystemContextMenu.editableText(editableTextState: editableTextState);
}
return CupertinoAdaptiveTextSelectionToolbar.editableText(editableTextState: editableTextState);

View File

@ -4,7 +4,6 @@
import 'dart:ui' as ui show BoxHeightStyle, BoxWidthStyle;
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
@ -280,7 +279,7 @@ class CupertinoTextFormFieldRow extends FormField<String> {
BuildContext context,
EditableTextState editableTextState,
) {
if (defaultTargetPlatform == TargetPlatform.iOS && SystemContextMenu.isSupported(context)) {
if (SystemContextMenu.isSupportedByField(editableTextState)) {
return SystemContextMenu.editableText(editableTextState: editableTextState);
}
return CupertinoAdaptiveTextSelectionToolbar.editableText(editableTextState: editableTextState);

View File

@ -1373,6 +1373,7 @@ class SearchBar extends StatefulWidget {
this.keyboardType,
this.scrollPadding = const EdgeInsets.all(20.0),
this.contextMenuBuilder = _defaultContextMenuBuilder,
this.readOnly = false,
});
/// Controls the text being edited in the search bar's text field.
@ -1532,11 +1533,14 @@ class SearchBar extends StatefulWidget {
/// be disabled and Flutter-rendered context menus to appear.
final EditableTextContextMenuBuilder? contextMenuBuilder;
/// {@macro flutter.widgets.editableText.readOnly}
final bool readOnly;
static Widget _defaultContextMenuBuilder(
BuildContext context,
EditableTextState editableTextState,
) {
if (defaultTargetPlatform == TargetPlatform.iOS && SystemContextMenu.isSupported(context)) {
if (SystemContextMenu.isSupportedByField(editableTextState)) {
return SystemContextMenu.editableText(editableTextState: editableTextState);
}
return AdaptiveTextSelectionToolbar.editableText(editableTextState: editableTextState);
@ -1700,6 +1704,7 @@ class _SearchBarState extends State<SearchBar> {
child: Semantics(
inputType: SemanticsInputType.search,
child: TextField(
readOnly: widget.readOnly,
autofocus: widget.autoFocus,
onTap: widget.onTap,
onTapAlwaysCalled: true,

View File

@ -879,7 +879,7 @@ class TextField extends StatefulWidget {
BuildContext context,
EditableTextState editableTextState,
) {
if (defaultTargetPlatform == TargetPlatform.iOS && SystemContextMenu.isSupported(context)) {
if (SystemContextMenu.isSupportedByField(editableTextState)) {
return SystemContextMenu.editableText(editableTextState: editableTextState);
}
return AdaptiveTextSelectionToolbar.editableText(editableTextState: editableTextState);

View File

@ -4,7 +4,6 @@
import 'dart:ui' as ui show BoxHeightStyle, BoxWidthStyle;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
@ -331,7 +330,7 @@ class TextFormField extends FormField<String> {
BuildContext context,
EditableTextState editableTextState,
) {
if (defaultTargetPlatform == TargetPlatform.iOS && SystemContextMenu.isSupported(context)) {
if (SystemContextMenu.isSupportedByField(editableTextState)) {
return SystemContextMenu.editableText(editableTextState: editableTextState);
}
return AdaptiveTextSelectionToolbar.editableText(editableTextState: editableTextState);

View File

@ -114,8 +114,29 @@ class SystemContextMenu extends StatefulWidget {
/// Whether the current device supports showing the system context menu.
///
/// Currently, this is only supported on newer versions of iOS.
///
/// See also:
///
/// * [isSupportedByField], which uses this method and determines whether an
/// individual [EditableTextState] supports the system context menu.
static bool isSupported(BuildContext context) {
return MediaQuery.maybeSupportsShowingSystemContextMenu(context) ?? false;
return defaultTargetPlatform == TargetPlatform.iOS &&
(MediaQuery.maybeSupportsShowingSystemContextMenu(context) ?? false);
}
/// Whether the given field supports showing the system context menu.
///
/// Currently [SystemContextMenu] is only supported with an active
/// [TextInputConnection]. In cases where this isn't possible, such as in a
/// read-only field, fall back to using a Flutter-rendered context menu like
/// [AdaptiveTextSelectionToolbar].
///
/// See also:
///
/// * [isSupported], which is used by this method and determines whether the
/// platform in general supports showing the system context menu.
static bool isSupportedByField(EditableTextState editableTextState) {
return !editableTextState.widget.readOnly && isSupported(editableTextState.context);
}
/// The default [items] for the given [EditableTextState].

View File

@ -10674,4 +10674,86 @@ void main() {
},
variant: TargetPlatformVariant.all(),
);
testWidgets(
'readOnly disallows SystemContextMenu',
(WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/170521.
tester.platformDispatcher.supportsShowingSystemContextMenu = true;
final TextEditingController controller = TextEditingController(text: 'abcdefghijklmnopqr');
addTearDown(() {
tester.platformDispatcher.resetSupportsShowingSystemContextMenu();
tester.view.reset();
controller.dispose();
});
bool readOnly = true;
late StateSetter setState;
await tester.pumpWidget(
// Don't wrap with the global View so that the change to
// platformDispatcher is read.
wrapWithView: false,
View(
view: tester.view,
child: CupertinoApp(
home: StatefulBuilder(
builder: (BuildContext context, StateSetter setter) {
setState = setter;
return CupertinoTextField(readOnly: readOnly, controller: controller);
},
),
),
),
);
final Duration waitDuration = SelectionOverlay.fadeDuration > kDoubleTapTimeout
? SelectionOverlay.fadeDuration
: kDoubleTapTimeout;
// Double tap to select the text.
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(kDoubleTapTimeout ~/ 2);
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(waitDuration);
// No error as in https://github.com/flutter/flutter/issues/170521.
// The Flutter-drawn context menu is shown. The SystemContextMenu is not
// shown because readOnly is true.
expect(find.byType(CupertinoAdaptiveTextSelectionToolbar), findsOneWidget);
expect(find.byType(SystemContextMenu), findsNothing);
// Turn off readOnly and hide the context menu.
setState(() {
readOnly = false;
});
await tester.tap(find.text('Copy'));
await tester.pump(waitDuration);
expect(find.byType(CupertinoAdaptiveTextSelectionToolbar), findsNothing);
expect(find.byType(SystemContextMenu), findsNothing);
// Double tap to show the context menu again.
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(kDoubleTapTimeout ~/ 2);
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(waitDuration);
// Now iOS is showing the SystemContextMenu while others continue to show
// the Flutter-drawn context menu.
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
expect(find.byType(SystemContextMenu), findsOneWidget);
case TargetPlatform.macOS:
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
expect(find.byType(CupertinoAdaptiveTextSelectionToolbar), findsOneWidget);
}
},
variant: TargetPlatformVariant.all(),
skip: kIsWeb, // [intended] on web the browser handles the context menu.
);
}

View File

@ -4,6 +4,7 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/src/services/spell_check.dart';
import 'package:flutter_test/flutter_test.dart';
@ -569,4 +570,86 @@ void main() {
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
);
});
testWidgets(
'readOnly disallows SystemContextMenu',
(WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/170521.
tester.platformDispatcher.supportsShowingSystemContextMenu = true;
final TextEditingController controller = TextEditingController(text: 'abcdefghijklmnopqr');
addTearDown(() {
tester.platformDispatcher.resetSupportsShowingSystemContextMenu();
tester.view.reset();
controller.dispose();
});
bool readOnly = true;
late StateSetter setState;
await tester.pumpWidget(
// Don't wrap with the global View so that the change to
// platformDispatcher is read.
wrapWithView: false,
View(
view: tester.view,
child: CupertinoApp(
home: StatefulBuilder(
builder: (BuildContext context, StateSetter setter) {
setState = setter;
return CupertinoTextFormFieldRow(readOnly: readOnly, controller: controller);
},
),
),
),
);
final Duration waitDuration = SelectionOverlay.fadeDuration > kDoubleTapTimeout
? SelectionOverlay.fadeDuration
: kDoubleTapTimeout;
// Double tap to select the text.
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(kDoubleTapTimeout ~/ 2);
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(waitDuration);
// No error as in https://github.com/flutter/flutter/issues/170521.
// The Flutter-drawn context menu is shown. The SystemContextMenu is not
// shown because readOnly is true.
expect(find.byType(CupertinoAdaptiveTextSelectionToolbar), findsOneWidget);
expect(find.byType(SystemContextMenu), findsNothing);
// Turn off readOnly and hide the context menu.
setState(() {
readOnly = false;
});
await tester.tap(find.text('Copy'));
await tester.pump(waitDuration);
expect(find.byType(CupertinoAdaptiveTextSelectionToolbar), findsNothing);
expect(find.byType(SystemContextMenu), findsNothing);
// Double tap to show the context menu again.
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(kDoubleTapTimeout ~/ 2);
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(waitDuration);
// Now iOS is showing the SystemContextMenu while others continue to show
// the Flutter-drawn context menu.
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
expect(find.byType(SystemContextMenu), findsOneWidget);
case TargetPlatform.macOS:
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
expect(find.byType(CupertinoAdaptiveTextSelectionToolbar), findsOneWidget);
}
},
variant: TargetPlatformVariant.all(),
skip: kIsWeb, // [intended] on web the browser handles the context menu.
);
}

View File

@ -4158,6 +4158,90 @@ void main() {
expect(lastItemBottom, lessThanOrEqualTo(fakeKeyboardTop));
},
);
testWidgets(
'readOnly disallows SystemContextMenu',
(WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/170521.
tester.platformDispatcher.supportsShowingSystemContextMenu = true;
final TextEditingController controller = TextEditingController(text: 'abcdefghijklmnopqr');
addTearDown(() {
tester.platformDispatcher.resetSupportsShowingSystemContextMenu();
tester.view.reset();
controller.dispose();
});
bool readOnly = true;
late StateSetter setState;
await tester.pumpWidget(
// Don't wrap with the global View so that the change to
// platformDispatcher is read.
wrapWithView: false,
View(
view: tester.view,
child: MaterialApp(
home: Material(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setter) {
setState = setter;
return SearchBar(controller: controller, readOnly: readOnly);
},
),
),
),
),
);
final Duration waitDuration = SelectionOverlay.fadeDuration > kDoubleTapTimeout
? SelectionOverlay.fadeDuration
: kDoubleTapTimeout;
// Double tap to select the text.
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(kDoubleTapTimeout ~/ 2);
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(waitDuration);
// No error as in https://github.com/flutter/flutter/issues/170521.
// The Flutter-drawn context menu is shown. The SystemContextMenu is not
// shown because readOnly is true.
expect(find.byType(AdaptiveTextSelectionToolbar), findsOneWidget);
expect(find.byType(SystemContextMenu), findsNothing);
// Turn off readOnly and hide the context menu.
setState(() {
readOnly = false;
});
await tester.tap(find.text('Copy'));
await tester.pump(waitDuration);
expect(find.byType(AdaptiveTextSelectionToolbar), findsNothing);
expect(find.byType(SystemContextMenu), findsNothing);
// Double tap to show the context menu again.
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(kDoubleTapTimeout ~/ 2);
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(waitDuration);
// Now iOS is showing the SystemContextMenu while others continue to show
// the Flutter-drawn context menu.
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
expect(find.byType(SystemContextMenu), findsOneWidget);
case TargetPlatform.macOS:
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
expect(find.byType(AdaptiveTextSelectionToolbar), findsOneWidget);
}
},
variant: TargetPlatformVariant.all(),
skip: kIsWeb, // [intended] on web the browser handles the context menu.
);
}
Future<void> checkSearchBarDefaults(

View File

@ -18640,6 +18640,90 @@ void main() {
final EditableText editableText = tester.widget(find.byType(EditableText));
expect(editableText.hintLocales, hintLocales);
});
testWidgets(
'readOnly disallows SystemContextMenu',
(WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/170521.
tester.platformDispatcher.supportsShowingSystemContextMenu = true;
final TextEditingController controller = TextEditingController(text: 'abcdefghijklmnopqr');
addTearDown(() {
tester.platformDispatcher.resetSupportsShowingSystemContextMenu();
tester.view.reset();
controller.dispose();
});
bool readOnly = true;
late StateSetter setState;
await tester.pumpWidget(
// Don't wrap with the global View so that the change to
// platformDispatcher is read.
wrapWithView: false,
View(
view: tester.view,
child: MaterialApp(
home: Material(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setter) {
setState = setter;
return TextField(readOnly: readOnly, controller: controller);
},
),
),
),
),
);
final Duration waitDuration = SelectionOverlay.fadeDuration > kDoubleTapTimeout
? SelectionOverlay.fadeDuration
: kDoubleTapTimeout;
// Double tap to select the text.
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(kDoubleTapTimeout ~/ 2);
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(waitDuration);
// No error as in https://github.com/flutter/flutter/issues/170521.
// The Flutter-drawn context menu is shown. The SystemContextMenu is not
// shown because readOnly is true.
expect(find.byType(AdaptiveTextSelectionToolbar), findsOneWidget);
expect(find.byType(SystemContextMenu), findsNothing);
// Turn off readOnly and hide the context menu.
setState(() {
readOnly = false;
});
await tester.tap(find.text('Copy'));
await tester.pump(waitDuration);
expect(find.byType(AdaptiveTextSelectionToolbar), findsNothing);
expect(find.byType(SystemContextMenu), findsNothing);
// Double tap to show the context menu again.
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(kDoubleTapTimeout ~/ 2);
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(waitDuration);
// Now iOS is showing the SystemContextMenu while others continue to show
// the Flutter-drawn context menu.
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
expect(find.byType(SystemContextMenu), findsOneWidget);
case TargetPlatform.macOS:
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
expect(find.byType(AdaptiveTextSelectionToolbar), findsOneWidget);
}
},
variant: TargetPlatformVariant.all(),
skip: kIsWeb, // [intended] on web the browser handles the context menu.
);
}
/// A Simple widget for testing the obscure text.

View File

@ -1764,4 +1764,88 @@ void main() {
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
);
});
testWidgets(
'readOnly disallows SystemContextMenu',
(WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/170521.
tester.platformDispatcher.supportsShowingSystemContextMenu = true;
final TextEditingController controller = TextEditingController(text: 'abcdefghijklmnopqr');
addTearDown(() {
tester.platformDispatcher.resetSupportsShowingSystemContextMenu();
tester.view.reset();
controller.dispose();
});
bool readOnly = true;
late StateSetter setState;
await tester.pumpWidget(
// Don't wrap with the global View so that the change to
// platformDispatcher is read.
wrapWithView: false,
View(
view: tester.view,
child: MaterialApp(
home: Material(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setter) {
setState = setter;
return TextFormField(readOnly: readOnly, controller: controller);
},
),
),
),
),
);
final Duration waitDuration = SelectionOverlay.fadeDuration > kDoubleTapTimeout
? SelectionOverlay.fadeDuration
: kDoubleTapTimeout;
// Double tap to select the text.
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(kDoubleTapTimeout ~/ 2);
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(waitDuration);
// No error as in https://github.com/flutter/flutter/issues/170521.
// The Flutter-drawn context menu is shown. The SystemContextMenu is not
// shown because readOnly is true.
expect(find.byType(AdaptiveTextSelectionToolbar), findsOneWidget);
expect(find.byType(SystemContextMenu), findsNothing);
// Turn off readOnly and hide the context menu.
setState(() {
readOnly = false;
});
await tester.tap(find.text('Copy'));
await tester.pump(waitDuration);
expect(find.byType(AdaptiveTextSelectionToolbar), findsNothing);
expect(find.byType(SystemContextMenu), findsNothing);
// Double tap to show the context menu again.
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(kDoubleTapTimeout ~/ 2);
await tester.tapAt(textOffsetToPosition(tester, 5));
await tester.pump(waitDuration);
// Now iOS is showing the SystemContextMenu while others continue to show
// the Flutter-drawn context menu.
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
expect(find.byType(SystemContextMenu), findsOneWidget);
case TargetPlatform.macOS:
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
expect(find.byType(AdaptiveTextSelectionToolbar), findsOneWidget);
}
},
variant: TargetPlatformVariant.all(),
skip: kIsWeb, // [intended] on web the browser handles the context menu.
);
}

View File

@ -737,4 +737,162 @@ void main() {
expect(diagnosticsNodes.first.name, 'title');
expect(diagnosticsNodes.first.value, title);
});
testWidgets(
'when supportsShowingSystemContextMenu is false, isSupported is false',
(WidgetTester tester) async {
final TextEditingController controller = TextEditingController(text: 'one two three');
addTearDown(controller.dispose);
late BuildContext buildContext;
await tester.pumpWidget(
Builder(
builder: (BuildContext context) {
final MediaQueryData mediaQueryData = MediaQuery.of(context);
return MediaQuery(
data: mediaQueryData.copyWith(supportsShowingSystemContextMenu: false),
child: MaterialApp(
home: Scaffold(
body: Builder(
builder: (BuildContext context) {
buildContext = context;
return TextField(
controller: controller,
contextMenuBuilder:
(BuildContext context, EditableTextState editableTextState) {
return SystemContextMenu.editableText(
editableTextState: editableTextState,
);
},
);
},
),
),
),
);
},
),
);
expect(SystemContextMenu.isSupported(buildContext), isFalse);
},
skip: kIsWeb, // [intended] SystemContextMenu is not supported on web.
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
);
testWidgets(
'when supportsShowingSystemContextMenu is true and the platform is iOS, isSupported is true',
(WidgetTester tester) async {
final TextEditingController controller = TextEditingController(text: 'one two three');
addTearDown(controller.dispose);
late BuildContext buildContext;
await tester.pumpWidget(
Builder(
builder: (BuildContext context) {
final MediaQueryData mediaQueryData = MediaQuery.of(context);
return MediaQuery(
data: mediaQueryData.copyWith(supportsShowingSystemContextMenu: true),
child: MaterialApp(
home: Scaffold(
body: Builder(
builder: (BuildContext context) {
buildContext = context;
return TextField(
controller: controller,
contextMenuBuilder:
(BuildContext context, EditableTextState editableTextState) {
return SystemContextMenu.editableText(
editableTextState: editableTextState,
);
},
);
},
),
),
),
);
},
),
);
expect(SystemContextMenu.isSupported(buildContext), switch (defaultTargetPlatform) {
TargetPlatform.iOS => isTrue,
_ => isFalse,
});
},
skip: kIsWeb, // [intended] SystemContextMenu is not supported on web.
variant: TargetPlatformVariant.all(),
);
for (final bool readOnly in <bool>[true, false]) {
testWidgets(
'read only fields do not support the system context menu',
(WidgetTester tester) async {
await tester.pumpWidget(
Builder(
builder: (BuildContext context) {
final MediaQueryData mediaQueryData = MediaQuery.of(context);
return MediaQuery(
data: mediaQueryData.copyWith(supportsShowingSystemContextMenu: true),
child: MaterialApp(
home: Scaffold(body: TextField(readOnly: readOnly)),
),
);
},
),
);
final EditableTextState editableTextState = tester.state(find.byType(EditableText));
expect(SystemContextMenu.isSupportedByField(editableTextState), switch (readOnly) {
true => isFalse,
false => isTrue,
});
},
skip: kIsWeb, // [intended] SystemContextMenu is not supported on web.
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
);
}
// Regression test for https://github.com/flutter/flutter/issues/170521.
testWidgets(
'when supportsShowingSystemContextMenu is false, SystemContextMenu throws',
(WidgetTester tester) async {
final TextEditingController controller = TextEditingController(text: 'one two three');
addTearDown(controller.dispose);
await tester.pumpWidget(
Builder(
builder: (BuildContext context) {
final MediaQueryData mediaQueryData = MediaQuery.of(context);
return MediaQuery(
data: mediaQueryData.copyWith(supportsShowingSystemContextMenu: false),
child: MaterialApp(
home: Scaffold(
body: Center(
child: TextField(
controller: controller,
contextMenuBuilder:
(BuildContext context, EditableTextState editableTextState) {
return SystemContextMenu.editableText(
editableTextState: editableTextState,
);
},
),
),
),
),
);
},
),
);
expect(find.byType(SystemContextMenu), findsNothing);
await tester.tap(find.byType(TextField));
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
expect(state.showToolbar(), true);
await tester.pump();
expect(tester.takeException(), isAssertionError);
},
skip: kIsWeb, // [intended] SystemContextMenu is not supported on web.
);
}