New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ButtonBar aligns in column when it overflows horizontally #43193
ButtonBar aligns in column when it overflows horizontally #43193
Conversation
… wrap around case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Main concern for me here is the public interfaces, but otherwise looks good.
class ButtonBarRow extends Flex { | ||
/// Creates a button bar that attempts to display in a row, but displays in | ||
/// a column if there is insufficient horizontal space. | ||
ButtonBarRow({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really want this as a public class? Do we envision apps using this directly? Seems like this is just an implementation detail. It might be confusing to have both ButtonBarand
ButtonBarRow` as part of the public API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I made this private. We can always make this public if the need arises
/// cross-axis/horizontal alignment. For example, if the buttons overflow and | ||
/// [ButtonBar.alignment] was set to [MainAxisAligment.start], the buttons would | ||
/// align to the horizontal start of the button bar. | ||
class RenderButtonBarRow extends RenderFlex { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want/need this to be public?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made this class private as well
case TextDirection.ltr: | ||
switch (mainAxisAlignment) { | ||
case MainAxisAlignment.center: | ||
final double midpoint = constraints.maxWidth / 2.0 - child.size.width / 2.0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really minor style thing, but it might be better to express this as:
(constraints.maxWidth - child.size.width) / 2.0
As it is probably less likely to loose precision with one division instead of two. Might be faster too.
@@ -95,7 +95,7 @@ void main() { | |||
); | |||
|
|||
// ButtonBar uses a Row internally to implement this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It now uses a ButtonBarRow
instead of a Row
now. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just trying to write tests for ButtonBarRow
and MainAxisAlignment
and cannot seem to find how this parameter is used. As far as I understand, it seems to only be valuable when Flexible children are present, but since each button is wrapped in a Padding widget, there is no way to make a child of ButtonBar flexible. I tried playing around with the param in a small app and see no difference between setting it to min and max.
Deleting the parameter would be a breaking change for apps that define this param, so maybe we could delete the tests for now, and then in a follow-up PR, try removing the parameter and see if anything breaks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind - I misunderstood how MainAxisSize works. It turns out that this basically tells its own parent how much space it intends to take. However, this just means that if ButtonBar.mainAxisSize was set to MainAxisSize.min, it sort of ignores alignment since there's minimal space and is aligned based on how the parent itself chooses to align itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, with a few cleanups.
/// widget, it aligns its buttons in a column. The key difference here | ||
/// is that the [MainAxisAlignment] will then be treated as a | ||
/// cross-axis/horizontal alignment. For example, if the buttons overflow and | ||
/// [ButtonBar.alignment] was set to [MainAxisAligment.start], the buttons would |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the buttons would => the column of buttons would
/// the widget, it then aligns its buttons in a column. The key difference here | ||
/// is that the [MainAxisAlignment] will then be treated as a | ||
/// cross-axis/horizontal alignment. For example, if the buttons overflow and | ||
/// [ButtonBar.alignment] was set to [MainAxisAligment.start], the buttons would |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the buttons => the column of buttons
child = childParentData.nextSibling; | ||
} | ||
size = constraints.constrain(Size(constraints.maxWidth, currentHeight)); | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it really necessary to performLayout() again, if the row fit? If so, this would be a good place to explain why.
It might be clearer if this clause was first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is, since the original performLayout()
call uses infinity as the maximum width constraint. The second layout is performed to ensure that we use the original maximum width. I'll add a comment to explain this and have it as the first clause.
|
||
// Lay out the child with the button bar's original constraints, but | ||
// with minimum width set to zero. | ||
child.layout(constraints.copyWith(minWidth: 0.0), parentUsesSize: true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could just bind childConstraints to a final var outside of the loop.
double currentHeight = 0.0; | ||
while (child != null) { | ||
final FlexParentData childParentData = child.parentData; | ||
assert(child.parentData == childParentData); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Presumably, this assert has already been applied by super.performLayout() or somewhere else in RenderFlex or its superclasses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it has already. I'll remove it from here then
break; | ||
} | ||
break; | ||
default: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could move this assert to the constructor. We don't really expect it to be null, even if there's no overflow.
Description
When AlertDialogs are used with buttons that are too wide, they simply overflow through the dialog, which is a possible situation when the text scale factor is bumped up to account for accessibility needs. This change provides ButtonBar provides behavior for laying out in a column in the event that the desired width of the ButtonBar exceeds the maximum width constraints it is provided.
Original behavior:
Overflow behavior, when text scale factor is increased:
In this implementation, when the buttons overflow, MainAxisAlignment is still treated as horizontal alignment instead of flipping it into vertical alignment. This makes sense in the button bar case since it's more likely that developers care about how buttons align horizontally over having the buttons align towards the vertical bottom of widgets that use
ButtonBar
, likeAlertDialog
. This means that thespaceAround
,spaceBetween
andspaceEvenly
default tostart
's layout algorithm.MainAxisAlignment.center:
MainAxisAlignment.start:
Related Issues
Fixes part of #42696
Tests
I added the following tests:
Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]
). This will ensure a smooth and quick review process.///
).flutter analyze --flutter-repo
) does not report any problems on my PR.Breaking Change
Does your PR require Flutter developers to manually update their apps to accommodate your change?