代码之家  ›  专栏  ›  技术社区  ›  Isaac Obella

颤音定制覆盖

  •  0
  • Isaac Obella  · 技术社区  · 7 年前

    我正在做一个应用程序,需要一个自定义的覆盖,是基于点击坐标定位。然后覆盖应该填充该位置的其余空间。点击距离表面较近的距离或底部的距离应以距离为准。

    我看过 Overlay Widget

    到目前为止,我已经创建了一个包含覆盖描述的StatefulWidget。

       class ScriptureDisplay extends StatefulWidget {
      @override
      _ScriptureDisplayState createState() => _ScriptureDisplayState();
    }
    
    class _ScriptureDisplayState extends State<ScriptureDisplay> {
    
      OverlayEntry _overlayEntry;
    
    
      OverlayEntry _createOverlayEntry() {
        RenderBox renderBox = context.findRenderObject();
        var size = renderBox.size;
        var offset = renderBox.localToGlobal(Offset.zero);
    
        return OverlayEntry(
            builder: (context) => Positioned(
                  left: offset.dx,
                  top: offset.dy + size.height + 5.0,
                  width: size.width,
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      nip(),
                      body(context, offset.dy),
                    ],
                  ),
                ));
      }
    
      @override
      Widget build(BuildContext context) {
    
      return RaisedButton(
        child: Text("Click Me"),
        onPressed: (){
          this._overlayEntry = this._createOverlayEntry();
          Overlay.of(context).insert(this._overlayEntry);
        },
      );
      }
    
      Widget body(BuildContext context, double offset) {
        return Material(
          borderRadius: BorderRadius.all(
            Radius.circular(8.0),
          ),
    //      color: this.color,
          elevation: 4.0,
          child: Container(
            width: MediaQuery.of(context).size.width,
            padding: const EdgeInsets.symmetric(horizontal: 8.0),
            child: ListView(
              padding: EdgeInsets.zero,
              shrinkWrap: true,
              children: ["First", "Second", "Third"]
                  .map((String s) => ListTile(
                        subtitle: Text(s),
                      ))
                  .toList(growable: false),
            ),
          ),
        );
      }
    
      Widget nip() {
        return FittedBox(
          child: Container(
            height: 20.0,
            width: 20.0,
            margin: EdgeInsets.only(left:10),
            child: CustomPaint(
              painter: OpenPainter(),
            ),
          ),
        );
      }
    }
    

    Sample Overlay

    0 回复  |  直到 7 年前
        1
  •  0
  •   Isaac Obella    7 年前

    OverlayEntry 确实是实现你提到的目标的好选择。你可以包装你的小部件,它应该创建这个覆盖,例如长按,用 GestureRecognizer LongPressStartDetails . 在那里你可以计算的位置和大小 覆盖入口 基于 globalPosition MediaQuery.of(context).size

    提示:移除 覆盖入口 remove() 覆盖入口 小工具本身!所以请保留对它的引用。

    我用了 堆栈小部件 手势识别 检测实际小部件之外的任何点击。点击这个 集装箱 _overlayEntry.remove(); 关闭覆盖。

    我终于用了 具有 顶部

    enum OVERLAY_POSITION { TOP, BOTTOM }
    
    class ScriptureDisplay extends StatefulWidget {
      @override
      _ScriptureDisplayState createState() => _ScriptureDisplayState();
    }
    
    class _ScriptureDisplayState extends State<ScriptureDisplay> {
      TapDownDetails _tapDownDetails;
      OverlayEntry _overlayEntry;
      OVERLAY_POSITION _overlayPosition;
    
      double _statusBarHeight;
      double _toolBarHeight;
    
      OverlayEntry _createOverlayEntry() {
        RenderBox renderBox = context.findRenderObject();
    
        var size = renderBox.size;
    
        var offset = renderBox.localToGlobal(Offset.zero);
        var globalOffset = renderBox.localToGlobal(_tapDownDetails.globalPosition);
    
        _statusBarHeight = MediaQuery.of(context).padding.top;
    
        // TODO: Calculate ToolBar Height Using MediaQuery
        _toolBarHeight = 50;
        var screenHeight = MediaQuery.of(context).size.height;
    
        var remainingScreenHeight = screenHeight - _statusBarHeight - _toolBarHeight;
    
        if (globalOffset.dy > remainingScreenHeight / 2) {
          _overlayPosition = OVERLAY_POSITION.TOP;
        } else {
          _overlayPosition = OVERLAY_POSITION.BOTTOM;
        }
        return OverlayEntry(builder: (context) {
          return Stack(
            children: <Widget>[
              GestureDetector(
                onTap: () {
                  _overlayEntry.remove();
                },
                child: Container(
                  width: MediaQuery.of(context).size.width,
                  height: MediaQuery.of(context).size.height,
                  color: Colors.blueGrey.withOpacity(0.1),
                ),
              ),
              Positioned(
                left: 10,
                top: _overlayPosition == OVERLAY_POSITION.TOP
                    ? _statusBarHeight + _toolBarHeight
                    : offset.dy + size.height - 5.0,
                width: MediaQuery.of(context).size.width - 20,
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    // ignore: sdk_version_ui_as_code
                    if (_overlayPosition == OVERLAY_POSITION.BOTTOM)
                      nip(),
                    body(context, offset.dy),
                    // ignore: sdk_version_ui_as_code
                    if (_overlayPosition == OVERLAY_POSITION.TOP)
                      nip(),
                  ],
                ),
              )
            ],
          );
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Padding(
          padding: EdgeInsets.only(top: 400, left: 10, right: 100),
          child: GestureDetector(
            child: Text("C"),
            onTapDown: (TapDownDetails tapDown) {
              setState(() {
                _tapDownDetails = tapDown;
              });
              this._overlayEntry = this._createOverlayEntry();
              Overlay.of(context).insert(this._overlayEntry);
            },
          ),
        );
      }
    
      Widget body(BuildContext context, double offset) {
        return Material(
          borderRadius: BorderRadius.all(
            Radius.circular(8.0),
          ),
          elevation: 4.0,
          child: Container(
            width: MediaQuery.of(context).size.width,
            height: _overlayPosition == OVERLAY_POSITION.BOTTOM
                ? MediaQuery.of(context).size.height -
                    _tapDownDetails.globalPosition.dy -
                    20
                : _tapDownDetails.globalPosition.dy -
                    _toolBarHeight -
                    _statusBarHeight -
                    15,
            padding: const EdgeInsets.symmetric(horizontal: 8.0),
            child: ListView(
              padding: EdgeInsets.zero,
              shrinkWrap: true,
              children: [
                "First",
                "Second",
                "Third",
                "First",
                "Second",
                "Third",
                "First",
                "Second",
                "Third"
              ]
                  .map((String s) => ListTile(
                        subtitle: Text(s),
                      ))
                  .toList(growable: false),
            ),
          ),
        );
      }
    
      Widget nip() {
        return Container(
          height: 10.0,
          width: 10.0,
          margin: EdgeInsets.only(left: _tapDownDetails.globalPosition.dx),
          child: CustomPaint(
            painter: OpenPainter(_overlayPosition),
          ),
        );
      }
    }
    
    class OpenPainter extends CustomPainter {
      final OVERLAY_POSITION overlayPosition;
    
      OpenPainter(this.overlayPosition);
    
      @override
      void paint(Canvas canvas, Size size) {
        switch (overlayPosition) {
          case OVERLAY_POSITION.TOP:
            var paint = Paint()
              ..style = PaintingStyle.fill
              ..color = Colors.white
              ..isAntiAlias = true;
    
            _drawThreeShape(canvas,
                first: Offset(0, 0),
                second: Offset(20, 0),
                third: Offset(10, 15),
                size: size,
                paint: paint);
    
            break;
          case OVERLAY_POSITION.BOTTOM:
            var paint = Paint()
              ..style = PaintingStyle.fill
              ..color = Colors.white
              ..isAntiAlias = true;
    
            _drawThreeShape(canvas,
                first: Offset(15, 0),
                second: Offset(0, 20),
                third: Offset(30, 20),
                size: size,
                paint: paint);
    
            break;
        }
    
        canvas.save();
        canvas.restore();
      }
    
      @override
      bool shouldRepaint(CustomPainter oldDelegate) => false;
    
      void _drawThreeShape(Canvas canvas,
          {Offset first, Offset second, Offset third, Size size, paint}) {
        var path1 = Path()
          ..moveTo(first.dx, first.dy)
          ..lineTo(second.dx, second.dy)
          ..lineTo(third.dx, third.dy);
        canvas.drawPath(path1, paint);
      }
    
      void _drawTwoShape(Canvas canvas,
          {Offset first, Offset second, Size size, paint}) {
        var path1 = Path()
          ..moveTo(first.dx, first.dy)
          ..lineTo(second.dx, second.dy);
        canvas.drawPath(path1, paint);
      }
    }
    

    结果 Result