Files
workavia-mail-front/lib/features/base/widget/smart_interaction_widget.dart
T

185 lines
5.0 KiB
Dart

import 'dart:async';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart' hide OverlayEntry;
import 'package:universal_html/html.dart' as html;
typedef OnRightMouseClickAction = void Function({RelativeRect? position});
typedef OnDoubleClickAction = void Function({RelativeRect? position});
typedef OnTapAction = void Function();
class SmartInteractionWidget extends StatefulWidget {
final Widget child;
final bool usePosition;
final OnRightMouseClickAction onRightMouseClickAction;
final OnDoubleClickAction onDoubleClickAction;
final OnTapAction onTapAction;
const SmartInteractionWidget({
super.key,
required this.child,
required this.onRightMouseClickAction,
required this.onDoubleClickAction,
required this.onTapAction,
this.usePosition = false,
});
@override
State<SmartInteractionWidget> createState() => _SmartInteractionWidgetState();
}
class _SmartInteractionWidgetState extends State<SmartInteractionWidget> {
GlobalKey? _childKey;
Offset? _lastPointerEventOffset;
StreamSubscription<html.MouseEvent>? _contextMenuSubscription;
@override
void initState() {
super.initState();
if (PlatformInfo.isWeb) {
_childKey = GlobalKey();
WidgetsBinding.instance.addPostFrameCallback((_) {
_addWebContextMenuListener();
});
}
}
void _addWebContextMenuListener() {
_contextMenuSubscription =
html.document.onContextMenu.listen(_onContextMenuListener);
}
void _onContextMenuListener(html.MouseEvent event) {
final renderBox =
_childKey?.currentContext?.findRenderObject() as RenderBox?;
if (renderBox == null) return;
final localPosition = Offset(
event.page.x.toDouble(),
event.page.y.toDouble(),
);
final renderBoxPosition = renderBox.localToGlobal(Offset.zero);
final size = renderBox.size;
final rect = Rect.fromLTWH(
renderBoxPosition.dx,
renderBoxPosition.dy,
size.width,
size.height,
);
if (rect.contains(localPosition)) {
event.preventDefault();
if (!mounted) return;
if (widget.usePosition) {
final relativeRect = _getRelativeRectFromOffset(context, localPosition);
widget.onRightMouseClickAction(position: relativeRect);
} else {
widget.onRightMouseClickAction();
}
}
}
RelativeRect _getRelativeRectFromOffset(
BuildContext context,
Offset offset,
) {
final widgetBox =
_childKey?.currentContext?.findRenderObject() as RenderBox?;
final overlayBox =
Overlay.maybeOf(context)?.context.findRenderObject() as RenderBox?;
if (widgetBox == null || overlayBox == null) {
final screenSize = MediaQuery.sizeOf(context);
return RelativeRect.fromLTRB(
offset.dx,
offset.dy,
screenSize.width - offset.dx,
screenSize.height - offset.dy,
);
} else {
final targetPosition = widgetBox.localToGlobal(
Offset.zero,
ancestor: overlayBox,
);
final overlaySize = overlayBox.size;
const topSpace = 4.0;
return RelativeRect.fromLTRB(
targetPosition.dx,
targetPosition.dy + widgetBox.size.height + topSpace,
overlaySize.width + targetPosition.dx,
overlaySize.height - targetPosition.dy - widgetBox.size.height,
);
}
}
void _handleDoubleTap() {
if (widget.usePosition && _lastPointerEventOffset != null) {
final relativeRect = _getRelativeRectFromOffset(
context,
_lastPointerEventOffset!,
);
widget.onDoubleClickAction(position: relativeRect);
} else {
widget.onDoubleClickAction();
}
}
void _handleOnTapAction() => widget.onTapAction();
@override
Widget build(BuildContext context) {
if (PlatformInfo.isWeb) {
if (widget.usePosition) {
return Listener(
key: _childKey,
onPointerDown: (event) {
if (event.kind == PointerDeviceKind.mouse ||
event.kind == PointerDeviceKind.touch) {
_lastPointerEventOffset = event.position;
}
},
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onDoubleTap: _handleDoubleTap,
onTap: _handleOnTapAction,
child: widget.child,
),
),
);
} else {
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
key: _childKey,
behavior: HitTestBehavior.opaque,
onDoubleTap: _handleDoubleTap,
onTap: _handleOnTapAction,
child: widget.child,
),
);
}
} else {
return GestureDetector(
onTap: _handleOnTapAction,
child: widget.child,
);
}
}
@override
void dispose() {
if (PlatformInfo.isWeb) {
_lastPointerEventOffset = null;
_contextMenuSubscription?.cancel();
}
super.dispose();
}
}