颤动圆角矩形边框,每边具有不同的颜色

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 是未定义的?

以上是关于颤动圆角矩形边框,每边具有不同的颜色的主要内容,如果未能解决你的问题,请参考以下文章

Android:如下关于绘制圆角矩形边框问题,怎么解决?

UIAlertViewController 中具有圆角矩形边框样式的文本字段。 (迅速)

SwiftUI:将背景颜色应用于圆角矩形

canvas怎么画一个渐变的圆角边框,填充的也行

安卓用shape画圆角矩形边框

android 圆角边框 阴影边框怎么设置