第一选择是采用原生的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<double> animation, Animation<double> 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: <Widget>[ 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: <Widget>[ 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的点击事件。
以上就是自定义PopupWindow的全部内容了。