美文网首页
关于遮罩箭头菜单

关于遮罩箭头菜单

作者: 91阿生 | 来源:发表于2020-12-09 11:43 被阅读0次
    先看效果图:
    menu.gif
    创建的文件:
    目录文件.png
    代码展示:

    menu.dart

    import 'package:flutter/material.dart';
    
    import 'arrow_list.dart';
    import 'fade_popup.dart';
    import 'mask.dart';
    
    
    class FCArrowMenuScreen extends StatefulWidget {
      static const routeName = '/arrow_menu';
    
      @override
      _FCArrowMenuScreenState createState() => _FCArrowMenuScreenState();
    }
    
    class _FCArrowMenuScreenState extends State<FCArrowMenuScreen> {
      GlobalKey<_FCArrowMenuScreenState> _globalKey = GlobalKey();
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Top Arrow Menu')
          ),
          body: Stack(
            children: [
              Positioned( // Positioned只能配合 Stack 使用
                left: 150,
                top: 150,
                child: IconButton(
                  key: _globalKey,
                  icon: Icon(Icons.add_circle, size: 35, color: Colors.yellow[800]), 
                  onPressed: () {
                    // 获取 IconButton 在屏幕上的坐标
                      // 1.先获取此组件, 利用组件绑定的key
                      // 2.组件自身的左上角 转屏幕坐标(零点)
                    RenderBox box = _globalKey.currentContext.findRenderObject();
    
                    Offset boxZeroOffset = box.localToGlobal(Offset.zero);
                    Size boxSize = box.size;
    
                    double menuX = boxZeroOffset.dx - FCArrowMenu.menuWidth * 0.5 + box.size.width * 0.5;
                    double menuY = boxZeroOffset.dy + boxSize.height;
    
                    Navigator.of(context).push(
                        /** 自定义动效路由*/
                      FCFadePopupRoute(child: FCMask(child: FCArrowMenu(), left: menuX, top: menuY))
    
                      /** 可使用 PageRouteBuilder路由: 系统自带动效路由; 
                          PageRouteBuilder继承PopupRoute*/
                      // PageRouteBuilder(
                      //   opaque: false,
                      //   barrierDismissible: true,
                      //   maintainState: true,
                      //   transitionDuration: Duration(milliseconds: 250),
                      //   pageBuilder: (context, animation, secondaryAnimation) {
                      //     return FadeTransition(
                      //       opacity: Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(parent: animation, curve: Curves.fastOutSlowIn)),
                      //       child: FCMask(child: FCArrowMenu(), left: menuX, top: menuY),
                      //     );
                      // })
                    );
                  }
                ),
              )
            ]
          ),
        );
      }
    }
    

    fade_popup.dart 自定义动效

    import 'package:flutter/material.dart';
    
    class FCFadePopupRoute extends PopupRoute {
      final Widget child;
    
      FCFadePopupRoute({@required this.child});
    
      @override
      Color get barrierColor => null;
    
      @override
      String get barrierLabel => null;
    
      @override
      bool get barrierDismissible => true;
    
      @override
      bool get maintainState => true;
    
      @override
      Duration get transitionDuration => Duration(milliseconds: 250);
    
      @override
      Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
        return FadeTransition(
          opacity: Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(parent: animation, curve: Curves.easeIn)),
          child: child,
        );
      }
    }
    

    mask.dart

    import 'package:flutter/material.dart';
    
    class FCMask extends StatelessWidget {
      final Widget child;
      final double left;
      final double top;
    
      FCMask({
        @required this.child,
        this.left, 
        this.top
      });
    
      @override
      Widget build(BuildContext context) {
        return Scaffold( // 这里也可以使用 Material(color: Colors.transparent, child: xxx)
          backgroundColor: Colors.transparent,
          body: GestureDetector(
            onTap: ()=>Navigator.of(context).pop(),
            child: Stack(
              children: [
                Container( // 底部全屏黑色透明背景
                  width: double.infinity,
                  height: double.infinity,
                  color: Colors.black.withOpacity(0.55)
                ),
                Positioned(
                  top: top,
                  left: left,
                  child: child
                )
              ],
            )
          ),
        );
      }
    }
    

    arrow_list.dart

    import 'package:flutter/material.dart';
    import 'dart:ui' as ui;
    
    // cell显示内容数据模型
    class MenuItem {
      final Widget leading;
      final String title;
    
      MenuItem({
        this.leading,
        this.title
      });
    }
    
    // items数据源
    List<MenuItem> items = [
      MenuItem(leading: Icon(Icons.warning_amber_rounded, color: Colors.white, size: 28), title: "异常提示"),
      MenuItem(leading: Icon(Icons.update_rounded, color: Colors.white, size: 28), title: "更新信息"),
      MenuItem(leading: Icon(Icons.message_rounded, color: Colors.white, size: 28), title: "回复信息")
    ];
    
    
    class FCArrowMenu extends StatelessWidget {
      static double menuWidth = MediaQueryData.fromWindow(ui.window).size.width *0.35;
      
      @override
      Widget build(BuildContext context) {
        return Container(
          width: menuWidth,
          child: Column(
            children: [
              // Image.asset('assets/images/img_up_arrow.png', width: 25.0, height: 13.4),
              FCTrigon(), // 自定义绘制箭头
    
              Container(
                constraints: BoxConstraints(maxHeight: 45.0*5), // 设置最大容器高度
                padding: EdgeInsets.only(left: 8, right: 8),
                decoration: BoxDecoration(
                  color: Colors.black.withOpacity(0.55),
                  borderRadius: BorderRadius.circular(5)
                ),
                child: ListView.builder(
                  shrinkWrap: true,
                  padding: EdgeInsets.zero,
                  itemCount: items.length,
                  itemBuilder: (context, index) {
                    Widget leading = items[index].leading;
                    String title = items[index].title;
    
                    return GestureDetector(
                      child: Container(
                        padding: EdgeInsets.only(top: 8, bottom: 8),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.start,
                          crossAxisAlignment: CrossAxisAlignment.center,
                          children: [
                            leading,
                            SizedBox(width: 8),
    
                            Expanded(
                              child: Text(title, style: TextStyle(
                                fontSize: 17,
                                color: Colors.white,
                                fontWeight: FontWeight.bold
                              )),
                            )
                          ],
                        ),
                      ),
                      onTap: () {
                        print("$title");
                      },
                    );
                  },
                ),
              )
            ],
          ),
        );
      }
    }
    
    /* 绘制三角形 */ 
    class FCTrigon extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return ClipPath(
          clipper: FCTrigonPath(),
          child: Container(
            width: 25.0,
            height: 13.4,
            color: Colors.black.withOpacity(0.55),
          ),
        );
      }
    }
    class FCTrigonPath extends CustomClipper<Path> {
      
      @override
      Path getClip(Size size) {
        Path path = Path();
    
        path.moveTo(0, size.height);
        path.lineTo(size.width * 0.5, 0);
        path.lineTo(size.width, size.height);
    
        path.close();
    
        return path;
      }
      
      @override
      bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
        return true;
      }
    }
    
    
    

    相关文章

      网友评论

          本文标题:关于遮罩箭头菜单

          本文链接:https://www.haomeiwen.com/subject/odnfgktx.html