blob: ec47a8a09a58ccf16d14218cf13d9ff60c382dd7 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math' as math;
import 'package:flutter/foundation.dart';
import 'basic_types.dart';
import 'border_radius.dart';
import 'borders.dart';
import 'edge_insets.dart';
/// A rectangular border with flattened or "beveled" corners.
/// The line segments that connect the rectangle's four sides will
/// begin and at locations offset by the corresponding border radius,
/// 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 {
/// 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,
this.borderRadius =,
}) : assert(side != null),
assert(borderRadius != null);
/// The style of this border.
final BorderSide side;
/// The radii for each corner.
/// Each corner [Radius] defines the endpoints of a line segment that
/// spans the corner. The endpoints are located in the same place as
/// they would be for [RoundedRectangleBorder], but they're connected
/// by a straight line instead of an arc.
/// Negative radius values are clamped to 0.0 by [getInnerPath] and
/// [getOuterPath].
final BorderRadiusGeometry borderRadius;
EdgeInsetsGeometry get dimensions {
return EdgeInsets.all(side.width);
ShapeBorder scale(double t) {
return BeveledRectangleBorder(
side: side.scale(t),
borderRadius: borderRadius * t,
ShapeBorder lerpFrom(ShapeBorder a, double t) {
assert(t != null);
if (a is BeveledRectangleBorder) {
return BeveledRectangleBorder(
side: BorderSide.lerp(a.side, side, t),
borderRadius: BorderRadiusGeometry.lerp(a.borderRadius, borderRadius, t),
return super.lerpFrom(a, t);
ShapeBorder lerpTo(ShapeBorder b, double t) {
assert(t != null);
if (b is BeveledRectangleBorder) {
return BeveledRectangleBorder(
side: BorderSide.lerp(side, b.side, t),
borderRadius: BorderRadiusGeometry.lerp(borderRadius, b.borderRadius, t),
return super.lerpTo(b, t);
Path _getPath(RRect rrect) {
final Offset centerLeft = Offset(rrect.left,;
final Offset centerRight = Offset(rrect.right,;
final Offset centerTop = Offset(,;
final Offset centerBottom = Offset(, rrect.bottom);
final double tlRadiusX = math.max(0.0, rrect.tlRadiusX);
final double tlRadiusY = math.max(0.0, rrect.tlRadiusY);
final double trRadiusX = math.max(0.0, rrect.trRadiusX);
final double trRadiusY = math.max(0.0, rrect.trRadiusY);
final double blRadiusX = math.max(0.0, rrect.blRadiusX);
final double blRadiusY = math.max(0.0, rrect.blRadiusY);
final double brRadiusX = math.max(0.0, rrect.brRadiusX);
final double brRadiusY = math.max(0.0, rrect.brRadiusY);
final List<Offset> vertices = <Offset>[
Offset(rrect.left, math.min(centerLeft.dy, + tlRadiusY)),
Offset(math.min(centerTop.dx, rrect.left + tlRadiusX),,
Offset(math.max(centerTop.dx, rrect.right -trRadiusX),,
Offset(rrect.right, math.min(centerRight.dy, + trRadiusY)),
Offset(rrect.right, math.max(centerRight.dy, rrect.bottom - brRadiusY)),
Offset(math.max(centerBottom.dx, rrect.right - brRadiusX), rrect.bottom),
Offset(math.min(centerBottom.dx, rrect.left + blRadiusX), rrect.bottom),
Offset(rrect.left, math.max(centerLeft.dy, rrect.bottom - blRadiusY)),
return Path()..addPolygon(vertices, true);
Path getInnerPath(Rect rect, { TextDirection textDirection }) {
return _getPath(borderRadius.resolve(textDirection).toRRect(rect).deflate(side.width));
Path getOuterPath(Rect rect, { TextDirection textDirection }) {
return _getPath(borderRadius.resolve(textDirection).toRRect(rect));
void paint(Canvas canvas, Rect rect, { TextDirection textDirection }) {
if (rect.isEmpty)
switch ( {
case BorderStyle.none:
case BorderStyle.solid:
final Path path = getOuterPath(rect, textDirection: textDirection)
..addPath(getInnerPath(rect, textDirection: textDirection),;
canvas.drawPath(path, side.toPaint());
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is BeveledRectangleBorder
&& other.side == side
&& other.borderRadius == borderRadius;
int get hashCode => hashValues(side, borderRadius);
String toString() {
return '${objectRuntimeType(this, 'BeveledRectangleBorder')}($side, $borderRadius)';