diff --git a/examples/api/lib/material/context_menu/context_menu_controller.0.dart b/examples/api/lib/material/context_menu/context_menu_controller.0.dart index 0bf5ae58ef..2a901cf17b 100644 --- a/examples/api/lib/material/context_menu/context_menu_controller.0.dart +++ b/examples/api/lib/material/context_menu/context_menu_controller.0.dart @@ -7,15 +7,21 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; void main() => runApp(const MyApp()); /// A builder that includes an Offset to draw the context menu at. typedef ContextMenuBuilder = Widget Function(BuildContext context, Offset offset); -class MyApp extends StatelessWidget { +class MyApp extends StatefulWidget { const MyApp({super.key}); + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { void _showDialog (BuildContext context) { Navigator.of(context).push( DialogRoute( @@ -26,6 +32,24 @@ class MyApp extends StatelessWidget { ); } + @override + void initState() { + super.initState(); + // On web, disable the browser's context menu since this example uses a custom + // Flutter-rendered context menu. + if (kIsWeb) { + BrowserContextMenu.disableContextMenu(); + } + } + + @override + void dispose() { + if (kIsWeb) { + BrowserContextMenu.enableContextMenu(); + } + super.dispose(); + } + @override Widget build(BuildContext context) { return MaterialApp( @@ -58,7 +82,7 @@ class MyApp extends StatelessWidget { child: ListView( children: [ Container(height: 20.0), - const Text('Right click or long press anywhere (not just on this text!) to show the custom menu.'), + const Text('Right click (desktop) or long press (mobile) anywhere, not just on this text, to show the custom menu.'), ], ), ), diff --git a/examples/api/lib/material/context_menu/editable_text_toolbar_builder.0.dart b/examples/api/lib/material/context_menu/editable_text_toolbar_builder.0.dart index c932abcda9..7a4f07905f 100644 --- a/examples/api/lib/material/context_menu/editable_text_toolbar_builder.0.dart +++ b/examples/api/lib/material/context_menu/editable_text_toolbar_builder.0.dart @@ -6,17 +6,42 @@ // appearance. import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); -class MyApp extends StatelessWidget { - MyApp({super.key}); +class MyApp extends StatefulWidget { + const MyApp({super.key}); + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { final TextEditingController _controller = TextEditingController( - text: 'Right click or long press to see the menu with custom buttons.', + text: 'Right click (desktop) or long press (mobile) to see the menu with custom buttons.', ); + @override + void initState() { + super.initState(); + // On web, disable the browser's context menu since this example uses a custom + // Flutter-rendered context menu. + if (kIsWeb) { + BrowserContextMenu.disableContextMenu(); + } + } + + @override + void dispose() { + if (kIsWeb) { + BrowserContextMenu.enableContextMenu(); + } + super.dispose(); + } + @override Widget build(BuildContext context) { return MaterialApp( diff --git a/examples/api/lib/material/context_menu/editable_text_toolbar_builder.1.dart b/examples/api/lib/material/context_menu/editable_text_toolbar_builder.1.dart index 4f599d8e05..588fca63bb 100644 --- a/examples/api/lib/material/context_menu/editable_text_toolbar_builder.1.dart +++ b/examples/api/lib/material/context_menu/editable_text_toolbar_builder.1.dart @@ -5,15 +5,23 @@ // This example demonstrates showing a custom context menu only when some // narrowly defined text is selected. +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); const String emailAddress = 'me@example.com'; const String text = 'Select the email address and open the menu: $emailAddress'; -class MyApp extends StatelessWidget { - MyApp({super.key}); +class MyApp extends StatefulWidget { + const MyApp({super.key}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { final TextEditingController _controller = TextEditingController( text: text, @@ -29,6 +37,24 @@ class MyApp extends StatelessWidget { ); } + @override + void initState() { + super.initState(); + // On web, disable the browser's context menu since this example uses a custom + // Flutter-rendered context menu. + if (kIsWeb) { + BrowserContextMenu.disableContextMenu(); + } + } + + @override + void dispose() { + if (kIsWeb) { + BrowserContextMenu.enableContextMenu(); + } + super.dispose(); + } + @override Widget build(BuildContext context) { return MaterialApp( diff --git a/examples/api/lib/material/context_menu/editable_text_toolbar_builder.2.dart b/examples/api/lib/material/context_menu/editable_text_toolbar_builder.2.dart index cd8c686dfe..dcc9f1bfc2 100644 --- a/examples/api/lib/material/context_menu/editable_text_toolbar_builder.2.dart +++ b/examples/api/lib/material/context_menu/editable_text_toolbar_builder.2.dart @@ -5,17 +5,42 @@ // This example demonstrates how to create a custom toolbar that retains the // look of the default buttons for the current platform. +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); -class MyApp extends StatelessWidget { - MyApp({super.key}); +class MyApp extends StatefulWidget { + const MyApp({super.key}); + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { final TextEditingController _controller = TextEditingController( - text: 'Right click or long press to see the menu with a custom toolbar.', + text: 'Right click (desktop) or long press (mobile) to see the menu with a custom toolbar.', ); + @override + void initState() { + super.initState(); + // On web, disable the browser's context menu since this example uses a custom + // Flutter-rendered context menu. + if (kIsWeb) { + BrowserContextMenu.disableContextMenu(); + } + } + + @override + void dispose() { + if (kIsWeb) { + BrowserContextMenu.enableContextMenu(); + } + super.dispose(); + } + @override Widget build(BuildContext context) { return MaterialApp( diff --git a/examples/api/lib/material/context_menu/selectable_region_toolbar_builder.0.dart b/examples/api/lib/material/context_menu/selectable_region_toolbar_builder.0.dart index 9f40adcea7..91137ce9ed 100644 --- a/examples/api/lib/material/context_menu/selectable_region_toolbar_builder.0.dart +++ b/examples/api/lib/material/context_menu/selectable_region_toolbar_builder.0.dart @@ -5,15 +5,22 @@ // This example demonstrates a custom context menu in non-editable text using // SelectionArea. +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; void main() => runApp(const MyApp()); -const String text = 'I am some text inside of SelectionArea. Right click or long press me to show the customized context menu.'; +const String text = 'I am some text inside of SelectionArea. Right click (desktop) or long press (mobile) me to show the customized context menu.'; -class MyApp extends StatelessWidget { +class MyApp extends StatefulWidget { const MyApp({super.key}); + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { void _showDialog (BuildContext context) { Navigator.of(context).push( DialogRoute( @@ -24,6 +31,24 @@ class MyApp extends StatelessWidget { ); } + @override + void initState() { + super.initState(); + // On web, disable the browser's context menu since this example uses a custom + // Flutter-rendered context menu. + if (kIsWeb) { + BrowserContextMenu.disableContextMenu(); + } + } + + @override + void dispose() { + if (kIsWeb) { + BrowserContextMenu.enableContextMenu(); + } + super.dispose(); + } + @override Widget build(BuildContext context) { return MaterialApp( diff --git a/examples/api/test/material/context_menu/context_menu_controller.0_test.dart b/examples/api/test/material/context_menu/context_menu_controller.0_test.dart index 48aee51d7f..a22cc69e49 100644 --- a/examples/api/test/material/context_menu/context_menu_controller.0_test.dart +++ b/examples/api/test/material/context_menu/context_menu_controller.0_test.dart @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_api_samples/material/context_menu/context_menu_controller.0.dart' as example; import 'package:flutter_test/flutter_test.dart'; @@ -13,6 +15,8 @@ void main() { const example.MyApp(), ); + expect(BrowserContextMenu.enabled, !kIsWeb); + expect(find.byType(AdaptiveTextSelectionToolbar), findsNothing); // Right clicking the middle of the app shows the custom context menu. diff --git a/examples/api/test/material/context_menu/editable_text_toolbar_builder.0_test.dart b/examples/api/test/material/context_menu/editable_text_toolbar_builder.0_test.dart index 38c28eee05..c49730090d 100644 --- a/examples/api/test/material/context_menu/editable_text_toolbar_builder.0_test.dart +++ b/examples/api/test/material/context_menu/editable_text_toolbar_builder.0_test.dart @@ -3,16 +3,20 @@ // found in the LICENSE file. import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_api_samples/material/context_menu/editable_text_toolbar_builder.0.dart' as example; import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets('showing and hiding the context menu in TextField with custom buttons', (WidgetTester tester) async { await tester.pumpWidget( - example.MyApp(), + const example.MyApp(), ); + expect(BrowserContextMenu.enabled, !kIsWeb); + await tester.tap(find.byType(EditableText)); await tester.pump(); diff --git a/examples/api/test/material/context_menu/editable_text_toolbar_builder.1_test.dart b/examples/api/test/material/context_menu/editable_text_toolbar_builder.1_test.dart index bee046b805..4b2b42adb4 100644 --- a/examples/api/test/material/context_menu/editable_text_toolbar_builder.1_test.dart +++ b/examples/api/test/material/context_menu/editable_text_toolbar_builder.1_test.dart @@ -2,17 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_api_samples/material/context_menu/editable_text_toolbar_builder.1.dart' as example; import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets('showing and hiding the custom context menu in TextField with a specific selection', (WidgetTester tester) async { await tester.pumpWidget( - example.MyApp(), + const example.MyApp(), ); + expect(BrowserContextMenu.enabled, !kIsWeb); + expect(find.byType(AdaptiveTextSelectionToolbar), findsNothing); // Right clicking the Text in the TextField shows the custom context menu, diff --git a/examples/api/test/material/context_menu/editable_text_toolbar_builder.2_test.dart b/examples/api/test/material/context_menu/editable_text_toolbar_builder.2_test.dart index 90f2a23de8..5897cc0080 100644 --- a/examples/api/test/material/context_menu/editable_text_toolbar_builder.2_test.dart +++ b/examples/api/test/material/context_menu/editable_text_toolbar_builder.2_test.dart @@ -5,15 +5,18 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_api_samples/material/context_menu/editable_text_toolbar_builder.2.dart' as example; import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets('showing and hiding the context menu in TextField with a custom toolbar', (WidgetTester tester) async { await tester.pumpWidget( - example.MyApp(), + const example.MyApp(), ); + expect(BrowserContextMenu.enabled, !kIsWeb); + await tester.tap(find.byType(EditableText)); await tester.pump(); diff --git a/examples/api/test/material/context_menu/selectable_region_toolbar_builder.0_test.dart b/examples/api/test/material/context_menu/selectable_region_toolbar_builder.0_test.dart index 090eba45ec..59be9cc37b 100644 --- a/examples/api/test/material/context_menu/selectable_region_toolbar_builder.0_test.dart +++ b/examples/api/test/material/context_menu/selectable_region_toolbar_builder.0_test.dart @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_api_samples/material/context_menu/selectable_region_toolbar_builder.0.dart' as example; import 'package:flutter_test/flutter_test.dart'; @@ -13,6 +15,8 @@ void main() { const example.MyApp(), ); + expect(BrowserContextMenu.enabled, !kIsWeb); + // Allow the selection overlay geometry to be created. await tester.pump(); diff --git a/packages/flutter/lib/src/widgets/context_menu_controller.dart b/packages/flutter/lib/src/widgets/context_menu_controller.dart index bfc6f96b65..664c27d62a 100644 --- a/packages/flutter/lib/src/widgets/context_menu_controller.dart +++ b/packages/flutter/lib/src/widgets/context_menu_controller.dart @@ -18,6 +18,11 @@ import 'overlay.dart'; /// /// ** See code in examples/api/lib/material/context_menu/context_menu_controller.0.dart ** /// {@end-tool} +/// +/// See also: +/// +/// * [BrowserContextMenu], which allows the browser's context menu on web to +/// be disabled and Flutter-rendered context menus to appear. class ContextMenuController { /// Creates a context menu that can be shown with [show]. ContextMenuController({ diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index 244c45120d..4ce4f42314 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -1773,6 +1773,8 @@ class EditableText extends StatefulWidget { /// * [AdaptiveTextSelectionToolbar.getAdaptiveButtons], which builds the /// button Widgets for the current platform given /// [ContextMenuButtonItem]s. + /// * [BrowserContextMenu], which allows the browser's context menu on web + /// to be disabled and Flutter-rendered context menus to appear. /// {@endtemplate} /// /// If not provided, no context menu will be shown.