Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
0cb5772e
Unverified
Commit
0cb5772e
authored
Apr 26, 2022
by
Jonah Williams
Committed by
GitHub
Apr 26, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[framework] allow other RenderObjects to behave like repaint boundaries (#101952)
parent
61bbaaa4
Changes
7
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
794 additions
and
58 deletions
+794
-58
object.dart
packages/flutter/lib/src/rendering/object.dart
+187
-18
proxy_box.dart
packages/flutter/lib/src/rendering/proxy_box.dart
+30
-34
heroes.dart
packages/flutter/lib/src/widgets/heroes.dart
+3
-5
debug_test.dart
packages/flutter/test/rendering/debug_test.dart
+2
-1
proxy_box_test.dart
packages/flutter/test/rendering/proxy_box_test.dart
+207
-0
animated_opacity_repaint_test.dart
...s/flutter/test/widgets/animated_opacity_repaint_test.dart
+90
-0
opacity_repaint_test.dart
packages/flutter/test/widgets/opacity_repaint_test.dart
+275
-0
No files found.
packages/flutter/lib/src/rendering/object.dart
View file @
0cb5772e
This diff is collapsed.
Click to expand it.
packages/flutter/lib/src/rendering/proxy_box.dart
View file @
0cb5772e
...
...
@@ -843,7 +843,14 @@ class RenderOpacity extends RenderProxyBox {
super
(
child
);
@override
bool
get
alwaysNeedsCompositing
=>
child
!=
null
&&
(
_alpha
>
0
);
bool
get
isRepaintBoundary
=>
child
!=
null
&&
(
_alpha
>
0
);
@override
OffsetLayer
updateCompositedLayer
({
required
covariant
OpacityLayer
?
oldLayer
})
{
final
OpacityLayer
updatedLayer
=
oldLayer
??
OpacityLayer
();
updatedLayer
.
alpha
=
_alpha
;
return
updatedLayer
;
}
int
_alpha
;
...
...
@@ -864,13 +871,13 @@ class RenderOpacity extends RenderProxyBox {
assert
(
value
>=
0.0
&&
value
<=
1.0
);
if
(
_opacity
==
value
)
return
;
final
bool
didNeedCompositing
=
alwaysNeedsCompositing
;
final
bool
wasRepaintBoundary
=
isRepaintBoundary
;
final
bool
wasVisible
=
_alpha
!=
0
;
_opacity
=
value
;
_alpha
=
ui
.
Color
.
getAlphaFromOpacity
(
_opacity
);
if
(
didNeedCompositing
!=
alwaysNeedsCompositing
)
if
(
wasRepaintBoundary
!=
isRepaintBoundary
)
markNeedsCompositingBitsUpdate
();
markNeeds
Paint
();
markNeeds
CompositedLayerUpdate
();
if
(
wasVisible
!=
(
_alpha
!=
0
)
&&
!
alwaysIncludeSemantics
)
markNeedsSemanticsUpdate
();
}
...
...
@@ -891,19 +898,10 @@ class RenderOpacity extends RenderProxyBox {
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
child
!=
null
)
{
if
(
_alpha
==
0
)
{
// No need to keep the layer. We'll create a new one if necessary.
layer
=
null
;
return
;
}
assert
(
needsCompositing
);
layer
=
context
.
pushOpacity
(
offset
,
_alpha
,
super
.
paint
,
oldLayer:
layer
as
OpacityLayer
?);
assert
(()
{
layer
!.
debugCreator
=
debugCreator
;
return
true
;
}());
}
super
.
paint
(
context
,
offset
);
}
@override
...
...
@@ -929,8 +927,15 @@ mixin RenderAnimatedOpacityMixin<T extends RenderObject> on RenderObjectWithChil
int
?
_alpha
;
@override
bool
get
alwaysNeedsCompositing
=>
child
!=
null
&&
_currentlyNeedsCompositing
!;
bool
?
_currentlyNeedsCompositing
;
bool
get
isRepaintBoundary
=>
child
!=
null
&&
_currentlyIsRepaintBoundary
!;
bool
?
_currentlyIsRepaintBoundary
;
@override
OffsetLayer
updateCompositedLayer
({
required
covariant
OpacityLayer
?
oldLayer
})
{
final
OpacityLayer
updatedLayer
=
oldLayer
??
OpacityLayer
();
updatedLayer
.
alpha
=
_alpha
;
return
updatedLayer
;
}
/// The animation that drives this render object's opacity.
///
...
...
@@ -990,11 +995,11 @@ mixin RenderAnimatedOpacityMixin<T extends RenderObject> on RenderObjectWithChil
final
int
?
oldAlpha
=
_alpha
;
_alpha
=
ui
.
Color
.
getAlphaFromOpacity
(
opacity
.
value
);
if
(
oldAlpha
!=
_alpha
)
{
final
bool
?
didNeedCompositing
=
_currentlyNeedsCompositing
;
_currently
NeedsCompositing
=
_alpha
!
>
0
;
if
(
child
!=
null
&&
didNeedCompositing
!=
_currentlyNeedsCompositing
)
final
bool
?
wasRepaintBoundary
=
_currentlyIsRepaintBoundary
;
_currently
IsRepaintBoundary
=
_alpha
!
>
0
;
if
(
child
!=
null
&&
wasRepaintBoundary
!=
_currentlyIsRepaintBoundary
)
markNeedsCompositingBitsUpdate
();
markNeeds
Paint
();
markNeeds
CompositedLayerUpdate
();
if
(
oldAlpha
==
0
||
_alpha
==
0
)
markNeedsSemanticsUpdate
();
}
...
...
@@ -1002,19 +1007,10 @@ mixin RenderAnimatedOpacityMixin<T extends RenderObject> on RenderObjectWithChil
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
child
!=
null
)
{
if
(
_alpha
==
0
)
{
// No need to keep the layer. We'll create a new one if necessary.
layer
=
null
;
return
;
}
assert
(
needsCompositing
);
layer
=
context
.
pushOpacity
(
offset
,
_alpha
!,
super
.
paint
,
oldLayer:
layer
as
OpacityLayer
?);
assert
(()
{
layer
!.
debugCreator
=
debugCreator
;
return
true
;
}());
}
super
.
paint
(
context
,
offset
);
}
@override
...
...
packages/flutter/lib/src/widgets/heroes.dart
View file @
0cb5772e
...
...
@@ -545,13 +545,11 @@ class _HeroFlight {
bottom:
offsets
.
bottom
,
left:
offsets
.
left
,
child:
IgnorePointer
(
child:
RepaintBoundary
(
child:
FadeTransition
(
opacity:
_heroOpacity
,
child:
child
,
),
),
),
);
},
);
...
...
packages/flutter/test/rendering/debug_test.dart
View file @
0cb5772e
...
...
@@ -231,7 +231,8 @@ void main() {
rootLayer
,
const
Rect
.
fromLTWH
(
0
,
0
,
500
,
500
),
);
root
.
paint
(
context
,
const
Offset
(
40
,
40
));
context
.
paintChild
(
root
,
const
Offset
(
40
,
40
));
final
OpacityLayer
opacityLayer
=
rootLayer
.
firstChild
!
as
OpacityLayer
;
expect
(
opacityLayer
.
offset
,
const
Offset
(
40
,
40
));
debugDisableOpacityLayers
=
false
;
...
...
packages/flutter/test/rendering/proxy_box_test.dart
View file @
0cb5772e
...
...
@@ -561,6 +561,178 @@ void main() {
// The follower is still hit testable because there is a leader layer.
expect
(
follower
.
hitTest
(
hitTestResult
,
position:
Offset
.
zero
),
isTrue
);
});
test
(
'RenderObject can become a repaint boundary'
,
()
{
final
ConditionalRepaintBoundary
childBox
=
ConditionalRepaintBoundary
();
final
ConditionalRepaintBoundary
renderBox
=
ConditionalRepaintBoundary
(
child:
childBox
);
layout
(
renderBox
,
phase:
EnginePhase
.
composite
);
expect
(
childBox
.
paintCount
,
1
);
expect
(
renderBox
.
paintCount
,
1
);
renderBox
.
isRepaintBoundary
=
true
;
renderBox
.
markNeedsCompositingBitsUpdate
();
renderBox
.
markNeedsCompositedLayerUpdate
();
pumpFrame
(
phase:
EnginePhase
.
composite
);
// The first time the render object becomes a repaint boundary
// we must repaint from the parent to allow the layer to be
// created.
expect
(
childBox
.
paintCount
,
2
);
expect
(
renderBox
.
paintCount
,
2
);
expect
(
renderBox
.
debugLayer
,
isA
<
OffsetLayer
>());
renderBox
.
markNeedsCompositedLayerUpdate
();
expect
(
renderBox
.
debugNeedsPaint
,
false
);
expect
(
renderBox
.
debugNeedsCompositedLayerUpdate
,
true
);
pumpFrame
(
phase:
EnginePhase
.
composite
);
// The second time the layer exists and we can skip paint.
expect
(
childBox
.
paintCount
,
2
);
expect
(
renderBox
.
paintCount
,
2
);
expect
(
renderBox
.
debugLayer
,
isA
<
OffsetLayer
>());
renderBox
.
isRepaintBoundary
=
false
;
renderBox
.
markNeedsCompositingBitsUpdate
();
pumpFrame
(
phase:
EnginePhase
.
composite
);
// Once it stops being a repaint boundary we must repaint to
// remove the layer. its required that the render object
// perform this action in paint.
expect
(
childBox
.
paintCount
,
3
);
expect
(
renderBox
.
paintCount
,
3
);
expect
(
renderBox
.
debugLayer
,
null
);
// When the render object is not a repaint boundary, calling
// markNeedsLayerPropertyUpdate is the same as calling
// markNeedsPaint.
renderBox
.
markNeedsCompositedLayerUpdate
();
expect
(
renderBox
.
debugNeedsPaint
,
true
);
expect
(
renderBox
.
debugNeedsCompositedLayerUpdate
,
true
);
});
test
(
'RenderObject with repaint boundary asserts when a composited layer is replaced during layer property update'
,
()
{
final
ConditionalRepaintBoundary
childBox
=
ConditionalRepaintBoundary
(
isRepaintBoundary:
true
);
final
ConditionalRepaintBoundary
renderBox
=
ConditionalRepaintBoundary
(
child:
childBox
);
// Ignore old layer.
childBox
.
offsetLayerFactory
=
(
OffsetLayer
?
oldLayer
)
{
return
TestOffsetLayerA
();
};
layout
(
renderBox
,
phase:
EnginePhase
.
composite
);
expect
(
childBox
.
paintCount
,
1
);
expect
(
renderBox
.
paintCount
,
1
);
renderBox
.
markNeedsCompositedLayerUpdate
();
pumpFrame
(
phase:
EnginePhase
.
composite
,
onErrors:
expectAssertionError
);
},
skip:
kIsWeb
);
// https://github.com/flutter/flutter/issues/102086
test
(
'RenderObject with repaint boundary asserts when a composited layer is replaced during painting'
,
()
{
final
ConditionalRepaintBoundary
childBox
=
ConditionalRepaintBoundary
(
isRepaintBoundary:
true
);
final
ConditionalRepaintBoundary
renderBox
=
ConditionalRepaintBoundary
(
child:
childBox
);
// Ignore old layer.
childBox
.
offsetLayerFactory
=
(
OffsetLayer
?
oldLayer
)
{
return
TestOffsetLayerA
();
};
layout
(
renderBox
,
phase:
EnginePhase
.
composite
);
expect
(
childBox
.
paintCount
,
1
);
expect
(
renderBox
.
paintCount
,
1
);
renderBox
.
markNeedsPaint
();
pumpFrame
(
phase:
EnginePhase
.
composite
,
onErrors:
expectAssertionError
);
},
skip:
kIsWeb
);
// https://github.com/flutter/flutter/issues/102086
test
(
'RenderObject with repaint boundary asserts when a composited layer tries to update its own offset'
,
()
{
final
ConditionalRepaintBoundary
childBox
=
ConditionalRepaintBoundary
(
isRepaintBoundary:
true
);
final
ConditionalRepaintBoundary
renderBox
=
ConditionalRepaintBoundary
(
child:
childBox
);
// Ignore old layer.
childBox
.
offsetLayerFactory
=
(
OffsetLayer
?
oldLayer
)
{
return
(
oldLayer
??
TestOffsetLayerA
())..
offset
=
const
Offset
(
2133
,
4422
);
};
layout
(
renderBox
,
phase:
EnginePhase
.
composite
);
expect
(
childBox
.
paintCount
,
1
);
expect
(
renderBox
.
paintCount
,
1
);
renderBox
.
markNeedsPaint
();
pumpFrame
(
phase:
EnginePhase
.
composite
,
onErrors:
expectAssertionError
);
},
skip:
kIsWeb
);
// https://github.com/flutter/flutter/issues/102086
test
(
'RenderObject markNeedsPaint while repaint boundary, and then updated to no longer be a repaint boundary with '
'calling markNeedsCompositingBitsUpdate 1'
,
()
{
final
ConditionalRepaintBoundary
childBox
=
ConditionalRepaintBoundary
(
isRepaintBoundary:
true
);
final
ConditionalRepaintBoundary
renderBox
=
ConditionalRepaintBoundary
(
child:
childBox
);
// Ignore old layer.
childBox
.
offsetLayerFactory
=
(
OffsetLayer
?
oldLayer
)
{
return
oldLayer
??
TestOffsetLayerA
();
};
layout
(
renderBox
,
phase:
EnginePhase
.
composite
);
expect
(
childBox
.
paintCount
,
1
);
expect
(
renderBox
.
paintCount
,
1
);
childBox
.
markNeedsPaint
();
childBox
.
isRepaintBoundary
=
false
;
childBox
.
markNeedsCompositingBitsUpdate
();
expect
(()
=>
pumpFrame
(
phase:
EnginePhase
.
composite
),
returnsNormally
);
});
test
(
'RenderObject markNeedsPaint while repaint boundary, and then updated to no longer be a repaint boundary with '
'calling markNeedsCompositingBitsUpdate 2'
,
()
{
final
ConditionalRepaintBoundary
childBox
=
ConditionalRepaintBoundary
(
isRepaintBoundary:
true
);
final
ConditionalRepaintBoundary
renderBox
=
ConditionalRepaintBoundary
(
child:
childBox
);
// Ignore old layer.
childBox
.
offsetLayerFactory
=
(
OffsetLayer
?
oldLayer
)
{
return
oldLayer
??
TestOffsetLayerA
();
};
layout
(
renderBox
,
phase:
EnginePhase
.
composite
);
expect
(
childBox
.
paintCount
,
1
);
expect
(
renderBox
.
paintCount
,
1
);
childBox
.
isRepaintBoundary
=
false
;
childBox
.
markNeedsCompositingBitsUpdate
();
childBox
.
markNeedsPaint
();
expect
(()
=>
pumpFrame
(
phase:
EnginePhase
.
composite
),
returnsNormally
);
});
test
(
'RenderObject markNeedsPaint while repaint boundary, and then updated to no longer be a repaint boundary with '
'calling markNeedsCompositingBitsUpdate 3'
,
()
{
final
ConditionalRepaintBoundary
childBox
=
ConditionalRepaintBoundary
(
isRepaintBoundary:
true
);
final
ConditionalRepaintBoundary
renderBox
=
ConditionalRepaintBoundary
(
child:
childBox
);
// Ignore old layer.
childBox
.
offsetLayerFactory
=
(
OffsetLayer
?
oldLayer
)
{
return
oldLayer
??
TestOffsetLayerA
();
};
layout
(
renderBox
,
phase:
EnginePhase
.
composite
);
expect
(
childBox
.
paintCount
,
1
);
expect
(
renderBox
.
paintCount
,
1
);
childBox
.
isRepaintBoundary
=
false
;
childBox
.
markNeedsCompositedLayerUpdate
();
childBox
.
markNeedsCompositingBitsUpdate
();
expect
(()
=>
pumpFrame
(
phase:
EnginePhase
.
composite
),
returnsNormally
);
});
}
class
_TestRectClipper
extends
CustomClipper
<
Rect
>
{
...
...
@@ -631,3 +803,38 @@ class _TestSemanticsUpdateRenderFractionalTranslation extends RenderFractionalTr
super
.
markNeedsSemanticsUpdate
();
}
}
class
ConditionalRepaintBoundary
extends
RenderProxyBox
{
ConditionalRepaintBoundary
({
this
.
isRepaintBoundary
=
false
,
RenderBox
?
child
})
:
super
(
child
);
@override
bool
isRepaintBoundary
=
false
;
OffsetLayer
Function
(
OffsetLayer
?)?
offsetLayerFactory
;
int
paintCount
=
0
;
@override
OffsetLayer
updateCompositedLayer
({
required
covariant
OffsetLayer
?
oldLayer
})
{
if
(
offsetLayerFactory
!=
null
)
{
return
offsetLayerFactory
!.
call
(
oldLayer
);
}
return
super
.
updateCompositedLayer
(
oldLayer:
oldLayer
);
}
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
paintCount
+=
1
;
super
.
paint
(
context
,
offset
);
}
}
class
TestOffsetLayerA
extends
OffsetLayer
{}
void
expectAssertionError
(
)
{
final
FlutterErrorDetails
errorDetails
=
TestRenderingFlutterBinding
.
instance
.
takeFlutterErrorDetails
()!;
final
bool
asserted
=
errorDetails
.
toString
().
contains
(
'Failed assertion'
);
if
(!
asserted
)
{
FlutterError
.
reportError
(
errorDetails
);
}
}
packages/flutter/test/widgets/animated_opacity_repaint_test.dart
0 → 100644
View file @
0cb5772e
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
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:
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
);
controller
.
stop
();
await
tester
.
pump
();
expect
(
RenderTestObject
.
paintCount
,
1
);
});
testWidgets
(
'RenderAnimatedOpacityMixin allows opacity layer to be disposed when animating to 0 opacity'
,
(
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:
1
,
end:
0
);
await
tester
.
pumpWidget
(
Container
(
color:
Colors
.
red
,
child:
FadeTransition
(
opacity:
controller
.
drive
(
opacityTween
),
child:
const
TestWidget
(),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
expect
(
tester
.
layers
,
contains
(
isA
<
OpacityLayer
>()));
controller
.
forward
();
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
seconds:
2
));
expect
(
RenderTestObject
.
paintCount
,
1
);
controller
.
stop
();
await
tester
.
pump
();
expect
(
tester
.
layers
,
isNot
(
contains
(
isA
<
OpacityLayer
>())));
});
}
class
TestWidget
extends
SingleChildRenderObjectWidget
{
const
TestWidget
({
super
.
key
,
super
.
child
});
@override
RenderObject
createRenderObject
(
BuildContext
context
)
{
return
RenderTestObject
();
}
}
class
RenderTestObject
extends
RenderProxyBox
{
static
int
paintCount
=
0
;
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
paintCount
+=
1
;
super
.
paint
(
context
,
offset
);
}
}
packages/flutter/test/widgets/opacity_repaint_test.dart
0 → 100644
View file @
0cb5772e
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
testWidgets
(
'RenderOpacity acts as a repaint boundary for changes above the widget when partially opaque'
,
(
WidgetTester
tester
)
async
{
RenderTestObject
.
paintCount
=
0
;
await
tester
.
pumpWidget
(
Container
(
color:
Colors
.
red
,
child:
const
Opacity
(
opacity:
0.5
,
child:
TestWidget
(),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
await
tester
.
pumpWidget
(
Container
(
color:
Colors
.
blue
,
child:
const
Opacity
(
opacity:
0.5
,
child:
TestWidget
(),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
});
testWidgets
(
'RenderOpacity acts as a repaint boundary for changes above the widget when fully opaque'
,
(
WidgetTester
tester
)
async
{
RenderTestObject
.
paintCount
=
0
;
await
tester
.
pumpWidget
(
Container
(
color:
Colors
.
red
,
child:
const
Opacity
(
opacity:
1
,
child:
TestWidget
(),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
await
tester
.
pumpWidget
(
Container
(
color:
Colors
.
blue
,
child:
const
Opacity
(
opacity:
1
,
child:
TestWidget
(),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
});
testWidgets
(
'RenderOpacity can update its opacity without repainting its child - partially opaque to partially opaque'
,
(
WidgetTester
tester
)
async
{
RenderTestObject
.
paintCount
=
0
;
await
tester
.
pumpWidget
(
Container
(
color:
Colors
.
red
,
child:
const
Opacity
(
opacity:
0.5
,
child:
TestWidget
(),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
await
tester
.
pumpWidget
(
Container
(
color:
Colors
.
blue
,
child:
const
Opacity
(
opacity:
0.9
,
child:
TestWidget
(),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
});
testWidgets
(
'RenderOpacity can update its opacity without repainting its child - partially opaque to fully opaque'
,
(
WidgetTester
tester
)
async
{
RenderTestObject
.
paintCount
=
0
;
await
tester
.
pumpWidget
(
Container
(
color:
Colors
.
red
,
child:
const
Opacity
(
opacity:
0.5
,
child:
TestWidget
(),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
await
tester
.
pumpWidget
(
Container
(
color:
Colors
.
blue
,
child:
const
Opacity
(
opacity:
1
,
child:
TestWidget
(),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
});
testWidgets
(
'RenderOpacity can update its opacity without repainting its child - fully opaque to partially opaque'
,
(
WidgetTester
tester
)
async
{
RenderTestObject
.
paintCount
=
0
;
await
tester
.
pumpWidget
(
Container
(
color:
Colors
.
red
,
child:
const
Opacity
(
opacity:
1
,
child:
TestWidget
(),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
await
tester
.
pumpWidget
(
Container
(
color:
Colors
.
blue
,
child:
const
Opacity
(
opacity:
0.5
,
child:
TestWidget
(),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
});
testWidgets
(
'RenderOpacity can update its opacity without repainting its child - fully opaque to fully transparent'
,
(
WidgetTester
tester
)
async
{
RenderTestObject
.
paintCount
=
0
;
await
tester
.
pumpWidget
(
Container
(
color:
Colors
.
red
,
child:
const
Opacity
(
opacity:
1
,
child:
TestWidget
(),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
await
tester
.
pumpWidget
(
Container
(
color:
Colors
.
blue
,
child:
const
Opacity
(
opacity:
0
,
child:
TestWidget
(),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
});
testWidgets
(
'RenderOpacity must paint child - fully transparent to partially opaque'
,
(
WidgetTester
tester
)
async
{
RenderTestObject
.
paintCount
=
0
;
await
tester
.
pumpWidget
(
Container
(
color:
Colors
.
red
,
child:
const
Opacity
(
opacity:
0
,
child:
TestWidget
(),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
0
);
await
tester
.
pumpWidget
(
Container
(
color:
Colors
.
blue
,
child:
const
Opacity
(
opacity:
0.5
,
child:
TestWidget
(),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
});
testWidgets
(
'RenderOpacity allows child to update without updating parent'
,
(
WidgetTester
tester
)
async
{
RenderTestObject
.
paintCount
=
0
;
await
tester
.
pumpWidget
(
TestWidget
(
child:
Opacity
(
opacity:
0.5
,
child:
Container
(
color:
Colors
.
red
,
),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
await
tester
.
pumpWidget
(
TestWidget
(
child:
Opacity
(
opacity:
0.5
,
child:
Container
(
color:
Colors
.
blue
,
),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
});
testWidgets
(
'RenderOpacity disposes of opacity layer when opacity is updated to 0'
,
(
WidgetTester
tester
)
async
{
RenderTestObject
.
paintCount
=
0
;
await
tester
.
pumpWidget
(
Container
(
color:
Colors
.
red
,
child:
const
Opacity
(
opacity:
0.5
,
child:
TestWidget
(),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
expect
(
tester
.
layers
,
contains
(
isA
<
OpacityLayer
>()));
await
tester
.
pumpWidget
(
Container
(
color:
Colors
.
blue
,
child:
const
Opacity
(
opacity:
0
,
child:
TestWidget
(),
),
)
);
expect
(
RenderTestObject
.
paintCount
,
1
);
expect
(
tester
.
layers
,
isNot
(
contains
(
isA
<
OpacityLayer
>())));
});
}
class
TestWidget
extends
SingleChildRenderObjectWidget
{
const
TestWidget
({
super
.
key
,
super
.
child
});
@override
RenderObject
createRenderObject
(
BuildContext
context
)
{
return
RenderTestObject
();
}
}
class
RenderTestObject
extends
RenderProxyBox
{
static
int
paintCount
=
0
;
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
paintCount
+=
1
;
super
.
paint
(
context
,
offset
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment