Disable context menu (#128365)

## Description

Changes the context menu example for `MenuAnchor` so that it uses right-click, or (on macOS and iOS only) ctrl-left-click, for the context menu. Also disables the browser context menu on web platforms.

## Tests
 - Updated test to reflect new triggers.
This commit is contained in:
Greg Spencer 2023-06-07 16:40:17 -07:00 committed by GitHub
parent 5328bd9ae0
commit 36f73cf645
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 12 deletions

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -41,6 +42,7 @@ class _MyContextMenuState extends State<MyContextMenu> {
final FocusNode _buttonFocusNode = FocusNode(debugLabel: 'Menu Button'); final FocusNode _buttonFocusNode = FocusNode(debugLabel: 'Menu Button');
final MenuController _menuController = MenuController(); final MenuController _menuController = MenuController();
ShortcutRegistryEntry? _shortcutsEntry; ShortcutRegistryEntry? _shortcutsEntry;
bool _menuWasEnabled = false;
Color get backgroundColor => _backgroundColor; Color get backgroundColor => _backgroundColor;
Color _backgroundColor = Colors.red; Color _backgroundColor = Colors.red;
@ -62,6 +64,12 @@ class _MyContextMenuState extends State<MyContextMenu> {
} }
} }
@override
void initState() {
super.initState();
_disableContextMenu();
}
@override @override
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();
@ -84,15 +92,38 @@ class _MyContextMenuState extends State<MyContextMenu> {
void dispose() { void dispose() {
_shortcutsEntry?.dispose(); _shortcutsEntry?.dispose();
_buttonFocusNode.dispose(); _buttonFocusNode.dispose();
_reenableContextMenu();
super.dispose(); super.dispose();
} }
Future<void> _disableContextMenu() async {
if (!kIsWeb) {
// Does nothing on non-web platforms.
return;
}
_menuWasEnabled = BrowserContextMenu.enabled;
if (_menuWasEnabled) {
await BrowserContextMenu.disableContextMenu();
}
}
void _reenableContextMenu() {
if (!kIsWeb) {
// Does nothing on non-web platforms.
return;
}
if (_menuWasEnabled && !BrowserContextMenu.enabled) {
BrowserContextMenu.enableContextMenu();
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Padding( return Padding(
padding: const EdgeInsets.all(50), padding: const EdgeInsets.all(50),
child: GestureDetector( child: GestureDetector(
onTapDown: _handleTapDown, onTapDown: _handleTapDown,
onSecondaryTapDown: _handleSecondaryTapDown,
child: MenuAnchor( child: MenuAnchor(
controller: _menuController, controller: _menuController,
anchorTapClosesMenu: true, anchorTapClosesMenu: true,
@ -142,7 +173,7 @@ class _MyContextMenuState extends State<MyContextMenu> {
children: <Widget>[ children: <Widget>[
const Padding( const Padding(
padding: EdgeInsets.all(8.0), padding: EdgeInsets.all(8.0),
child: Text('Ctrl-click anywhere on the background to show the menu.'), child: Text('Right-click anywhere on the background to show the menu.'),
), ),
Padding( Padding(
padding: const EdgeInsets.all(12.0), padding: const EdgeInsets.all(12.0),
@ -185,13 +216,29 @@ class _MyContextMenuState extends State<MyContextMenu> {
} }
} }
void _handleTapDown(TapDownDetails details) { void _handleSecondaryTapDown(TapDownDetails details) {
if (!HardwareKeyboard.instance.logicalKeysPressed.contains(LogicalKeyboardKey.controlLeft) &&
!HardwareKeyboard.instance.logicalKeysPressed.contains(LogicalKeyboardKey.controlRight)) {
return;
}
_menuController.open(position: details.localPosition); _menuController.open(position: details.localPosition);
} }
void _handleTapDown(TapDownDetails details) {
switch (defaultTargetPlatform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
// Don't open the menu on these platforms with a Ctrl-tap (or a
// tap).
break;
case TargetPlatform.iOS:
case TargetPlatform.macOS:
// Only open the menu on these platforms if the control button is down
// when the tap occurs.
if (HardwareKeyboard.instance.logicalKeysPressed.contains(LogicalKeyboardKey.controlLeft) ||
HardwareKeyboard.instance.logicalKeysPressed.contains(LogicalKeyboardKey.controlRight)) {
_menuController.open(position: details.localPosition);
}
}
}
} }
class ContextMenuApp extends StatelessWidget { class ContextMenuApp extends StatelessWidget {

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_api_samples/material/menu_anchor/menu_anchor.1.dart' as example; import 'package:flutter_api_samples/material/menu_anchor/menu_anchor.1.dart' as example;
@ -18,15 +19,13 @@ void main() {
await tester.pumpWidget(const example.ContextMenuApp()); await tester.pumpWidget(const example.ContextMenuApp());
await tester.sendKeyDownEvent(LogicalKeyboardKey.controlRight); await tester.tapAt(const Offset(100, 200), buttons: kSecondaryButton);
await tester.tapAt(const Offset(100, 200));
await tester.pump(); await tester.pump();
expect(tester.getRect(findMenu()), equals(const Rect.fromLTRB(100.0, 200.0, 433.0, 360.0))); expect(tester.getRect(findMenu()), equals(const Rect.fromLTRB(100.0, 200.0, 433.0, 360.0)));
// Make sure tapping in a different place causes the menu to move. // Make sure tapping in a different place causes the menu to move.
await tester.tapAt(const Offset(200, 100)); await tester.tapAt(const Offset(200, 100), buttons: kSecondaryButton);
await tester.pump(); await tester.pump();
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlRight);
expect(tester.getRect(findMenu()), equals(const Rect.fromLTRB(200.0, 100.0, 533.0, 260.0))); expect(tester.getRect(findMenu()), equals(const Rect.fromLTRB(200.0, 100.0, 533.0, 260.0)));
@ -67,8 +66,7 @@ void main() {
); );
// Open the menu so we can look for state changes reflected in the menu. // Open the menu so we can look for state changes reflected in the menu.
await tester.sendKeyDownEvent(LogicalKeyboardKey.controlRight); await tester.tapAt(const Offset(100, 200), buttons: kSecondaryButton);
await tester.tapAt(const Offset(100, 200));
await tester.pump(); await tester.pump();
expect(find.text(example.MenuEntry.showMessage.label), findsOneWidget); expect(find.text(example.MenuEntry.showMessage.label), findsOneWidget);