Skip to content

Conversation

@andrewkolos
Copy link
Collaborator

@andrewkolos andrewkolos commented Dec 15, 2025

Description

Supersedes #588. Fixes #559.

Changes

  • fix 1: SurfaceUpdateTool wasn't parsing the value of the weight property and passing it to the Component constructor for any component it was constructing1. Definition of weight property:
    'weight': S.integer(
    description:
    'Optional layout weight for use in Row/Column children.',
    ),
    . This change fixes this and makes sure the weight value gets plumbed through properly.
    • I will also use this opportunity to say that optional fields should be largely avoided in internal APIs. Optional parameters are very prone to situations like these where code authors forget to pass values when they meant to (or simply never discovered the parameter's existence to begin with). Optional parameters are best reserved for highly flexible user-facing libraries that need to be both very simple to start with but also highly configurable (e.g. components in UI frameworks, such as Text in Flutter). Since we are dealing with an internal API here that is not suffering from any significant amount of API bloat, this PR opts to make weight parameter required. The only material drawback of making this parameter required is some extra verbosity in test code, which I personally assign very little importance to.
  • (necessary) refactoring: buildWeightedChild no longer has a required Component? component parameter and instead has a required int? weight parameter. component was only being read for the weight value anyway, and this refactoring also lets us "override" the weight of a component. Continue reading to see why we need this.
  • (hacky) fix 2: the builder of the row catalog item now checks the type of each child component. If it sees a TextField without a weight, it will assign it a weight of 1. This will prevent the layout-induced crashes we see in GenUI TextField crashes with “InputDecorator unbounded width” when generated using A2UI (Flutter GenUI v0.5.1) #559. However, I call this hacky because 1) Row has to be explicitly aware of another catalog component and treat it specially and 2) this is essentially a concession that many A2UI layouts are doomed to be laid out in noticeably different ways across different renderers (even if you tweaked the common variables like colors and padding to be the same across each of the renderers). I am okay with making this concession for now, especially that different UI frameworks fundamentally compute constraints in differing ways, making this a high-cost (near intractable?) problem to solve. It also isn't clear to me how many software products will really suffer from the problem to a significant degree (e.g. "we need a piece of generated UI to look identical on both our HTML and native Android apps").

Screenshots

image

Created using the repro instructions provided by the parent issue. In this scenario, the agent did not provide any weights for any child elements of rows, so this PR's change prevents a crash by assigning a weight of 1 to the text fields.

Another example (using simple_chat):

image

Pre-launch Checklist

Footnotes

  1. https://github.com/flutter/genui/blob/061ba8943160894a16ab0ffeaae99b7859e0ac49/packages/genui/lib/src/core/ui_tools.dart#L35-L37

… default `TextField` `width` to 1 when nested in a `Row`
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors how component weight is handled, transitioning from passing the entire Component object to buildWeightedChild to directly passing its weight property. This involved updating Column and Row widgets to extract the weight from the component, with Row specifically adding logic to default a TextField's weight to 1 if not explicitly defined. The buildWeightedChild helper function was modified to accept weight directly, and the Component model and SurfaceUpdateTool were extended to parse and store this new weight property, supporting both integer and double values. New tests were added to validate the TextField weighting behavior within Row and the correct parsing of weight values. A review comment identified a performance optimization in the Row widget's templateListWidgetBuilder, suggesting that itemContext.getComponent(componentId) should be hoisted out of the loop to prevent redundant calls and improve performance for large lists.

@jacobsimionato
Copy link
Collaborator

Hey this sounds very good! Can you include a screenshot with some example rendered layouts?

@andrewkolos
Copy link
Collaborator Author

Hey this sounds very good! Can you include a screenshot with some example rendered layouts?

Added some screenshots.

@andrewkolos andrewkolos requested review from jacobsimionato and removed request for jacobsimionato December 16, 2025 18:56
Copy link
Collaborator

@jacobsimionato jacobsimionato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

templateListWidgetBuilder: (context, list, componentId, dataBinding) {
final Component? component = itemContext.getComponent(componentId);
final int? weight =
component?.weight ?? (component?.type == 'TextField' ? 1 : null);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM as a practical fix for this now! I think we need a more scalable solution long term, to avoid other leaf components having this issue. We should figure out what circumstances this error happens in - is it for any leaf component that has an unbounded max width or height? Maybe we need them to be identifiable somehow. Either way, this is a great first step I think!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, constraints go down; sizes go up and all that1. Basically any widget that relies on a constraint from their parent to figure out how large they should be. For example, I suspect sliders within rows will fall victim to this exact crash.

Footnotes

  1. https://docs.flutter.dev/ui/layout/constraints

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GenUI TextField crashes with “InputDecorator unbounded width” when generated using A2UI (Flutter GenUI v0.5.1)

2 participants