-
Notifications
You must be signed in to change notification settings - Fork 29.7k
iOS Dialog blur, brightness, and layout #18381
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
iOS Dialog blur, brightness, and layout #18381
Conversation
…ect overflow layout behavior for iOS alert content and buttons. Added blend mode to BoxDecoration. (flutter#15431)
|
@xster @gspencergoog - Here's a PR to look at how the desired iOS alert layout can be implemented. From here if there are ideas to achieve a significantly less complex layout, and/or a much more performant layout, please chime in. There are still changes to be made: barrier color, 2 button stacking, enter animation, etc. But the blur, brightness, and layout alone should be a good step in the right direction. |
xster
left a comment
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.
General LGTM. Please add some layout tests.
| // TODO(gspencer): This color isn't correct. Instead, we should carve a hole in | ||
| // the dialog and show more of the background. | ||
| const Color _kButtonDividerColor = const Color(0xffd5d5d5); | ||
| const Color _kDialogForegroundColor = const Color(0xC0FFFFFF); |
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's a bit ambiguous what's the difference between this and _kCupertinoDialogFrontFillDecoration. Feel free to add more words to the variable name.
| children.add(const Padding(padding: const EdgeInsets.only(top: 8.0))); | ||
| } | ||
|
|
||
| return new ClipRRect( |
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 need this? It'll end up getting clipped by the outermost cliprrect anyway right? (Just because there's a cost to these clips)
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.
we shouldn't have any clips. Just draw the right shape.
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.
Might be tricky with BackdropFilter
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 top corners of the content, and all corners of the backdrop filter have been given corner radii by way of a container decoration instead of ClipRRect. There is still one remaining ClipRRect on the bottom because the bottom actions are arbitrary developer-provided widgets.
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.
We can define a protocol for telling those widgets which corner they're in so that they can do the right thing, then we can make the widgets we provide implement it without a clip.
We want to get rid of all clips. They are prohibitively expensive.
| mainAxisSize: MainAxisSize.min, | ||
| crossAxisAlignment: CrossAxisAlignment.stretch, | ||
| children: children, | ||
| return new ClipRRect( |
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.
ditto
| children: <Widget>[ | ||
| new LayoutId( | ||
| id: _AlertDialogSection.blur, | ||
| child: _buildBlurBackground(), |
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.
Maybe you can just wrap the container at 239 with the contents of this function instead of having it have its own layout id.
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 container is actually the maximum possible height for a dialog. Remember that issue where the dialog was always the height of the screen because the parent size can't depend on the children? The way I'm getting around that is by manually laying out all the individual pieces of the dialog, including the blur area.
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.
We need a better solution. Why can't the parent size depend on the children?
|
|
||
| @override | ||
| void performLayout(Size size) { | ||
| // TODO: @mattcarroll, handle 2 buttons that need to stack due to long text |
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 don't really have an opinion but the rest of the todos have the format // TODO(mattcarroll):. Worth keeping with convention.
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 exact syntax is in the style guide: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo
| // Layout the blur, content, and the actions. | ||
| final double dialogTop = (size.height - dialogHeight) / 2; | ||
| positionChild( | ||
| _AlertDialogSection.blur, |
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.
Ya, I might be missing something but from reading this, I still think we don't need 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.
spoke offline. the blur is needed in the custom layout because the parent layout is the full height of the screen. therefore moving the blur to the parent would blur most of the screen.
|
|
||
| Widget _buildHorizontalDivider() { | ||
| return new Container( | ||
| height: 0.3, |
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.
This will only work on 3x density devices right? Might have to read the MediaQuery
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.
yeah you probably want this to be 1/dpr.
If we were using strokeWidth, we could do it by setting the strokeWidth to 0.0 (hairline), but I don't see how that could work here.
| this.boxShadow, | ||
| this.gradient, | ||
| this.shape = BoxShape.rectangle, | ||
| this.backgroundBlendMode: BlendMode.srcOver, |
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.
@Hixie might have more opinions. I'd just let this be nullable and let the default come from Paint so we don't add cross-package defaulting and to avoid the setter for every decoration construction.
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.
yeah, @xster's comment seems reasonable
| ), | ||
| ); | ||
| positionChild( | ||
| _AlertDialogSection.content, |
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.
nit: 2 indent
|
|
||
| final int actionsCount; | ||
|
|
||
| _CupertinoAlertDialogLayoutDelegate({ |
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.
constructor first.
|
|
||
| @override | ||
| bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) { | ||
| return false; |
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.
why false? what if the actionsCount changes?
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'll change it to check action count. This invalidation will need some thought to get it right. Because it's not just action count, it's text length and stacking. I'll add a todo for that.
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.
we should make sure to get this right before landing.
i don't see the two things you mentioned in the list of constructor arguments, though, so how can they affect the algorithm?
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.
Yeah, this was a rough approach for the common case because there are a few issues that come together here. First, I'm not sure that the CupertinoDialogAction handles text wrapping correctly. Maybe it does, but I need to check. Second, we're not stacking buttons correctly. Currently we're only stacking when 3+ buttons are used. But, actually, based on the desired width of the buttons, there are times when we should stack 2. That as-of-yet-unimplemented stacking behavior impacts this invalidation. There is also an open ended question as to what the button layout logic means with regard to arbitrary Widgets that a developer chooses to pass instead of CupertinoDialogActions.
Due to those TODOs and unknowns I figured we should take the implest approach possible here that handles the typical cases. Which I think this does.
| ); | ||
|
|
||
| // Layout the blur, content, and the actions. | ||
| final double dialogTop = (size.height - dialogHeight) / 2; |
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.
2.0
|
|
||
| List<Widget> _buildVerticalButtons() { | ||
| final List<Widget> buttonsWithDividers = <Widget>[]; | ||
| for (int i = 0; i < children.length; ++i) { |
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.
prefer i += 1
| } | ||
|
|
||
| return buttonsWithDividers; | ||
| } |
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.
here's a crazy trick for _buildVerticalButtons:
Iterable<Widget> _buildVerticalButtons() sync* {
for (Widget child in children) {
yield _buildHorizontalDivider();
yield _buildDialogButton(child);
}
}...then at the callsite use it as _buildVerticalButtons().toList().
| return Row( | ||
| children: <Widget>[ | ||
| new Expanded( | ||
| child: _buildDialogButton(children[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.
if you know there's only one entry, prefer children.single
| canvas.drawLine(Offset.zero, new Offset(size.width, 0.0), paint); | ||
| for (int i = 1; i < count; ++i) { | ||
| // TODO(abarth): Hide the divider when one of the adjacent buttons is | ||
| // highlighted. |
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 you handle this todo?
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.
Nope. I just put the TODO back in.
| /// the standard painting blend mode that adds colors together the way | ||
| /// developers are used to. | ||
| /// | ||
| /// If no [color] or [gradient] is provided then blend mode has no impact. |
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.
you should assert that it's null in that case, i think, to help people who try to use it without understanding it
| bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) { | ||
| return false; | ||
| } | ||
|
|
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.
extraneous blank line
…hub.com:matthew-carroll/flutter into 15431_cupertino-dialog-blur-opaqueness-and-size
| const double _kCupertinoDialogWidth = 270.0; | ||
| const BoxDecoration _kCupertinoDialogFrontFillDecoration = const BoxDecoration( | ||
| const BoxDecoration _kCupertinoDialogBlurOverlayDecoration = const BoxDecoration( | ||
| borderRadius: const BorderRadius.all(const Radius.circular(_kDialogCornerRadius)), |
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.
Mini-nit: you can just say BorderRadius.circular
…on for high level review.
|
@xster I just pushed the render object implementation. As mentioned, this is a rough and messy implementation - will clean and test pending your review of the high level structure. |
xster
left a comment
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.
General approach LGTM
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| import 'dart:math'; |
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.
nit: seems like we're always importing 'dart:math' as math in a global search.
| // TODO(gspencer): This color isn't correct. Instead, we should carve a hole in | ||
| // the dialog and show more of the background. | ||
| const Color _kButtonDividerColor = const Color(0xffd5d5d5); | ||
| const Color _kDialogColor = const Color(0xC0FFFFFF); |
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.
Would still either make this variable name longer or add a comment above it saying it's a color that's put on top of _kCupertinoDialogBlurOverlayDecoration
| decoration: _kCupertinoDialogFrontFillDecoration, | ||
| child: child, | ||
| ), | ||
| borderRadius: const BorderRadius.all(const Radius.circular(_kDialogCornerRadius)), |
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.
you can just say BorderRadius.circular
| return new Center( | ||
| child: new Container( | ||
| width: _kCupertinoDialogWidth, | ||
| padding: const EdgeInsets.symmetric(vertical: _kEdgePadding), |
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.
Had to read this twice to understand this one. Might be clearer if we made this a margin instead of padding
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.
What's a margin? Do you just mean create a Padding widget outside the container?
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.
Ya. Though Container has a specific argument that does that for you inside the Container's build
|
|
||
| class CupertinoDialogRenderBox extends RenderBox | ||
| with ContainerRenderObjectMixin<RenderBox, CupertinoDialogRenderBoxParentData>, | ||
| RenderBoxContainerDefaultsMixin<RenderBox, CupertinoDialogRenderBoxParentData> { |
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.
nit: align with ContainerRenderObjectMixin (+1 space)
| minActionSpace = 1.5 * _kButtonHeight; | ||
| } | ||
|
|
||
| final size = constraints.constrain(const Size(double.infinity, double.infinity)); |
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.
constraints.biggest probably gives you the same thing
| // Size alert dialog content. | ||
| final Size maxContentSize = new Size(size.width, size.height - minActionSpace); | ||
| _content.layout( | ||
| new BoxConstraints.loose(maxContentSize), |
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.
ah, you might not need to convert the constraints to a size and back.
You can probably just do constraints.deflate(new EdgeInsets(top: minActionSpace))
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.
Looks like we probably can do that. But should we? Does that code better express the intention of the calculation than what is there now?
| this.size = new Size(size.width, dialogHeight); | ||
|
|
||
| // Layout the blur, content, and the actions. | ||
| _content.layout( |
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 don't think you need to do these 2 steps. You're giving back to the child the size that it itself just gave you.
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.
yep, you're right.
| ); | ||
| (_actions.parentData as CupertinoDialogRenderBoxParentData).offset = new Offset(0.0, contentSize.height); | ||
| } | ||
|
|
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.
@override here and below
| } | ||
| } | ||
|
|
||
| class CupertinoDialogRenderBoxParentData extends ContainerBoxParentData<RenderBox> { |
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.
make private
|
|
||
| final double contentHeight = _content.getMaxIntrinsicHeight(width); | ||
| final double actionsHeight = _actions.getMaxIntrinsicHeight(width); | ||
| final double height = min(contentHeight, actionsHeight); |
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.
Actually is this right? For its max intrinsic height, you're reporting the smaller of the content or the actions's height?
Reading the doc for this method, since your width doesn't depend on your height, I think your max and min intrinsic heights should be the same.
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 guess that should actually be contentHeight+actionsHeight, right?
This is another reason why a render object seems like a strange place for this. We compute all relevant sizing in the layout method, but we also have to implement these other methods....and it seems they're so unrelated that even a bad implementation doesn't effect the dialog....
…inoLayoutId to superclass of LayoutId.
|
@matthew-carroll can we close this PR while you work on it? (I'm trying to clear out our review queue.) |
|
@Hixie I suppose you can. But isn't this what the queue is supposed to have? The PR isn't done in any sense, so closing it seems odd. |
|
|
||
| // ParentData applied to individual action buttons that report whether or not | ||
| // that button is currently pressed by the user. | ||
| class _ActionButtonParentData extends MultiChildLayoutParentData { |
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.
probably doesn't need to inherit from MultiChildLayoutParentData
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.
Changed to extend ParentData
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.
Actually I think I do need it here. The action buttons are used in a RenderObject that has mixins that depend on MultiChildLayoutParentData
| // TODO(mattcarroll): The following logic is not entirely correct. It is also | ||
| // TODO(mattcarroll): the case that if content text does not contain a space, | ||
| // TODO(mattcarroll): it should also wrap instead of ellipsizing. We are | ||
| // TODO(mattcarroll): consciously not implementing that now due to complexity. |
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.
if it doesn't contain a space it should wrap?
nit: only one TODO per TODO, otherwise you'll be overly-inflating the technical debt benchmark :-)
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.
Fixed the TODOs. And yes, if the text does not contain a space then it wraps, whereas if even a single space exists in the text, the text ellipsizes.
| // TODO(mattcarroll): the case that if content text does not contain a space, | ||
| // TODO(mattcarroll): it should also wrap instead of ellipsizing. We are | ||
| // TODO(mattcarroll): consciously not implementing that now due to complexity. | ||
| final Widget sizedContent = _CupertinoAccessibility.of(context)?.isInAccessibilityMode ?? false |
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.
ah, interesting, i guess this is why you needed _CupertinoAccessibility...
Might be best to just have a function that takes a scale factor and tells you if you need accessibility mode, and to rely on MediaQuery instead of this private inherited widget, because:
What happens if someone uses this widget outside of where you'd expect? e.g. in a test case or demo or some such. Right now, it'll never correctly the determine accessibility mode.
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.
Switched the implementation from inherited widget to a top-level private function
| parentData.isPressed = isPressed; | ||
|
|
||
| // Force a repaint. | ||
| final AbstractNode targetParent = renderObject.parent; |
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 this needed instead of just calling markNeedsPaint? Since this is private and there's no repaint boundary, when this paints, the parent will also paint right?
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.
"this" is a parent data widget, it doesn't have a render object and so has nothing to paint.
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.
Ah I see. Makes sense. My bad.
| @required TextStyle textStyle, | ||
| @required Widget content, | ||
| }) { | ||
| final bool isInAccessibilityMode = _CupertinoAccessibility.of(context)?.isInAccessibilityMode ?? false; |
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 would just derive this from MediaQuery based on a static function instead of adding an ancestor widget dependency
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.
Made that change after chatting with you and Ian.
| final double dialogWidth = isInAccessibilityMode | ||
| ? _kAccessibilityCupertinoDialogWidth | ||
| : _kCupertinoDialogWidth; | ||
| final double textScaleFactor = MediaQuery.textScaleFactorOf(context); |
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.
Since you're pulling it here anyway :)
| ? _kAccessibilityCupertinoDialogWidth | ||
| : _kCupertinoDialogWidth; | ||
| final double textScaleFactor = MediaQuery.textScaleFactorOf(context); | ||
| final double fontSizeRatio = (textScaleFactor * textStyle.fontSize) / _kMinButtonFontSize; |
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.
Perhaps add some more code comments or name this variable to describe what this is for
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.
Added comments
| /// value. | ||
| bool get enabled => onPressed != null; | ||
|
|
||
| double _calculatePadding(BuildContext context) { |
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.
Add code comment or call this _textScaleBasedPadding or something instead of a generic _calculatePadding
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'm not sure that either of those help. I think the code is very clear as to what it's doing, and I don't think the caller needs to know that text scale is being used. From the build method we just want to know what the padding is, right? Why would the build method care about the implementation details?
| } | ||
| RenderObject createRenderObject(BuildContext context) { | ||
| return new _RenderCupertinoDialogActions( | ||
| dialogWidth: _CupertinoAccessibility.of(context)?.isInAccessibilityMode ?? false |
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.
Same here, I suspect if this widget is built once and then the text scale changes on the fly, this dialog width wouldn't update since it goes through updateRenderObject
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.
added dialog width to the update call
| double _dialogWidth; | ||
| set dialogWidth(double newWidth) { | ||
| if (newWidth != _dialogWidth) { | ||
| _dialogWidth = newWidth; |
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.
nit: be consistent with if != and if == below
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.
Fixed.
| // Check that the dialog size is the same as the actions section size. This | ||
| // ensures that an empty content section doesn't accidentally render some | ||
| // empty space in the dialog. | ||
| final Finder contentSectionFinder = find.byElementPredicate((Element element) { |
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.
+1 for naming these variables
|
|
||
| expect( | ||
| actionsSectionBox.size.height, | ||
| 67.83333333333337, |
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.
use moreOrLessEquals for these doubles?
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 think we'd need to chat about that. I haven't seen it done elsewhere, and I'm not sure what an appropriate envelope would be.
| /// | ||
| /// If the center of the widget is not exposed, this might send events to | ||
| /// another object. | ||
| Future<TestGesture> press(Finder finder, { int pointer }) { |
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 gesture is still held down after this returns right? Perhaps call it pressDown or something to make it not sound like a complete press down+up sequence.
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.
If I remember correctly, @Hixie suggested this name because it reads semantically with the subject being passed in. I think pressDown was avoided because it didn't read as well....
|
LGTM! |
This reverts commit 21bc9f1.
|
|
||
| return new IntrinsicHeight( | ||
| child: new SizedBox( | ||
| width: double.infinity, |
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'm pretty sure this SizedBox doesn't do anything useful.
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.
Indeed
Approximated standard iOS alert blur and brightness. Implemented correct overflow layout behavior for iOS alert content and buttons. Added blend mode to BoxDecoration. (#15431)