How to get the size constraints in Widget.build

3,163 views
Skip to first unread message

Alexandre Ardhuin

unread,
Jun 3, 2016, 7:46:05 AM6/3/16
to Flutter Dev
Hi,

I try to put an image on the screen. The image url allows to specify the size of the image returned.
How can I get the BoxConstraints in my widget build method to optimize the url?

I tried to use `context.findRenderObject().constraints` but it always returns null on the first call (that leads to a red box and the error NoSuchMethodError: method not found: 'constraints' Receiver: null)

  Widget build(BuildContext context) {
    final constraints = context.findRenderObject().constraints;
    int height = 600;
    int width = 600;
    if (constraints is BoxConstraints) {
      height = min(height, constraints.constrainHeight());
      width = min(width, constraints.constrainWidth());
    }

Alexandre

Adam Barth

unread,
Jun 3, 2016, 11:10:54 AM6/3/16
to Alexandre Ardhuin, Flutter Dev
It sounds like LayoutBuilder [1] might be able to solve your problem.

Most widgets build before the layout phase of the pipeline (which is where the size and positions of the render objects is determined).  That means the constraints for a widget aren't yet determined when the build function runs, which is why the constraints are null the first time.  Subsequent times build() runs, they'll be the constraints from the previous frame rather than the constraints that will be used this frame.

Some widgets defer their building until the layout phase.  For example, the LayoutBuilder widget [1] calls its builder callback during the layout phase.  That means it's able to provide its size to its callback [2].

Adam



--
You received this message because you are subscribed to the Google Groups "Flutter Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to flutter-dev...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Alexandre Ardhuin

unread,
Jun 3, 2016, 12:31:57 PM6/3/16
to Adam Barth, Flutter Dev
Thanks Adam

I have now the size of the parent and I can compute the best size.

However I face a problem with my changes. Now I have only one Card displayed (in a Block with many Cards) instead of many (actually, the builder is called so the cards seem to be on each other). Here is a simple repro :


void main() {
  runApp(new MaterialApp(home: new Scaffold(
      appBar: new AppBar(title: new Text('test')),
      body: new Block(
          padding: new EdgeInsets.symmetric(horizontal: 8.0),
          children: ['a', 'b', 'c'].map((m) => new MyWidget(m)).toList()))));
}

// with LayoutBuilder the Block container displays only the 'a' card
class MyWidget extends StatelessWidget {
  MyWidget(this.text);
  String text;
  @override
  Widget build(BuildContext context) {
    return new LayoutBuilder(builder: (context, size) {
      print('build $text');
      final height = min(200, size.height).toInt();
      final width = min(400, size.width).toInt();
      return new Card(child: new Column(children: [
        new NetworkImage(src: 'https://maps.googleapis.com/maps/api/staticmap'
            '?size=${width}x$height'
            '&center=Berkeley,CA&zoom=14'),
        new Text(text)
      ]));
    });
  }
}

// without LayoutBuilder the Block container displays all the card
class MyWidget2 extends StatelessWidget {
  MyWidget2(this.text);
  String text;
  @override
  Widget build(BuildContext context) {
    final height = 200;
    final width = 400;
    return new Card(child: new Column(children: [
      new NetworkImage(src: 'https://maps.googleapis.com/maps/api/staticmap'
          '?size=${width}x$height'
          '&center=Berkeley,CA&zoom=14'),
      new Text(text)
    ]));
  }
}


Is there an issue with my code ?

Alexandre

Adam Barth

unread,
Jun 3, 2016, 1:06:59 PM6/3/16
to Alexandre Ardhuin, Flutter Dev
A Block scrolls vertically, which means it gives its children unlimited vertical space to work with.  Typically that means children either have a specific height in mind for themselves or they adopt the height of their children.

In this situation, the LayoutBuilder needs to determine its height without consulting its children because the children it builds depends on its size.  If the size depended on its children, that would create a circular dependency.

When placed in an environment with unlimited vertical space, the LayoutBuilder doesn't know how much height to use.  It doesn't have a specific height in mind and it's not allowed to adopt the height of its children.

To solve this problem, you need to decide how tall you want each of the cards to be.  If you want them to be a fixed height, you can wrap the LayoutBuilder in a Container that has its height property set.  If you want them to be a specific aspect ratio, you can wrap the LayoutBuilder in an AspectRatio widget, which will cause the height to be determined from the width.

If you want the Card to adopt the height of the image plus the height of the text, I'd recommend the follow structure:

- Card
  - AspectRatio
    - LayoutBuilder
      - NetworkImage
  - Text

That will cause the image inside the card to have a specific aspect ratio and the card itself to adopt a height that is the sum of the image's height and the text's height.

Adam

Alexandre Ardhuin

unread,
Jun 3, 2016, 3:51:36 PM6/3/16
to Adam Barth, Flutter Dev
Thanks for the explanation and the advice!

Alexandre
Reply all
Reply to author
Forward
0 new messages