Initial implementation of AnnotatedRegion for system chrome#17672
Initial implementation of AnnotatedRegion for system chrome#17672jonahwilliams merged 55 commits intoflutter:masterfrom
Conversation
|
|
||
| void _updateSystemChrome() { | ||
| final SystemUiOverlayStyle overlayStyle = layer.findRegion( | ||
| Offset.zero, |
There was a problem hiding this comment.
The suggestion I had was for when we just had one colour for everything. Now that we have two colours (at least), we probably want to independently get the details for the top and the bottom.
For the top, I'd probably aim for a point half way across the width, and half of the way down the top padding.
Similarly for the other sources.
| _latestStyle = _pendingStyle; | ||
| } | ||
| _pendingStyle = null; | ||
| }); |
There was a problem hiding this comment.
we might want to keep the old behaviour around in case anyone else wants to use this API, and doesn't have a way to guarantee they only call it once per frame.
| } | ||
|
|
||
| /// Render object for the [AnnotatedRegion]. | ||
| class AnnotatedRegionRenderObject<T> extends RenderProxyBox { |
There was a problem hiding this comment.
shouldn't this be in proxy_box?
There was a problem hiding this comment.
The render_object, or the whole annotated_region API?
|
This looks fantastic.
not sure what you mean by "proper"? This seems pretty good already.
We could have a default. I'd be tempted to just say if there's no information, we don't change it. I would recommend adding more tests, e.g. to check if it works with crazy layer types like the leader/follower layers, if it works with scrolling of slivers, etc. |
|
When you fix whatever angered the bots, please see if you can also fix the _verifyNoBadImportsInFlutter logic to not crash when handling whatever you did... thanks :-) |
| import 'package:flutter/services.dart'; | ||
| import 'package:flutter/widgets.dart'; | ||
|
|
||
| void main() => runApp(const Center(child: const Text('Hello, world!', textDirection: TextDirection.ltr))); |
There was a problem hiding this comment.
Please don't change the hello world example. The whole point of this example is to be a one liner.
There was a problem hiding this comment.
Sorry commited by mistake, was using this to debug values - will revert
|
|
||
| @override | ||
| Object findRegion(Offset offset, Type type) { | ||
| return super.findRegion(offset + this.offset, type); |
There was a problem hiding this comment.
Please name the argument something that doesn't shadow a member variable.
| @override | ||
| Object findRegion(Offset offset, Type type) { | ||
| return super.findRegion(offset + this.offset, type); | ||
| if (offset <= this.offset) { |
| systemNavigationBarDividerColor: lowerOverlayStyle.systemNavigationBarDividerColor, | ||
| )); | ||
| } else if (upperOverlayStyle != null || lowerOverlayStyle != null) { | ||
| SystemChrome.setSystemUIOverlayStyle(upperOverlayStyle ?? lowerOverlayStyle); |
There was a problem hiding this comment.
It seems weird that when an upper overlay style goes out of view, suddenly the lower overlay style controls the top system ui overlay.
| S find<S>(Offset regionOffset) { | ||
| if (_invertedTransform == null) { | ||
| _invertedTransform ??= new Matrix4.zero(); | ||
| _transform?.copyInto(_invertedTransform)?.invert(); |
There was a problem hiding this comment.
there's a method or constructor or something that'll get you a new matrix that represents the inverse of another, which'll save you having to manually create the new one.
| @override | ||
| S find<S>(Offset regionOffset) { | ||
| final Offset transformed = regionOffset - offset; | ||
| return super.find<S>(transformed); |
There was a problem hiding this comment.
i don't mind which we do, but we should be consistent in the style between this one and LeaderLayer's.
| } | ||
| final Vector4 vector = new Vector4(regionOffset.dx, regionOffset.dy, 0.0, 1.0); | ||
| final Vector4 result = _invertedTransform.transform(vector); | ||
| return super.find<S>(new Offset(result[0] - linkedOffset.dx, result[1] - linkedOffset.dy)); |
There was a problem hiding this comment.
this doesn't seem to handle the case of link.leader == null && !showWhenUnlinked. I assume in that case we should just return null?
| /// a [Size] is provided to this layer, then find will check if the provided | ||
| /// offset is within the bounds of the layer. | ||
| class AnnotatedRegionLayer<T> extends ContainerLayer { | ||
| /// Creates a new annotated layer. |
There was a problem hiding this comment.
These docs don't match our style guide.
There was a problem hiding this comment.
(Because they don't tell you anything you can't tell from the identifiers. Maybe mention the contract on size, e.g. that if null it won't clip, or something.)
There was a problem hiding this comment.
maybe say value can't be null? and assert it
| /// The [size] is optionally used to clip the hit-testing of [find]. | ||
| /// | ||
| /// If not provided, all offsets are considered to be contained within this | ||
| /// layer, unless a parent layer applies a clip. |
| super.debugFillProperties(properties); | ||
| properties.add(new DiagnosticsProperty<T>('value', value)); | ||
| if (size != null) | ||
| properties.add(new DiagnosticsProperty<Size>('size', size)); |
There was a problem hiding this comment.
you can always add this property, even if it's null. Just set defaultValue to null.
| /// * [Layer.find], for an example of how this value is retrieved. | ||
| /// * [AnnotatedRegionLayer], the layer this render object creates. | ||
| class AnnotatedRegionRenderObject<T> extends RenderProxyBox { | ||
| /// A value which can be retrieved using [Layer.find]. |
There was a problem hiding this comment.
The lack of a constructor is very atypical for RenderObject subclasses.
| /// * [AnnotatedRegionLayer], the layer this render object creates. | ||
| class AnnotatedRegionRenderObject<T> extends RenderProxyBox { | ||
| /// A value which can be retrieved using [Layer.find]. | ||
| T value; |
There was a problem hiding this comment.
shouldn't changing value cause markNeedsPaint?
| T value; | ||
|
|
||
| /// Whether the render object will pass its [size] to the [AnnotatedRegionLayer]. | ||
| bool sized; |
There was a problem hiding this comment.
similarly here, shouldn't mutating sized cause markNeedsPaint?
| if (child != null) { | ||
| final AnnotatedRegionLayer<T> layer = new AnnotatedRegionLayer<T>(value, size: sized ? size : null); | ||
| context.pushLayer(layer, super.paint, offset); | ||
| } |
There was a problem hiding this comment.
why do you only push the layer if there's a child?
| @override | ||
| void updateRenderObject(BuildContext context, AnnotatedRegionRenderObject<T> renderObject) { | ||
| renderObject.value = value; | ||
| renderObject.sized = sized; |
There was a problem hiding this comment.
prefer cascade operator; see other updateRenderObject implementations
|
Found issue with TestRecordingCanvas and with the location of the annotated region layer in app bar. Both fixes, should be all green |
| /// object to clip the results of [Layer.findRegion]. | ||
| /// | ||
| /// Neither [value] nor [sized] can be null. | ||
| AnnotatedRegionRenderObject(T value, bool sized) |
There was a problem hiding this comment.
btw the naming scheme is usually RenderFoo for the render object, and Foo for the widget.
| : assert(value != null), | ||
| assert(sized != null), | ||
| _value = value, | ||
| _sized = sized; |
There was a problem hiding this comment.
isn't that set by the element?
There was a problem hiding this comment.
The render object layer can be used directly, it doesn't have to be used via widgets. When used directly, the first child is usually set in the constructor. Look at the other classes in this file. :-)
There was a problem hiding this comment.
ahhh right, fixed
|
yaaay it passed |
|
Friendly ping. I've been informed this feature is a blocker for customer mulligan. |
|
Thanks for doing this, this is awesome! |



In order to fix the unpredictability of SystemChrome changes, I've added the AnnotatedRegion API as proposed by @Hixie on #4630.
Changes
AnnotatedRegion<T>, a widget which places anAnnotatedRegionLayer<T>in the layer tree.Layer.findRegion(Offset, Type)which searches for the lastAnnotatedRegionLayerthat matchesTypeand containsOffsetsetSystemUiOverlayStylewithAnnotatedRegion<SystemUiOverlayStyle>setSystemUiOverlayStyleafter the layer tree is built usingfindRegion.lightanddarkthemes to always have dark app bars for Android - this is so enabling the default themes is non-breaking.Questions
Edit
Unfortunately I swapped the meaning of the brightness values for iOS in the engine, so I need to fix that separately