Flex grow not working, with explanation!

So, if any of you set flex-grow on any of your elements and were puzzled as to why it wasn’t working outside of the editor, I have an explanation. When you call Instantiate on your UXML asset and add it to the root, Unity wraps your entire UXML in another VisualElement and hands that to you. That VisualElement wrapper does not have flex-grow on it, so Unity happily renders the wrapper container with your stuff in it using the minimum required space and leaves the rest of the window space underneath your markup as empty space. Here’s a thread that discusses it: https://forum.unity.com/threads/visualelements-wont-grow-to-fill-space-for-some-reason.759725/#post-5101916

And the solution, which I have not tried implementing yet, seems to be to loop through the children of the wrapper that you get from Instantiate() and manually add those elements to your root, bypassing the wrapper altogether, rather than adding the VisualElement returned from Instantiate directly.

Yes, this is now working properly. There is lots of bonus Unity stupidity in the process, so buckle up. My UI now looks like this in the actual editor window, with the correct flex-grow on the task list (no border, but notice that the save progress button and progress bar are indeed at the bottom)

Instead of a line that looks something like this: this.root.Add(this.uxmlAsset.Instantiate());

you want this instead:

TemplateContainer uxml = this.uxmlAsset.Instantiate();
int childCount = uxml.childCount;

for (var i = 0; i < childCount; i++)
{
  root.Add(uxml.ElementAt(0));
}

Note that the intermediate int childCount is required because (and this is the Unity stupidity, or maybe there’s a good reason, whatever…) when you add the child element inside the for loop, Unity removes the element from the uxml TemplateContainer, so if your for loop works over uxml.childCount instead of copying it to the separate int, it’ll only run half as often as you expect because childCount goes down by 1 every time you add the element to the root.

Anyway, TLDR, instead of doing this.root.add(), do the code block above, and you’ll add your elements directly to the root instead of getting a bogus extra container that wrecks your flex-grow.

Additional information!

You can manually set flex-grow on the TemplateContainer that you add to the root, and that seems to work as well:

TemplateContainer uxml = this.uxmlAsset.Instantiate();
uxml.style.flexGrow = 1f;
this.root.Add(uxml);

This code is simpler, but maybe clumsier. I’m going with this version.

Good find! That’s been a frustration for many, I would say that the last solution is the best choice.

A simpler solution to this issue is to add a style to the root element window whose “Flex Grow” setting is set to “1”

Or just add to you uss file for such window:
:root {
flex-grow: 1;
}

2 Likes

Privacy & Terms