Unverified Commit c12b0de0 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

remove opacity layer at fully opaque (#106351)

parent 10fb7b48
...@@ -887,6 +887,7 @@ class RenderOpacity extends RenderProxyBox { ...@@ -887,6 +887,7 @@ class RenderOpacity extends RenderProxyBox {
@override @override
OffsetLayer updateCompositedLayer({required covariant OpacityLayer? oldLayer}) { OffsetLayer updateCompositedLayer({required covariant OpacityLayer? oldLayer}) {
assert(_alpha != 255);
final OpacityLayer updatedLayer = oldLayer ?? OpacityLayer(); final OpacityLayer updatedLayer = oldLayer ?? OpacityLayer();
updatedLayer.alpha = _alpha; updatedLayer.alpha = _alpha;
return updatedLayer; return updatedLayer;
...@@ -1060,7 +1061,7 @@ mixin RenderAnimatedOpacityMixin<T extends RenderObject> on RenderObjectWithChil ...@@ -1060,7 +1061,7 @@ mixin RenderAnimatedOpacityMixin<T extends RenderObject> on RenderObjectWithChil
_alpha = ui.Color.getAlphaFromOpacity(opacity.value); _alpha = ui.Color.getAlphaFromOpacity(opacity.value);
if (oldAlpha != _alpha) { if (oldAlpha != _alpha) {
final bool? wasRepaintBoundary = _currentlyIsRepaintBoundary; final bool? wasRepaintBoundary = _currentlyIsRepaintBoundary;
_currentlyIsRepaintBoundary = _alpha! > 0; _currentlyIsRepaintBoundary = _alpha! > 0 && _alpha! < 255;
if (child != null && wasRepaintBoundary != _currentlyIsRepaintBoundary) { if (child != null && wasRepaintBoundary != _currentlyIsRepaintBoundary) {
markNeedsCompositingBitsUpdate(); markNeedsCompositingBitsUpdate();
} }
......
...@@ -272,7 +272,7 @@ void main() { ...@@ -272,7 +272,7 @@ void main() {
expect(renderAnimatedOpacity.needsCompositing, false); expect(renderAnimatedOpacity.needsCompositing, false);
}); });
test('RenderAnimatedOpacity does composite if it is opaque', () { test('RenderAnimatedOpacity does not composite if it is opaque', () {
final Animation<double> opacityAnimation = AnimationController( final Animation<double> opacityAnimation = AnimationController(
vsync: FakeTickerProvider(), vsync: FakeTickerProvider(),
)..value = 1.0; )..value = 1.0;
...@@ -282,6 +282,20 @@ void main() { ...@@ -282,6 +282,20 @@ void main() {
child: RenderSizedBox(const Size(1.0, 1.0)), // size doesn't matter child: RenderSizedBox(const Size(1.0, 1.0)), // size doesn't matter
); );
layout(renderAnimatedOpacity, phase: EnginePhase.composite);
expect(renderAnimatedOpacity.needsCompositing, false);
});
test('RenderAnimatedOpacity does composite if it is partially opaque', () {
final Animation<double> opacityAnimation = AnimationController(
vsync: FakeTickerProvider(),
)..value = 0.5;
final RenderAnimatedOpacity renderAnimatedOpacity = RenderAnimatedOpacity(
opacity: opacityAnimation,
child: RenderSizedBox(const Size(1.0, 1.0)), // size doesn't matter
);
layout(renderAnimatedOpacity, phase: EnginePhase.composite); layout(renderAnimatedOpacity, phase: EnginePhase.composite);
expect(renderAnimatedOpacity.needsCompositing, true); expect(renderAnimatedOpacity.needsCompositing, true);
}); });
......
...@@ -98,10 +98,10 @@ void main() { ...@@ -98,10 +98,10 @@ void main() {
expect(renderSliverAnimatedOpacity.needsCompositing, false); expect(renderSliverAnimatedOpacity.needsCompositing, false);
}); });
test('RenderSliverAnimatedOpacity does composite if it is opaque', () { test('RenderSliverAnimatedOpacity does composite if it is partially opaque', () {
final Animation<double> opacityAnimation = AnimationController( final Animation<double> opacityAnimation = AnimationController(
vsync: FakeTickerProvider(), vsync: FakeTickerProvider(),
)..value = 1.0; )..value = 0.5;
final RenderSliverAnimatedOpacity renderSliverAnimatedOpacity = RenderSliverAnimatedOpacity( final RenderSliverAnimatedOpacity renderSliverAnimatedOpacity = RenderSliverAnimatedOpacity(
opacity: opacityAnimation, opacity: opacityAnimation,
...@@ -121,6 +121,29 @@ void main() { ...@@ -121,6 +121,29 @@ void main() {
expect(renderSliverAnimatedOpacity.needsCompositing, true); expect(renderSliverAnimatedOpacity.needsCompositing, true);
}); });
test('RenderSliverAnimatedOpacity does not composite if it is opaque', () {
final Animation<double> opacityAnimation = AnimationController(
vsync: FakeTickerProvider(),
)..value = 1.0;
final RenderSliverAnimatedOpacity renderSliverAnimatedOpacity = RenderSliverAnimatedOpacity(
opacity: opacityAnimation,
sliver: RenderSliverToBoxAdapter(
child: RenderSizedBox(const Size(1.0, 1.0)), // size doesn't matter
),
);
final RenderViewport root = RenderViewport(
crossAxisDirection: AxisDirection.right,
offset: ViewportOffset.zero(),
cacheExtent: 250.0,
children: <RenderSliver>[renderSliverAnimatedOpacity],
);
layout(root, phase: EnginePhase.composite);
expect(renderSliverAnimatedOpacity.needsCompositing, false);
});
test('RenderSliverAnimatedOpacity reuses its layer', () { test('RenderSliverAnimatedOpacity reuses its layer', () {
final Animation<double> opacityAnimation = AnimationController( final Animation<double> opacityAnimation = AnimationController(
vsync: FakeTickerProvider(), vsync: FakeTickerProvider(),
......
...@@ -7,7 +7,7 @@ import 'package:flutter/rendering.dart'; ...@@ -7,7 +7,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
testWidgets('RenderAnimatedOpacityMixin avoids repainting child as it animates', (WidgetTester tester) async { testWidgets('RenderAnimatedOpacityMixin drops layer when animating to 1', (WidgetTester tester) async {
RenderTestObject.paintCount = 0; RenderTestObject.paintCount = 0;
final AnimationController controller = AnimationController(vsync: const TestVSync(), duration: const Duration(seconds: 1)); final AnimationController controller = AnimationController(vsync: const TestVSync(), duration: const Duration(seconds: 1));
final Tween<double> opacityTween = Tween<double>(begin: 0, end: 1); final Tween<double> opacityTween = Tween<double>(begin: 0, end: 1);
...@@ -32,6 +32,39 @@ void main() { ...@@ -32,6 +32,39 @@ void main() {
await tester.pump(); await tester.pump();
await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500));
expect(RenderTestObject.paintCount, 2);
controller.stop();
await tester.pump();
expect(RenderTestObject.paintCount, 2);
});
testWidgets('RenderAnimatedOpacityMixin avoids repainting child as it animates', (WidgetTester tester) async {
RenderTestObject.paintCount = 0;
final AnimationController controller = AnimationController(vsync: const TestVSync(), duration: const Duration(seconds: 1));
final Tween<double> opacityTween = Tween<double>(begin: 0, end: 0.99); // Layer is dropped at 1
await tester.pumpWidget(
Container(
color: Colors.red,
child: FadeTransition(
opacity: controller.drive(opacityTween),
child: const TestWidget(),
),
)
);
expect(RenderTestObject.paintCount, 0);
controller.forward();
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
expect(RenderTestObject.paintCount, 1);
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
expect(RenderTestObject.paintCount, 1); expect(RenderTestObject.paintCount, 1);
controller.stop(); controller.stop();
...@@ -43,7 +76,7 @@ void main() { ...@@ -43,7 +76,7 @@ void main() {
testWidgets('RenderAnimatedOpacityMixin allows opacity layer to be disposed when animating to 0 opacity', (WidgetTester tester) async { testWidgets('RenderAnimatedOpacityMixin allows opacity layer to be disposed when animating to 0 opacity', (WidgetTester tester) async {
RenderTestObject.paintCount = 0; RenderTestObject.paintCount = 0;
final AnimationController controller = AnimationController(vsync: const TestVSync(), duration: const Duration(seconds: 1)); final AnimationController controller = AnimationController(vsync: const TestVSync(), duration: const Duration(seconds: 1));
final Tween<double> opacityTween = Tween<double>(begin: 1, end: 0); final Tween<double> opacityTween = Tween<double>(begin: 0.99, end: 0);
await tester.pumpWidget( await tester.pumpWidget(
Container( Container(
color: Colors.red, color: Colors.red,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment