Ste*_*eAp 2 flutter flutter-layout flutter-web
我的应用程序在某个视图的几列中显示各种Container() Widget() 。
我尝试在Container()中放置一些图标以提供删除、最小化等操作。不幸的是,这在本机目标上看起来不太好。
因此,我想保持视觉外观不变,并在鼠标指针移动到Container()上方时在实际Container ()上方显示操作菜单。
该菜单将位于所有其他小部件之上,是非模态的,一旦指针离开Container()的边界框,该菜单就会消失。Containers()不应更改大小和位置。
使用MouseRegion(),我可以使菜单出现和消失。
我可以将一些Widget()放置在Container() [或其他小部件]的边界矩形之外吗?理想情况下,我想将其相对于另一个边界框放置。
更新 2022-03-24
创建了一个 OverlayMenu() 类,它呈现如下内容:
用法:
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())
Run Code Online (Sandbox Code Playgroud)
以及OverlayMenu()的实现:
导入“包: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');
}
}
Run Code Online (Sandbox Code Playgroud)
您可以用来实现您想要的效果的一种方法是覆盖小部件,因为它是非模式的,并且也不需要更改布局/大小来命中可测试的项目。
根据你的问题,我认为这个流程就是想要的:

指针进入小部件后插入覆盖条目,并在指针离开后将其删除
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Stack(
children: [
MouseRegion(
onEnter: (_) {
Overlay.of(context)!.insert(this._overlayEntry);
},
onExit: (_) => clear(),
child: FloatingActionButton(
key: buttonKey,
onPressed: () {},
),
),
],
),
),
);
}
Run Code Online (Sandbox Code Playgroud)
这就是我们删除条目的方式,检查是否可以删除以及延迟(用于平滑):
Future<void> clear() async {
if (!keepMenuVisible) {
await Future.delayed(Duration(milliseconds: 200));
if (!keepMenuVisible) {
_overlayEntry.remove();
}
}
}
Run Code Online (Sandbox Code Playgroud)
额外的延迟用于确保菜单不会反应性地绝望,而是使其变得更加平滑。
keepMenuVisible用于锁定菜单并在菜单本身悬停后保持其可见。
最后,我们创建条目并将项目相对于主小部件(在本例中为 FAB)放置:
@override
void initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
_overlayEntry = _createOverlayEntry();
});
}
OverlayEntry _createOverlayEntry() {
final renderBox = buttonKey.currentContext!.findRenderObject() as RenderBox;
var size = renderBox.size;
var offset = renderBox.localToGlobal(Offset.zero);
return OverlayEntry(
builder: (_) => Positioned(
left: offset.dx,
top: offset.dy + size.height + 5.0,
width: 200,
child: MouseRegion(
onEnter: (_) {
keepMenuVisible = true;
},
onHover: (_) {
keepMenuVisible = true;
},
onExit: (_) async {
keepMenuVisible = false;
clear();
},
child: Material(
elevation: 4.0,
child: ListView(
padding: EdgeInsets.zero,
shrinkWrap: true,
children: <Widget>[
ListTile(
onTap: () => print('tap action 1'),
title: Text('Action 1'),
),
ListTile(
onTap: () => print('tap action 2'),
title: Text('Action 2'),
)
],
),
),
),
),
);
}
Run Code Online (Sandbox Code Playgroud)
在这里查看完整的示例
| 归档时间: |
|
| 查看次数: |
1705 次 |
| 最近记录: |