通过其他小部件为小部件约束提供绝对位置

时间:2018-04-22 21:02:53

标签: dart flutter

我试图在窗口小部件树的绝对位置上放置一个小部件。因为它位于小部件树中的某个地方,所以很可能会有其祖先设置的某些布局约束。我正在寻找可以忽略这些约束并只使用绝对坐标的东西。

如果解释令人困惑,我画了一些代表我想要的结果的方案: enter image description here

我尝试使用Stack执行此操作,但这使得传递绝对坐标很困难,即使使用Positioned也是如此,因为设置属性left: 0.0会使用相对坐标到祖先。因此将其设置为零并不一定意味着小部件将位于屏幕的左上角 我也尝试使用Overlay但我的结果与Stack实现的结果非常相似。

在Flutter中这样做的推荐方法是什么?

1 个答案:

答案 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.builderMaterialApp.builder的孩子。如果_overlayBuilder不为null,您将只创建第二个子节点;如果它不为null,则第二个孩子应该是new Builder(builder: _overlayBuilder)

您可能需要做一些正确调整堆栈大小的事情(即将其放入Expanded或其他东西)。