如何获取小部件的高度? [英] How to get height of a Widget?

查看:32
本文介绍了如何获取小部件的高度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不明白如何使用 LayoutBuilder 来获取 Widget 的高度.

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 中并将其放入 Stack,但我总是得到 minHeight 0.0maxHeight 无限.我是否滥用了 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?

EDIT:似乎 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 constraint) 作为约束,我得到 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不需要手动重建吗?

是的,他们有.但是还有一点需要注意:ScrollController,传递给 Scrollable,是一个类似于 AnimationController 的可监听对象.

Yes, they do. But there's another thing to be aware of: ScrollController, passed to a Scrollable, is a listenable similar to AnimationController.

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

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆