parent
437e4c089b
commit
53194ed477
@ -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,
|
||||
}) {
|
||||
|
@ -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)),
|
||||
isNull,
|
||||
new Border(left: magenta3) + new Border(left: yellow2),
|
||||
isNot(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', () {
|
||||
|
73
packages/flutter/test/painting/shape_border_test.dart
Normal file
73
packages/flutter/test/painting/shape_border_test.dart
Normal file
@ -0,0 +1,73 @@
|
||||
// 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)
|
||||
);
|
||||
});
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user