mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

This auto-formats all *.dart files in the repository outside of the `engine` subdirectory and enforces that these files stay formatted with a presubmit check. **Reviewers:** Please carefully review all the commits except for the one titled "formatted". The "formatted" commit was auto-generated by running `dev/tools/format.sh -a -f`. The other commits were hand-crafted to prepare the repo for the formatting change. I recommend reviewing the commits one-by-one via the "Commits" tab and avoiding Github's "Files changed" tab as it will likely slow down your browser because of the size of this PR. --------- Co-authored-by: Kate Lovett <katelovett@google.com> Co-authored-by: LongCatIsLooong <31859944+LongCatIsLooong@users.noreply.github.com>
178 lines
5.1 KiB
Dart
178 lines
5.1 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// This sample demonstrates allowing a context menu to be shown in a widget
|
|
// subtree in response to user gestures.
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
|
|
void main() => runApp(const ContextMenuControllerExampleApp());
|
|
|
|
/// A builder that includes an Offset to draw the context menu at.
|
|
typedef ContextMenuBuilder = Widget Function(BuildContext context, Offset offset);
|
|
|
|
class ContextMenuControllerExampleApp extends StatefulWidget {
|
|
const ContextMenuControllerExampleApp({super.key});
|
|
|
|
@override
|
|
State<ContextMenuControllerExampleApp> createState() => _ContextMenuControllerExampleAppState();
|
|
}
|
|
|
|
class _ContextMenuControllerExampleAppState extends State<ContextMenuControllerExampleApp> {
|
|
void _showDialog(BuildContext context) {
|
|
Navigator.of(context).push(
|
|
DialogRoute<void>(
|
|
context: context,
|
|
builder: (BuildContext context) => const AlertDialog(title: Text('You clicked print!')),
|
|
),
|
|
);
|
|
}
|
|
|
|
@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(
|
|
home: Scaffold(
|
|
appBar: AppBar(title: const Text('Context menu outside of text')),
|
|
body: _ContextMenuRegion(
|
|
contextMenuBuilder: (BuildContext context, Offset offset) {
|
|
// The custom context menu will look like the default context menu
|
|
// on the current platform with a single 'Print' button.
|
|
return AdaptiveTextSelectionToolbar.buttonItems(
|
|
anchors: TextSelectionToolbarAnchors(primaryAnchor: offset),
|
|
buttonItems: <ContextMenuButtonItem>[
|
|
ContextMenuButtonItem(
|
|
onPressed: () {
|
|
ContextMenuController.removeAny();
|
|
_showDialog(context);
|
|
},
|
|
label: 'Print',
|
|
),
|
|
],
|
|
);
|
|
},
|
|
// In this case this wraps a big open space in a GestureDetector in
|
|
// order to show the context menu, but it could also wrap a single
|
|
// widget like an Image to give it a context menu.
|
|
child: ListView(
|
|
children: <Widget>[
|
|
Container(height: 20.0),
|
|
const Text(
|
|
'Right click (desktop) or long press (mobile) anywhere, not just on this text, to show the custom menu.',
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Shows and hides the context menu based on user gestures.
|
|
///
|
|
/// By default, shows the menu on right clicks and long presses.
|
|
class _ContextMenuRegion extends StatefulWidget {
|
|
/// Creates an instance of [_ContextMenuRegion].
|
|
const _ContextMenuRegion({required this.child, required this.contextMenuBuilder});
|
|
|
|
/// Builds the context menu.
|
|
final ContextMenuBuilder contextMenuBuilder;
|
|
|
|
/// The child widget that will be listened to for gestures.
|
|
final Widget child;
|
|
|
|
@override
|
|
State<_ContextMenuRegion> createState() => _ContextMenuRegionState();
|
|
}
|
|
|
|
class _ContextMenuRegionState extends State<_ContextMenuRegion> {
|
|
Offset? _longPressOffset;
|
|
|
|
final ContextMenuController _contextMenuController = ContextMenuController();
|
|
|
|
static bool get _longPressEnabled {
|
|
switch (defaultTargetPlatform) {
|
|
case TargetPlatform.android:
|
|
case TargetPlatform.iOS:
|
|
return true;
|
|
case TargetPlatform.macOS:
|
|
case TargetPlatform.fuchsia:
|
|
case TargetPlatform.linux:
|
|
case TargetPlatform.windows:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void _onSecondaryTapUp(TapUpDetails details) {
|
|
_show(details.globalPosition);
|
|
}
|
|
|
|
void _onTap() {
|
|
if (!_contextMenuController.isShown) {
|
|
return;
|
|
}
|
|
_hide();
|
|
}
|
|
|
|
void _onLongPressStart(LongPressStartDetails details) {
|
|
_longPressOffset = details.globalPosition;
|
|
}
|
|
|
|
void _onLongPress() {
|
|
assert(_longPressOffset != null);
|
|
_show(_longPressOffset!);
|
|
_longPressOffset = null;
|
|
}
|
|
|
|
void _show(Offset position) {
|
|
_contextMenuController.show(
|
|
context: context,
|
|
contextMenuBuilder: (BuildContext context) {
|
|
return widget.contextMenuBuilder(context, position);
|
|
},
|
|
);
|
|
}
|
|
|
|
void _hide() {
|
|
_contextMenuController.remove();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_hide();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
onSecondaryTapUp: _onSecondaryTapUp,
|
|
onTap: _onTap,
|
|
onLongPress: _longPressEnabled ? _onLongPress : null,
|
|
onLongPressStart: _longPressEnabled ? _onLongPressStart : null,
|
|
child: widget.child,
|
|
);
|
|
}
|
|
}
|