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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM