最近在网上看到一段话。产品有三宝-弹窗,浮层加引导,设计有三宝-透明,阴影加圆角,运营有三宝-短信,push加红包。在日常开发中经常会遇到弹窗、浮层之类的效果,这些在Android中实现很简单,可以用PopupWindow完成。但是在flutter中怎么做呢,起初我是用flutter中的PopupMenuItem来做,但是这种效果体验特差,不能自定义内容、不能自定义位置,所以放弃了该方案。于是我看了下PopupMenuItem中的源码,发现PopupRoute可以自定义弹窗。
效果如下:
自定义PopupRoute
代码如下:
class PopRoute extends PopupRoute {
final Duration _duration = Duration(milliseconds: 200);
Widget child;
PopRoute({@required this.child});
///弹出蒙层的颜色,null则为透明
@override
Color get barrierColor => null;
@override
bool get barrierDismissible => true;
@override
String get barrierLabel => null;
@override
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return child;
}
@override
Duration get transitionDuration => _duration;
}
根据targetwidget定位
我们的需求是点击一个按钮在这个按钮上下左右弹出浮层,那么就必须targetwidget在屏幕中的位置坐标,获取位置可以用flutter中的RenderObject
RenderBox renderBox = widget.popKey.currentContext.findRenderObject();
Offset localToGlobal = renderBox.localToGlobal(Offset.zero); //targetWidget的坐标位置
Size size = renderBox.size; //targetWidget的size
计算popupwindow偏移量
根据popupwindow
的宽高和targetwidget
计算出popupwindow的偏移量
RenderBox buttonBox = buttonKey.currentContext.findRenderObject();
var buttonSize = buttonBox.size; //button的size
switch (widget.popDirection) {
case PopDirection.left:
left = localToGlobal.dx - buttonSize.width - widget.offset;
top = localToGlobal.dy + size.height / 2 - buttonSize.height / 2;
break;
case PopDirection.top:
left = localToGlobal.dx + size.width / 2 - buttonSize.width / 2;
top = localToGlobal.dy - buttonSize.height - widget.offset;
fixPosition(buttonSize);
break;
case PopDirection.right:
left = localToGlobal.dx + size.width + widget.offset;
top = localToGlobal.dy + size.height / 2 - buttonSize.height / 2;
break;
case PopDirection.bottom:
left = localToGlobal.dx + size.width / 2 - buttonSize.width / 2;
top = localToGlobal.dy + size.height + widget.offset;
fixPosition(buttonSize);
break;
}
popupwindow整体布局
@override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: GestureDetector(
child: Stack(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.transparent,
),
Positioned(
child: GestureDetector(
child: widget.popWidget == null
? _buildWidget(widget.msg)
: _buildCustomWidget(widget.popWidget),
),
//根据`popupwindow`的宽高和`targetwidget`计算出popupwindow的偏移量
left: left,x
top: top,
)
],
),
onTap: () {
Navigator.pop(context);
},
),
);
}
使用方法
定义了一个showPopWindow
的静态方法,在需要弹出popupWindow时,调用这个方法传入popWidget
等参数即可。
static void showPopWindow(context, String msg, GlobalKey popKey,
[PopDirection popDirection = PopDirection.bottom,
Widget popWidget,
double offset = 0]) {
Navigator.push(
context,
PopRoute(
child: PopupWindow(
msg: msg,
popKey: popKey,
popDirection: popDirection,
popWidget: popWidget,
offset: offset,
)));
}