Unverified Commit 26dc70dc authored by Hans Muller's avatar Hans Muller Committed by GitHub

Add OutlinedBorder class (#56341)

parent fd4d6d70
......@@ -18,19 +18,17 @@ import 'edge_insets.dart';
/// but not farther than the side's center. If all the border radii
/// exceed the sides' half widths/heights the resulting shape is
/// diamond made by connecting the centers of the sides.
class BeveledRectangleBorder extends ShapeBorder {
class BeveledRectangleBorder extends OutlinedBorder {
/// Creates a border like a [RoundedRectangleBorder] except that the corners
/// are joined by straight lines instead of arcs.
///
/// The arguments must not be null.
const BeveledRectangleBorder({
this.side = BorderSide.none,
BorderSide side = BorderSide.none,
this.borderRadius = BorderRadius.zero,
}) : assert(side != null),
assert(borderRadius != null);
/// The style of this border.
final BorderSide side;
assert(borderRadius != null),
super(side: side);
/// The radii for each corner.
///
......@@ -80,6 +78,16 @@ class BeveledRectangleBorder extends ShapeBorder {
return super.lerpTo(b, t);
}
/// Returns a copy of this RoundedRectangleBorder with the given fields
/// replaced with the new values.
@override
BeveledRectangleBorder copyWith({ BorderSide side, BorderRadius borderRadius }) {
return BeveledRectangleBorder(
side: side ?? this.side,
borderRadius: borderRadius ?? this.borderRadius,
);
}
Path _getPath(RRect rrect) {
final Offset centerLeft = Offset(rrect.left, rrect.center.dy);
final Offset centerRight = Offset(rrect.right, rrect.center.dy);
......
......@@ -492,6 +492,27 @@ abstract class ShapeBorder {
}
}
/// A ShapeBorder that draws an outline with the width and color specified
/// by [side].
@immutable
abstract class OutlinedBorder extends ShapeBorder {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
///
/// The value of [side] must not be null.
const OutlinedBorder({ this.side = BorderSide.none }) : assert(side != null);
/// The border outline's color and weight.
///
/// If [side] is [BorderSide.none], which is the default, an outline is not drawn.
/// Otherwise the outline is centered over the shape's boundary.
final BorderSide side;
/// Returns a copy of this OutlinedBorder that draws its outline with the
/// specified [side], if [side] is non-null.
OutlinedBorder copyWith({ BorderSide side });
}
/// Represents the addition of two otherwise-incompatible borders.
///
/// The borders are listed from the outside to the inside.
......
......@@ -23,14 +23,11 @@ import 'edge_insets.dart';
/// * [BorderSide], which is used to describe each side of the box.
/// * [Border], which, when used with [BoxDecoration], can also
/// describe a circle.
class CircleBorder extends ShapeBorder {
class CircleBorder extends OutlinedBorder {
/// Create a circle border.
///
/// The [side] argument must not be null.
const CircleBorder({ this.side = BorderSide.none }) : assert(side != null);
/// The style of this border.
final BorderSide side;
const CircleBorder({ BorderSide side = BorderSide.none }) : assert(side != null), super(side: side);
@override
EdgeInsetsGeometry get dimensions {
......@@ -72,6 +69,11 @@ class CircleBorder extends ShapeBorder {
));
}
@override
CircleBorder copyWith({ BorderSide side }) {
return CircleBorder(side: side ?? this.side);
}
@override
void paint(Canvas canvas, Rect rect, { TextDirection textDirection }) {
switch (side.style) {
......
......@@ -32,13 +32,14 @@ import 'edge_insets.dart';
/// however its straight sides change into a rounded corner with a circular
/// radius in a step function instead of gradually like the
/// [ContinuousRectangleBorder].
class ContinuousRectangleBorder extends ShapeBorder {
class ContinuousRectangleBorder extends OutlinedBorder {
/// The arguments must not be null.
const ContinuousRectangleBorder({
this.side = BorderSide.none,
BorderSide side = BorderSide.none,
this.borderRadius = BorderRadius.zero,
}) : assert(side != null),
assert(borderRadius != null);
assert(borderRadius != null),
super(side: side);
/// The radius for each corner.
///
......@@ -46,10 +47,7 @@ class ContinuousRectangleBorder extends ShapeBorder {
/// [getOuterPath].
final BorderRadiusGeometry borderRadius;
/// The style of this border.
final BorderSide side;
@override
@override
EdgeInsetsGeometry get dimensions => EdgeInsets.all(side.width);
@override
......@@ -134,6 +132,14 @@ class ContinuousRectangleBorder extends ShapeBorder {
return _getPath(borderRadius.resolve(textDirection).toRRect(rect));
}
@override
ContinuousRectangleBorder copyWith({ BorderSide side, BorderRadius borderRadius }) {
return ContinuousRectangleBorder(
side: side ?? this.side,
borderRadius: borderRadius ?? this.borderRadius,
);
}
@override
void paint(Canvas canvas, Rect rect, { TextDirection textDirection }) {
if (rect.isEmpty)
......
......@@ -24,18 +24,16 @@ import 'edge_insets.dart';
/// * [BorderSide], which is used to describe each side of the box.
/// * [Border], which, when used with [BoxDecoration], can also
/// describe a rounded rectangle.
class RoundedRectangleBorder extends ShapeBorder {
class RoundedRectangleBorder extends OutlinedBorder {
/// Creates a rounded rectangle border.
///
/// The arguments must not be null.
const RoundedRectangleBorder({
this.side = BorderSide.none,
BorderSide side = BorderSide.none,
this.borderRadius = BorderRadius.zero,
}) : assert(side != null),
assert(borderRadius != null);
/// The style of this border.
final BorderSide side;
assert(borderRadius != null),
super(side: side);
/// The radii for each corner.
final BorderRadiusGeometry borderRadius;
......@@ -91,6 +89,16 @@ class RoundedRectangleBorder extends ShapeBorder {
return super.lerpTo(b, t);
}
/// Returns a copy of this RoundedRectangleBorder with the given fields
/// replaced with the new values.
@override
RoundedRectangleBorder copyWith({ BorderSide side, BorderRadius borderRadius }) {
return RoundedRectangleBorder(
side: side ?? this.side,
borderRadius: borderRadius ?? this.borderRadius,
);
}
@override
Path getInnerPath(Rect rect, { TextDirection textDirection }) {
return Path()
......@@ -140,16 +148,15 @@ class RoundedRectangleBorder extends ShapeBorder {
}
}
class _RoundedRectangleToCircleBorder extends ShapeBorder {
class _RoundedRectangleToCircleBorder extends OutlinedBorder {
const _RoundedRectangleToCircleBorder({
this.side = BorderSide.none,
BorderSide side = BorderSide.none,
this.borderRadius = BorderRadius.zero,
@required this.circleness,
}) : assert(side != null),
assert(borderRadius != null),
assert(circleness != null);
final BorderSide side;
assert(circleness != null),
super(side: side);
final BorderRadiusGeometry borderRadius;
......@@ -263,6 +270,15 @@ class _RoundedRectangleToCircleBorder extends ShapeBorder {
..addRRect(_adjustBorderRadius(rect, textDirection).toRRect(_adjustRect(rect)));
}
@override
_RoundedRectangleToCircleBorder copyWith({ BorderSide side, BorderRadius borderRadius, double circleness }) {
return _RoundedRectangleToCircleBorder(
side: side ?? this.side,
borderRadius: borderRadius ?? this.borderRadius,
circleness: circleness ?? this.circleness,
);
}
@override
void paint(Canvas canvas, Rect rect, { TextDirection textDirection }) {
switch (side.style) {
......
......@@ -24,14 +24,11 @@ import 'rounded_rectangle_border.dart';
/// See also:
///
/// * [BorderSide], which is used to describe the border of the stadium.
class StadiumBorder extends ShapeBorder {
class StadiumBorder extends OutlinedBorder {
/// Create a stadium border.
///
/// The [side] argument must not be null.
const StadiumBorder({this.side = BorderSide.none}) : assert(side != null);
/// The style of this border.
final BorderSide side;
const StadiumBorder({ BorderSide side = BorderSide.none }) : assert(side != null), super(side: side);
@override
EdgeInsetsGeometry get dimensions {
......@@ -83,6 +80,11 @@ class StadiumBorder extends ShapeBorder {
return super.lerpTo(b, t);
}
@override
StadiumBorder copyWith({ BorderSide side }) {
return StadiumBorder(side: side ?? this.side);
}
@override
Path getInnerPath(Rect rect, { TextDirection textDirection }) {
final Radius radius = Radius.circular(rect.shortestSide / 2.0);
......@@ -129,14 +131,13 @@ class StadiumBorder extends ShapeBorder {
}
// Class to help with transitioning to/from a CircleBorder.
class _StadiumToCircleBorder extends ShapeBorder {
class _StadiumToCircleBorder extends OutlinedBorder {
const _StadiumToCircleBorder({
this.side = BorderSide.none,
BorderSide side = BorderSide.none,
this.circleness = 0.0,
}) : assert(side != null),
assert(circleness != null);
final BorderSide side;
assert(circleness != null),
super(side: side);
final double circleness;
......@@ -239,6 +240,14 @@ class _StadiumToCircleBorder extends ShapeBorder {
..addRRect(_adjustBorderRadius(rect).toRRect(_adjustRect(rect)));
}
@override
_StadiumToCircleBorder copyWith({ BorderSide side, double circleness }) {
return _StadiumToCircleBorder(
side: side ?? this.side,
circleness: circleness ?? this.circleness,
);
}
@override
void paint(Canvas canvas, Rect rect, { TextDirection textDirection }) {
switch (side.style) {
......@@ -278,16 +287,15 @@ class _StadiumToCircleBorder extends ShapeBorder {
}
// Class to help with transitioning to/from a RoundedRectBorder.
class _StadiumToRoundedRectangleBorder extends ShapeBorder {
class _StadiumToRoundedRectangleBorder extends OutlinedBorder {
const _StadiumToRoundedRectangleBorder({
this.side = BorderSide.none,
BorderSide side = BorderSide.none,
this.borderRadius = BorderRadius.zero,
this.rectness = 0.0,
}) : assert(side != null),
assert(borderRadius != null),
assert(rectness != null);
final BorderSide side;
assert(rectness != null),
super(side: side);
final BorderRadius borderRadius;
......@@ -381,6 +389,15 @@ class _StadiumToRoundedRectangleBorder extends ShapeBorder {
..addRRect(_adjustBorderRadius(rect).toRRect(rect));
}
@override
_StadiumToRoundedRectangleBorder copyWith({ BorderSide side, BorderRadius borderRadius, double rectness }) {
return _StadiumToRoundedRectangleBorder(
side: side ?? this.side,
borderRadius: borderRadius ?? this.borderRadius,
rectness: rectness ?? this.rectness
);
}
@override
void paint(Canvas canvas, Rect rect, { TextDirection textDirection }) {
switch (side.style) {
......
......@@ -8,6 +8,23 @@ import 'package:flutter_test/flutter_test.dart';
import '../rendering/mock_canvas.dart';
void main() {
test('BeveledRectangleBorder defaults', () {
const BeveledRectangleBorder border = BeveledRectangleBorder();
expect(border.side, BorderSide.none);
expect(border.borderRadius, BorderRadius.zero);
});
test('BeveledRectangleBorder copyWith, ==, hashCode', () {
expect(const BeveledRectangleBorder(), const BeveledRectangleBorder().copyWith());
expect(const BeveledRectangleBorder().hashCode, const BeveledRectangleBorder().copyWith().hashCode);
const BorderSide side = BorderSide(width: 10.0, color: Color(0xff123456));
const BorderRadius radius = BorderRadius.all(Radius.circular(16.0));
expect(
const BeveledRectangleBorder().copyWith(side: side, borderRadius: radius),
const BeveledRectangleBorder(side: side, borderRadius: radius),
);
});
test('BeveledRectangleBorder scale and lerp', () {
final BeveledRectangleBorder c10 = BeveledRectangleBorder(side: const BorderSide(width: 10.0), borderRadius: BorderRadius.circular(100.0));
final BeveledRectangleBorder c15 = BeveledRectangleBorder(side: const BorderSide(width: 15.0), borderRadius: BorderRadius.circular(150.0));
......
......@@ -9,6 +9,18 @@ import '../rendering/mock_canvas.dart';
import 'common_matchers.dart';
void main() {
test('CircleBorder defaults', () {
const CircleBorder border = CircleBorder();
expect(border.side, BorderSide.none);
});
test('CircleBorder copyWith, ==, hashCode', () {
expect(const CircleBorder(), const CircleBorder().copyWith());
expect(const CircleBorder().hashCode, const CircleBorder().copyWith().hashCode);
const BorderSide side = BorderSide(width: 10.0, color: Color(0xff123456));
expect(const CircleBorder().copyWith(side: side), const CircleBorder(side: side));
});
test('CircleBorder', () {
const CircleBorder c10 = CircleBorder(side: BorderSide(width: 10.0));
const CircleBorder c15 = CircleBorder(side: BorderSide(width: 15.0));
......
......@@ -9,6 +9,23 @@ import 'package:flutter_test/flutter_test.dart';
import '../rendering/mock_canvas.dart';
void main() {
test('ContinuousRectangleBorder defaults', () {
const ContinuousRectangleBorder border = ContinuousRectangleBorder();
expect(border.side, BorderSide.none);
expect(border.borderRadius, BorderRadius.zero);
});
test('ContinuousRectangleBorder copyWith, ==, hashCode', () {
expect(const ContinuousRectangleBorder(), const ContinuousRectangleBorder().copyWith());
expect(const ContinuousRectangleBorder().hashCode, const ContinuousRectangleBorder().copyWith().hashCode);
const BorderSide side = BorderSide(width: 10.0, color: Color(0xff123456));
const BorderRadius radius = BorderRadius.all(Radius.circular(16.0));
expect(
const ContinuousRectangleBorder().copyWith(side: side, borderRadius: radius),
const ContinuousRectangleBorder(side: side, borderRadius: radius),
);
});
test('ContinuousRectangleBorder scale and lerp', () {
final ContinuousRectangleBorder c10 = ContinuousRectangleBorder(side: const BorderSide(width: 10.0), borderRadius: BorderRadius.circular(100.0));
final ContinuousRectangleBorder c15 = ContinuousRectangleBorder(side: const BorderSide(width: 15.0), borderRadius: BorderRadius.circular(150.0));
......
......@@ -9,6 +9,23 @@ import '../rendering/mock_canvas.dart';
import 'common_matchers.dart';
void main() {
test('RoundedRectangleBorder defaults', () {
const RoundedRectangleBorder border = RoundedRectangleBorder();
expect(border.side, BorderSide.none);
expect(border.borderRadius, BorderRadius.zero);
});
test('RoundedRectangleBorder copyWith, ==, hashCode', () {
expect(const RoundedRectangleBorder(), const RoundedRectangleBorder().copyWith());
expect(const RoundedRectangleBorder().hashCode, const RoundedRectangleBorder().copyWith().hashCode);
const BorderSide side = BorderSide(width: 10.0, color: Color(0xff123456));
const BorderRadius radius = BorderRadius.all(Radius.circular(16.0));
expect(
const RoundedRectangleBorder().copyWith(side: side, borderRadius: radius),
const RoundedRectangleBorder(side: side, borderRadius: radius),
);
});
test('RoundedRectangleBorder', () {
final RoundedRectangleBorder c10 = RoundedRectangleBorder(side: const BorderSide(width: 10.0), borderRadius: BorderRadius.circular(100.0));
final RoundedRectangleBorder c15 = RoundedRectangleBorder(side: const BorderSide(width: 15.0), borderRadius: BorderRadius.circular(150.0));
......
......@@ -9,6 +9,18 @@ import '../rendering/mock_canvas.dart';
import 'common_matchers.dart';
void main() {
test('StadiumBorder defaults', () {
const StadiumBorder border = StadiumBorder();
expect(border.side, BorderSide.none);
});
test('StadiumBorder copyWith, ==, hashCode', () {
expect(const StadiumBorder(), const StadiumBorder().copyWith());
expect(const StadiumBorder().hashCode, const StadiumBorder().copyWith().hashCode);
const BorderSide side = BorderSide(width: 10.0, color: Color(0xff123456));
expect(const StadiumBorder().copyWith(side: side), const StadiumBorder(side: side));
});
test('StadiumBorder', () {
const StadiumBorder c10 = StadiumBorder(side: BorderSide(width: 10.0));
const StadiumBorder c15 = StadiumBorder(side: BorderSide(width: 15.0));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment