通过其他小部件给出一个小部件约束的绝对位置(Giving an absolute position to a widget constraint by other widgets)

编程入门 行业动态 更新时间:2024-10-17 00:29:35
通过其他小部件给出一个小部件约束的绝对位置(Giving an absolute position to a widget constraint by other widgets)

我试图在小部件树的绝对位置中给出一个小部件。 由于它位于小部件树中的某处,所以很可能会由其祖先设置某些布局约束。 我正在寻找可以忽略这些约束的东西,并使用绝对坐标。

如果解释混乱,我画了一些代表我想要的结果的计划:

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

在Flutter中这样做的建议方式是什么?

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:

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.

What is the recommended way of doing this in Flutter?

最满意答案

我将描述两种方法来做到这一点,但我不会提供代码或示例,因为我不认为以可复制粘贴的方式将其放在那里是一个好主意 - 我同意@ Remi的评论说如果你需要在应用程序中这样做,很有可能会重构应用程序以避免它。 但是,有一种方法可以做到这一点 - 虽然有人会警告,但它可能会导致一些问题,例如触摸事件(尽管我认为有办法解决这个问题 - 请参阅AbsorbPointer和IgnorePointer以获取起点)。 既然你没有真正解释你想要的,我会假设你不需要这样做。

我鼓励你研究一下你想做什么的其他方法,并询问你是否希望找出更好的方法。

不管怎样,对好东西= P:


方法#1 - 使用现有的覆盖:

我实际上已经使用这种方法来模拟加载指示器,我希望在下面的页面之间进行推送,并且可以在应用程序中的任何位置调用哪个模式加载指示器,而不需要在它上面的树中有任何小部件。 这是一个非常具体的用例,但我认为这种用法是合理的。

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

要访问它,你可以使用OverlayState rootOverlay = context.rootAncestorStateOfType(const TypeMatcher<OverlayState>()); 以查看根重叠(你可能想要断言它实际上找到了一个)。

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


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

如果你只是在自己的应用程序中做这件事,我会认为这种做法稍微干净一些,尽管这可能不是一个好主意。

基本上,你想要做的是在你的应用程序中创建一个有状态的小部件 - 高于任何会占用屏幕空间的东西。 这在理论上可能意味着高于你的MaterialApp / WidgetApp /等,尽管如果你使用WidgetApp提供的主题/文本方向性/等等,这可能会给你带来问题。 我想你可以使用WidgetApp.builder将你的小部件放在你需要的地方。 为了方便起见,我们将其PageOverlay (带有相应的PageOverlayState 。

在你的小部件中,你将拥有如下的静态方法(这些在整个扑动的源代码中都是散布的,并且或多或少是一种约定):

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);

在PageOverlayState的构建器中,您将创建一个堆栈; 第一个孩子将成为您传递给WidgetApp.builder或MaterialApp.builder的孩子。 如果_overlayBuilder不为null,则只会创建第二个子项; 如果它不为null,则第二个孩子应该是new Builder(builder: _overlayBuilder) 。

你可能需要做些什么来适当地调整堆栈大小(即将其放在扩展或其他东西中)。

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.

Anyways, on to the good stuff =P :


Method # 1 - using an existing overlay:

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.

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.

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).

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.


Method #2 - using your own widget

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.

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.

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; }

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);

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).

更多推荐

本文发布于:2023-08-05 22:49:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1440929.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:部件   位置   Giving   absolute   widgets

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!