Flutter:实现窥视和流行效果

时间:2018-07-23 14:09:49

标签: dart flutter

我试图实现偷看和弹出效果-当用户点击并按住卡片时,将打开一个对话框,当用户不再点击屏幕时不再显示该对话框:

Listener(
    onPointerDown: (PointerDownEvent e) {
      // open dialog
      showDialog(
          context: context,
          builder: (context) => Container(
                child: Card(),
              ));
    },
    onPointerUp: (PointerUpEvent e) {
      // dismiss dialog
      if (Navigator.of(context).canPop()) {
        Navigator.of(context).pop('dialog');
      }
    },
    child: Card()
)

效果很好,当我握住卡时会显示对话框,而当我不再握住水龙头时将其关闭。

但是我想在调用onPointerDown之前要有一些延迟,例如GestureDectector中的onLongPress-我能够使对话框在长按时显示,但是当我离开屏幕时却从未调用过onTapUp:

GestureDetector(
  onLongPress: () {
    // open dialog
    showDialog(
        context: context,
        builder: (context) => Container(child: Card()));
  },
  onTapUp: (TapUpDetails d) {
    // dismiss dialog
    if (Navigator.of(context).canPop()) {
      Navigator.of(context).pop();
    }
  },
  child: Card()
)

我尝试通过以下方式进行操作,但也从未调用过onTapUp:

  GestureDetector(
  onTapDown: (TapDownDetails d) {
    // open dialog
    showDialog(context: context, builder: (context) => Card());
  },
  onTapUp: (TapUpDetails d) {
    // dismiss dialog
    if (Navigator.of(context).canPop()) {
      Navigator.of(context).pop();
    }
  },
  child: Card())

但是以下显示了分接头已正确注册:

GestureDetector(
  onTapDown: (TapDownDetails d) {
    print("down")
  },
  onTapUp: (TapUpDetails d) {
    print("up")
  },
  child: Card()
)

环顾四周后,我注意到此flutter PR添加了onLongPressUp-我将这些更改添加到了flutter中,然后尝试重新实现我以前的代码,如下所示:

GestureDetector(
  onLongPress: () {
    // open dialog
    showDialog(
        context: context,
        builder: (context) => Container(child: Card()));
  },
  onLongPressUp: () {
    // dismiss dialog
    if (Navigator.of(context).canPop()) {
      Navigator.of(context).pop();
    }
  },
  child: Card()
)

长按时会显示该对话框,但是当我不再长按时该对话框从未被关闭-onLongPressUp似乎没有被调用,但是以下显示它正确注册了我的拍子:

GestureDetector(
  onLongPress: () {
    print("longpress")
  },
  onLongPressUp: () {
    print("longpressup")
  },
  child: Card()
)

在所有这些中,仅使用侦听器,我就可以通过单击并打开来打开和关闭对话框,但是我想在调用onPointerDown之前添加一个延迟,我也尝试在onPointerDown中添加一个Timer但似乎不起作用。

有解决方案吗?

2 个答案:

答案 0 :(得分:1)

这是一个使用StatefulWidgetStack而不是Navigator的解决方案。

我在您的代码中遇到的问题是,当您使用Listener打开对话框时,showDialog不再收到指针事件,因此最好将所有内容都保留在一个顶级小部件中。

我还使用了Timer,当指针升起或用户离开页面时会取消。

import 'dart:async';

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Timer _showDialogTimer;
  bool _dialogVisible = false;

  @override
  void dispose() {
    _showDialogTimer?.cancel();
    super.dispose();
  }

  void _onPointerDown(PointerDownEvent event) {
    _showDialogTimer = Timer(Duration(seconds: 1), _showDialog);
  }

  void _onPointerUp(PointerUpEvent event) {
    _showDialogTimer?.cancel();
    _showDialogTimer = null;
    setState(() {
      _dialogVisible = false;
    });
  }

  void _showDialog() {
    setState(() {
      _dialogVisible = true;
    });
  }

  @override
  Widget build(BuildContext context) {
    final layers = <Widget>[];

    layers.add(_buildPage());

    if(_dialogVisible) {
      layers.add(_buildDialog());
    }

    return Listener(
      onPointerDown: _onPointerDown,
      onPointerUp: _onPointerUp,
      child: Stack(
        fit: StackFit.expand,
        children: layers,
      ),
    );
  }

  Widget _buildPage() {
    return Scaffold(
      appBar: AppBar(
        title: Text('Example App'),
      ),
      body: Center(
        child: Text('Press me'),
      ),
    );
  }

  Widget _buildDialog() {
    return Container(
      color: Colors.black.withOpacity(0.5),
      padding: EdgeInsets.all(50.0),
      child: Card(),
    );
  }
}

答案 1 :(得分:-1)

编辑3:v1.0.0终于发布了!比以往更流畅,更优化,更美丽。非常可定制,非常易于使用。在PubGitHub上看到它。

编辑2:v0.1.9不再需要对Flutter的常规“ binding.dart”进行任何修改!您可以让Flutter源代码保持孤独和快乐。如果要从早期版本进行更新,则可以将“ binding.dart”还原为原始格式。感谢大家的反馈。

编辑:人们对修改Flutter的常规“ binding.dart”表示担忧。不用担心-即将发布的v0.1.9不需要进行此修改。目前,您可以暂时按照安装说明进行操作,并从v0.1.8开始开发。更新后,您可以将“ binding.dart”还原为原始格式。

我不知道它是否仍然有用,但是我正在为此目的开发Flutter软件包,您可以找到herehere

这是Flutter的Peek&Pop实现,基于同名的iOS功能。

此套件的强大功能是我喜欢称之为“手势识别重新路由”的功能。通常,当将具有GestureDetector或类似功能的新窗口小部件推到用于检测强制按下的初始窗口小部件上时,用户必须重新启动手势,Flutter才能恢复对其进行更新。该程序包解决了该问题。如文档中所述:

///This function is called by the instantiated [PeekAndPopChild] once it is ready to be included in the Peek & Pop process. Perhaps the most
///essential functionality of this package also takes places in this function: The gesture recognition is rerouted from the  [PeekAndPopDetector]
///to the instantiated [PeekAndPopChild]. This is important for avoiding the necessity of having the user stop and restart their Force Press.
///Instead, the [PeekAndPopController] does this automatically so that the existing Force Press can continue to update even when if
///[PeekAndPopDetector] is blocked by the view which is often the case especially when using PlatformViews.

该软件包的核心是“ PeekAndPopController”小部件。这个小部件是高度可定制的。您可以控制整个过程,甚至可以随意阻止默认行为,而是运行自己的序列。

请看此video的示例。这是V0.1.0的视频,因此该软件包现在有了更多优化-运行效果更好,更流畅。

让我知道您是否有任何疑问!