Flutter:实现 peek & pop 效果
Posted
技术标签:
【中文标题】Flutter:实现 peek & pop 效果【英文标题】:Flutter: Implementing a peek & pop effect 【发布时间】:2018-07-23 14:09:48 【问题描述】:我试图实现 peek & pop 效果 - 当用户点击并按住卡片时,会打开一个对话框,并在用户不再点击屏幕时关闭:
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 - 我将这些更改添加到我的颤振中,然后尝试重新实现我以前的代码,如下所示:
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()
)
在所有这些中,只有使用 Listener 我才能通过点击向下和向上点击来打开和关闭对话框,但我想在调用 onPointerDown 之前添加一个延迟,我还尝试在 onPointerDown 添加一个 Timer但似乎不起作用。
有什么解决办法吗?
【问题讨论】:
我来晚了一点,但你知道,从我的理解来看,这似乎是 BuildContext 的问题。显然,函数正在被调用,并且当您将导航器替换为堆栈时这会起作用,因此问题一定是导航器无法识别它可以弹出的事实。也许这是整个框架的一个更深层次的潜在问题? 【参考方案1】:这是一个使用StatefulWidget
和Stack
而不是Navigator
的解决方案。
我在您的代码中遇到的问题是,当您使用showDialog
打开对话框时,Listener
不再接收指针事件,因此最好将所有内容放在一个***小部件下。
我也在使用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(),
);
【讨论】:
我没有意识到它可以这样使用,感谢您的解决方案和您的时间!【参考方案2】:编辑 3:v1.0.0 终于发布了!比以往更流畅、更优化、更美观。非常可定制且非常易于使用。在Pub 或GitHub 上查看。
编辑 2:v0.1.9 不再需要对 Flutter 的普通“binding.dart”进行任何修改!你可以让你的 Flutter 源代码保持愉快。如果您从早期版本更新,您可以将“binding.dart”恢复为原始格式。感谢大家的反馈。
编辑:人们对修改 Flutter 的正常“binding.dart”表示担忧。不用担心,即将发布的 v0.1.9 不需要此修改。现在,您可以暂时按照安装说明开始使用 v0.1.8 进行开发。更新后,您可以将“binding.dart”恢复为原始格式。
我不知道它是否仍然相关,但我正在为此特定目的开发一个 Flutter 包,您可以找到 here 和 here。
这是基于同名 ios 功能的 Flutter 的 Peek & Pop 实现。
这个包的强大之处就是我喜欢称之为“手势识别重新路由”。通常,当带有 GestureDetector 或类似的新小部件被推送到用于检测 Force Press 的初始小部件上时,用户必须重新启动手势以便 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 的视频,因此该软件包现在更加优化 - 它运行得更好,更流畅。
如果您有任何问题,请告诉我!
【讨论】:
欢迎提供解决方案链接,但请确保您的答案在没有它的情况下有用:add context around the link 这样您的其他用户就会知道它是什么以及为什么会出现,然后引用最相关的内容您链接到的页面的一部分,以防目标页面不可用。 Answers that are little more than a link may be deleted.以上是关于Flutter:实现 peek & pop 效果的主要内容,如果未能解决你的问题,请参考以下文章
Flutter:Navigator.of(context).pop() 返回黑屏
Flutter:在 Navigation.pop 上刷新父小部件
断言失败:/C:/Flutter/sdk/flutter/packages/flutter/lib/src/widgets/will_pop_scope.dart:135:12 _route == M