Ant D
Ant D

Reputation: 2683

How can I get the size of a widget in Flutter?

I came across this example to get the size/position of a widget: https://medium.com/@diegoveloper/flutter-widget-size-and-position-b0a9ffed9407

What is wrong with my code?

I get the error:

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

Upvotes: 15

Views: 42470

Answers (5)

Kalpesh Khandla
Kalpesh Khandla

Reputation: 736

There isn't any direct way to calculate the size of the widget, so to find that we have to take the help of the context of the widget.

Calling context.size returns us the Size object, which contains the height and width of the widget. context.size calculates the render box of a widget and returns the size.

Checkout Flutter: How to get the height of the widget?

Upvotes: 0

Hamza
Hamza

Reputation: 933

In the link you referred to, after creating the GlobalKey it is assigned to the red container.

But in your code you haven't assigned it to the container, so it can be referred to from the _getSizes() and _getPositions(), _keyRed have to be attached to a Context in your code before you use it.

// 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,
        ),
    ),

Upvotes: 2

anmol.majhail
anmol.majhail

Reputation: 51186

This is because the _keyRed is not attached to Context in your code.

To fix it:

@override
Widget build(BuildContext context) {
  return Scaffold(
    key: _keyRed,  // Add this.
    appBar: AppBar(

Upvotes: 8

Guillem Puche
Guillem Puche

Reputation: 1404

Get the size of the current widget with RenderBox. Read all the properties RenderBox can offer you.

Code to get the height:

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,
        ...
     )
  }
}

Considerations from findRenderObject:

  • "This method will only return a valid result after the build phase is complete. It is therefore not valid to call this from a build method. It should only be called from interaction event handlers (e.g. gesture callbacks) or layout or paint callbacks. It is also not valid to call if State.mounted returns false."
  • "Calling this method is theoretically relatively expensive (O(N) in the depth of the tree), but in practice is usually cheap because the tree usually has many render objects and therefore the distance to the nearest render object is usually short." This means my code could be improved, because findRenderObject() will be called every time build() is called.

Upvotes: 3

Vitali
Vitali

Reputation: 854

You can wrap in LayoutBuilder:

return LayoutBuilder(
    builder: (BuildContext context, BoxConstraints constraints) {
//      height: constraints.maxHeight,
//      width: constraints.maxWidth
        return YourWidget();
    }
);

Upvotes: 19

Related Questions