关于遮罩箭头菜单

2020-12-09  本文已影响0人  91阿生
先看效果图:
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;
  }
}


上一篇下一篇

猜你喜欢

热点阅读