flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
162.17k stars 26.65k forks source link

[Feature Request] Ability to delay build until layout phase is completed #146334

Closed Gieted closed 2 weeks ago

Gieted commented 1 month ago

Use case

When developing an app, you often need to create a widget which position, size or even appearance is dependent on position/size of other widgets. Currently, there are 2 ways to achieve that in Flutter:

  1. Using FollowerLayer
  2. Reading other widget's position via RenderBox.localToGlobal() and offsetting it using something like Positioned or CustomSingleChildLayout

Unfortunately, each of this method comes with a drawback: The follower layer can only shift position, but doesn't allow the follower to adjust it's size or appearance basing on the target.

localToGlobal() and .size on the other hand gives the programmer much more freedom, but is always late by 1 frame (because we use it at build phase, which is before layout is updated for the current frame). This is problematic when the widget is moving or resizing, because it will be noticable to the user, that the 2 widgets are out-of-sync.

Let's have a more concrete example: obraz In my app, I have a popup that needs to calculate its size and position basing on the position of a button it's anchored to and also move left or right if needed to don't overflow its parent container. Additionally, it has an arrow which points to the button. Because the size and appearance of the popup depend on the position of the button, I cannot use the FollowerLayer for this use case, which leaves me with the second method. However, this causes popup and button positions to be out-of-sync when I resize the app window.

Proposal

My idea how this could be resolved is by adding a new Widget called DelayedBuilder, which would do its build, layout and paint after its parent tree has completed the layout phase (it could do just the size = constraints.biggest in its layout and rebuild and relayout children in the paint phase).

e.g.

DelayedBuilder(builder: (_) {
  final object = someKey.currentContext?.findRenderObject() as RenderBox?;
  final position = object?.localToGlobal(Offset.zero); // 'object' has already bean laid out this frame, so the position is now accurate
  return Positioned(
    left: position!.dx,
    top: position.dy,
    child: Container(
      width: 200,
      height: 200,
      color: Colors.green,
    ),
 );
})

Implementing such Widget probably requires doing some changes to the framework itself, because it would need to manage it's own PipelineOwner, while still being child of the main render tree.

darshankawar commented 1 month ago

@Gieted Can you take a look at this and see if it resembles your case or not ?

Gieted commented 1 month ago

I've read this issue, but I don't think it's the same use case as mine. They are mostly just having trouble understanding how layout works, without coming up with an actual well-defined feature request or pointing out missing functionality.

darshankawar commented 1 month ago

Thanks for the update. Please check this one and see if it resembles your case or not.

github-actions[bot] commented 2 weeks ago

Without additional information, we are unfortunately not sure how to resolve this issue. We are therefore reluctantly going to close this bug for now. If you find this problem please file a new issue with the same description, what happens, logs and the output of 'flutter doctor -v'. All system setups can be slightly different so it's always better to open new issues and reference the related ones. Thanks for your contribution.

github-actions[bot] commented 1 day ago

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.