context menu

This commit is contained in:
quantulr
2023-05-17 00:04:36 +08:00
parent eb918803d4
commit bfaff7f7fa
6 changed files with 277 additions and 143 deletions

View File

@ -0,0 +1,97 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
typedef ContextMenuBuilder = Widget Function(
BuildContext context, Offset offset);
/// 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({
super.key,
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: (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,
);
}
}

View File

@ -1,5 +1,6 @@
import 'dart:io';
// import 'dart:html' as html;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -30,7 +31,7 @@ void main() async {
await windowManager.focus();
});
}
}else{
} else {
dio.options.baseUrl = '/api';
}
final prefs = await SharedPreferences.getInstance();

View File

@ -5,14 +5,15 @@ import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:momo/lib/context_menu_region.dart';
import 'package:momo/models/image_list_resp.dart';
import 'package:momo/models/image_resp.dart';
import 'package:momo/provider/rerender.dart';
import 'package:momo/request/http_client.dart';
/// A builder that includes an Offset to draw the context menu at.
typedef ContextMenuBuilder = Widget Function(
BuildContext context, Offset offset);
// typedef ContextMenuBuilder = Widget Function(
// BuildContext context, Offset offset);
class Gallery extends ConsumerWidget {
const Gallery({Key? key}) : super(key: key);
@ -92,118 +93,151 @@ class ImageItem extends ConsumerStatefulWidget {
}
class _ImageItemState extends ConsumerState<ImageItem> {
final ContextMenuController _contextMenuController = ContextMenuController();
// final ContextMenuController _contextMenuController = ContextMenuController();
@override
void dispose() {
// TODO: implement dispose
if (_contextMenuController.isShown) {
_contextMenuController.remove();
}
// if (_contextMenuController.isShown) {
// _contextMenuController.remove();
// }
super.dispose();
}
void _showContextMenu(Offset position) {
_contextMenuController.show(
context: context,
contextMenuBuilder: (BuildContext context) {
return AdaptiveTextSelectionToolbar.buttonItems(
anchors: TextSelectionToolbarAnchors(
primaryAnchor: position,
),
buttonItems: <ContextMenuButtonItem>[
ContextMenuButtonItem(
onPressed: () {
ContextMenuController.removeAny();
showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text('删除图片'),
content: const Text("确认删除图片"),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, 'Cancel'),
child: const Text('Cancel'),
),
TextButton(
onPressed: () {
Navigator.pop(context, 'OK');
dio
.delete("/image/delete/${widget.image.id}")
.then((resp) {
ref
.read(uniqueIdProvider.notifier)
.updateId();
// context.go("/");
});
},
child: const Text('OK'),
),
],
));
// _showDialog(context);
},
label: '删除',
),
ContextMenuButtonItem(
onPressed: () {
ContextMenuController.removeAny();
// _showDialog(context);
Clipboard.setData(ClipboardData(
text:
"${dio.options.baseUrl}/image/${widget.image.file_path}"));
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(
Icons.check_circle,
color: Colors.white,
),
SizedBox(
width: 10,
),
Text("已拷贝到剪贴板")
],
),
backgroundColor: Colors.lightGreen,
));
},
label: '复制链接',
),
ContextMenuButtonItem(
onPressed: () {
ContextMenuController.removeAny();
// _showDialog(context);
},
label: '查看详情',
),
],
);
},
);
}
// void _showContextMenu(Offset position) {
// _contextMenuController.show(
// context: context,
// contextMenuBuilder: (BuildContext context) {
// return AdaptiveTextSelectionToolbar.buttonItems(
// anchors: TextSelectionToolbarAnchors(
// primaryAnchor: position,
// ),
// buttonItems: <ContextMenuButtonItem>[
// ContextMenuButtonItem(
// onPressed: () {
// ContextMenuController.removeAny();
// showDialog(
// context: context,
// builder: (BuildContext context) =>
// AlertDialog(
// title: const Text('删除图片'),
// content: const Text("确认删除图片"),
// actions: [
// TextButton(
// onPressed: () => Navigator.pop(context, 'Cancel'),
// child: const Text('Cancel'),
// ),
// TextButton(
// onPressed: () {
// Navigator.pop(context, 'OK');
// dio
// .delete("/image/delete/${widget.image.id}")
// .then((resp) {
// ref
// .read(uniqueIdProvider.notifier)
// .updateId();
// // context.go("/");
// });
// },
// child: const Text('OK'),
// ),
// ],
// ));
// // _showDialog(context);
// },
// label: '删除',
// ),
// ContextMenuButtonItem(
// onPressed: () {
// ContextMenuController.removeAny();
// // _showDialog(context);
// Clipboard.setData(ClipboardData(
// text:
// "${dio.options.baseUrl}/image/${widget.image.file_path}"));
// ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
// content: Row(
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// Icon(
// Icons.check_circle,
// color: Colors.white,
// ),
// SizedBox(
// width: 10,
// ),
// Text("已拷贝到剪贴板")
// ],
// ),
// backgroundColor: Colors.lightGreen,
// ));
// },
// label: '复制链接',
// ),
// ContextMenuButtonItem(
// onPressed: () {
// ContextMenuController.removeAny();
// // _showDialog(context);
// },
// label: '查看详情',
// ),
// ],
// );
// },
// );
// }
@override
Widget build(BuildContext context) {
return GestureDetector(
onSecondaryTapUp: (TapUpDetails details) {
_showContextMenu(details.globalPosition);
},
onTap: () {
context.go(Uri(
path: "/detail",
queryParameters: {"id": "${widget.image.id}"}).toString());
},
child: Image.network(
kIsWeb
? "/images-api/?url=${dio.options.baseUrl}/image/${widget.image.file_path}&w=512"
: 'https://raichi.hodokencho.com/images-api/?url=${dio.options.baseUrl}/image/${widget.image.file_path}&w=512',
fit: BoxFit.cover,
errorBuilder: (BuildContext context, o, err) {
return const Icon(Icons.error_outline);
return ContextMenuRegion(
contextMenuBuilder: (BuildContext context, Offset offset) {
return AdaptiveTextSelectionToolbar.buttonItems(
anchors: TextSelectionToolbarAnchors(
primaryAnchor: offset,
),
buttonItems: <ContextMenuButtonItem>[
ContextMenuButtonItem(
onPressed: () {
ContextMenuController.removeAny();
Clipboard.setData(ClipboardData(
text:
"https://raichi.hodokencho.com/api/image/${widget.image.file_path}"));
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(
Icons.check_circle,
color: Colors.white,
),
SizedBox(
width: 10,
),
Text("已拷贝到剪贴板")
],
),
backgroundColor: Colors.lightGreen,
));
},
label: '复制链接',
),
],
);
},
),
);
child: GestureDetector(
onTap: () {
context.go(Uri(
path: "/detail",
queryParameters: {"id": "${widget.image.id}"}).toString());
},
child: Image.network(
kIsWeb
? "/images-api/?url=${dio.options.baseUrl}/image/${widget.image.file_path}&w=512"
: 'https://raichi.hodokencho.com/images-api/?url=${dio.options.baseUrl}/image/${widget.image.file_path}&w=512',
fit: BoxFit.cover,
errorBuilder: (BuildContext context, o, err) {
return const Icon(Icons.error_outline);
},
),
));
}
}