Flutter-widget相对屏幕的位置,动态展示dialog

 

主要是通过 RenderObject 获取widget 相对屏幕的坐标, 从而动态设置 Dialog 的位置.

函数   getTransformTo(RenderObject ancestor)   参数 ancestor  为null, 表示相对根组件的位置(也就是相对屏幕的位置)

 

代码示例如下: 

 

所点击的widget

class CloseTap extends StatefulWidget {
  @override
  _CloseTapTapState createState() => _CloseTapTapState();
}

class _CloseTapTapState extends State<CloseTap> with WidgetsBindingObserver {
  void _onAfterRendering(Duration timeStamp) {
    RenderObject renderObject = context.findRenderObject();
    Size size = renderObject.paintBounds.size;
    var vector3 = renderObject.getTransformTo(null)?.getTranslation();
    CommonUtils.showChooseDialog(context, size, vector3);
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Icon(Icons.close),
      onTapDown: (TapDownDetails details) {
        WidgetsBinding.instance.addPostFrameCallback(_onAfterRendering);
        setState(() {});
      },
    );
  }
}

 

根据所点击的widget的坐标, 展示dialog

class CommonUtils {
  
  static showChooseDialog(BuildContext context, Size size, var vector3) {
    final double wx = size.height;
    final double dx = vector3[0];
    final double dy = vector3[1];
    final double w = MediaQuery.of(context).size.width;
    final double h = MediaQuery.of(context).size.height;

    return showDialog(
      context: context,
      builder: (BuildContext context) {
        return new Material(
          color: Colors.transparent,
          child: Container(
            width: double.infinity,
            height: double.infinity,
            child: Stack(
              children: <Widget>[
                GestureDetector(
                  child: Container(
                    width: double.infinity,
                    height: double.infinity,
                    child: Text(''),
                  ),
                  onTap: () {
                    Navigator.of(context).pop();
                  },
                ),
                Positioned(
                  left: 10.0,
                  top: dy < h / 2 ? dy + wx / 2 : null,
                  bottom: dy < h / 2 ? null : (h - dy + wx / 2),
                  child: Container(
                    decoration: BoxDecoration(
                      borderRadius: BorderRadius.all(
                        Radius.circular(10.0),
                      ),
                      color: Colors.white,
                    ),
                    width: w - 20.0,
                    child: GestureDetector(
                      child: Column(
                        children: <Widget>[
                          ListTile(
                              leading: Icon(Icons.highlight_off),
                              title: Text('不感兴趣'),
                              subtitle: Text('减少这类内容')),
                          Divider(),
                          ListTile(
                              leading: Icon(Icons.error_outline),
                              title: Text('反馈垃圾内容'),
                              subtitle: Text('低俗、标题党等')),
                          Divider(),
                          ListTile(
                              leading: Icon(Icons.not_interested),
                              title: Text('屏蔽'),
                              subtitle: Text('请选择屏蔽的广告类型')),
                          Divider(),
                          ListTile(
                            leading: Icon(Icons.help_outline),
                            title: Text('为什么看到此广告'),
                          ),
                        ],
                      ),
                      onTap: () { 
                        Navigator.of(context).pop();
                      },
                    ),
                  ),
                ),
                Positioned(
                  left: dx - 10.0,
                  top: dy < h / 2 ? dy - wx / 2 : null,
                  bottom: dy < h / 2 ? null : (h - dy - wx / 2),
                  child: ClipPath(
                    clipper: Triangle(dir: dy - h / 2),
                    child: Container(
                      width: 30.0,
                      height: 30.0,
                      color: Colors.white,
                      child: null,
                    ),
                  ),
                ),
              ],
            ),
          ),
        );
      },
    );
  }
}

 

小三角组件, 利用贝塞尔曲线api, 以及 CustomClipper 的使用

class Triangle extends CustomClipper<Path> {
  double dir;
  Triangle({this.dir});
  @override
  Path getClip(Size size) {
    var path = Path();
    double w = size.width;
    double h = size.height;
    if (dir < 0) {
      path.moveTo(0, h);
      path.quadraticBezierTo(0, 0, w * 2 / 3, 0);
      path.quadraticBezierTo(w / 4, h / 2, w, h);
    } else {
      path.quadraticBezierTo(0, h / 2, w * 2 / 3, h);
      path.quadraticBezierTo(w / 3, h / 3, w, 0);
      path.lineTo(0, 0);
    }
    return path;
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

 

posted @ 2021-02-25 11:18  Da雪山  阅读(1924)  评论(0编辑  收藏  举报