颤动圆角矩形边框,每边具有不同的颜色
Posted
技术标签:
【中文标题】颤动圆角矩形边框,每边具有不同的颜色【英文标题】:Flutter rounded rectangle border with different colours for each side 【发布时间】:2019-11-17 14:40:04 【问题描述】:我正在尝试重新创建一个我用 React Native 制作的按钮,它的每一面都有不同的颜色,给它一种轮廓分明的效果,有点像 Photoshop 斜面和浮雕,但也有圆角。目前我在按钮外面有一个容器,上面有边框,在里面我使用RawMaterialButton
。容器的代码是这样的:
Container(
BoxDecoration(
border: Border(
left: BorderSide(
color: Colors.black,
width: 1.0,
),
top: BorderSide(
color: Colors.black,
width: 1.0,
),
),
),
)
如何使此边框/容器的角变圆?
像这样,但有圆角:
【问题讨论】:
请您发布预期结果图片 @Manoj Perumarath Ok 发布了一张图片。像那样,但有圆角。 目前,我认为这是不可能的。您可以有一个盒子装饰,但要能够设置边框半径,边框需要在所有边上保持一致。这意味着您不能有不同的颜色或边框笔触宽度 @Ajil O. 是的,这就是我所担心的。BorderSide
似乎不接受任何类型的半径设置。很遗憾,因为 React Native 中那个具有轮廓分明的外观和圆角的按钮看起来真的很棒。
@Hasen 你应该仍然可以使用 CustomPainter 来做到这一点。
【参考方案1】:
Ajil 的解决方案看起来是正确的方向。不过你是对的,对于这么小的东西来说,这是一个惊人的工作量。
这是我的繁琐工作,需要简化以进行整理,但它可以更准确地表示您的图像。
class Example extends StatefulWidget
@override
_ExampleState createState()
return _ExampleState();
class _ExampleState extends State<Example>
double height;
double width;
@override
void initState()
super.initState();
@override
Widget build(BuildContext context)
height = MediaQuery.of(context).size.height;
width = MediaQuery.of(context).size.width;
const double pi = 3.1415926535897932;
return Scaffold(
appBar: AppBar(
title: Text("Some project"),
),
body: Center(
child:
Container(
width: width = 350,
height: height = 300,
child: Stack(children: <Widget>[
Align(
alignment: Alignment.center,
child: Container(
width: 300,
height: 300,
// color: Colors.red,
child: FlatButton(
onPressed: ()
print('button pressed');
,
child: Text(
'BUTTON',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
)),
)),
Align(
//TOP BORDER
alignment: Alignment.topCenter,
child: Container(
decoration: BoxDecoration(
color: Colors.blue[200],
border:
Border.all(width: 3.0, color: Colors.transparent),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
topRight: Radius.circular(10.0)),
),
height: 50,
width: width,
),
),
Align(
//LEFT BORDER
alignment: Alignment.topLeft,
child: Container(
height: height,
width: 30,
decoration: BoxDecoration(
color: Colors.blue[100],
border: Border.all(
width: 3.0, color: Colors.transparent),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
bottomLeft: Radius.circular(10.0))),
),
),
Align(
//RIGHT BORDER
alignment: Alignment.topRight,
child: Container(
height: height,
width: 15,
decoration: BoxDecoration(
color: Colors.blue[300],
border: Border.all(
width: 3.0, color: Colors.transparent),
borderRadius: BorderRadius.only(
topRight: Radius.circular(10.0),
bottomRight: Radius.circular(10.0))),
),
),
Align(
//BOTTOM BORDER
alignment: Alignment.bottomCenter,
child: Container(
height: 15,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(
width: 3.0, color: Colors.transparent),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(10.0),
bottomRight: Radius.circular(10.0))),
),
),
Align(
//TOPLEFT BORDER
alignment: Alignment.topLeft,
child: Container(
decoration: BoxDecoration(
gradient: SweepGradient(
center: FractionalOffset.bottomRight,
startAngle: pi * 1.349,
endAngle: pi * 1.35,
colors: [
Colors.blue[100],
Colors.blue[200],
]),
border:
Border.all(width: 3.0, color: Colors.transparent),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
),
),
width: 30,
height: 50,
),
),
Align(
//TOP RIGHT BORDER
alignment: Alignment.topRight,
child: Container(
decoration: BoxDecoration(
gradient: SweepGradient(
center: FractionalOffset.topRight,
startAngle: 1.86,
endAngle: 1.87,
colors: [
Colors.blue[300],
Colors.blue[200],
]),
border: Border.all(
width: 3.0, color: Colors.transparent),
borderRadius: BorderRadius.only(
topRight: Radius.circular(10.0),
)),
width: 15,
height: 50,
)),
Align(
//BOTTOMLEFT BORDER
alignment: Alignment.bottomLeft,
child: Container(
decoration: BoxDecoration(
gradient: SweepGradient(
center: FractionalOffset.bottomLeft,
startAngle: 5.8,
endAngle: 5.81,
colors: [
Colors.blue[100],
Colors.blue,
]),
border:
Border.all(width: 3.0, color: Colors.transparent),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(10.0),
),
),
width: 30,
height: 15,
),
),
Align(
//BOTTOM RIGHT BORDER
alignment: Alignment.bottomRight,
child: Container(
decoration: BoxDecoration(
gradient: SweepGradient(
center: FractionalOffset.bottomRight,
startAngle: 3.99,
endAngle: 4,
colors: [
Colors.blue,
Colors.blue[300],
]),
border: Border.all(
width: 3.0, color: Colors.transparent),
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(10.0),
// bottomRight: Radius.circular(10.0)
)),
width: 15,
height: 15,
))
])),
));
【讨论】:
这个其实挺好的。也许可以更好地构建它,以便可以动态更改边框的宽度、半径等。 同意,如果它投入生产我会这样做,这只是原型解决方案。【参考方案2】:这不是 100% 有效的解决方案。我刚刚编写了一些路径函数来使用CustomPainter
绘制所有边。
圆形ChiseledBorder
class RoundedChiseledBorder extends StatelessWidget
final Widget child;
final Color leftBorderColor;
final Color rightBorderColor;
final Color bottomBorderColor;
final Color topBorderColor;
final double borderRadius;
final double borderWidth;
RoundedChiseledBorder(
@required this.child,
this.borderRadius = 1,
this.borderWidth = 2,
this.bottomBorderColor = Colors.black,
this.topBorderColor = Colors.black,
this.rightBorderColor = Colors.black,
this.leftBorderColor = Colors.black,
);
@override
Widget build(BuildContext context)
return Stack(
children: <Widget>[
Positioned.fill(
child: CustomPaint(
painter: RoundedBorderPainter(
radius: borderRadius,
strokeWidth: borderWidth,
bottomBorderColor: bottomBorderColor,
leftBorderColor: leftBorderColor,
rightBorderColor: rightBorderColor,
topBorderColor: topBorderColor,
),
),
),
child,
],
);
RoundedBorderPainterBorder
import 'package:vector_math/vector_math.dart' as vm;
class RoundedBorderPainter extends CustomPainter
final Color leftBorderColor;
final Color rightBorderColor;
final Color bottomBorderColor;
final Color topBorderColor;
final double strokeWidth;
final StrokeCap strokeCap = StrokeCap.round;
double radius;
Size size;
RoundedBorderPainter(
this.leftBorderColor = Colors.black,
this.rightBorderColor = Colors.black,
this.topBorderColor = Colors.black,
this.bottomBorderColor = Colors.black,
this.strokeWidth = 2,
this.radius = 1,
)
if (radius <= 1) this.radius = 1;
@override
void paint(Canvas canvas, Size size)
radius = size.shortestSide / 2 < radius ? size.shortestSide / 2 : radius;
this.size = size;
Paint topPaint = Paint()
..color = topBorderColor
..strokeWidth = strokeWidth
..strokeCap = strokeCap
..style = PaintingStyle.stroke;
Paint rightPaint = Paint()
..color = rightBorderColor
..strokeCap = strokeCap
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke;
Paint bottomPaint = Paint()
..color = bottomBorderColor
..strokeCap = strokeCap
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke;
Paint leftPaint = Paint()
..strokeCap = strokeCap
..color = leftBorderColor
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke;
canvas.drawPath(getPath1(), topPaint);
canvas.drawPath(getPath2(), rightPaint);
canvas.drawPath(getPath3(), bottomPaint);
canvas.drawPath(getPath4(), leftPaint);
Path getPath1()
return Path()
..addPath(getTopLeftPath2(), Offset(0, 0))
..addPath(getTopPath(), Offset(0, 0))
..addPath(getTopRightPath1(), Offset(0, 0));
Path getPath2()
return Path()
..addPath(getTopRightPath2(), Offset(0, 0))
..addPath(getRightPath(), Offset(0, 0))
..addPath(getBottomRightPath1(), Offset(0, 0));
Path getPath3()
return Path()
..addPath(getBottomRightPath2(), Offset(0, 0))
..addPath(getBottomPath(), Offset(0, 0))
..addPath(getBottomLeftPath1(), Offset(0, 0));
Path getPath4()
return Path()
..addPath(getBottomLeftPath2(), Offset(0, 0))
..addPath(getLeftPath(), Offset(0, 0))
..addPath(getTopLeftPath1(), Offset(0, 0));
Path getTopPath()
return Path()
..moveTo(0 + radius, 0)
..lineTo(size.width - radius, 0);
Path getRightPath()
return Path()
..moveTo(size.width, 0 + radius)
..lineTo(size.width, size.height - radius);
Path getBottomPath()
return Path()
..moveTo(size.width - radius, size.height)
..lineTo(0 + radius, size.height);
Path getLeftPath()
return Path()
..moveTo(0, size.height - radius)
..lineTo(0, 0 + radius);
Path getTopRightPath1()
return Path()
..addArc(
Rect.fromLTWH(size.width - (radius * 2), 0, radius * 2, radius * 2),
vm.radians(-45),
vm.radians(-45),
);
Path getTopRightPath2()
return Path()
..addArc(
Rect.fromLTWH(size.width - (radius * 2), 0, radius * 2, radius * 2),
vm.radians(0),
vm.radians(-45),
);
Path getBottomRightPath1()
return Path()
..addArc(
Rect.fromLTWH(size.width - (radius * 2), size.height - (radius * 2), radius * 2, radius * 2),
vm.radians(45),
vm.radians(-45),
);
Path getBottomRightPath2()
return Path()
..addArc(
Rect.fromLTWH(size.width - (radius * 2), size.height - (radius * 2), radius * 2, radius * 2),
vm.radians(90),
vm.radians(-45),
);
Path getBottomLeftPath1()
return Path()
..addArc(
Rect.fromLTWH(0, size.height - (radius * 2), radius * 2, radius * 2),
vm.radians(135),
vm.radians(-45),
);
Path getBottomLeftPath2()
return Path()
..addArc(
Rect.fromLTWH(0, size.height - (radius * 2), radius * 2, radius * 2),
vm.radians(180),
vm.radians(-45),
);
Path getTopLeftPath1()
return Path()
..addArc(
Rect.fromLTWH(0, 0, radius * 2, radius * 2),
vm.radians(225),
vm.radians(-45),
);
Path getTopLeftPath2()
return Path()
..addArc(
Rect.fromLTWH(0, 0, radius * 2, radius * 2),
vm.radians(270),
vm.radians(-45),
);
@override
bool shouldRepaint(CustomPainter oldDelegate)
return true;
用法
要使用它,只需调用
class HomePage extends StatelessWidget
@override
Widget build(BuildContext context)
return Scaffold(
body: Center(
child: RoundedChiseledBorder(
borderRadius: 10,
borderWidth: 4,
bottomBorderColor: Colors.red,
leftBorderColor: Colors.black,
rightBorderColor: Colors.amber,
topBorderColor: Colors.green,
child: Container(
height: 30,
width: 300,
alignment: Alignment.center,
child: Text('Hello'),
),
),
),
);
【讨论】:
哇,这么长。这一切都是因为他们忘记为BorderSide
添加边框半径选项。无论如何,我会尝试一下,看看它是如何工作的。
平心而论,它可以简化,代码可以更短。我会把它构建成一个包,并在我有时间的时候将它推送到 pub。
是的,听起来很棒。在 Flutter 中有这个选项当然很有用。 :)
如果你想看看,不确定是否有人可以在这里回答我的其他 Flutter 按钮问题:***.com/questions/56930636/…
现在尝试,但在RoundedBorderPainter
中获取vm
是未定义的?以上是关于颤动圆角矩形边框,每边具有不同的颜色的主要内容,如果未能解决你的问题,请参考以下文章