本文起因是由于一个弹窗,要实现一个能够通过点击显示隐藏的功能,效果如下:
第一选择是采用原生的PopupMenuItem,但是本应用的需求比较特殊,弹出的item自带背景图片,并且图片上面附件了一个Text的文本,item个数恒定为1个。采用PopupMenuItem会给弹出的item附加一个背景色,默认是跟Theme的cardColor颜色,导致图片周围有颜色,并且点击的时候也会有颜色,看起来体验极差,放弃该方案。
第二选择是采用一个隐藏的widget,当点击的时候展示,然后点击其他地方的时候隐藏,这个方案是可行的,但是由于该页面有很多点击事件,如果每个事件做处理比较麻烦。
第三种方案是模拟原生的PopupMenuItem的控件,自定义子控件。于是阅读源代码,发现了PopupRoute,这是一个可以弹出透明的布局的抽象Route,因此需要自定义一个PopRoute,代码如下:
class PopRoute extends PopupRoute{
final Duration _duration = Duration(milliseconds: 300);
Widget child;
PopRoute({@required this.child});
@override
Color get barrierColor => null;
@override
bool get barrierDismissible => true;
@override
String get barrierLabel => null;
@override
Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) {
return child;
}
@override
Duration get transitionDuration => _duration;
}
需要注意的是transitionDuration不能返回null,否则会报错。
这个Route是为了点击按钮的时候弹出一个新的页面,这个新页面除了自己定义的child,其他全是透明,来看看新的页面的代码:
class Popup extends StatelessWidget{
final Widget child;
final Function onClick; //点击child事件
final double left; //距离左边位置
final double top; //距离上面位置
Popup({
@required this.child,
this.onClick,
this.left,
this.top,
});
@override
Widget build(BuildContext context) {
return Material(color: Colors.transparent, child: GestureDetector(child: Stack(
children: [
Container(width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, color: Colors.transparent,),
Positioned(child: GestureDetector(child: child, onTap: (){ //点击子child
if(onClick != null){
Navigator.of(context).pop();
onClick();
}
}),
left: left,
top: top,),
],
),
onTap: (){ //点击空白处
Navigator.of(context).pop();
},
),);
}
}
以上代码就是用来展示的popup的代码了。
来看看是怎么使用的:
///构建用户头像按钮
///点击头像弹出退出按钮
Widget _buildUserIcon() {
return Padding(
padding: EdgeInsets.fromLTRB(20, 22, 0, 0),
child: GestureDetector(
child: Container(
child: Image.asset(
"assets/images/icon_user.png",
),
width: 32,
height: 32,
alignment: AlignmentDirectional.bottomStart),
onTap: _showExit,
),
);
}
///构建退出按钮
Widget _buildExit() {
return Container(
width: 91,
height: 36,
child: Stack(
children: [
Image.asset(
"assets/images/exit.png",
fit: BoxFit.fill,
),
Center(
child: Text(
ProjectLocalizations.of(context).exit,
style: TextStyle(fontSize: 14, color: Colors.black),
),
),
],
),
);
}
///弹出退出按钮
///点击退出调用onClick
void _showExit() {
Navigator.push(context, PopRoute(child: Popup(
child: _buildExit(),
left: 64,
top: 22,
onClick: (){
print("exit");
},
),),);
}
使用起来也很方便,直接用Navigator的push方法即可,需要注意的是可以通过left和top计算出来新页面item的展示位置,可以通过onClick监听item的点击事件。
原文链接:https://blog.csdn.net/email_jade/article/details/87922051