如何在Flutter中获取小部件的大小?

14
我发现了一个获取小部件大小/位置的示例: https://medium.com/@diegoveloper/flutter-widget-size-and-position-b0a9ffed9407 我的代码有什么问题吗?
我收到了错误信息:
The following NoSuchMethodError was thrown while handling a gesture:
I/flutter ( 8566): The method 'findRenderObject' was called on null.
I/flutter ( 8566): Receiver: null
I/flutter ( 8566): Tried calling: findRenderObject()

void main() {
      runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: MyWidgetSizeTMP());
  }
}

class MyWidgetSizeTMP extends StatefulWidget{
  @override
  MyWidgetSizeTMPState createState() {
    return new MyWidgetSizeTMPState();
  }
}

class MyWidgetSizeTMPState extends State<MyWidgetSizeTMP> {

  //creating Key for red panel
  GlobalKey _keyRed = GlobalKey();

  _getSizes() {
    final RenderBox renderBoxRed = _keyRed.currentContext.findRenderObject();
    final sizeRed = renderBoxRed.size;
    print("SIZE of Red: $sizeRed");
  }

  _getPositions() {
    final RenderBox renderBoxRed = _keyRed.currentContext.findRenderObject();
    final positionRed = renderBoxRed.localToGlobal(Offset.zero);
    print("POSITION of Red: $positionRed ");
  }

  _afterLayout(_) {
    _getSizes();
    _getPositions();
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
      ),
      body: Column(
        children: <Widget>[
          Flexible(
            flex: 2,
            child: Container(
              color: Colors.red,
            ),
          ),
          Flexible(
            flex: 1,
            child: Container(
              color: Colors.purple,
            ),
          ),
          Flexible(
            flex: 3,
            child: Container(
              color: Colors.green,
            ),
          ),
          Spacer(),
          Padding(
            padding: const EdgeInsets.only(bottom: 8.0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                MaterialButton(
                  elevation: 5.0,
                  padding: EdgeInsets.all(15.0),
                  color: Colors.grey,
                  child: Text("Get Sizes"),
                  onPressed: _getSizes,
                ),
                MaterialButton(
                  elevation: 5.0,
                  color: Colors.grey,
                  padding: EdgeInsets.all(15.0),
                  child: Text("Get Positions"),
                  onPressed: _getPositions,
                )
              ],
            ),
          )
        ],
      ),
    );
  }
}
5个回答

17
您可以使用LayoutBuilder进行包装:
return LayoutBuilder(
    builder: (BuildContext context, BoxConstraints constraints) {
//      height: constraints.maxHeight,
//      width: constraints.maxWidth
        return YourWidget();
    }
);

4
这怎么能给你尺寸大小呢? - 68060
@68060 builder lambda接收BoxConstraints。您可以使用它来获取布局的大小。因此,在构建builder内部的内容之前,LayoutBuilder的大小已经确定了。这回答了问题的标题,但并没有帮助解决提问者的具体用例。 - undefined

7
这是因为您的代码中_keyRed未附加到上下文中。
解决方法:
@override
Widget build(BuildContext context) {
  return Scaffold(
    key: _keyRed,  // Add this.
    appBar: AppBar(

1
我现在明白了。任何小部件所需的大小/位置都可以在其构造函数中传递给它的关键字。谢谢。 - Ant D
我已经在使用全局键以显示 Snackbar 弹出框。final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); 我该如何传递两个全局键到 Scaffold 以解决这个问题? - Kamlesh
1
这是否返回Scaffold的大小/位置?除非我误解了,我怀疑下面Hamza的答案实际上是正确的。 - Ian

2
在您所提及的链接中,在创建了GlobalKey之后,它被分配给red容器。
但是在您的代码中,您还没有将其分配给容器,因此可以从_getSizes()_getPositions()_keyRed引用它,在使用之前必须将其附加到代码中的上下文。
// Creating Key for red panel
GlobalKey _keyRed = GlobalKey();

    ...

    // Set key
    Flexible(
        flex: 2,
        child: Container(
            key: _keyRed, // Assigned as a key of the red container.
            color: Colors.red,
        ),
    ),

1

使用RenderBox获取当前widget的大小。阅读所有RenderBox可以提供给您的属性。

获取高度的代码:

class _YourWidgetState extends State<YourWidget> {
  // Remember to assign this key to a widget.
  late final GlobalKey _key = GlobalKey();

  ...

  Widget build(BuildContext context) {
     var renderBox = null;
     if (_key.currentContext?.findRenderObject() != null) {
        renderBox = _key.currentContext!.findRenderObject() as RenderBox;
     }

     final height = renderBox?.size.height;

     ...

     return SomeWidget(
        key: _key,
        ...
     )
  }
}

考虑到来自 findRenderObject 的注意事项:
  • "此方法仅在构建阶段完成后才返回有效结果。因此,从构建方法调用此方法无效。它只应该从交互事件处理程序(例如手势回调)或布局或绘制回调中调用。如果State.mounted返回false,则也无效。"
  • "理论上,调用此方法相对昂贵(树深度为O(N)),但实际上通常很便宜,因为树通常具有许多渲染对象,因此到最近的渲染对象的距离通常很短。" 这意味着我的代码可以得到改进,因为每次调用build()时都会调用findRenderObject()

0

没有直接计算小部件大小的方法,因此我们必须借助小部件的上下文来找到它。

调用context.size将返回一个Size对象,其中包含小部件的高度和宽度。context.size计算小部件的呈现框并返回其大小。

请查看Flutter:如何获取小部件的高度?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接