My app shows various Container() Widget()s in several columns in a certain view.
I tried to place some icons inside the Container()s to provide operations like delete, minimize etc. Unfortunately, that doesn't look good on native targets.
Therefore I'd like to keep the visual appearance as is and show an actions menu above the actual Container() once the mouse pointer moves over the Container().
This menu would be above all other widgets, be non-modal and disappear, once the pointer leaves the bound box of the Container(). Containers() shouldn't change size and location.
Using MouseRegion(), I'd make the menu appear and disappear.
May I place some Widget() outside the bounding rectangle of a Container() [or other widgets)? Ideally, I'd like to place it relative to the other bounding box.
UPDATE 2022-03-24
Created an OverlayMenu() class which renders something like this:
Usage:
OverlayMenu(
actionWidget:
const Icon(Icons.settings, color: Colors.blue, size: 20),
callbacks: [
() {
// Click on icons 1 action
},
() {
// Click on icon 2 action
}
],
icons: [
Icons.delete, Icons.tv
],
leftOffset: StoreProvider.of<EModel>(context)
.state
.columnWidth
.toDouble())
And the implementation of OverlayMenu():
import 'package:flutter/material.dart';
class OverlayMenu {
List<IconData> icons;
List<Function> callbacks;
Widget actionWidget;
double leftOffset = 0.0;
bool mayShowMenu = true;
OverlayMenu({ required this.actionWidget, required this.icons, required this.callbacks, required this.leftOffset });
Widget insert( BuildContext context ) {
return MouseRegion(
onEnter: (_) {
if (mayShowMenu) {
_showOverlay( context );
}
},
onExit: (_) {
mayShowMenu = true;
},
child: const Icon(Icons.settings, color: Colors.blue, size: 20),
);
}
///
///
///
void _showOverlay(BuildContext outerContext ) async {
final renderBox = outerContext.findRenderObject() as RenderBox;
var size = renderBox.size;
var offset = renderBox.localToGlobal(Offset.zero);
assert( icons.length == callbacks.length, 'Need to provide as many icons as callbacks' );
List<Widget> actionElements = List.empty( growable: true );
for( var n=0; n<icons.length; n++ ) {
actionElements.add( GestureDetector(
onTap: () {
callbacks[ n ]();
},
child: Icon( icons[ n ], size: 22 ))
);
}
// Declaring and Initializing OverlayState
// and OverlayEntry objects
OverlayState? overlayState = Overlay.of(outerContext);
OverlayEntry? overlayEntry;
overlayEntry = OverlayEntry(builder: (context) {
// You can return any widget you like here
// to be displayed on the Overlay
return Stack(children: [
Positioned(
top: offset.dy,
left: offset.dx +
leftOffset -
40,
child: MouseRegion(
onExit: (_) {
overlayEntry?.remove();
print(DateTime.now().toString() + ' ovl: removed');
},
cursor: SystemMouseCursors.contextMenu,
child: Material(
child:Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
border: Border.all(
color: Colors.grey.shade300,
width: 1,
),
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 4,
blurRadius: 4,
offset: const Offset( 4,4 ),
blurStyle: BlurStyle.normal),
]
),
child: SizedBox(
width: 60,
height: 60,
child: Wrap(spacing: 4, children: actionElements
))))),
),
]);
});
// Inserting the OverlayEntry into the Overlay
overlayState?.insert(overlayEntry);
mayShowMenu = true;
print(DateTime.now().toString() + ' ovl: inserted');
}
}

