如何获得小部件的高度?

问题描述 投票:43回答:2

我不知道如何使用LayoutBuilder来获取小部件的高度。

我需要显示小部件列表并获取它们的高度,以便我可以计算一些特殊的滚动效果。我正在开发一个程序包,其他开发人员提供了小部件(我不控制它们)。我读过LayoutBuilder可以用来获取高度。

[在非常简单的情况下,我尝试将Widget包装在LayoutBuilder.builder中,并将其放入堆栈中,但是我总是得到minHeight 0.0maxHeight INFINITY。我在滥用LayoutBuilder吗?

EDIT:看来LayoutBuilder是行不通的。我发现CustomSingleChildLayout几乎是解决方案。

我扩展了该委托,并且可以在getPositionForChild(Size size, Size childSize)方法中获取小部件的高度。但是,第一个调用的方法是Size getSize(BoxConstraints constraints),作为约束,我将0赋给INFINITY,因为我将这些CustomSingleChildLayouts放置在ListView中。

我的问题是SingleChildLayoutDelegate getSize的运行就像需要返回视图的高度一样。我当时不知道孩子的身高。我只能返回constraints.smallest(为0,高度为0),或者返回constraints.biggest,它为无穷大并导致应用崩溃。

在文档中甚至说:

...但是父母的大小不能取决于孩子的大小。

这是一个怪异的限制。

flutter flutter-widget
2个回答
77
投票

要获取窗口小部件的大小/位置,您可以使用GlobalKey获取其BuildContext,然后找到该特定窗口小部件的RenderBox,其中将包含其全局位置和渲染的大小。

仅需注意的一件事:如果未呈现小部件,则该上下文可能不存在。这可能会导致ListView出现问题,因为仅在可能可见的情况下才渲染小部件。

另一个问题是,由于尚未渲染小部件,因此在RenderBox调用期间您无法获得小部件的build


但是我需要在构建过程中调整大小!我该怎么办?

[有一个很酷的小工具可以提供帮助:Overlay及其OverlayEntry。它们用于在其他所有内容之上显示小部件(类似于堆栈)。

但是最酷的是它们在不同的build流中;它们是在[[after常规小部件中构建的。

具有一个非常酷的含义:OverlayEntry的大小取决于实际小部件树的小部件。


好。但是,OverlayEntry不需要手动重建吗?

是的,他们愿意。但是还有另一件事要注意:传递给ScrollControllerScrollableAnimationController类似,是可收听的。

这意味着您可以将AnimatedBuilderScrollController结合使用,这具有在滚动条上自动重建窗口小部件的效果。非常适合这种情况,对吧?


将所有内容组合为示例:

在下面的示例中,您会看到一个叠加层,该叠加层位于ListView内部的小部件之后,并具有相同的高度。

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( opaque: false, // lambda created to help working with hot-reload builder: (context) => stickyBuilder(context), ); // not possible inside initState SchedulerBinding.instance.addPostFrameCallback((_) { Overlay.of(context).insert(sticky); }); super.initState(); } @override void dispose() { // remove possible overlays on dispose as they would be visible even after [Navigator.push] 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(); }, ); } }


1
投票
如果我理解正确,您想测量一些任意小部件的尺寸,然后可以将这些小部件包装到另一个小部件中。在这种情况下,this answer中的方法应该适合您。

基本上,解决方案是在小部件生命周期中绑定一个回调,在呈现第一帧之后将调用该回调,您可以从那里访问context.size。要注意的是,您必须将要测量的小部件包装在有状态的小部件中。并且,如果您绝对需要build()以内的尺寸,则只能在第二个渲染中访问它(仅在第一个渲染之后才可用)。

© www.soinside.com 2019 - 2024. All rights reserved.