首页 > 建站教程 > APP开发,混合APP >  flutter入门之使用PopupRoute自定义实现PopupWindow功能正文

flutter入门之使用PopupRoute自定义实现PopupWindow功能

    本文起因是由于一个弹窗,要实现一个能够通过点击显示隐藏的功能,效果如下:



    第一选择是采用原生的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的全部内容了。