Flutter: get widget size before build
Asked Answered
F

3

15

Is there any way to measure the dimensions of a widget that hasn't been build, but it's just a variable example:

Column myColumn = Column(children[......]);

myColumn.iWouldLikeToGetItsHeightAndWidth();

Filature answered 24/11, 2020 at 15:16 Comment(3)
Please take a look at this answer, this might help you.Weft
The simplest solution is to follow the example in the doc of BuildOwner. See this answer. That way you can use final size = MeasureUtil.measureWidget(myColumn)Misguide
can you please tell us the actual requirement?Ko
D
2

To iterate, the post mentioned in the comment seems to be the solution of what you are looking for. As mentioned in the answer:

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.

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.

Another problem is that you can't get a widget's RenderBox during build call as the widget hasn't been rendered yet.

To appreciate it better, you can visit the example in this blog. Take a look from the demo code:

   _getSizes() {
   }

   _getPositions(){
   }

  @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,
                )
              ],
            ),
          )
        ],
      ),
    );
  }

enter image description here

In this example, focus on the red panel:

Get the size of a Widget In order to do that, we need our Widget to have a Key, for this we create a GlobalKey and assign it to our

Widget.

//creating Key for red panel
GlobalKey _keyRed = GlobalKey();
...
//set key
    Flexible(
             flex: 2,
             child: Container(
               key: _keyRed,
               color: Colors.red,
             ),
     ),

Once our Widget already has a Key, we can use this Key to be able to obtain the size in the following way:

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

If we press the Get Sizes button, you’ll get this result in the console:

flutter: SIZE of Red: Size(375.0, 152.9)

now we know that our Red panel has 375.0 as width and 152.9 as height

Donohoe answered 5/8, 2022 at 16:18 Comment(1)
I'm getting compile error on this line, final RenderBox renderBoxRed = _keyRed.currentContext.findRenderObject();. Can't cast render object to render boxSpringtime
G
0

you can use mixin class and override afterFirstLayout and get size from context


import 'dart:async';

import 'package:flutter/widgets.dart';

mixin AfterLayoutMixin<T extends StatefulWidget> on State<T> {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.endOfFrame.then(
      (_) {
        if (mounted) afterFirstLayout(context);
      },
    );
  }

  FutureOr<void> afterFirstLayout(BuildContext context);
}
Gona answered 9/1, 2024 at 12:42 Comment(0)
P
-1
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';

class WidgetSize extends StatefulWidget {
  final Widget child;
  final Function<Size> onChange;

  const WidgetSize({
    Key? key,
    required this.onChange,
    required this.child,
  }) : super(key: key);

  @override
  State<WidgetSize> createState() => _WidgetSizeState();
}

class _WidgetSizeState extends State<WidgetSize> {
  @override
  Widget build(BuildContext context) {
    SchedulerBinding.instance.addPostFrameCallback(postFrameCallback);
    return Container(
      key: widgetKey,
      child: widget.child,
    );
  }

  var widgetKey = GlobalKey();
  Size? oldSize;

  void postFrameCallback(_) {
    var context = widgetKey.currentContext;
    if (context == null) return;

    var newSize = context.size;
    if (oldSize == newSize) return;

    oldSize = newSize;
    widget.onChange(newSize);
  }
}
Pinkston answered 6/2, 2024 at 5:46 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Pompeii

© 2022 - 2025 — McMap. All rights reserved.