如何检测小部件外部的点击?

时间:2019-04-02 01:01:55

标签: flutter

我有一个在按下按钮时显示的菜单,我希望能够在菜单外有Tap的情况下关闭菜单,就像常规的PopupMenuButton一样。

我当前的解决方案是使用Stack作为其子级之一的GestureDetector作为检测抽头的方法,但是使用此解决方案,我的对齐方式由Stack控制,我只想由菜单的父级而不是Stack控制的对齐方式。

//part of the build method

return Stack(
          fit: StackFit.expand,
          children: <Widget>[
            Column(             
              mainAxisAlignment: MainAxisAlignment.end,
              children: <Widget>[
                //Holds the list of menu items
                menuContainer, 
                //button that displays the menu
                floatingActionButton]                                 
              ),
              //Detects taps outside the menu
              GestureDetector(onTap: (() => setVisibility(false))
        )
      ]
    ); 

3 个答案:

答案 0 :(得分:1)

您可以使用自定义路由来实现此行为:

class CustomDialog extends PopupRoute {
  @override
  Color get barrierColor => Colors.transparent;

  @override
  bool get barrierDismissible => true;

  @override
  String get barrierLabel => null;

  @override
  Widget buildPage(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation) {
    return _builder(context);
  }

  Widget _builder(BuildContext context) {
  return Container();
  }

  @override
  Duration get transitionDuration => Duration(milliseconds: 300);
}

barrierDismissible替代指示在小部件外部检测到触摸时是否应将路由从堆栈中弹出。每当您想要显示菜单时,您都可以调用Navigator.of(context).push(CustomDialog())

此解决方案不允许菜单返回任何值,如果您需要从菜单中返回数据,则可以替代ModalRoute并获得类似的结果。

答案 1 :(得分:1)

bobajeff的答案对我有用,但是我发现使用覆盖不透明的吸气剂并将barrierColor吸气剂设置为null会更清洁。

class CustomDialog extends PageRoute {
  @override
  Color get barrierColor => null;

  @override
  bool get opaque => false;

  @override
  bool get maintainState => false;

  @override
  bool get barrierDismissible => true;

  @override
  String get barrierLabel => null;

  @override
  Widget buildPage(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation) {
    return _builder(context);
  }

  Widget _builder(BuildContext context) {
    return Container();
  }

  @override
  Duration get transitionDuration => Duration(milliseconds: 300);
}

答案 2 :(得分:0)

Mariano Uvalle的答案不再起作用,因为他们在_buildModalBarrier中放入了一个断言,以检查是否将屏障颜色设置为透明。

我的解决方法是将/packages/flutter/lib/src/widgets/routes.dart文件复制到我自己的/libs文件夹中,并在line: 1226处注释掉该断言

      assert(barrierColor != _kTransparent);

还要替换顶部的这些非dart:package:进口

import 'basic.dart';
import 'focus_manager.dart';
import 'focus_scope.dart';
import 'framework.dart';
import 'modal_barrier.dart';
import 'navigator.dart';
import 'overlay.dart';
import 'page_storage.dart';
import 'transitions.dart';

使用

import 'package:flutter/widgets.dart';

然后在我的应用中 import 'routes.dart' as my; 并使用他发布的Mariano Uvalle的class。只有extend才是my.PopupRoute

这并不完美,但是可以解决问题。


编辑: 我已经尝试了上面的ModalRoute版本,它也可以工作:

class CustomDialog extends my.ModalRoute<bool> {

  @override
  Color get barrierColor => Colors.transparent;

  @override
  bool get barrierDismissible => true;

  @override
  String get barrierLabel => null;

  @override
  Widget buildPage(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation) {
    return _builder(context);
  }


  Widget _builder(BuildContext context) {
    return Container(alignment: Alignment.bottomLeft,
      child: Container(height: kToolbarHeight,
        child: Material(elevation: 8.0,
          child:
            Wrap(
              children: <Widget>[
                ListTile(
                  leading: new Icon(Icons.add_photo_alternate),
                  title: new Text('Add Image'),
                  onTap: () {
                    Navigator.pop(context, true);
                  },
                )
              ],
            ),

        ),
      ),
    );
  }



  @override
  Duration get transitionDuration => Duration(milliseconds: 300);

  @override
  bool get maintainState => false;

  @override
  bool get opaque => false;
}

然后我将其设为压缩值:

onPressed: () async {
                          bool notDismissed = await Navigator.of(context).push(CustomDialog());
                          if (notDismissed == true)
                          {
                            print('user clicke option');
                          }
                          else {
                            print('dismissed dialog');
                          }

同样,它并不完美,但有时可能会派上用场。但是,我发现我确实需要针对我的用例检测对特定小部件的点击。