-
Notifications
You must be signed in to change notification settings - Fork 309
compose_box [nfc]: Add InsetShadowBox #990
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
Conversation
490c274
to
9390c9d
Compare
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.
Thanks! Small comments below, and I'll mark for Greg's review.
lib/widgets/inset_shadow_box.dart
Outdated
@@ -0,0 +1,45 @@ | |||
import 'package:flutter/material.dart'; | |||
|
|||
/// A widget that overlays inset shadows on a 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.
Could you expand on this a little bit, maybe mentioning the scroll-through-shadow use case?
There's a similar-sounding effect that this looks like it might describe, but it turns out it doesn't; that's box-shadow: inset
. It would be nice to have a full implementation of box-shadow: inset
—we have a TODO in the reaction-chip UI:
// TODO shadow effect, following web, which uses `box-shadow: inset`:
// https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow#inset
// Needs Flutter support for something like that:
// https://github.com/flutter/flutter/issues/18636
// https://github.com/flutter/flutter/issues/52999
// Until then use a solid color; a much-lightened version of the shadow color.
// Also adapt by making [borderUnselected] more transparent, so we'll
// want to check that against web when implementing the shadow.
bgUnselected: const HSLColor.fromAHSL(0.08, 210, 0.50, 0.875).toColor(),
—but that's not what InsetShadowBox
is. 🙂 (InsetShadowBox
wouldn't handle the "stadium" shape of the reaction chips, for example.)
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.
Makes sense. I think this means that we should rename this widget to RectangularInsetShadowBox
, to make it clearer.
lib/widgets/inset_shadow_box.dart
Outdated
Widget build(BuildContext context) { | ||
return Stack( | ||
children: [ | ||
child, | ||
Positioned(top: 0, left: 0, right: 0, | ||
child: Container(height: top, decoration: _shadowFrom(Alignment.topCenter))), | ||
Positioned(bottom: 0, left: 0, right: 0, | ||
child: Container(height: bottom, decoration: _shadowFrom(Alignment.bottomCenter))), | ||
]); | ||
} |
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.
Widget build(BuildContext context) { | |
return Stack( | |
children: [ | |
child, | |
Positioned(top: 0, left: 0, right: 0, | |
child: Container(height: top, decoration: _shadowFrom(Alignment.topCenter))), | |
Positioned(bottom: 0, left: 0, right: 0, | |
child: Container(height: bottom, decoration: _shadowFrom(Alignment.bottomCenter))), | |
]); | |
} | |
Widget build(BuildContext context) { | |
return Stack(children: [ | |
child, | |
Positioned(top: 0, left: 0, right: 0, | |
child: Container(height: top, decoration: _shadowFrom(Alignment.topCenter))), | |
Positioned(bottom: 0, left: 0, right: 0, | |
child: Container(height: bottom, decoration: _shadowFrom(Alignment.bottomCenter))), | |
]); | |
} |
lib/widgets/inset_shadow_box.dart
Outdated
@@ -0,0 +1,45 @@ | |||
import 'package:flutter/material.dart'; |
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:
import 'package:flutter/material.dart'; | |
import 'package:flutter/widgets.dart'; |
(not sure how much we care about being agnostic about Material though.)
978c9be
to
8fd9107
Compare
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.
Thanks @PIG208, and thanks @chrisbobbe for the previous review!
Generally this looks great. A few nits below.
Let's also have a test or two.
- One thing to test is that the shadows don't add padding — so e.g. a child with height 40 and shadows of height 8 produce height 40, not 56.
- Ideally I'd like to have a test for the widget's actual main functionality too. I'm not sure there's a good way to do that without more work than it's worth, though.
- One way would be with golden images. That seems like definitely more work than it's worth here.
- The other possible way would be a painting test. That'd be worth spending like 15 minutes investigating to try to get to work — it might not be hard.
Positioned(top: 0, left: 0, right: 0, | ||
child: Container(height: top, decoration: _shadowFrom(Alignment.topCenter))), |
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: Positioned can control the height, right?
Positioned(top: 0, left: 0, right: 0, | |
child: Container(height: top, decoration: _shadowFrom(Alignment.topCenter))), | |
Positioned(top: 0, height: top, left: 0, right: 0, | |
child: Container(decoration: _shadowFrom(Alignment.topCenter))), |
Then also Container
can I think be reduced to DecoratedBox
.
/// | ||
/// See also: | ||
/// * https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=3860-11890&node-type=frame&t=oOVTdwGZgtvKv9i8-0 | ||
class RectangularInsetShadowBox 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.
"rectangular box" seems redundant — a box is necessarily rectangular. I think InsetShadowBox would be a fine name.
Or I guess this is a box with rectangular inset shadows (the shadows are rectangular). But I think an "inset shadow" on a box, which is a rectangle, is also going to be rectangular.
It's true this doesn't cover as wide a range of cases as CSS box-shadow: inset
, as discussed at #990 (comment) . I think that reflects the CSS feature growing to be wider than its name fits. To clarify that this doesn't cover that CSS feature, the clearest thing would be to just mention it in the doc.
@@ -0,0 +1,51 @@ | |||
import 'package:flutter/widgets.dart'; |
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: generally we've followed the Flutter upstream tree in grouping related functionality together into source files, rather than atomizing each class into its own tiny file.
One consequence is that the file names are shorter and more general than the class names.
Here there might not be any existing other code for this to share a file with, but we can still give it a bit of a broader, shorter name. Say inset_shadow.dart
.
8fd9107
to
d25c594
Compare
Updated the PR with tests and fixes. This is ready for review! |
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.
Thanks for the revision! Comments below.
test/widgets/inset_shadow.dart
Outdated
|
||
import '../flutter_checks.dart'; | ||
|
||
void main() { |
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 test doesn't actually run in flutter test
(or tools/check
) because the name doesn't end with _test.dart
🙂
test/widgets/inset_shadow.dart
Outdated
return (Symbol methodName, List<dynamic> arguments) { | ||
check(methodName).equals(#drawRect); | ||
check(arguments[0]).isA<Rect>().equals(rect); | ||
check(arguments[1]).isA<Paint>().shader.isA<ui.Gradient>(); |
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.
Worth a one-line comment saying that Gradient is opaque and so this is the most we can check about it.
test/widgets/inset_shadow.dart
Outdated
child: SizedBox(width: 20, height: 20, | ||
child: InsetShadowBox(top: 7, bottom: 3, | ||
color: Colors.red, | ||
child: Placeholder()))))); | ||
|
||
final childRect = tester.getRect(find.byType(Placeholder)); | ||
check(childRect).equals(const Rect.fromLTRB(0, 0, 20, 20)); |
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 this check would pass no matter what InsetShadowBox
attempted to do for its layout. Its parent (the SizedBox
) has decreed exactly what its size is, so it's not able to be any other size. See:
https://docs.flutter.dev/get-started/fundamentals/layout#constraints
https://api.flutter.dev/flutter/rendering/RenderBox-class.html#layout
It's worth trying to verify that — edit InsetShadowBox so that it does use top
and bottom
as padding, and see if this test still passes. I believe it will.
To make the test effective, I think what's needed is to have a SizedBox
as the child, not the parent. Then do confirm that that test fails if you make that change to InsetShadowBox.
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 actually caught a bug with the implementation that allows InsetShadowBox
to modify the constraints from the parent. The test is updated to catch layout changes.
test/widgets/inset_shadow.dart
Outdated
child: SizedBox(width: 20, height: 20, | ||
child: InsetShadowBox(top: 7, bottom: 3, | ||
color: Colors.red, | ||
child: Placeholder()))))); |
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.
In tests, typically preferable to say something like SizedBox.shrink()
instead of Placeholder
. The Placeholder widget has a more complicated behavior that's useful for making it visible in development, but in a test makes the behavior more complex to reason through.
d25c594
to
fb1ef14
Compare
This casts fixed size shadows on top of a child widget from its top edge and bottom edge. Signed-off-by: Zixuan James Li <zixuan@zulip.com>
fb1ef14
to
a1b5579
Compare
Thanks for the revision! Looks good; merging. |
This casts a static shadow on top of a child widget from its top edge and bottom edge.
To review its effect, see #928. This is intended to prep for #853.