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
53194ed4
Commit
53194ed4
authored
Oct 02, 2017
by
Ian Hickson
Committed by
GitHub
Oct 02, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ShapeBorder (#12335)
This class lays the groundwork for RTL borders.
parent
437e4c08
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
451 additions
and
18 deletions
+451
-18
borders.dart
packages/flutter/lib/src/painting/borders.dart
+367
-7
border_test.dart
packages/flutter/test/painting/border_test.dart
+11
-11
shape_border_test.dart
packages/flutter/test/painting/shape_border_test.dart
+73
-0
No files found.
packages/flutter/lib/src/painting/borders.dart
View file @
53194ed4
...
...
@@ -259,6 +259,318 @@ class BorderSide {
String
toString
()
=>
'
$runtimeType
(
$color
,
${width.toStringAsFixed(1)}
,
$style
)'
;
}
/// Base class for shape outlines.
///
/// This class handles how to add multiple borders together.
@immutable
abstract
class
ShapeBorder
{
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const
ShapeBorder
();
/// The widths of the sides of this border represented as an [EdgeInsets].
///
/// Specifically, this is the amount by which a rectangle should be inset so
/// as to avoid painting over any important part of the border. It is the
/// amount by which additional borders will be inset before they are drawn.
///
/// This can be used, for example, with a [Padding] widget to inset a box by
/// the size of these borders.
///
/// Shapes that have a fixed ratio regardless of the area on which they are
/// painted, or that change their rendering based on the size they are given
/// when painting (for instance [CircleBorder]), will not return valid
/// [dimensions] information because they cannot know their eventual size when
/// computing their [dimensions].
EdgeInsetsGeometry
get
dimensions
;
/// Attempts to create a new object that represents the amalgamation of [this]
/// border and the `other` border.
///
/// If the type of the other border isn't known, or the given instance cannot
/// be reasonably added to this instance, then this should return null.
///
/// This method is used by the [operator +] implementation.
///
/// The `reversed` argument is true if this object was the right operand of
/// the `+` operator, and false if it was the left operand.
@protected
ShapeBorder
add
(
ShapeBorder
other
,
{
bool
reversed:
false
})
=>
null
;
/// Creates a new border consisting of the two borders on either side of the
/// operator.
///
/// If the borders belong to classes that know how to add themselves, then
/// this results in a new border that represents the intelligent addition of
/// those two borders (see [add]). Otherwise, an object is returned that
/// merely paints the two borders sequentially, with the left hand operand on
/// the inside and the right hand operand on the outside.
ShapeBorder
operator
+(
ShapeBorder
other
)
{
return
add
(
other
)
??
other
.
add
(
this
,
reversed:
true
)
??
new
_CompoundBorder
(<
ShapeBorder
>[
other
,
this
]);
}
/// Creates a new border with the widths of this border multiplied by `t`.
ShapeBorder
scale
(
double
t
);
/// Linearly interpolates from `a` to [this].
///
/// When implementing this method in subclasses, return null if this class
/// cannot interpolate from `a`. In that case, [lerp] will try `a`'s [lerpTo]
/// method instead. If `a` is null, this must not return null.
///
/// The base class implementation handles the case of `a` being null by
/// deferring to [scale].
///
/// Instead of calling this directly, use [ShapeBorder.lerp].
@protected
ShapeBorder
lerpFrom
(
ShapeBorder
a
,
double
t
)
{
if
(
a
==
null
)
return
scale
(
t
);
return
null
;
}
/// Linearly interpolates from [this] to `b`.
///
/// This is called if `b`'s [lerpTo] did not know how to handle this class.
///
/// When implementing this method in subclasses, return null if this class
/// cannot interpolate from `b`. In that case, [lerp] will apply a default
/// behavior instead. If `b` is null, this must not return null.
///
/// The base class implementation handles the case of `b` being null by
/// deferring to [scale].
///
/// Instead of calling this directly, use [ShapeBorder.lerp].
@protected
ShapeBorder
lerpTo
(
ShapeBorder
b
,
double
t
)
{
if
(
b
==
null
)
return
scale
(
1.0
-
t
);
return
null
;
}
/// Linearly interpolates from `begin` to `end`.
///
/// This defers to `end`'s [lerpTo] function if `end` is not null. If `end` is
/// null or if its [lerpTo] returns null, it uses `begin`'s [lerpFrom]
/// function instead. If both return null, it returns `begin` before `t=0.5`
/// and `end` after `t=0.5`.
static
ShapeBorder
lerp
(
ShapeBorder
begin
,
ShapeBorder
end
,
double
t
)
{
ShapeBorder
result
;
if
(
end
!=
null
)
result
=
end
.
lerpFrom
(
begin
,
t
);
if
(
result
==
null
&&
begin
!=
null
)
result
=
begin
.
lerpTo
(
end
,
t
);
return
result
??
(
t
<
0.5
?
begin
:
end
);
}
/// Create a [Path] that describes the outer edge of the border.
///
/// This path must not cross the path given by [getInnerPath] for the same
/// [Rect].
///
/// To obtain a [Path] that describes the area of the border itself, set the
/// [Path.fillType] of the returned object to [PathFillType.evenOdd], and add
/// to this object the path returned from [getInnerPath] (using
/// [Path.addPath]).
///
/// The `textDirection` argument must be provided non-null if the border
/// has a text direction dependency (for example if it is expressed in terms
/// of "start" and "end" instead of "left" and "right"). It may be null if
/// the border will not need the text direction to paint itself.
///
/// See also:
///
/// * [getInnerPath], which creates the path for the inner edge.
/// * [Path.contains], which can tell if an [Offset] is within a [Path].
Path
getOuterPath
(
Rect
rect
,
{
TextDirection
textDirection
});
/// Create a [Path] that describes the inner edge of the border.
///
/// This path must not cross the path given by [getOuterPath] for the same
/// [Rect].
///
/// To obtain a [Path] that describes the area of the border itself, set the
/// [Path.fillType] of the returned object to [PathFillType.evenOdd], and add
/// to this object the path returned from [getOuterPath] (using
/// [Path.addPath]).
///
/// The `textDirection` argument must be provided and non-null if the border
/// has a text direction dependency (for example if it is expressed in terms
/// of "start" and "end" instead of "left" and "right"). It may be null if
/// the border will not need the text direction to paint itself.
///
/// See also:
///
/// * [getOuterPath], which creates the path for the outer edge.
/// * [Path.contains], which can tell if an [Offset] is within a [Path].
Path
getInnerPath
(
Rect
rect
,
{
TextDirection
textDirection
});
/// Paints the border within the given [Rect] on the given [Canvas].
///
/// The `textDirection` argument must be provided and non-null if the border
/// has a text direction dependency (for example if it is expressed in terms
/// of "start" and "end" instead of "left" and "right"). It may be null if
/// the border will not need the text direction to paint itself.
void
paint
(
Canvas
canvas
,
Rect
rect
,
{
TextDirection
textDirection
});
@override
String
toString
()
{
return
'
$runtimeType
()'
;
}
}
/// Represents the addition of two otherwise-incompatible borders.
///
/// The borders are listed from the outside to the inside.
class
_CompoundBorder
extends
ShapeBorder
{
_CompoundBorder
(
this
.
borders
)
{
assert
(
borders
!=
null
);
assert
(
borders
.
length
>=
2
);
assert
(!
borders
.
any
((
ShapeBorder
border
)
=>
border
is
_CompoundBorder
));
}
final
List
<
ShapeBorder
>
borders
;
@override
EdgeInsetsGeometry
get
dimensions
{
return
borders
.
fold
<
EdgeInsetsGeometry
>(
EdgeInsets
.
zero
,
(
EdgeInsetsGeometry
previousValue
,
ShapeBorder
border
)
{
return
previousValue
.
add
(
border
.
dimensions
);
},
);
}
@override
ShapeBorder
add
(
ShapeBorder
other
,
{
bool
reversed:
false
})
{
// This wraps the list of borders with "other", or, if "reversed" is true,
// wraps "other" with the list of borders.
// If "reversed" is false, "other" should end up being at the start of the
// list, otherwise, if "reversed" is true, it should end up at the end.
// First, see if we can merge the new adjacent borders.
if
(
other
is
!
_CompoundBorder
)
{
// Here, "ours" is the border at the side where we're adding the new
// border, and "merged" is the result of attempting to merge it with the
// new border. If it's null, it couldn't be merged.
final
ShapeBorder
ours
=
reversed
?
borders
.
last
:
borders
.
first
;
final
ShapeBorder
merged
=
ours
.
add
(
other
,
reversed:
reversed
)
??
other
.
add
(
ours
,
reversed:
!
reversed
);
if
(
merged
!=
null
)
{
final
List
<
ShapeBorder
>
result
=
<
ShapeBorder
>[];
result
.
addAll
(
borders
);
result
[
reversed
?
result
.
length
-
1
:
0
]
=
merged
;
return
new
_CompoundBorder
(
result
);
}
}
// We can't, so fall back to just adding the new border to the list.
final
List
<
ShapeBorder
>
mergedBorders
=
<
ShapeBorder
>[];
if
(
reversed
)
mergedBorders
.
addAll
(
borders
);
if
(
other
is
_CompoundBorder
)
mergedBorders
.
addAll
(
other
.
borders
);
else
mergedBorders
.
add
(
other
);
if
(!
reversed
)
mergedBorders
.
addAll
(
borders
);
return
new
_CompoundBorder
(
mergedBorders
);
}
@override
ShapeBorder
scale
(
double
t
)
{
return
new
_CompoundBorder
(
borders
.
map
<
ShapeBorder
>((
ShapeBorder
border
)
=>
border
.
scale
(
t
)).
toList
()
);
}
@override
ShapeBorder
lerpFrom
(
ShapeBorder
a
,
double
t
)
{
return
_CompoundBorder
.
lerp
(
a
,
this
,
t
);
}
@override
ShapeBorder
lerpTo
(
ShapeBorder
b
,
double
t
)
{
return
_CompoundBorder
.
lerp
(
this
,
b
,
t
);
}
static
_CompoundBorder
lerp
(
ShapeBorder
a
,
ShapeBorder
b
,
double
t
)
{
assert
(
a
is
_CompoundBorder
||
b
is
_CompoundBorder
);
// Not really necessary, but all call sites currently intend this.
final
List
<
ShapeBorder
>
aList
=
a
is
_CompoundBorder
?
a
.
borders
:
<
ShapeBorder
>[
a
];
final
List
<
ShapeBorder
>
bList
=
b
is
_CompoundBorder
?
b
.
borders
:
<
ShapeBorder
>[
b
];
final
List
<
ShapeBorder
>
results
=
<
ShapeBorder
>[];
final
int
length
=
math
.
max
(
aList
.
length
,
bList
.
length
);
for
(
int
index
=
0
;
index
<
length
;
index
+=
1
)
{
final
ShapeBorder
localA
=
index
<
aList
.
length
?
aList
[
index
]
:
null
;
final
ShapeBorder
localB
=
index
<
bList
.
length
?
bList
[
index
]
:
null
;
if
(
localA
!=
null
&&
localB
!=
null
)
{
final
ShapeBorder
localResult
=
localA
.
lerpTo
(
localB
,
t
)
??
localB
.
lerpFrom
(
localA
,
t
);
if
(
localResult
!=
null
)
{
results
.
add
(
localResult
);
continue
;
}
}
// If we're changing from one shape to another, make sure the shape that is coming in
// is inserted before the shape that is going away, so that the outer path changes to
// the new border earlier rather than later. (This affects, among other things, where
// the ShapeDecoration class puts its background.)
if
(
localB
!=
null
)
results
.
add
(
localB
.
scale
(
t
));
if
(
localA
!=
null
)
results
.
add
(
localA
.
scale
(
1.0
-
t
));
}
return
new
_CompoundBorder
(
results
);
}
@override
Path
getInnerPath
(
Rect
rect
,
{
TextDirection
textDirection
})
{
for
(
int
index
=
0
;
index
<
borders
.
length
-
1
;
index
+=
1
)
rect
=
borders
[
index
].
dimensions
.
resolve
(
textDirection
).
deflateRect
(
rect
);
return
borders
.
last
.
getInnerPath
(
rect
);
}
@override
Path
getOuterPath
(
Rect
rect
,
{
TextDirection
textDirection
})
{
return
borders
.
first
.
getOuterPath
(
rect
);
}
@override
void
paint
(
Canvas
canvas
,
Rect
rect
,
{
TextDirection
textDirection
})
{
for
(
ShapeBorder
border
in
borders
)
{
border
.
paint
(
canvas
,
rect
,
textDirection:
textDirection
);
rect
=
border
.
dimensions
.
resolve
(
textDirection
).
deflateRect
(
rect
);
}
}
@override
bool
operator
==(
dynamic
other
)
{
if
(
identical
(
this
,
other
))
return
true
;
if
(
runtimeType
!=
other
.
runtimeType
)
return
false
;
final
_CompoundBorder
typedOther
=
other
;
if
(
borders
==
typedOther
.
borders
)
return
true
;
if
(
borders
.
length
!=
typedOther
.
borders
.
length
)
return
false
;
for
(
int
index
=
0
;
index
<
borders
.
length
;
index
+=
1
)
{
if
(
borders
[
index
]
!=
typedOther
.
borders
[
index
])
return
false
;
}
return
true
;
}
@override
int
get
hashCode
=>
hashList
(
borders
);
@override
String
toString
()
{
// We list them in reverse order because when adding two borders they end up
// in the list in the opposite order of what the source looks like: a + b =>
// [b, a]. We do this to make the painting code more optimal, and most of
// the rest of the code doesn't care, except toString() (for debugging).
return
borders
.
reversed
.
map
<
String
>((
ShapeBorder
border
)
=>
border
.
toString
()).
join
(
' + '
);
}
}
/// A border of a box, comprised of four sides.
///
/// The sides are represented by [BorderSide] objects.
...
...
@@ -315,8 +627,7 @@ class BorderSide {
/// * [BorderSide], which is used to describe each side of the box.
/// * [Theme], from the material layer, which can be queried to obtain appropriate colors
/// to use for borders in a material app, as shown in the "divider" sample above.
@immutable
class
Border
{
class
Border
extends
ShapeBorder
{
/// Creates a border.
///
/// All the sides of the border default to [BorderSide.none].
...
...
@@ -373,10 +684,7 @@ class Border {
/// The left side of this border.
final
BorderSide
left
;
/// The widths of the sides of this border represented as an [EdgeInsets].
///
/// This can be used, for example, with a [Padding] widget to inset a box by
/// the size of these borders.
@override
EdgeInsetsGeometry
get
dimensions
{
return
new
EdgeInsets
.
fromLTRB
(
left
.
width
,
top
.
width
,
right
.
width
,
bottom
.
width
);
}
...
...
@@ -410,7 +718,11 @@ class Border {
return
true
;
}
Border
add
(
Border
typedOther
)
{
@override
Border
add
(
ShapeBorder
other
,
{
bool
reversed:
false
})
{
if
(
other
is
!
Border
)
return
null
;
final
Border
typedOther
=
other
;
if
(
BorderSide
.
canMerge
(
top
,
typedOther
.
top
)
&&
BorderSide
.
canMerge
(
right
,
typedOther
.
right
)
&&
BorderSide
.
canMerge
(
bottom
,
typedOther
.
bottom
)
&&
...
...
@@ -421,6 +733,7 @@ class Border {
}
/// Creates a new border with the widths of this border multiplied by `t`.
@override
Border
scale
(
double
t
)
{
return
new
Border
(
top:
top
.
scale
(
t
),
...
...
@@ -430,6 +743,34 @@ class Border {
);
}
/// Linearly interpolates from `a` to [this].
///
/// If `a` is null, this defers to [scale].
///
/// If `a` is also a [Border], this uses [Border.lerp].
///
/// Otherwise, it defers to [ShapeBorder.lerpFrom].
@override
ShapeBorder
lerpFrom
(
ShapeBorder
a
,
double
t
)
{
if
(
a
is
Border
)
return
Border
.
lerp
(
a
,
this
,
t
);
return
super
.
lerpFrom
(
a
,
t
);
}
/// Linearly interpolates from [this] to `b`.
///
/// If `b` is null, this defers to [scale].
///
/// If `b` is also a [Border], this uses [Border.lerp].
///
/// Otherwise, it defers to [ShapeBorder.lerpTo].
@override
ShapeBorder
lerpTo
(
ShapeBorder
b
,
double
t
)
{
if
(
b
is
Border
)
return
Border
.
lerp
(
this
,
b
,
t
);
return
super
.
lerpTo
(
b
,
t
);
}
/// Linearly interpolate between two borders.
///
/// If a border is null, it is treated as having four [BorderSide.none]
...
...
@@ -449,6 +790,18 @@ class Border {
);
}
@override
Path
getInnerPath
(
Rect
rect
,
{
TextDirection
textDirection
})
{
return
new
Path
()
..
addRect
(
dimensions
.
resolve
(
textDirection
).
deflateRect
(
rect
));
}
@override
Path
getOuterPath
(
Rect
rect
,
{
TextDirection
textDirection
})
{
return
new
Path
()
..
addRect
(
rect
);
}
/// Paints the border within the given [Rect] on the given [Canvas].
///
/// Uniform borders are more efficient to paint than more complex borders.
...
...
@@ -460,10 +813,17 @@ class Border {
/// may specify a [BorderRadius]. If a `borderRadius` is specified, there is
/// the requirement that the border [isUniform].
///
/// The [getInnerPath] and [getOuterPath] methods do not know about the
/// `shape` and `borderRadius` arguments.
///
/// The `textDirection` argument is not used by this paint method.
///
/// See also:
///
/// * [paintBorder], which is used if the border is not uniform.
@override
void
paint
(
Canvas
canvas
,
Rect
rect
,
{
TextDirection
textDirection
,
BoxShape
shape:
BoxShape
.
rectangle
,
BorderRadius
borderRadius
,
})
{
...
...
packages/flutter/test/painting/border_test.dart
View file @
53194ed4
...
...
@@ -51,34 +51,34 @@ void main() {
final
BorderSide
yellow2
=
const
BorderSide
(
color:
const
Color
(
0xFFFFFF00
),
width:
2.0
);
final
BorderSide
yellowNone0
=
const
BorderSide
(
color:
const
Color
(
0xFFFFFF00
),
width:
0.0
,
style:
BorderStyle
.
none
);
expect
(
new
Border
(
top:
yellow2
)
.
add
(
new
Border
(
right:
magenta3
)
),
new
Border
(
top:
yellow2
)
+
new
Border
(
right:
magenta3
),
new
Border
(
top:
yellow2
,
right:
magenta3
),
);
expect
(
new
Border
(
bottom:
magenta3
)
.
add
(
new
Border
(
bottom:
magenta3
)
),
new
Border
(
bottom:
magenta3
)
+
new
Border
(
bottom:
magenta3
),
new
Border
(
bottom:
magenta6
),
);
expect
(
new
Border
(
left:
magenta3
,
right:
yellowNone0
)
.
add
(
new
Border
(
right:
yellow2
)
),
new
Border
(
left:
magenta3
,
right:
yellowNone0
)
+
new
Border
(
right:
yellow2
),
new
Border
(
left:
magenta3
,
right:
yellow2
),
);
expect
(
const
Border
()
.
add
(
const
Border
()
),
const
Border
()
+
const
Border
(
),
const
Border
(),
);
expect
(
new
Border
(
left:
magenta3
)
.
add
(
new
Border
(
left:
yellow2
)
),
isN
ull
,
new
Border
(
left:
magenta3
)
+
new
Border
(
left:
yellow2
),
isN
ot
(
const
isInstanceOf
<
Border
>()),
// see shape_border_test.dart for better tests of this case
);
final
Border
b3
=
new
Border
(
top:
magenta3
);
final
Border
b6
=
new
Border
(
top:
magenta6
);
expect
(
b3
.
add
(
b3
)
,
b6
);
expect
(
b3
+
b3
,
b6
);
final
Border
b0
=
new
Border
(
top:
yellowNone0
);
final
Border
bZ
=
const
Border
();
expect
(
b0
.
add
(
b0
)
,
bZ
);
expect
(
bZ
.
add
(
bZ
)
,
bZ
);
expect
(
b0
.
add
(
bZ
)
,
bZ
);
expect
(
bZ
.
add
(
b0
)
,
bZ
);
expect
(
b0
+
b0
,
bZ
);
expect
(
bZ
+
bZ
,
bZ
);
expect
(
b0
+
bZ
,
bZ
);
expect
(
bZ
+
b0
,
bZ
);
});
test
(
'Border.scale'
,
()
{
...
...
packages/flutter/test/painting/shape_border_test.dart
0 → 100644
View file @
53194ed4
// Copyright 2017 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/painting.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'../rendering/mock_canvas.dart'
;
void
main
(
)
{
test
(
'Compound borders'
,
()
{
final
Border
b1
=
new
Border
.
all
(
color:
const
Color
(
0xFF00FF00
));
final
Border
b2
=
new
Border
.
all
(
color:
const
Color
(
0xFF0000FF
));
expect
(
(
b1
+
b2
).
toString
(),
'Border.all(BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid)) + '
'Border.all(BorderSide(Color(0xff0000ff), 1.0, BorderStyle.solid))'
,
);
expect
(
(
b1
+
(
b2
+
b2
)).
toString
(),
'Border.all(BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid)) + '
'Border.all(BorderSide(Color(0xff0000ff), 2.0, BorderStyle.solid))'
,
);
expect
(
((
b1
+
b2
)
+
b2
).
toString
(),
'Border.all(BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid)) + '
'Border.all(BorderSide(Color(0xff0000ff), 2.0, BorderStyle.solid))'
,
);
expect
((
b1
+
b2
)
+
b2
,
b1
+
(
b2
+
b2
));
expect
(
(
b1
+
b2
).
scale
(
3.0
).
toString
(),
'Border.all(BorderSide(Color(0xff00ff00), 3.0, BorderStyle.solid)) + '
'Border.all(BorderSide(Color(0xff0000ff), 3.0, BorderStyle.solid))'
,
);
expect
(
(
b1
+
b2
).
scale
(
0.0
).
toString
(),
'Border.all(BorderSide(Color(0xff00ff00), 0.0, BorderStyle.none)) + '
'Border.all(BorderSide(Color(0xff0000ff), 0.0, BorderStyle.none))'
,
);
expect
(
ShapeBorder
.
lerp
(
b2
+
b1
,
b1
+
b2
,
0.0
).
toString
(),
'Border.all(BorderSide(Color(0xff0000ff), 1.0, BorderStyle.solid)) + '
'Border.all(BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid))'
,
);
expect
(
ShapeBorder
.
lerp
(
b2
+
b1
,
b1
+
b2
,
0.25
).
toString
(),
'Border.all(BorderSide(Color(0xff003fbf), 1.0, BorderStyle.solid)) + '
'Border.all(BorderSide(Color(0xff00bf3f), 1.0, BorderStyle.solid))'
,
);
expect
(
ShapeBorder
.
lerp
(
b2
+
b1
,
b1
+
b2
,
0.5
).
toString
(),
'Border.all(BorderSide(Color(0xff007f7f), 1.0, BorderStyle.solid)) + '
'Border.all(BorderSide(Color(0xff007f7f), 1.0, BorderStyle.solid))'
,
);
expect
(
ShapeBorder
.
lerp
(
b2
+
b1
,
b1
+
b2
,
1.0
).
toString
(),
'Border.all(BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid)) + '
'Border.all(BorderSide(Color(0xff0000ff), 1.0, BorderStyle.solid))'
);
expect
((
b1
+
b2
).
dimensions
,
const
EdgeInsets
.
all
(
2.0
));
final
Rect
rect
=
new
Rect
.
fromLTRB
(
11.0
,
15.0
,
299.0
,
175.0
);
expect
((
Canvas
canvas
)
=>
(
b1
+
b2
).
paint
(
canvas
,
rect
),
paints
..
rect
(
rect:
rect
.
deflate
(
0.5
),
color:
b2
.
top
.
color
)
..
rect
(
rect:
rect
.
deflate
(
1.5
),
color:
b1
.
top
.
color
)
);
expect
((
b1
+
b2
+
b1
).
dimensions
,
const
EdgeInsets
.
all
(
3.0
));
expect
((
Canvas
canvas
)
=>
(
b1
+
b2
+
b1
).
paint
(
canvas
,
rect
),
paints
..
rect
(
rect:
rect
.
deflate
(
0.5
),
color:
b1
.
top
.
color
)
..
rect
(
rect:
rect
.
deflate
(
1.5
),
color:
b2
.
top
.
color
)
..
rect
(
rect:
rect
.
deflate
(
2.5
),
color:
b1
.
top
.
color
)
);
});
}
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