229

I don't understand how LayoutBuilder is used to get the height of a widget.

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 a widget (I don't control them). I read that LayoutBuilder can be used to get the height.

In a very simple case, I tried to wrap a 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?

It seems that LayoutBuilder is a no go. I found the CustomSingleChildLayout which is almost a solution.

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.

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, and the height is 0), or constraints.biggest which is infinity and crashes the app.

In the documentation it even says:

...but the size of the parent cannot depend on the size of the child.

And that's a weird limitation.

8
  • 2
    LayoutBuilder will give you the box constraints of the parent. If you want the sizes of the child you need a different strategy. One example I can point to is the Wrap widget, it does layout based on the size of it's children in the associated RenderWrap class. This happens during layout though, not build(). Commented Mar 29, 2018 at 15:48
  • @JonahWilliams Hmm. I don't see how Wrap can help me since it's widget designed to layout children around (works something like flexbox grid from the web). I have one child widget that I need to find the height of. Please, see the edit in the question. I almost solved the problem with CustomSingleChildLayout but got stuck on its limitation.
    – JoKr
    Commented Mar 31, 2018 at 9:13
  • Can you explain what you want more specifically ? There are multiple solutions. But each have different use cases. Commented Mar 31, 2018 at 20:01
  • Sure. I am developing a package. User/Developer provides a Widgets to my class. We are talking about any widget here, from new Text("hello") to more complex ones. I lay these widgets into ListView, and I need their height to compute some scroll effects. I am OK with getting the height at the layout time, just like what SingleChildLayoutDelegate is doing.
    – JoKr
    Commented Mar 31, 2018 at 20:56
  • What do you mean by "Scroll effects" ? Do you have an example ? Commented Apr 1, 2018 at 13:21

16 Answers 16

332
+50

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.

There is 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 the build call as the widget hasn't been rendered yet.


But what if I need to get the size during the build! What can I do?

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 the stack).

But the coolest thing is that they are on a different build flow; they are built after regular widgets.

That have one super cool implication: OverlayEntry can have a size that depends on widgets of the actual widget tree.


Okay. But don't OverlayEntry requires to be rebuilt manually?

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

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:

In the following example, you'll see an overlay that follows a widget inside a 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
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final controller = ScrollController();
  OverlayEntry? sticky;
  GlobalKey stickyKey = GlobalKey();

  @override
  void initState() {
    sticky?.remove();

    sticky = OverlayEntry(
      builder: (context) => stickyBuilder(context),
    );

    SchedulerBinding.instance.addPostFrameCallback((_) {
      if (sticky != null) {
        Overlay.of(context).insert(sticky!);
      }
    });

    super.initState();
  }

  @override
  void dispose() {
    sticky?.remove();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      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: (context, 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();
      },
    );
  }
}

Note:

When navigating to a different screen, call the following. Otherwise, sticky would stay visible.

sticky.remove();
9
  • 8
    Ok, finally got time to test it. So, I actually only needed the first part. I didn't know I can use access the context.height with GlobalKey. Great answer.
    – JoKr
    Commented Apr 7, 2018 at 15:38
  • 1
    how to import schedular binder?
    – siddhesh
    Commented Dec 30, 2018 at 8:52
  • 1
    i have tried import 'package:flutter/scheduler.dart.'; but i got error target uri doesn't exists @rémi-rousselet
    – siddhesh
    Commented Dec 30, 2018 at 8:53
  • @rémi-rousselet how do I make this work when I have a widget behind the ListView whose height, I want to control according to the ListView's height? Commented Sep 11, 2019 at 12:22
  • 2
    Remi, Thanks for the clever solution and great explanation. I have a question. What if we want to be able to know the Rect of any ListItem of a ListView.builder when they are pressed. Would it be an overkill to set GlobalKey listItemKey = GlobalKey(); for every listItem? Let's say i have +10000 items. Is there a clever way to manage this without performance/memory issues?
    – aytunch
    Commented Sep 15, 2019 at 18:08
138

This is (I think) the most straightforward way to do this.

Copy-paste the following into your project.

Using RenderProxyBox results in a slightly more correct implementation, because it's called on every rebuild of the child and its descendants, which is not always the case for the top-level build() method.

Note: This is not exactly an efficient way to do this, as pointed by Hixie here. But it is the easiest.

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';


typedef void OnWidgetSizeChange(Size size);

class MeasureSizeRenderObject extends RenderProxyBox {
  Size? oldSize;
  OnWidgetSizeChange onChange;

  MeasureSizeRenderObject(this.onChange);

  @override
  void performLayout() {
    super.performLayout();

    Size newSize = child!.size;
    if (oldSize == newSize) return;

    oldSize = newSize;
    WidgetsBinding.instance!.addPostFrameCallback((_) {
      onChange(newSize);
    });
  }
}

class MeasureSize extends SingleChildRenderObjectWidget {
  final OnWidgetSizeChange onChange;

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

  @override
  RenderObject createRenderObject(BuildContext context) {
    return MeasureSizeRenderObject(onChange);
  }

  @override
  void updateRenderObject(
      BuildContext context, covariant MeasureSizeRenderObject renderObject) {
    renderObject.onChange = onChange;
  }
}

Then, simply wrap the widget whose size you would like to measure with MeasureSize.

var myChildSize = Size.zero;

Widget build(BuildContext context) {
  return ...(
    child: MeasureSize(
      onChange: (size) {
        setState(() {
          myChildSize = size;
        });
      },
      child: ...
    ),
  );
}

So yes, the size of the parent can depend on the size of the child if you try hard enough.


Personal anecdote: This is handy for restricting the size of widgets like Align, which likes to take up an absurd amount of space.

10
  • 2
    This works, but it's difficult to use in some places. For example, in a PreferredSizeWidget, preferredSize is only called once, so you can't set the height in a easy way. Commented Oct 12, 2020 at 22:26
  • 3
    This solution doesn't work for SliverAppBar. Commented Nov 29, 2020 at 13:17
  • 1
    Crashing on iOS 14.5 beta Commented Apr 22, 2021 at 12:26
  • 1
    Non-nullable instance field 'oldSize' must be initialized. on 'MeasureSizeRenderObject(this.onChange);'
    – Anup Gupta
    Commented Aug 10, 2021 at 19:17
  • 2
    Look at Boxy, which encapsulates this kind of strategy of getting the size of children before fully rendering it. Commented Jan 2, 2022 at 17:19
31

Here's a sample of how you can use LayoutBuilder to determine the widget's size.

Since the LayoutBuilder widget is able to determine its parent widget's constraints, one of its use cases is to be able to have its child widgets adapt to their parent's dimensions.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  var dimension = 40.0;

  increaseWidgetSize() {
    setState(() {
      dimension += 20;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(children: <Widget>[
          Text('Dimension: $dimension'),
          Container(
            color: Colors.teal,
            alignment: Alignment.center,
            height: dimension,
            width: dimension,
            // LayoutBuilder inherits its parent widget's dimension. In this case, the Container in teal
            child: LayoutBuilder(builder: (context, constraints) {
              debugPrint('Max height: ${constraints.maxHeight}, max width: ${constraints.maxWidth}');
              return Container(); // Create a function here to adapt to the parent widget's constraints
            }),
          ),
        ]),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: increaseWidgetSize,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

Demo

Demo

Logs

I/flutter (26712): Max height: 40.0, max width: 40.0
I/flutter (26712): Max height: 60.0, max width: 60.0
I/flutter (26712): Max height: 80.0, max width: 80.0
I/flutter (26712): Max height: 100.0, max width: 100.0

You can also use MediaQuery to achieve a similar function.

@override
Widget build(BuildContext context) {
  var screenSize = MediaQuery.of(context).size;
  if (screenSize.width > layoutSize) {
    return Widget();
  } else {
    return Widget(); // Widget if doesn't match the size
  }
}
3
  • 1
    Could you elaborate the "dynamic size" that causes the issue? Do you have a minimal repro that I can check?
    – Omatt
    Commented Dec 12, 2020 at 14:51
  • to get the widget width/height dynamically you need to call .findRenderObejct() and then .size. RenderBox box = widget.context.findRenderObject(); print(box.size); Commented Dec 12, 2020 at 18:09
  • you may also pass a GlobalKey as a key of widget and then call _myKey.currentContext.findRenderObject() Commented Dec 12, 2020 at 18:10
20

Let me give you a widget for that

class SizeProviderWidget extends StatefulWidget {
  final Widget child;
  final Function(Size) onChildSize;

  const SizeProviderWidget(
      {Key? key, required this.onChildSize, required this.child})
      : super(key: key);
  @override
  _SizeProviderWidgetState createState() => _SizeProviderWidgetState();
}

class _SizeProviderWidgetState extends State<SizeProviderWidget> {
  @override
  void initState() {
    ///add size listener for first build
    _onResize();
    super.initState();
  }

  void _onResize() {
    WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
      if (context.size is Size) {
        widget.onChildSize(context.size!);
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    ///add size listener for every build uncomment the fallowing
    ///_onResize();
    return widget.child;
  }
}

Just wrap the SizeProviderWidget with OrientationBuilder to make it respect the orientation of the device.

3
  • 2
    Hi! This appeared to work well initially, but I discovered one caveat: the size did not update on device orientation changes. I suspect it is because state for a Stateful Widget does not get reinitialized on device rotation.
    – mattorb
    Commented Dec 16, 2020 at 15:40
  • 5
    hi, flutter is so modular like lego, just wrap the above Widget with a OrientationBuilder which starts to respect any orientation, i mean of the device haha
    – Yadu
    Commented Dec 17, 2020 at 4:58
  • 1
    crashing on iOS 14.5 beta Commented Apr 22, 2021 at 12:30
12

I made this widget as a simple stateless solution:

class ChildSizeNotifier extends StatelessWidget {
  final ValueNotifier<Size> notifier = ValueNotifier(const Size(0, 0));
  final Widget Function(BuildContext context, Size size, Widget child) builder;
  final Widget child;
  ChildSizeNotifier({
    Key key,
    @required this.builder,
    this.child,
  }) : super(key: key) {}

  @override
  Widget build(BuildContext context) {
    WidgetsBinding.instance.addPostFrameCallback(
      (_) {
        notifier.value = (context.findRenderObject() as RenderBox).size;
      },
    );
    return ValueListenableBuilder(
      valueListenable: notifier,
      builder: builder,
      child: child,
    );
  }
}

Use it like this

ChildSizeNotifier(
  builder: (context, size, child) {
    // size is the size of the text
    return Text(size.height > 50 ? 'big' : 'small');
  },
)
5
  • 1
    This is actually really smart
    – Giraldi
    Commented Aug 10, 2022 at 14:55
  • In your example you include 'child' here, but on the usage there is no child? what is the purpose? also we keep getting the value of 0.0 randomly; any ideas? Commented Sep 21, 2022 at 9:31
  • thanks this really helped with my textformfield! Commented Dec 22, 2022 at 2:50
  • 1
    @OliverDixon It has been a while, but this should actually been a stateful widget, you're getting the 0.0 because it's rebuilding the stateless widget with the default value (0.0). If the widget is stateful it won't do that. Commented Jan 13, 2023 at 20:18
  • 1
    Also, looking back this solution is not very good. If you still want to stick with it I'd suggest make it stateful and move the addPostFrameCallback to initState or didUpdateWidget and then check if any size values have changed first before doing the actual call. Commented Jan 13, 2023 at 20:22
11

In cases where you don't want to wait for a frame to get the size, but want to know it before including it in your tree:

The simplest way is to follow the example of the BuildOwner documentation.

With the following you can just do

final size = MeasureUtil.measureWidget(MyWidgetTree());
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

/// Small utility to measure a widget before actually putting it on screen.
///
/// This can be helpful e.g. for positioning context menus based on the size they will take up.
///
/// NOTE: Use sparingly, since this takes a complete layout and sizing pass for the subtree you
/// want to measure.
///
/// Compare https://api.flutter.dev/flutter/widgets/BuildOwner-class.html
class MeasureUtil {
  static Size measureWidget(Widget widget, [BoxConstraints constraints = const BoxConstraints()]) {
    final PipelineOwner pipelineOwner = PipelineOwner();
    final _MeasurementView rootView = pipelineOwner.rootNode = _MeasurementView(constraints);
    final BuildOwner buildOwner = BuildOwner(focusManager: FocusManager());
    final RenderObjectToWidgetElement<RenderBox> element = RenderObjectToWidgetAdapter<RenderBox>(
      container: rootView,
      debugShortDescription: '[root]',
      child: widget,
    ).attachToRenderTree(buildOwner);
    try {
      rootView.scheduleInitialLayout();
      pipelineOwner.flushLayout();
      return rootView.size;
    } finally {
      // Clean up.
      element.update(RenderObjectToWidgetAdapter<RenderBox>(container: rootView));
      buildOwner.finalizeTree();
    }
  }
}

class _MeasurementView extends RenderBox with RenderObjectWithChildMixin<RenderBox> {
  final BoxConstraints boxConstraints;
  _MeasurementView(this.boxConstraints);

  @override
  void performLayout() {
    assert(child != null);
    child!.layout(boxConstraints, parentUsesSize: true);
    size = child!.size;
  }

  @override
  void debugAssertDoesMeetConstraints() => true;
}

This creates an entirely new render tree separate from the main one, and won’t be shown on your screen.

So for example

  print(
    MeasureUtil.measureWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: const [
            Icon(Icons.abc),
            SizedBox(
              width: 100,
            ),
            Text("Moin Meister")
          ],
        ),
      ),
    ),
  );

Would give you Size(210.0, 24.0).

3
  • And where did you get MeasureUtil from? Commented Apr 26, 2023 at 14:01
  • @OliverDixon thats just a thin wrapper around BuildOwner
    – omnesia
    Commented May 18, 2023 at 9:44
  • This answer worked great for me, although I am not sure what the perf implications are relative to the other ones.
    – Rik
    Commented Jul 15, 2023 at 19:32
6

If I understand correctly, you want to measure the dimension of some arbitrary widgets, and you can wrap those widgets with another widget. In that case, the method in the this answer should work for you.

Basically, the solution is to bind a callback in the widget lifecycle, which will be called after the first frame is rendered, and from there you can access context.size. The catch is that you have to wrap the widget you want to measure within a stateful widget. And, if you absolutely need the size within build() then you can only access it in the second render (it's only available after the first render).

1
4

findRenderObject() returns the RenderBox which is used to give the size of the drawn widget and it should be called after the widget tree is built, so it must be used with some callback mechanism or addPostFrameCallback() callbacks.

class SizeWidget extends StatefulWidget {
  @override
  _SizeWidgetState createState() => _SizeWidgetState();
}

class _SizeWidgetState extends State<SizeWidget> {
  final GlobalKey _textKey = GlobalKey();
  Size textSize;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) => getSizeAndPosition());
  }

  getSizeAndPosition() {
    RenderBox _cardBox = _textKey.currentContext.findRenderObject();
    textSize = _cardBox.size;
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Size Position"),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          Text(
            "Currern Size of Text",
            key: _textKey,
            textAlign: TextAlign.center,
            style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
          ),
          SizedBox(
            height: 20,
          ),
          Text(
            "Size - $textSize",
            textAlign: TextAlign.center,
          ),
        ],
      ),
    );
  }
}

Output:

enter image description here

1
  • 2
    _textKey.currentContext.size is enough Commented Dec 12, 2020 at 2:25
3

You can measure the dynamic size of a widget in Flutter by using a custom SizeMeasureWidget. Here's a simple implementation:

import 'package:flutter/material.dart';

class SizeMeasureWidget extends StatefulWidget {
  final Widget child;
  final ValueChanged<Size> onSizeMeasured;

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

  @override
  _SizeMeasureWidgetState createState() => _SizeMeasureWidgetState();
}

class _SizeMeasureWidgetState extends State<SizeMeasureWidget> {
  final GlobalKey _sizeKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Container(
      key: _sizeKey,
      child: widget.child,
    );
  }

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _getSize();
    });
  }

  void _getSize() {
    RenderBox renderBox = _sizeKey.currentContext!.findRenderObject() as RenderBox;
    Size size = renderBox.size;
    widget.onSizeMeasured(size);
  }
}

Usage

SizeMeasureWidget(
  onSizeMeasured: (size) {
    print("Widget size: $size");
  },
  child: Container(
    color: Colors.blue,
    height: 200,
    width: 200,
    child: Center(child: Text('Measure my size!')),
  ),
)

For a detailed explanation, check out this blog post: How to dynamically measure widget size in Flutter.

2

It might be this could help.

It was tested on Flutter: 2.2.3

Copy the below code to your project.

import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';

class WidgetSize extends StatefulWidget {
  final Widget child;
  final Function onChange;

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

  @override
  _WidgetSizeState 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();
  var oldSize;

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

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

    oldSize = newSize;
    widget.onChange(newSize);
  }
}

Declare a variable to store Size.

Size mySize = Size.zero;

Add the following code to get the size:

 child: WidgetSize(
          onChange: (Size mapSize) {
            setState(() {
              mySize = mapSize;
              print("mySize:" + mySize.toString());
            });
          },
         child: ()
2

This is Remi's answer with null safety. Since the edit queue is full, I have to post it here.

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  MyHomePageState createState() => MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  final controller = ScrollController();
  OverlayEntry? sticky;
  GlobalKey stickyKey = GlobalKey();

  @override
  void initState() {
    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) => 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) => AnimatedBuilder(
    animation: controller,
    builder: (_, Widget? child) {
      final keyContext = stickyKey.currentContext;
      if (keyContext == null || !keyContext.mounted) {
        return Container();
      }
      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"),
          ),
        ),
      );
    },
  );
}
1
  • For safety, you probably want to return Container() not just when keyContext == null, but also when !keyContext.mounted. I'll submit an edit to your answer. Commented Aug 9, 2023 at 0:20
1

The easiest way is to use MeasuredSize. It's a widget that calculates the size of its child at runtime.

You can use it like so:

MeasuredSize(
  onChange: (Size size) {
    setState(() {
      print(size);
    });
  },
  child: Text(
    '$_counter',
    style: Theme.of(context).textTheme.headline4,
  ),
);

You can find it here: https://pub.dev/packages/measured_size

1
1

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.

Check out Flutter: How can I get the height of the widget?.

4
0

Use the z_tools package.

The steps:

1. Change the main file

void main() async {
  runZoned(
    () => runApp(
      CalculateWidgetAppContainer(
        child: Center(
          child: LocalizedApp(delegate, MyApp()),
        ),
      ),
    ),
    onError: (Object obj, StackTrace stack) {
      print('global exception: obj = $obj;\nstack = $stack');
    },
  );
}

2. Use in a function

_Cell(
    title: 'cal: Column-min',
    callback: () async {
        Widget widget1 = Column(
        mainAxisSize: MainAxisSize.min,
        children: [
            Container(
            width: 100,
            height: 30,
            color: Colors.blue,
            ),
            Container(
            height: 20.0,
            width: 30,
            ),
            Text('111'),
        ],
        );
        // size = Size(100.0, 66.0)
        print('size = ${await getWidgetSize(widget1)}');
    },
),
0

It's easy and still can be done in StatelessWidget.

class ColumnHeightWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final scrollController = ScrollController();
    final columnKey = GlobalKey();

    _scrollToCurrentProgress(columnKey, scrollController);

    return Scaffold(
      body: SingleChildScrollView(
        controller: scrollController,
        child: Column(
          children: [],
        ),
      ),
    );
  }

  void _scrollToCurrentProgress(GlobalKey<State<StatefulWidget>> columnKey,
      ScrollController scrollController) {
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      final RenderBox renderBoxRed =
          columnKey.currentContext.findRenderObject();
      final height = renderBoxRed.size.height;
      scrollController.animateTo(percentOfHeightYouWantToScroll * height,
          duration: Duration(seconds: 1), curve: Curves.decelerate);
    });
  }
}

In the same manner, you can calculate any widget child height and scroll to that position.

-1
**Credit to @Manuputty**    

class OrigChildWH extends StatelessWidget {
  final Widget Function(BuildContext context, Size size, Widget? child) builder;
  final Widget? child;
  const XRChildWH({
    Key? key,
    required this.builder,
    this.child,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return OrientationBuilder(builder: (context, orientation) {
      return ChildSizeNotifier(builder: builder);
    });
  }
}

class ChildSizeNotifier extends StatelessWidget {
  final ValueNotifier<Size> notifier = ValueNotifier(const Size(0, 0));
  final Widget Function(BuildContext context, Size size, Widget? child) builder;
  final Widget? child;
  ChildSizeNotifier({
    Key? key,
    required this.builder,
    this.child,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    WidgetsBinding.instance!.addPostFrameCallback(
      (_) {
        notifier.value = (context.findRenderObject() as RenderBox).size;
      },
    );
    return ValueListenableBuilder(
      valueListenable: notifier,
      builder: builder,
      child: child,
    );
  }
}

**Simple to use:**

OrigChildWH(
  builder: (context, size, child) {
    //Your child here: mine:: Container()
    return Container()
  })
2
  • 2
    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.
    – Community Bot
    Commented Sep 14, 2021 at 8:50
  • Who is "Manuputty"? Who or what does it refer to? Where was this copied from? Commented Mar 30, 2023 at 17:51

Not the answer you're looking for? Browse other questions tagged or ask your own question.