如何获得小部件的高度? [英] How to get height of a Widget?
问题描述
我不明白如何使用 LayoutBuilder
获取小部件的高度.
I don't understand how LayoutBuilder
is used to get the height of a Widget.
我需要显示小部件列表并获取它们的高度,以便我可以计算一些特殊的滚动效果.我正在开发一个程序包,其他开发人员提供了小部件(我不控制它们).我读过LayoutBuilder可以用来获取高度.
I need to display the list of Widgets and get their height so I can compute some special scroll effects. I am developing a package and other developers provide widget (I don't control them). I read that LayoutBuilder can be used to get height.
在非常简单的情况下,我尝试将Widget包装到LayoutBuilder.builder中并将其放入堆栈中,但是我总是得到 minHeight
0.0
和 maxHeight
INFINITY
.我在滥用LayoutBuilder吗?
In very simple case, I tried to wrap Widget in LayoutBuilder.builder and put it in the Stack, but I always get minHeight
0.0
, and maxHeight
INFINITY
. Am I misusing the LayoutBuilder?
编辑:看来LayoutBuilder是行不通的.我发现了 CustomSingleChildLayout 几乎是一个解决方案.
EDIT: It seems that LayoutBuilder is a no go. I found the CustomSingleChildLayout which is almost a solution.
我扩展了该委托,并且可以在 getPositionForChild(Size size,Size childSize)
方法中获取小部件的高度.但是,第一个调用的方法是 Size getSize(BoxConstraints约束)
,作为约束,我将0赋给INFINITY,因为我将这些CustomSingleChildLayouts放置在ListView中.
I extended that delegate, and I was able to get the height of widget in getPositionForChild(Size size, Size childSize)
method. BUT, the first method that is called is Size getSize(BoxConstraints constraints)
and as constraints, I get 0 to INFINITY because I'm laying these CustomSingleChildLayouts in a ListView.
我的问题是SingleChildLayoutDelegate getSize
的运行就像需要返回视图的高度一样.我当时不知道孩子的身高.我只能返回constraints.smallest(为0,高度为0),或者返回constraints.biggest,它为无穷大并导致应用崩溃.
My problem is that SingleChildLayoutDelegate getSize
operates like it needs to return the height of a view. I don't know the height of a child at that moment. I can only return constraints.smallest (which is 0, the height is 0), or constraints.biggest which is infinity and crashes the app.
在文档中甚至说:
...但是父母的大小不能取决于孩子的大小.
...but the size of the parent cannot depend on the size of the child.
那是一个奇怪的限制.
推荐答案
要获取屏幕上小部件的大小/位置,可以使用 GlobalKey
来获取其 BuildContext
然后找到该特定小部件的 RenderBox
,其中将包含其全局位置和渲染大小.
To get the size/position of a widget on screen, you can use GlobalKey
to get its BuildContext
to then find the RenderBox
of that specific widget, which will contain its global position and rendered size.
需要注意的一件事:如果未呈现窗口小部件,则该上下文可能不存在.这可能会导致 ListView
出现问题,因为仅在可能可见的情况下才渲染窗口小部件.
Just one thing to be careful of: That context may not exist if the widget is not rendered. Which can cause a problem with ListView
as widgets are rendered only if they are potentially visible.
另一个问题是,由于尚未渲染小部件,因此在 build
调用期间无法获得小部件的 RenderBox
.
Another problem is that you can't get a widget's RenderBox
during build
call as the widget hasn't been rendered yet.
但是我需要在构建过程中调整大小!我该怎么办?
有一个很酷的小部件可以帮助您: Overlay
及其 OverlayEntry
.它们用于将小部件显示在其他所有事物之上(类似于堆栈).
There's one cool widget that can help: Overlay
and its OverlayEntry
.
They are used to display widgets on top of everything else (similar to stack).
但是最酷的是它们在不同的 build
流程中;它们是在 常规小部件之后构建的.
But the coolest thing is that they are on a different build
flow; they are built after regular widgets.
具有一个超酷的含义: OverlayEntry
的大小取决于实际小部件树的小部件.
That have one super cool implication: OverlayEntry
can have a size that depends on widgets of the actual widget tree.
好的.但是,OverlayEntry不需要手动重建吗?
是的,他们这样做.但是还有另一件事要注意:传递给 Scrollable
的 ScrollController
与 AnimationController
类似,是可监听的.
Yes, they do. But there's another thing to be aware of: ScrollController
, passed to a Scrollable
, is a listenable similar to AnimationController
.
这意味着您可以将 AnimatedBuilder
与 ScrollController
结合使用,这将具有在滚动条上自动重建小部件的可爱效果.非常适合这种情况,对吗?
Which means you could combine an AnimatedBuilder
with a ScrollController
, it would have the lovely effect to rebuild your widget automatically on a scroll. Perfect for this situation, right?
将所有内容组合为示例:
Combining everything into an example:
在下面的示例中,您将看到一个叠加层,该叠加层位于 ListView
内部的小部件之后,并具有相同的高度.
In the following example, you'll see an overlay that follows a widget inside ListView
and shares the same height.
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final controller = ScrollController();
OverlayEntry sticky;
GlobalKey stickyKey = GlobalKey();
@override
void initState() {
if (sticky != null) {
sticky.remove();
}
sticky = OverlayEntry(
builder: (context) => stickyBuilder(context),
);
SchedulerBinding.instance.addPostFrameCallback((_) {
Overlay.of(context).insert(sticky);
});
super.initState();
}
@override
void dispose() {
sticky.remove();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
controller: controller,
itemBuilder: (context, index) {
if (index == 6) {
return Container(
key: stickyKey,
height: 100.0,
color: Colors.green,
child: const Text("I'm fat"),
);
}
return ListTile(
title: Text(
'Hello $index',
style: const TextStyle(color: Colors.white),
),
);
},
),
);
}
Widget stickyBuilder(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (_,Widget child) {
final keyContext = stickyKey.currentContext;
if (keyContext != null) {
// widget is visible
final box = keyContext.findRenderObject() as RenderBox;
final pos = box.localToGlobal(Offset.zero);
return Positioned(
top: pos.dy + box.size.height,
left: 50.0,
right: 50.0,
height: box.size.height,
child: Material(
child: Container(
alignment: Alignment.center,
color: Colors.purple,
child: const Text("^ Nah I think you're okay"),
),
),
);
}
return Container();
},
);
}
}
注意:
导航到其他屏幕时,跟随通话,否则粘性将保持可见.
When navigating to a different screen, call following otherwise sticky would stay visible.
sticky.remove();
这篇关于如何获得小部件的高度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!