本文介绍了通过其他小部件为小部件约束赋予绝对位置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在小部件树的绝对位置中提供一个小部件.因为它位于小部件树中的某个位置,所以很可能会由其祖先设置某些布局约束.我正在寻找可以忽略这些约束而仅使用绝对坐标的东西.

I'm trying to give a widget far down in the widget tree absolute positions. Because it is somewhere down in the widget tree, it is most likely that there will be certain layout constraints set by its ancestors. I'm looking for something that can ignore those constraints and just use absolute coordinates.

如果解释令人困惑,那么我会画一些代表我想要的结果的小方案:

If the explanation is confusing, I drew a little scheme of something that represents my wanted outcome:

我尝试使用Stack进行此操作,但是即使使用Positioned,也很难传递绝对坐标,因为设置属性left: 0.0会使用祖先的相对坐标.因此,将其设置为零并不一定意味着该小部件将位于屏幕的左上方.
我也尝试使用Overlay,但结果与Stack实现几乎相同.

I tried doing this using a Stack, but that makes it difficult to pass in absolute coordinates, even with a Positioned because setting the property left: 0.0 would use relative coordinates to the ancestor. So setting it to zero would not necessarily mean that the widget would be positioned at the top left of the screen.
I also tried using an Overlay but I the results are pretty much the same as with the Stack implementation.

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

What is the recommended way of doing this in Flutter?

推荐答案

我将介绍两种方法来实现此目的,但是由于我不希望将代码或示例放在此处而不会提供代码或示例.可复制粘贴的方式肯定是个好主意-我同意@Remi的评论,即如果您需要在应用程序中执行此操作,则很有可能可以重构该应用程序以避免这种情况.但是,有一种方法可以执行此操作-尽管会被警告,但这可能会导致诸如触摸事件之类的问题(尽管我认为有解决方法-请参见 AbsorbPointer和IgnorePointer 作为起点).既然您还没有真正解释您想要的功能,我将假定您不需要这些.

I'll describe two ways to do this, but I won't provide the code or an example as I don't think putting this out there in a copy-paste-able way is necessarily a good idea - I agree with @Remi's comment that if you need to do this in an app, there's a good chance that the app could be refactored to avoid it. However, there is a way to do this - although be warned, it might cause some problems with things such as touch events (although I think there's ways to get around that - see AbsorbPointer and IgnorePointer for starting points). Since you haven't really explained what you want this for, I'm going to assume that isn't something you need.

我鼓励您研究尝试做的其他方式,并询问是否需要帮助找出更好的方式.

I would encourage you to look into other ways of doing whatever you're trying to do and ask if you'd like help figuring out that better way.

无论如何,要介绍好东西= P:

Anyways, on to the good stuff =P :

方法1-使用现有的叠加层:

我本人实际上已将此方法用于模式加载指示器,我希望在加载下面的页面之间时保持该状态,并且可以在应用程序中的任何位置调用该方法,而无需在其上方的树中放置任何小部件.这是一个非常具体的用例,我认为可以证明这种用法是正确的.

I have actually used this method myself for a modal loading indicator which I want to persist while between pages underneath are being pushed and which can be called from anywhere within an app without requiring any widgets be in the tree above it. That's a very specific use-case though which I think justifies this usage.

基本上,您想使用一个Overlay,而不是创建自己的(与正在处理的小部件具有相同大小的),而是要使用现有的.如果您使用的是MaterialApp,WidgetApp,甚至只是在应用程序中某个位置的导航器,则已经有一个Overlay,该Overlay几乎可以确定与屏幕大小相同.如果您有多层叠加层,则希望将其放在顶部,因为它最有可能覆盖整个屏幕.

Basically, you want to use an Overlay, but instead of creating your own (which will have the same size as the widget you're dealing with), you want to use an existing one. If you're using a MaterialApp, WidgetApp, or even just a Navigator somewhere in your app, you have an Overlay already which is almost for sure the same size as the screen. If you have multiple layers of overlays, you want to get the one at the top as that's the most likely to cover the entire screen.

要访问它,可以使用OverlayState rootOverlay = context.rootAncestorStateOfType(const TypeMatcher<OverlayState>());获取根覆盖图(您可能想断言它实际上找到了一个).

To get access to it, you can use OverlayState rootOverlay = context.rootAncestorStateOfType(const TypeMatcher<OverlayState>()); to get ahold of the root overlay (you'll probably want to assert that it actually found one).

一旦可以访问它,就可以创建一个OverlayEntry来构建您的窗口小部件,然后调用rootOverlay.insert(...)进行添加.只要您的叠加层本身覆盖了整个屏幕,您的OverlayEntry生成的项目将从左上角定位到屏幕的整个范围(您可以自己进行偏移).您还需要确保在某些时候rootOverlay.remove(...),这意味着保留对OverlayEntry的引用.我将亲自创建一个名为OverlayInsertionManager的类,或者一个可以跟踪叠加层条目并进行插入/移除的类.

Once you have access to it, you can make an OverlayEntry which builds whatever your widget is, and call rootOverlay.insert(...) to add it. The item built by your OverlayEntry will be positioned from the top left and to the extent of the screen so long as the overlay itself covers the entire screen (you can do the offset yourself). You'll also want to make sure you rootOverlay.remove(...) at some point, which means keeping a reference to the OverlayEntry. I'd personally create a class called OverlayInsertionManager or something which keeps track of the overlay entry and does insertion/removal.

方法2-使用您自己的小部件

如果您只是在自己的应用程序中执行此操作,则我认为这种方法可以使其工作更简洁,尽管可能仍然不是一个好主意.

I would consider this way of doing it slightly cleaner if you're just doing it within your own app, although still probably not a great idea.

基本上,您想要做的是在应用程序中的上方创建一个有状态的小部件-在会占用屏幕空间的所有内容上方.从理论上讲,这可能意味着要高于MaterialApp/WidgetApp/etc,但如果使用WidgetApp提供的主题/文本方向性/等可能会给您带来麻烦.我认为您可以使用 WidgetApp.builder 将小部件放在你需要它.为了方便起见,我们将其称为PageOverlay(带有相应的PageOverlayState.

Basically, what you want to do is create a Stateful Widget high up in your app - above anything that would take up space on the screen. This could theoretically mean above your MaterialApp/WidgetApp/etc although that might cause problems for you if you're using theme/text directionality/etc that WidgetApp provides. I think you can use the WidgetApp.builder to place your widget where you need it. Let's call it PageOverlay for convenience (with a corresponding PageOverlayState.

在窗口小部件中,您将具有一个类似于以下内容的静态of方法(这些方法在flutter的源代码中乱七八糟,或多或少是一种惯例):

In your widget you'll have a static of method like the following (these are littered throughout flutter's source code and are more or less a convention):

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这样的变量,该变量通常为空,而一个方法/设置是像set overlayBuilder(WidgetBuilder overlayBuilder) => setState(() => _overlayBuilder = overlayBuilder);

Within your PageOverlayState, you're going to have a variable something like WidgetBuilder _overlayBuidler which is normally null, and a method/setter something like set overlayBuilder(WidgetBuilder overlayBuilder) => setState(() => _overlayBuilder = overlayBuilder);

在PageOverlayState的构建器中,您将创建一个Stack;第一个孩子将是您传入WidgetApp.builderMaterialApp.builder的孩子.仅当_overlayBuilder不为null时,才创建第二个子级.如果它不为null,则第二个孩子应该类似于new Builder(builder: _overlayBuilder).

In the PageOverlayState's builder, you'll create a Stack; the first child will be the child you get passed into the WidgetApp.builder or MaterialApp.builder. You'll only create a second child if _overlayBuilder is not null; if it is not null the second child should be something like new Builder(builder: _overlayBuilder).

您可能需要适当地调整堆栈大小(即,将其放入扩展的堆栈中).

You might have to do something around sizing the stack properly (i.e. put it in an Expanded or something).

这篇关于通过其他小部件为小部件约束赋予绝对位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-24 09:22