如何通过在颤动中单击外部来关闭叠加层

kri*_*aji 4 dart flutter

我正在创建一个按钮的 onClick 叠加层,从下面的代码可以正常工作,但我想通过在它外部单击来关闭叠加层。

在此处输入图片说明

参考代码:

  • 为了创建覆盖,我正在使用 OverlayEntry

  • 使用在六个按钮中的任何一个上粘贴时可用的偏移量来设置覆盖位置。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: GesturePositionDetector(),
    );
  }
}

class GesturePositionDetector extends StatelessWidget {
  OverlayState overlayState;
  OverlayEntry _overlayEntry;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
          height: MediaQuery.of(context).size.height,
          width: MediaQuery.of(context).size.width,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              _getButtons([1, 2, 3], context),
              _getButtons([4, 5, 6], context),
              _getButtons([7, 8, 9], context)
            ],
          )),
    );
  }

  Widget _getButtons(List<int> labels, BuildContext context) {
    var listOfButtons = List<Widget>();

    labels.forEach((int label) {
      listOfButtons.add(_getButtonView('Button $label', context, label));
    });

    return Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: listOfButtons);
  }

  Widget _getButtonView(String label, BuildContext context, int index) {
    return GestureDetector(
      child: Container(
        height: 50,
        width: 150,
        margin : EdgeInsets.fromLTRB(15, 25, 15, 20),
        decoration: BoxDecoration(
            color: Colors.blueAccent,
            borderRadius: BorderRadius.all(Radius.circular(10))),
        child: Center(
          child: Text(
            label,
            style: TextStyle(fontWeight: FontWeight.bold),
          ),
        ),
      ),
      onTapDown: (details) {
        onTap(details, context, index);
      },
    );
  }

  onTap(TapDownDetails details, context, int index) {
    var size = MediaQuery.of(context).size;
    var offset = details.globalPosition;

      _overlayEntry?.remove();
    overlayState = Overlay.of(context);

    _overlayEntry = new OverlayEntry(
        builder: (BuildContext context) => Positioned(
            left: offset.dx + 300 >= size.width ? offset.dx - 300 : offset.dx,
            top: offset.dy + 200 >= size.height ? offset.dy - 200 : offset.dy,
            child: Material(
              color: Colors.transparent,
              child: Container(
                  width: 300,
                  height: 200,
                  child: Container(
                      decoration: BoxDecoration(
                          color: Colors.white70,
                          borderRadius: BorderRadius.circular(20),
                          boxShadow: [
                            BoxShadow(
                              color: Colors.grey,
                              blurRadius: 5.0,
                            ),
                          ]),
                      margin: EdgeInsets.symmetric(horizontal: 20),
                      padding: EdgeInsets.fromLTRB(16, 10, 16, 10),
                      child: Wrap(
                        crossAxisAlignment: WrapCrossAlignment.center,
                        alignment: WrapAlignment.center,
                        direction: Axis.vertical,
                        spacing: 10,
                        children: <Widget>[
                          Text(
                            'Main Text',
                            style: TextStyle(
                                fontWeight: FontWeight.bold, fontSize: 25),
                          ),
                          Text('sub text 1'),
                          Text('sub text 2'),
                          Text('sub text 3'),
                          Text('sub text 4')
                        ],
                      ))),
            )));
    overlayState.insert(_overlayEntry);
  }
}
Run Code Online (Sandbox Code Playgroud)

小智 16

我已经看到您标记为正确答案的答案。尽管它可能适合您的情况,因为您已在一个屏幕上编写了所有代码。但是,如果您想提取覆盖层或创建一个可以从任何地方调用的自定义对话框,那么这个答案将不起作用。为此,您需要考虑我的回答。

包装从 OverlayEntry 构建器返回的小部件,如下所示:

    return OverlayEntry(
      builder: (context) => SizedBox(
        width: screenSize.width,
        height: screenSize.height,
        child: Stack(
          children: [
            ModalBarrier(
              onDismiss: () {
                _closeOverlay();
              },
            ),
            Positioned(
              width: width,
              child: CompositedTransformFollower(
                link: _layerLink,
                showWhenUnlinked: false,
                offset: Offset(
                  0,
                  parentSize.height,
                ),
                child: Child(),
              ),
            ),
          ],
        ),
      ),
    );
Run Code Online (Sandbox Code Playgroud)

在任何情况下,您可能需要裁剪 ModalBarrier 区域的一部分,该区域不应与 ModalBarrier 重叠。在这种情况下,您可以使用 CustomClipper 裁剪出 ModalBarrier 的部分。


小智 9

手势检测器包裹身体,点击身体时,点击事件将触发,在点击事件方法中关闭覆盖,如下面的代码所示

代码:

body: GestureDetector(
  onTap: (){
    _overlayEntry?.remove();
  },
  child : Container(
  height: MediaQuery.of(context).size.height,
  width: MediaQuery.of(context).size.width,
Run Code Online (Sandbox Code Playgroud)