flutter-web - flutter-web:如何在flutter web中制作悬停菜单?
问题描述
有没有办法在flutter web中做悬停菜单栏?
已经尝试使用 OverlayEntry 和鼠标区域来检测是否悬停,但似乎从菜单栏移动到菜单项之间存在间隔时间。
该项目的示例代码:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:overlay_container/overlay_container.dart';
class DropDownMenu extends StatefulWidget {
final String title;
final List<String> route;
final List<String> subTitle;
final BorderRadius borderRadius;
final RenderBox renderBox;
const DropDownMenu({
Key key,
this.title,
this.borderRadius,
this.renderBox,
this.route,
this.subTitle,
}) : super(key: key);
@override
DropDownMenuState createState() => DropDownMenuState();
}
class DropDownMenuState extends State<DropDownMenu> with SingleTickerProviderStateMixin {
GlobalKey _key;
bool isMenuOpen = false;
Offset buttonPosition;
Size buttonSize;
OverlayEntry _overlayEntry;
BorderRadius _borderRadius;
AnimationController _animationController;
RenderBox renderBox;
bool isHover = false;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 100),
);
_borderRadius = widget.borderRadius ?? BorderRadius.circular(4);
_key = LabeledGlobalKey("button_icon");
renderBox = widget.renderBox;
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
void closeMenu() {
_overlayEntry.remove();
_animationController.reverse();
isMenuOpen = !isMenuOpen;
}
void openMenu() {
findButton();
_animationController.forward();
_overlayEntry = _overlayEntryBuilder();
Overlay.of(context).insert(_overlayEntry);
isMenuOpen = !isMenuOpen;
}
findButton() {
RenderBox renderBox = _key.currentContext.findRenderObject();
buttonSize = renderBox.size;
buttonPosition = renderBox.localToGlobal(Offset.zero);
}
@override
Widget build(BuildContext context) {
return Container(
height: 200,
key: _key,
padding: EdgeInsets.only(
left: 20,
),
color: Colors.amberAccent,
child: InkWell(
// onTap: () {
// print('---');
// if (isMenuOpen) {
// closeMenu();
// } else {
// openMenu();
// }
// },
// onHover: (isHover) {
// print(isHover);
// this.isHover = isHover;
// if (isMenuOpen) {
// closeMenu();
// } else {
// openMenu();
// }
// },
child: Column(
children: [
Container(
height: 60,
child: Text(
widget.title ?? '',
style: TextStyle(
color: isHover ? Colors.yellow : Colors.white,
),
),
),
// OverlayContainer(
// show: true,
// // Let's position this overlay to the right of the button.
// position: OverlayContainerPosition(
// // Left position.
// buttonPosition.dx,
// // Bottom position.
// buttonPosition.dy + buttonSize.height - 15,
// ),
// // The content inside the overlay.
// child: Container(
// height: 70,
// padding: const EdgeInsets.all(20),
// margin: const EdgeInsets.only(top: 5),
// decoration: BoxDecoration(
// color: Colors.white,
// boxShadow: <BoxShadow>[
// BoxShadow(
// color: Colors.grey[300],
// blurRadius: 3,
// spreadRadius: 6,
// )
// ],
// ),
// child: Text("I render outside the \nwidget hierarchy."),
// ),
// ),
],
),
),
);
}
OverlayEntry _overlayEntryBuilder() {
return OverlayEntry(
builder: (context) {
return Positioned(
top: buttonPosition.dy + buttonSize.height - 15,
left: buttonPosition.dx,
width: buttonSize.width,
child: Material(
color: Colors.transparent,
child: Stack(
children: <Widget>[
Align(
alignment: Alignment.topCenter,
child: ClipPath(
clipper: ArrowClipper(),
child: Container(
width: 17,
height: 17,
color: Color(0xFFF),
),
),
),
Padding(
padding: const EdgeInsets.only(top: 15.0),
child: Container(
height: buttonSize.height * 5 * 10,
decoration: BoxDecoration(
borderRadius: _borderRadius,
),
child: Theme(
data: ThemeData(
iconTheme: IconThemeData(),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: List.generate(widget.subTitle.length, (index) {
return GestureDetector(
onTap: () {
Get.toNamed(widget.route[index] ?? '/');
},
child: Container(
width: buttonSize.width,
height: buttonSize.height + 10,
padding: EdgeInsets.only(bottom: 10),
color: Colors.amberAccent,
child: Center(
child: SelectableText(
widget.subTitle[index] ?? '',
style: TextStyle(
color: Colors.yellow,
),
),
),
),
);
}),
),
),
),
),
],
),
),
);
},
);
}
}
class ArrowClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
Path path = Path();
path.moveTo(0, size.height);
path.lineTo(size.width / 2, size.height / 2);
path.lineTo(size.width, size.height);
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return true;
}
}
解决方案
推荐阅读
- regex - 用于匹配斜体文本格式的预定义规则的正则表达式
- xcode - 每次我在 Xcode 中运行程序时,debugserver 都会意外退出
- sql - 单程 SQL 算术
- amazon-web-services - 无法使用 create_dynamic_frame.from_catalog 从 AWS 粘合作业访问使用分区投影配置的表中的数据
- javascript - 为什么我无法访问 request.query?
- reactjs - 将自定义样式添加到 react-sortable-tree
- python - 尝试使用鼠标滚轮更改滚动的画布宽度
- bash - Curl 命令在终端中有效,但在终端中运行的 Shell 脚本中无效
- c++ - 在 C++ 中进行头文件练习时,遇到“错误”
- c# - 加载子对象的集合,覆盖单个子对象(这是延迟加载问题吗?)