我试图在窗口小部件树的绝对位置上放置一个小部件。因为它位于小部件树中的某个地方,所以很可能会有其祖先设置的某些布局约束。我正在寻找可以忽略这些约束并只使用绝对坐标的东西。
我尝试使用Stack
执行此操作,但这使得传递绝对坐标很困难,即使使用Positioned
也是如此,因为设置属性left: 0.0
会使用相对坐标到祖先。因此将其设置为零并不一定意味着小部件将位于屏幕的左上角
我也尝试使用Overlay
但我的结果与Stack
实现的结果非常相似。
在Flutter中这样做的推荐方法是什么?
答案 0 :(得分:1)
我将描述两种方法来实现这一点,但我不会提供代码或示例,因为我不认为以复制粘贴的方式将其放在那里一定是个好主意 - 我同意@ Remi评论说,如果你需要在一个应用程序中执行此操作,很有可能重构应用程序以避免它。但是,有一种方法可以做到这一点 - 虽然被警告,但它可能会导致触摸事件等问题(虽然我认为有办法解决这个问题 - 请参阅AbsorbPointer and IgnorePointer获取起点)。既然你还没有真正解释过你想要的东西,我会认为这不是你需要的东西。
我会鼓励你研究其他做任何事情的方法,并询问你是否愿意帮助找出更好的方法。
无论如何,关于好东西= P:
方法#1 - 使用现有的叠加层:
我实际上已经将这个方法用于模态加载指示器,我想在下面的页面被推动之间保持这种指示,并且可以从应用程序内的任何地方调用,而不需要任何小部件在它上面的树中。这是一个非常具体的用例,虽然我认为这证明了这种用法。
基本上,您希望使用Overlay,但不是创建自己的(它将与您正在处理的小部件具有相同的大小),而是想要使用现有的。如果您正在使用MaterialApp,WidgetApp,或者甚至只是应用程序中的某个导航器,那么您已经拥有了一个几乎与屏幕大小相同的Overlay。如果您有多层叠加层,您希望将其置于顶层,因为它最有可能覆盖整个屏幕。
要访问它,您可以使用OverlayState rootOverlay = context.rootAncestorStateOfType(const TypeMatcher<OverlayState>());
来获取根覆盖(您可能想要断言它实际上找到了一个)。
一旦您有权访问它,您就可以创建一个OverlayEntry来构建您的小部件,并调用rootOverlay.insert(...)
来添加它。 OverlayEntry构建的项目将从左上角到屏幕范围定位,只要覆盖层本身覆盖整个屏幕(您可以自己进行偏移)。您还需要确保在某个时刻rootOverlay.remove(...)
,这意味着保留对OverlayEntry的引用。我个人创建了一个名为OverlayInsertionManager的类,它可以跟踪覆盖条目并插入/删除。
方法#2 - 使用您自己的小部件
如果您只是在自己的应用程序中执行此操作,我会考虑使用这种方式稍微清洁,尽管可能仍然不是一个好主意。
基本上,您要做的是在应用程序中创建一个高级状态窗口小部件 - 高于屏幕占用空间的任何内容。理论上,这可能意味着在您的MaterialApp / WidgetApp / etc之上,但如果您使用WidgetApp提供的主题/文本方向性等,则可能会给您带来问题。我认为您可以使用WidgetApp.builder将小部件放在您需要的位置。为方便起见,我们将其称为PageOverlay
(使用相应的PageOverlayState
。
在你的小部件中,你将拥有一个静态of
方法,如下所示(这些方法在flutter的源代码中散落,并且或多或少是一种惯例):
static PageOverlayState of(BuildContext context) {
final PageOverlayState result = context.ancestorStateOfType(const TypeMatcher<PageOverlayState>());
assert(() {
if (result == null) {
throw new FlutterError(
'No Overlay widget found.\n'
);
}
return true;
}());
return result;
}
在你的PageOverlayState中,你将有一个类似WidgetBuilder _overlayBuidler
的变量,通常为null,类似于set overlayBuilder(WidgetBuilder overlayBuilder) => setState(() => _overlayBuilder = overlayBuilder);
的方法/ setter
在PageOverlayState的构建器中,您将创建一个Stack;第一个孩子将成为您传递到WidgetApp.builder
或MaterialApp.builder
的孩子。如果_overlayBuilder不为null,您将只创建第二个子节点;如果它不为null,则第二个孩子应该是new Builder(builder: _overlayBuilder)
。
您可能需要做一些正确调整堆栈大小的事情(即将其放入Expanded或其他东西)。