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
ca94bfdf
Commit
ca94bfdf
authored
May 10, 2018
by
Ian Hickson
Committed by
Todd Volkert
May 10, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Factor our common Paint-building code used with BoxShadow (#17363)
parent
57322195
Changes
23
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
349 additions
and
34 deletions
+349
-34
goldens.version
bin/internal/goldens.version
+1
-1
painting.dart
packages/flutter/lib/painting.dart
+1
-0
mergeable_material.dart
packages/flutter/lib/src/material/mergeable_material.dart
+1
-3
box_decoration.dart
packages/flutter/lib/src/painting/box_decoration.dart
+1
-3
box_shadow.dart
packages/flutter/lib/src/painting/box_shadow.dart
+24
-1
debug.dart
packages/flutter/lib/src/painting/debug.dart
+33
-0
shape_decoration.dart
packages/flutter/lib/src/painting/shape_decoration.dart
+2
-6
layer.dart
packages/flutter/lib/src/rendering/layer.dart
+6
-0
proxy_box.dart
packages/flutter/lib/src/rendering/proxy_box.dart
+44
-13
banner.dart
packages/flutter/lib/src/widgets/banner.dart
+7
-3
basic.dart
packages/flutter/lib/src/widgets/basic.dart
+10
-0
mergeable_material_test.dart
packages/flutter/test/material/mergeable_material_test.dart
+2
-0
outline_button_test.dart
packages/flutter/test/material/outline_button_test.dart
+2
-0
mock_canvas.dart
packages/flutter/test/rendering/mock_canvas.dart
+4
-0
service_extensions_test_file
packages/flutter/test/service_extensions_test_file
+0
-0
banner_test.dart
packages/flutter/test/widgets/banner_test.dart
+4
-0
nested_scroll_view_test.dart
packages/flutter/test/widgets/nested_scroll_view_test.dart
+2
-0
physical_model_test.dart
packages/flutter/test/widgets/physical_model_test.dart
+2
-0
shadow_test.dart
packages/flutter/test/widgets/shadow_test.dart
+136
-0
flutter_goldens.dart
packages/flutter_goldens/lib/flutter_goldens.dart
+34
-0
binding.dart
packages/flutter_test/lib/src/binding.dart
+17
-0
goldens.dart
packages/flutter_test/lib/src/goldens.dart
+14
-3
widget_tester.dart
packages/flutter_test/lib/src/widget_tester.dart
+2
-1
No files found.
bin/internal/goldens.version
View file @
ca94bfdf
0ea80e6a0147f1a3a59ff57f460a3f038a0d2748
e3f3b6766b18e2461c89a371be6e30045d8e404f
packages/flutter/lib/painting.dart
View file @
ca94bfdf
...
...
@@ -29,6 +29,7 @@ export 'src/painting/box_fit.dart';
export
'src/painting/box_shadow.dart'
;
export
'src/painting/circle_border.dart'
;
export
'src/painting/colors.dart'
;
export
'src/painting/debug.dart'
;
export
'src/painting/decoration.dart'
;
export
'src/painting/decoration_image.dart'
;
export
'src/painting/edge_insets.dart'
;
...
...
packages/flutter/lib/src/material/mergeable_material.dart
View file @
ca94bfdf
...
...
@@ -686,9 +686,7 @@ class _RenderMergeableMaterialListBody extends RenderListBody {
void
_paintShadows
(
Canvas
canvas
,
Rect
rect
)
{
for
(
BoxShadow
boxShadow
in
boxShadows
)
{
final
Paint
paint
=
new
Paint
()
..
color
=
boxShadow
.
color
..
maskFilter
=
new
MaskFilter
.
blur
(
BlurStyle
.
normal
,
boxShadow
.
blurSigma
);
final
Paint
paint
=
boxShadow
.
toPaint
();
// TODO(dragostis): Right now, we are only interpolating the border radii
// of the visible Material slices, not the shadows; they are not getting
// interpolated and always have the same rounded radii. Once shadow
...
...
packages/flutter/lib/src/painting/box_decoration.dart
View file @
ca94bfdf
...
...
@@ -366,9 +366,7 @@ class _BoxDecorationPainter extends BoxPainter {
if
(
_decoration
.
boxShadow
==
null
)
return
;
for
(
BoxShadow
boxShadow
in
_decoration
.
boxShadow
)
{
final
Paint
paint
=
new
Paint
()
..
color
=
boxShadow
.
color
..
maskFilter
=
new
MaskFilter
.
blur
(
BlurStyle
.
normal
,
boxShadow
.
blurSigma
);
final
Paint
paint
=
boxShadow
.
toPaint
();
final
Rect
bounds
=
rect
.
shift
(
boxShadow
.
offset
).
inflate
(
boxShadow
.
spreadRadius
);
_paintBox
(
canvas
,
bounds
,
paint
,
textDirection
);
}
...
...
packages/flutter/lib/src/painting/box_shadow.dart
View file @
ca94bfdf
...
...
@@ -8,13 +8,18 @@ import 'dart:ui' as ui show lerpDouble;
import
'package:flutter/foundation.dart'
;
import
'basic_types.dart'
;
import
'debug.dart'
;
/// A shadow cast by a box.
///
///
BoxShadow
can cast non-rectangular shadows if the box is non-rectangular
///
[BoxShadow]
can cast non-rectangular shadows if the box is non-rectangular
/// (e.g., has a border radius or a circular shape).
///
/// This class is similar to CSS box-shadow.
///
/// See also:
///
/// * [Canvas.drawShadow], which is a more efficient way to draw shadows.
@immutable
class
BoxShadow
{
/// Creates a box shadow.
...
...
@@ -55,6 +60,24 @@ class BoxShadow {
/// See the sigma argument to [MaskFilter.blur].
double
get
blurSigma
=>
convertRadiusToSigma
(
blurRadius
);
/// Create the [Paint] object that corresponds to this shadow description.
///
/// The [offset] and [spreadRadius] are not represented in the [Paint] object.
/// To honor those as well, the shape should be inflated by [spreadRadius] pixels
/// in every direction and then translated by [offset] before being filled using
/// this [Paint].
Paint
toPaint
()
{
final
Paint
result
=
new
Paint
()
..
color
=
color
..
maskFilter
=
new
MaskFilter
.
blur
(
BlurStyle
.
normal
,
blurSigma
);
assert
(()
{
if
(
debugDisableShadows
)
result
.
maskFilter
=
null
;
return
true
;
}());
return
result
;
}
/// Returns a new box shadow with its offset, blurRadius, and spreadRadius scaled by the given factor.
BoxShadow
scale
(
double
factor
)
{
return
new
BoxShadow
(
...
...
packages/flutter/lib/src/painting/debug.dart
0 → 100644
View file @
ca94bfdf
// Copyright 2018 The Chromium 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/foundation.dart'
;
/// Whether to replace all shadows with solid color blocks.
///
/// This is useful when writing golden file tests (see [matchesGoldenFile]) since
/// the rendering of shadows is not guaranteed to be pixel-for-pixel identical from
/// version to version (or even from run to run).
bool
debugDisableShadows
=
false
;
/// Returns true if none of the painting library debug variables have been changed.
///
/// This function is used by the test framework to ensure that debug variables
/// haven't been inadvertently changed.
///
/// See <https://docs.flutter.io/flutter/rendering/painting-library.html> for
/// a complete list.
///
/// The `debugDisableShadowsOverride` argument can be provided to override
/// the expected value for [debugDisableShadows]. (This exists because the
/// test framework itself overrides this value in some cases.)
bool
debugAssertAllPaintingVarsUnset
(
String
reason
,
{
bool
debugDisableShadowsOverride:
false
})
{
assert
(()
{
if
(
debugDisableShadows
!=
debugDisableShadowsOverride
)
{
throw
new
FlutterError
(
reason
);
}
return
true
;
}());
return
true
;
}
packages/flutter/lib/src/painting/shape_decoration.dart
View file @
ca94bfdf
...
...
@@ -328,12 +328,8 @@ class _ShapeDecorationPainter extends BoxPainter {
_shadowCount
=
_decoration
.
shadows
.
length
;
_shadowPaths
=
new
List
<
Path
>(
_shadowCount
);
_shadowPaints
=
new
List
<
Paint
>(
_shadowCount
);
for
(
int
index
=
0
;
index
<
_shadowCount
;
index
+=
1
)
{
final
BoxShadow
shadow
=
_decoration
.
shadows
[
index
];
_shadowPaints
[
index
]
=
new
Paint
()
..
color
=
shadow
.
color
..
maskFilter
=
new
MaskFilter
.
blur
(
BlurStyle
.
normal
,
shadow
.
blurSigma
);
}
for
(
int
index
=
0
;
index
<
_shadowCount
;
index
+=
1
)
_shadowPaints
[
index
]
=
_decoration
.
shadows
[
index
].
toPaint
();
}
for
(
int
index
=
0
;
index
<
_shadowCount
;
index
+=
1
)
{
final
BoxShadow
shadow
=
_decoration
.
shadows
[
index
];
...
...
packages/flutter/lib/src/rendering/layer.dart
View file @
ca94bfdf
...
...
@@ -818,6 +818,12 @@ class PhysicalModelLayer extends ContainerLayer {
///
/// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]).
///
/// In tests, the [debugDisableShadows] flag is set to true by default.
/// Several widgets and render objects force all elevations to zero when this
/// flag is set. For this reason, this property will often be set to zero in
/// tests even if the layer should be raised. To verify the actual value,
/// consider setting [debugDisableShadows] to false in your test.
double
elevation
;
/// The background color.
...
...
packages/flutter/lib/src/rendering/proxy_box.dart
View file @
ca94bfdf
...
...
@@ -1449,6 +1449,9 @@ abstract class _RenderPhysicalModelBase<T> extends _RenderCustomClip<T> {
super
(
child:
child
,
clipper:
clipper
);
/// The z-coordinate at which to place this material.
///
/// If [debugDisableShadows] is set, this value is ignored and no shadow is
/// drawn (an outline is rendered instead).
double
get
elevation
=>
_elevation
;
double
_elevation
;
set
elevation
(
double
value
)
{
...
...
@@ -1591,20 +1594,34 @@ class RenderPhysicalModel extends _RenderPhysicalModelBase<RRect> {
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
child
!=
null
)
{
_updateClip
();
final
RRect
offsetClipRRect
=
_clip
.
shift
(
offset
);
final
Rect
offsetBounds
=
offsetClipRRect
.
outerRect
;
final
Path
offsetClipPath
=
new
Path
()..
addRRect
(
offsetClipRRect
);
final
RRect
offsetRRect
=
_clip
.
shift
(
offset
);
final
Rect
offsetBounds
=
offsetRRect
.
outerRect
;
final
Path
offsetRRectAsPath
=
new
Path
()..
addRRect
(
offsetRRect
);
bool
paintShadows
=
true
;
assert
(()
{
if
(
debugDisableShadows
)
{
context
.
canvas
.
drawRRect
(
offsetRRect
,
new
Paint
()
..
color
=
shadowColor
..
style
=
PaintingStyle
.
stroke
..
strokeWidth
=
elevation
*
2.0
,
);
paintShadows
=
false
;
}
return
true
;
}());
if
(
needsCompositing
)
{
final
PhysicalModelLayer
physicalModel
=
new
PhysicalModelLayer
(
clipPath:
offset
Clip
Path
,
elevation:
elevation
,
clipPath:
offset
RRectAs
Path
,
elevation:
paintShadows
?
elevation
:
0.0
,
color:
color
,
shadowColor:
shadowColor
,
);
context
.
pushLayer
(
physicalModel
,
super
.
paint
,
offset
,
childPaintBounds:
offsetBounds
);
}
else
{
final
Canvas
canvas
=
context
.
canvas
;
if
(
elevation
!=
0.0
)
{
if
(
elevation
!=
0.0
&&
paintShadows
)
{
// The drawShadow call doesn't add the region of the shadow to the
// picture's bounds, so we draw a hardcoded amount of extra space to
// account for the maximum potential area of the shadow.
...
...
@@ -1614,25 +1631,25 @@ class RenderPhysicalModel extends _RenderPhysicalModelBase<RRect> {
_RenderPhysicalModelBase
.
_transparentPaint
,
);
canvas
.
drawShadow
(
offset
Clip
Path
,
offset
RRectAs
Path
,
shadowColor
,
elevation
,
color
.
alpha
!=
0xFF
,
);
}
canvas
.
drawRRect
(
offset
Clip
RRect
,
new
Paint
()..
color
=
color
);
canvas
.
drawRRect
(
offsetRRect
,
new
Paint
()..
color
=
color
);
canvas
.
save
();
canvas
.
clipRRect
(
offset
Clip
RRect
);
canvas
.
clipRRect
(
offsetRRect
);
// We only use a new layer for non-rectangular clips, on the basis that
// rectangular clips won't need antialiasing. This is not really
// correct, because if we're e.g. rotated, rectangles will also be
// aliased. Unfortunately, it's too much of a performance win to err on
// the side of correctness here.
// TODO(ianh): Find a better solution.
if
(!
offset
Clip
RRect
.
isRect
)
if
(!
offsetRRect
.
isRect
)
canvas
.
saveLayer
(
offsetBounds
,
_RenderPhysicalModelBase
.
_defaultPaint
);
super
.
paint
(
context
,
offset
);
if
(!
offset
Clip
RRect
.
isRect
)
if
(!
offsetRRect
.
isRect
)
canvas
.
restore
();
canvas
.
restore
();
assert
(
context
.
canvas
==
canvas
,
'canvas changed even though needsCompositing was false'
);
...
...
@@ -1701,17 +1718,31 @@ class RenderPhysicalShape extends _RenderPhysicalModelBase<Path> {
_updateClip
();
final
Rect
offsetBounds
=
offset
&
size
;
final
Path
offsetPath
=
_clip
.
shift
(
offset
);
bool
paintShadows
=
true
;
assert
(()
{
if
(
debugDisableShadows
)
{
context
.
canvas
.
drawPath
(
offsetPath
,
new
Paint
()
..
color
=
shadowColor
..
style
=
PaintingStyle
.
stroke
..
strokeWidth
=
elevation
*
2.0
,
);
paintShadows
=
false
;
}
return
true
;
}());
if
(
needsCompositing
)
{
final
PhysicalModelLayer
physicalModel
=
new
PhysicalModelLayer
(
clipPath:
offsetPath
,
elevation:
elevation
,
elevation:
paintShadows
?
elevation
:
0.0
,
color:
color
,
shadowColor:
shadowColor
,
);
context
.
pushLayer
(
physicalModel
,
super
.
paint
,
offset
,
childPaintBounds:
offsetBounds
);
}
else
{
final
Canvas
canvas
=
context
.
canvas
;
if
(
elevation
!=
0.0
)
{
if
(
elevation
!=
0.0
&&
paintShadows
)
{
// The drawShadow call doesn't add the region of the shadow to the
// picture's bounds, so we draw a hardcoded amount of extra space to
// account for the maximum potential area of the shadow.
...
...
packages/flutter/lib/src/widgets/banner.dart
View file @
ca94bfdf
...
...
@@ -5,6 +5,7 @@
import
'dart:math'
as
math
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/painting.dart'
;
import
'basic.dart'
;
import
'debug.dart'
;
...
...
@@ -107,15 +108,18 @@ class BannerPainter extends CustomPainter {
/// Defaults to bold, white text.
final
TextStyle
textStyle
;
static
const
BoxShadow
_shadow
=
const
BoxShadow
(
color:
const
Color
(
0x7F000000
),
blurRadius:
4.0
,
);
bool
_prepared
=
false
;
TextPainter
_textPainter
;
Paint
_paintShadow
;
Paint
_paintBanner
;
void
_prepare
()
{
_paintShadow
=
new
Paint
()
..
color
=
const
Color
(
0x7F000000
)
..
maskFilter
=
const
MaskFilter
.
blur
(
BlurStyle
.
normal
,
4.0
);
_paintShadow
=
_shadow
.
toPaint
();
_paintBanner
=
new
Paint
()
..
color
=
color
;
_textPainter
=
new
TextPainter
(
...
...
packages/flutter/lib/src/widgets/basic.dart
View file @
ca94bfdf
...
...
@@ -52,6 +52,7 @@ export 'package:flutter/rendering.dart' show
RelativeRect
,
SemanticsBuilderCallback
,
ShaderCallback
,
ShapeBorderClipper
,
SingleChildLayoutDelegate
,
StackFit
,
TextOverflow
,
...
...
@@ -731,6 +732,11 @@ class PhysicalModel extends SingleChildRenderObjectWidget {
///
/// [PhysicalModel] does the same but only supports shapes that can be expressed
/// as rectangles with rounded corners.
///
/// See also:
///
/// * [ShapeBorderClipper], which converts a [ShapeBorder] to a [CustomerClipper], as
/// needed by this widget.
class
PhysicalShape
extends
SingleChildRenderObjectWidget
{
/// Creates a physical model with an arbitrary shape clip.
///
...
...
@@ -751,6 +757,10 @@ class PhysicalShape extends SingleChildRenderObjectWidget {
super
(
key:
key
,
child:
child
);
/// Determines which clip to use.
///
/// If the path in question is expressed as a [ShapeBorder] subclass,
/// consider using the [ShapeBorderClipper] delegate class to adapt the
/// shape for use with this widget.
final
CustomClipper
<
Path
>
clipper
;
/// The z-coordinate at which to place this physical object.
...
...
packages/flutter/test/material/mergeable_material_test.dart
View file @
ca94bfdf
...
...
@@ -198,6 +198,7 @@ void main() {
});
testWidgets
(
'MergeableMaterial paints shadows'
,
(
WidgetTester
tester
)
async
{
debugDisableShadows
=
false
;
await
tester
.
pumpWidget
(
new
MaterialApp
(
home:
new
Scaffold
(
...
...
@@ -226,6 +227,7 @@ void main() {
find
.
byType
(
MergeableMaterial
),
paints
..
rrect
(
rrect:
rrect
,
color:
boxShadow
.
color
,
hasMaskFilter:
true
),
);
debugDisableShadows
=
true
;
});
testWidgets
(
'MergeableMaterial merge gap'
,
(
WidgetTester
tester
)
async
{
...
...
packages/flutter/test/material/outline_button_test.dart
View file @
ca94bfdf
...
...
@@ -45,6 +45,7 @@ void main() {
testWidgets
(
'Outline shape and border overrides'
,
(
WidgetTester
tester
)
async
{
debugDisableShadows
=
false
;
const
Color
fillColor
=
const
Color
(
0xFF00FF00
);
const
Color
borderColor
=
const
Color
(
0xFFFF0000
);
const
Color
highlightedBorderColor
=
const
Color
(
0xFF0000FF
);
...
...
@@ -111,6 +112,7 @@ void main() {
..
clipPath
(
pathMatcher:
coversSameAreaAs
(
clipPath
,
areaToCompare:
clipRect
.
inflate
(
10.0
)))
..
path
(
color:
borderColor
,
strokeWidth:
borderWidth
)
);
debugDisableShadows
=
true
;
});
...
...
packages/flutter/test/rendering/mock_canvas.dart
View file @
ca94bfdf
...
...
@@ -325,6 +325,10 @@ abstract class PaintPattern {
/// are compared to the actual [Canvas.drawShadow] call's `paint` argument,
/// and any mismatches result in failure.
///
/// In tests, shadows from framework features such as [BoxShadow] or
/// [Material] are disabled by default, and thus this predicate would not
/// match. The [debugDisableShadows] flag controls this.
///
/// To introspect the Path object (as it stands after the painting has
/// completed), the `includes` and `excludes` arguments can be provided to
/// specify points that should be considered inside or outside the path
...
...
packages/flutter/test/service_extensions_test_file
deleted
100644 → 0
View file @
57322195
File deleted
packages/flutter/test/widgets/banner_test.dart
View file @
ca94bfdf
...
...
@@ -248,6 +248,7 @@ void main() {
});
testWidgets
(
'Banner widget'
,
(
WidgetTester
tester
)
async
{
debugDisableShadows
=
false
;
await
tester
.
pumpWidget
(
const
Directionality
(
textDirection:
TextDirection
.
ltr
,
...
...
@@ -263,9 +264,11 @@ void main() {
..
paragraph
(
offset:
const
Offset
(-
40.0
,
29.0
))
..
restore
()
);
debugDisableShadows
=
true
;
});
testWidgets
(
'Banner widget in MaterialApp'
,
(
WidgetTester
tester
)
async
{
debugDisableShadows
=
false
;
await
tester
.
pumpWidget
(
new
MaterialApp
(
home:
const
Placeholder
()));
expect
(
find
.
byType
(
CheckedModeBanner
),
paints
..
save
()
...
...
@@ -276,5 +279,6 @@ void main() {
..
paragraph
(
offset:
const
Offset
(-
40.0
,
29.0
))
..
restore
()
);
debugDisableShadows
=
true
;
});
}
packages/flutter/test/widgets/nested_scroll_view_test.dart
View file @
ca94bfdf
...
...
@@ -344,6 +344,7 @@ void main() {
});
testWidgets
(
'NestedScrollView and internal scrolling'
,
(
WidgetTester
tester
)
async
{
debugDisableShadows
=
false
;
const
List
<
String
>
_tabs
=
const
<
String
>[
'Hello'
,
'World'
];
int
buildCount
=
0
;
await
tester
.
pumpWidget
(
...
...
@@ -565,6 +566,7 @@ void main() {
await
tester
.
pumpAndSettle
();
expect
(
buildCount
,
expectedBuildCount
);
expect
(
find
.
byType
(
NestedScrollView
),
isNot
(
paints
..
shadow
()));
debugDisableShadows
=
true
;
});
testWidgets
(
'NestedScrollView and iOS bouncing'
,
(
WidgetTester
tester
)
async
{
...
...
packages/flutter/test/widgets/physical_model_test.dart
View file @
ca94bfdf
...
...
@@ -5,6 +5,7 @@ import 'package:flutter_test/flutter_test.dart';
void
main
(
)
{
testWidgets
(
'PhysicalModel - creates a physical model layer when it needs compositing'
,
(
WidgetTester
tester
)
async
{
debugDisableShadows
=
false
;
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
PhysicalModel
(
...
...
@@ -25,5 +26,6 @@ void main() {
expect
(
physicalModelLayer
.
shadowColor
,
Colors
.
red
);
expect
(
physicalModelLayer
.
color
,
Colors
.
grey
);
expect
(
physicalModelLayer
.
elevation
,
1.0
);
debugDisableShadows
=
true
;
});
}
packages/flutter/test/widgets/shadow_test.dart
0 → 100644
View file @
ca94bfdf
// Copyright 2018 The Chromium 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
'dart:io'
show
Platform
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/material.dart'
;
void
main
(
)
{
testWidgets
(
'Shadows on BoxDecoration'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
Center
(
child:
new
RepaintBoundary
(
child:
new
Container
(
margin:
const
EdgeInsets
.
all
(
50.0
),
decoration:
new
BoxDecoration
(
boxShadow:
kElevationToShadow
[
9
],
),
height:
100.0
,
width:
100.0
,
),
),
),
);
await
expectLater
(
find
.
byType
(
Container
),
matchesGoldenFile
(
'shadow.BoxDecoration.disabled.png'
),
);
debugDisableShadows
=
false
;
tester
.
binding
.
reassembleApplication
();
await
tester
.
pump
();
if
(
Platform
.
isLinux
)
{
// TODO(ianh): use the skip argument instead once that doesn't hang, https://github.com/dart-lang/test/issues/830
await
expectLater
(
find
.
byType
(
Container
),
matchesGoldenFile
(
'shadow.BoxDecoration.enabled.png'
),
);
// shadows render differently on different platforms
}
debugDisableShadows
=
true
;
});
testWidgets
(
'Shadows on ShapeDecoration'
,
(
WidgetTester
tester
)
async
{
debugDisableShadows
=
false
;
Widget
build
(
int
elevation
)
{
return
new
Center
(
child:
new
RepaintBoundary
(
child:
new
Container
(
margin:
const
EdgeInsets
.
all
(
150.0
),
decoration:
new
ShapeDecoration
(
shape:
new
BeveledRectangleBorder
(
borderRadius:
new
BorderRadius
.
circular
(
20.0
)),
shadows:
kElevationToShadow
[
elevation
],
),
height:
100.0
,
width:
100.0
,
),
),
);
}
for
(
int
elevation
in
kElevationToShadow
.
keys
)
{
await
tester
.
pumpWidget
(
build
(
elevation
));
await
expectLater
(
find
.
byType
(
Container
),
matchesGoldenFile
(
'shadow.ShapeDecoration.
$elevation
.png'
),
);
}
debugDisableShadows
=
true
;
},
skip:
!
Platform
.
isLinux
);
// shadows render differently on different platforms
testWidgets
(
'Shadows with PhysicalLayer'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
Center
(
child:
new
RepaintBoundary
(
child:
new
Container
(
margin:
const
EdgeInsets
.
all
(
150.0
),
color:
Colors
.
yellow
[
200
],
child:
new
PhysicalModel
(
elevation:
9.0
,
color:
Colors
.
blue
[
900
],
child:
const
SizedBox
(
height:
100.0
,
width:
100.0
,
),
),
),
),
),
);
await
expectLater
(
find
.
byType
(
Container
),
matchesGoldenFile
(
'shadow.PhysicalModel.disabled.png'
),
);
debugDisableShadows
=
false
;
tester
.
binding
.
reassembleApplication
();
await
tester
.
pump
();
if
(
Platform
.
isLinux
)
{
// TODO(ianh): use the skip argument instead once that doesn't hang, https://github.com/dart-lang/test/issues/830
await
expectLater
(
find
.
byType
(
Container
),
matchesGoldenFile
(
'shadow.PhysicalModel.enabled.png'
),
);
// shadows render differently on different platforms
}
debugDisableShadows
=
true
;
});
testWidgets
(
'Shadows with PhysicalShape'
,
(
WidgetTester
tester
)
async
{
debugDisableShadows
=
false
;
Widget
build
(
double
elevation
)
{
return
new
Center
(
child:
new
RepaintBoundary
(
child:
new
Container
(
padding:
const
EdgeInsets
.
all
(
150.0
),
color:
Colors
.
yellow
[
200
],
child:
new
PhysicalShape
(
color:
Colors
.
green
[
900
],
clipper:
new
ShapeBorderClipper
(
shape:
new
BeveledRectangleBorder
(
borderRadius:
new
BorderRadius
.
circular
(
20.0
))),
elevation:
elevation
,
child:
const
SizedBox
(
height:
100.0
,
width:
100.0
,
),
),
),
),
);
}
for
(
int
elevation
in
kElevationToShadow
.
keys
)
{
await
tester
.
pumpWidget
(
build
(
elevation
.
toDouble
()));
await
expectLater
(
find
.
byType
(
Container
),
matchesGoldenFile
(
'shadow.PhysicalShape.
$elevation
.png'
),
);
}
debugDisableShadows
=
true
;
},
skip:
!
Platform
.
isLinux
);
// shadows render differently on different platforms
}
packages/flutter_goldens/lib/flutter_goldens.dart
View file @
ca94bfdf
...
...
@@ -14,6 +14,10 @@ import 'package:meta/meta.dart';
import
'package:platform/platform.dart'
;
import
'package:process/process.dart'
;
// If you are here trying to figure out how to use golden files in the Flutter
// repo itself, consider reading this wiki page:
// https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package%3Aflutter
const
String
_kFlutterRootKey
=
'FLUTTER_ROOT'
;
/// Main method that can be used in a `flutter_test_config.dart` file to set
...
...
@@ -105,20 +109,42 @@ class FlutterGoldenFileComparator implements GoldenFileComparator {
/// repository.
@visibleForTesting
class
GoldensClient
{
/// Create a handle to a local clone of the goldens repository.
GoldensClient
({
this
.
fs
:
const
LocalFileSystem
(),
this
.
platform
:
const
LocalPlatform
(),
this
.
process
:
const
LocalProcessManager
(),
});
/// The file system to use for storing the local clone of the repository.
///
/// This is useful in tests, where a local file system (the default) can
/// be replaced by a memory file system.
final
FileSystem
fs
;
/// A wrapper for the [dart:io.Platform] API.
///
/// This is useful in tests, where the system platform (the default) can
/// be replaced by a mock platform instance.
final
Platform
platform
;
/// A controller for launching subprocesses.
///
/// This is useful in tests, where the real process manager (the default)
/// can be replaced by a mock process manager that doesn't really create
/// subprocesses.
final
ProcessManager
process
;
RandomAccessFile
_lock
;
/// The local [Directory] where the Flutter repository is hosted.
///
/// Uses the [fs] file system.
Directory
get
flutterRoot
=>
fs
.
directory
(
platform
.
environment
[
_kFlutterRootKey
]);
/// The local [Directory] where the goldens repository is hosted.
///
/// Uses the [fs] file system.
Directory
get
repositoryRoot
=>
flutterRoot
.
childDirectory
(
fs
.
path
.
join
(
'bin'
,
'cache'
,
'pkg'
,
'goldens'
));
/// Prepares the local clone of the `flutter/goldens` repository for golden
...
...
@@ -222,9 +248,17 @@ class GoldensClient {
/// Exception that signals a process' exit with a non-zero exit code.
class
NonZeroExitCode
implements
Exception
{
/// Create an exception that represents a non-zero exit code.
///
/// The first argument must be non-zero.
const
NonZeroExitCode
(
this
.
exitCode
,
this
.
stderr
)
:
assert
(
exitCode
!=
0
);
/// The code that the process will signal to th eoperating system.
///
/// By definiton, this is not zero.
final
int
exitCode
;
/// The message to show on standard error.
final
String
stderr
;
@override
...
...
packages/flutter_test/lib/src/binding.dart
View file @
ca94bfdf
...
...
@@ -96,6 +96,7 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
/// [debugPrintOverride], which can be overridden by subclasses.
TestWidgetsFlutterBinding
()
{
debugPrint
=
debugPrintOverride
;
debugDisableShadows
=
disableShadows
;
debugCheckIntrinsicSizes
=
checkIntrinsicSizes
;
}
...
...
@@ -108,6 +109,15 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
@protected
DebugPrintCallback
get
debugPrintOverride
=>
debugPrint
;
/// The value to set [debugDisableShadows] to while tests are running.
///
/// This can be used to reduce the likelihood of golden file tests being
/// flaky, because shadow rendering is not always deterministic. The
/// [AutomatedTestWidgetsFlutterBinding] sets this to true, so that all tests
/// always run with shadows disabled.
@protected
bool
get
disableShadows
=>
false
;
/// The value to set [debugCheckIntrinsicSizes] to while tests are running.
///
/// This can be used to enable additional checks. For example,
...
...
@@ -525,6 +535,10 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
assert
(
debugAssertAllGesturesVarsUnset
(
'The value of a gestures debug variable was changed by the test.'
,
));
assert
(
debugAssertAllPaintingVarsUnset
(
'The value of a painting debug variable was changed by the test.'
,
debugDisableShadowsOverride:
disableShadows
,
));
assert
(
debugAssertAllRenderVarsUnset
(
'The value of a rendering debug variable was changed by the test.'
,
debugCheckIntrinsicSizesOverride:
checkIntrinsicSizes
,
...
...
@@ -588,6 +602,9 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
@override
DebugPrintCallback
get
debugPrintOverride
=>
debugPrintSynchronously
;
@override
bool
get
disableShadows
=>
true
;
@override
bool
get
checkIntrinsicSizes
=>
true
;
...
...
packages/flutter_test/lib/src/goldens.dart
View file @
ca94bfdf
...
...
@@ -51,11 +51,19 @@ abstract class GoldenFileComparator {
///
/// This comparator is used as the backend for [matchesGoldenFile].
///
/// The default comparator, [LocalFileComparator], will treat the golden key as
/// When using `flutter test`, a comparator implemented by [LocalFileComparator]
/// is used if no other comparator is specified. It treats the golden key as
/// a relative path from the test file's directory. It will then load the
/// golden file's bytes from disk and perform a byte-for-byte comparison of the
/// encoded PNGs, returning true only if there's an exact match.
///
/// When using `flutter test --update-goldens`, the [LocalFileComparator]
/// updates the files on disk to match the rendering.
///
/// When using `flutter run`, the default comparator (null) is used. It prints
/// a message to the console but otherwise does nothing. This allows tests to
/// be developed visually on a real device.
///
/// Callers may choose to override the default comparator by setting this to a
/// custom comparator during test set-up (or using directory-level test
/// configuration). For example, some projects may wish to install a more
...
...
@@ -119,7 +127,7 @@ class _UninitializedComparator implements GoldenFileComparator {
}
}
/// The default [GoldenFileComparator] implementation.
/// The default [GoldenFileComparator] implementation
for `flutter test`
.
///
/// This comparator loads golden files from the local file system, treating the
/// golden key as a relative path from the test file's directory.
...
...
@@ -128,13 +136,16 @@ class _UninitializedComparator implements GoldenFileComparator {
/// comparison of the encoded PNGs, returning true only if there's an exact
/// match. This means it will fail the test if two PNGs represent the same
/// pixels but are encoded differently.
///
/// When using `flutter test --update-goldens`, [LocalFileComparator]
/// updates the files on disk to match the rendering.
class
LocalFileComparator
implements
GoldenFileComparator
{
/// Creates a new [LocalFileComparator] for the specified [testFile].
///
/// Golden file keys will be interpreted as file paths relative to the
/// directory in which [testFile] resides.
///
/// The [testFile] UR
I
must represent a file.
/// The [testFile] UR
L
must represent a file.
LocalFileComparator
(
Uri
testFile
,
{
path
.
Style
pathStyle
})
:
basedir
=
_getBasedir
(
testFile
,
pathStyle
),
_path
=
_getPath
(
pathStyle
);
...
...
packages/flutter_test/lib/src/widget_tester.dart
View file @
ca94bfdf
...
...
@@ -177,7 +177,8 @@ Future<void> expectLater(dynamic actual, dynamic matcher, {
// We can't wrap the delegate in a guard, or we'll hit async barriers in
// [TestWidgetsFlutterBinding] while we're waiting for the matcher to complete
TestAsyncUtils
.
guardSync
();
return
test_package
.
expectLater
(
actual
,
matcher
,
reason:
reason
,
skip:
skip
);
return
test_package
.
expectLater
(
actual
,
matcher
,
reason:
reason
,
skip:
skip
)
.
then
<
void
>((
dynamic
value
)
=>
null
);
}
/// Class that programmatically interacts with widgets and the test environment.
...
...
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