-
Notifications
You must be signed in to change notification settings - Fork 29.7k
CupertinoActionSheet #19232
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
CupertinoActionSheet #19232
Conversation
| import 'colors.dart'; | ||
| import 'scrollbar.dart'; | ||
|
|
||
| const TextStyle _kCupertinoActionSheetActionTextStyle = const TextStyle( |
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 don't really need to have such a long name here, since it's local to this file: _kActionTextStyle would be fine.
|
|
||
| void _onTap() { | ||
| widget.onPressed; | ||
| Navigator.pop(context); //SHOULD THE ACTION SHEET BE HANDLING 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.
No, the onPressed will handle that. Look at how the dialog demo in the gallery does it. In order to do it here, you'd have to know what value to pass back.
| } | ||
|
|
||
| void _onTap() { | ||
| widget.onPressed; |
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.
Should be widget.onPressed(); (or it won't actually call the function).
| AnimationController createAnimationController() { | ||
| assert(_animationController == null); | ||
| _animationController = new AnimationController( | ||
| duration: const Duration(milliseconds: 500), |
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 feels too long to me, but if that's what iOS does, then it's fine.
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.
Yes, you are correct, was waiting to play with duration and curve until I got the positioning of the sheet correct (should have made a note of this)!
|
|
||
| return Padding( | ||
| padding: const EdgeInsets.symmetric(horizontal: 8.0), | ||
| child: new Center( |
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 your problem with getting the child size in your layout's getPositionForChild. The Center is expanding to fill the whole available area. Just remove this Center.
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.
Perfect, that worked! Do you think making that change will help with my sizing problem if I continue to play around with constraints?
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.
Yes, I think it should. Give it a try.
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.
I took a quick look at the route.dart part
| }) { | ||
| return Navigator.push(context, new _CupertinoModalPopupRoute<T>( | ||
| builder: builder, | ||
| barrierLabel: 'ModalPopup', |
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 the announced label for what happens when you tap the barrier. Probably 'dismiss' or something.
| } | ||
| } | ||
|
|
||
| class _ModalBottomSheet<T> extends StatefulWidget { |
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.
Does this need to be Stateful? Can we just collapse this whole thing into _CupertinoModalPopupRoute and avoid a layer of abstraction?
| Widget build(BuildContext context) { | ||
| return new AnimatedBuilder( | ||
| animation: widget.route.animation, | ||
| builder: (BuildContext context, Widget child) { |
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.
Building a AnimatedBuilder without using a child is fairly expensive (since the child tree itself needs to rebuild each frame even though the action sheets themselves presumably don't change during the animation).
This setup seems complex. Can't it just be a FractionalTranslation with a child?
|
@gspencergoog and @xster I've pushed my latest work on CupertinoActionSheet for your review. Matt requested that I copy any code I was using from his dialog PR (as opposed to me trying to land my work on top of his), so there is overlap between my PR and his - just so you're aware if you are also reviewing his PR. |
| textBaseline: TextBaseline.alphabetic, | ||
| ); | ||
|
|
||
| // _kCupertinoAlertBlurOverlayDecoration is applied to the blurred backdrop to |
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: Instead of saying _kCupertinoAlertBlurOverlayDecoration, just say "This decoration". That way, if the variable is renamed using a refactoring tool, the name won't be out of sync.
|
|
||
| /// An iOS-style action sheet. | ||
| /// | ||
| /// An action sheet is a specific style of alert that present the user |
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.
present --> presents
| /// sheet title and message text style. | ||
| /// | ||
| /// To display action buttons that look like standard iOS action sheet buttons, | ||
| /// provide [ActionSheetAction]s for the [actions] given to this action shet. |
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.
shet --> sheet
| } | ||
| } | ||
|
|
||
| /// A button typically used in an [CupertinoActionSheet]. |
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.
an --> a
(or should this still be ActionSheet instead of CupertinoActionSheet?)
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.
Yes, apologies for all of the ActionSheet/CupertinoActionSheet mixups. I believe it should be CupertinoActionSheet, in keeping with the Cupertino naming convention, but I started with it being ActionSheet and forgot to make the change throughout the comments.
| } | ||
| } | ||
|
|
||
| // iOS style layout policy for sizing an alert's content section and action |
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.
iOS style --> An iOS-style
| /// | ||
| /// See also: | ||
| /// | ||
| /// * [ActionSheet], which is the widget usually returned by the `builder` |
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.
Should ActionSheet be CupertinoActionSheet? Or is that all happening in the other rename PR?
|
|
||
| /// The blend mode applied to the [color] or [gradient] background of the box. | ||
| /// | ||
| /// If no [backgroundBlendMode] is provided then the default painting blend |
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.
provided --> provided,
(add comma)
| /// If no [backgroundBlendMode] is provided then the default painting blend | ||
| /// mode is used. | ||
| /// | ||
| /// 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.
provided --> provided,
(add comma)
| /// | ||
| /// See also: | ||
| /// | ||
| /// * [CupertinoActionSheet], an alert that presents the user with a set of two or |
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.
Should this be ActionSheet instead of CupertinoActionSheet?
| /// | ||
| /// * [CupertinoActionSheet], an alert that presents the user with a set of two or | ||
| /// more choices related to the current context. | ||
| class ActionSheetAction extends StatelessWidget { |
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.
Should this be CupertinoActinoSheetAction?
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.
Very nice. Just a few small comments.
|
|
||
| Widget _buildCancelButton() { | ||
| final double cancelPadding = (actions != null || message != null || title != null) | ||
| ? _kEdgeHorizontalPadding : 0.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.
You're reading a thing called horizontal padding but you're only using it vertically it seems.
| Widget build(BuildContext context) { | ||
| final List<Widget> children = <Widget>[ | ||
| new Flexible(child: ClipRRect( | ||
| borderRadius: BorderRadius.circular(12.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.
Don't use dart2 syntax yet
| } | ||
| } | ||
|
|
||
| class _CupertinoAlertRenderWidget extends RenderObjectWidget { |
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 assuming everything here below are things copied from @matthew-carroll's PR?
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.
Yes everything below is copied, with some modifications to make things work for action sheets specifically
| } | ||
|
|
||
| Widget createAppWithButtonThatLaunchesActionSheet(Widget actionSheet) { | ||
| return new MaterialApp( |
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.
Avoid using Material widgets here. Just use their Cupertino equivalents
| ), | ||
| ); | ||
|
|
||
| final DefaultTextStyle widget = tester.widget(find.byType(DefaultTextStyle)); |
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.
find.widgetWithText to be more precise
| await tester.pumpWidget( | ||
| createAppWithButtonThatLaunchesActionSheet( | ||
| new CupertinoActionSheet( | ||
| actions: <Widget>[ |
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.
indents are a bit off here and above
| await tester.pump(const Duration(seconds: 1)); | ||
|
|
||
| // Check that the title/message section is not displayed | ||
| expect(actionScrollController.offset, 0.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.
Not sure how the actions scroll controller's offset is indicative of the presence or absence of the title/message section
|
|
||
| // Check that the button's vertical size is the same. | ||
| expect(tester.getSize(find.widgetWithText(CupertinoActionSheetAction, 'One')).height, | ||
| equals(tester.getSize(find.widgetWithText(CupertinoActionSheetAction, 'Two')).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.
nit: indent
| expect(actionScrollController.offset, 0.0); | ||
|
|
||
| // Check that the button's vertical size is the same. | ||
| expect(tester.getSize(find.widgetWithText(CupertinoActionSheetAction, 'One')).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.
Perhaps check their exact coordinates?
| await tester.tap(find.text('Go')); | ||
| await tester.pump(); | ||
|
|
||
| expect(tester.getSize(find.byType(CupertinoActionSheet)).height, 132.33333333333334); |
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.
moreOrLessEquals (or it can be undeterministic depending on the machine it's running on)
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.
LGTM
| this.messageScrollController, | ||
| this.actionScrollController, | ||
| this.cancelButton, | ||
| }) : assert(actions != null || title != null || message != null || cancelButton != null), |
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 an error message
|
|
||
| if (cancelButton != null) { | ||
| children.add( | ||
| _buildCancelButton(), |
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 there no blur behind the cancel button?
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.
No, from observation/experimentation I've found that the cancel button isn't transparent like the other buttons.
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 cool, thanks for testing
| @override | ||
| Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) { | ||
| if (_animation.value == 1.0) { | ||
| context.findRenderObject().markNeedsSemanticsUpdate(); |
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.
buildTransition isn't guaranteed to be called each time the animation value 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've found in practice that the animation is long enough and the curve is such that buildTransitions ends up being called several times while the animation value is 1.0. I understand the concern though - could the markNeedsSemanticsUpdate() be called in buildPage, or do you know of another way to tell the render object there needs to be a semantics update when the animation is complete?
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 about dismissal? Don't you want to call it as soon as it's not 1.0 anymore? Add animation status listeners on createAnimation. (Make sure to unregister the listener when it's done though)
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 why do you call this here in the route itself? What is the issue you're solving?
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 thinking I'd want to call it when the animation was complete so the semantic bounds could be placed correctly? Does that make sense?
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.
Oh, just saw your followup question - the issue is that the semantic bounds for the action sheet end up being drawn offscreen, so I need to have the semantics update once the action sheets get in their final position.
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 see. I would leave this out altogether. If it's easy to produce a reduced test case that demonstrates that the semantic box is at the wrong place inside a fractional translation, this should be fixed in the semantics pipeline itself.
I'd leave it broken and leave an issue describing the misbehavior.
| return new Align( | ||
| alignment: Alignment.bottomCenter, | ||
| child: new FractionalTranslation( | ||
| translation: new Tween<Offset>( |
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.
Create this once in createAnimation
-Add showCupertinoModalPopup, CupertinoActionSheet