22

【Flutter 实战】各种各样形状的组件

 3 years ago
source link: https://segmentfault.com/a/1190000023943212
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

MveiIrB.png!mobile

老孟导读 :Flutter中很多组件都有一个叫做 shape 的属性,类型是 ShapeBorder ,比如 Button 类、 Card 等组件, shape 表示控件的形状,系统已经为我们提供了很多形状,对于没有此属性的组件,可以使用 Clip 类组件进行裁减。

BeveledRectangleBorder

斜角矩形边框,用法如下:

RaisedButton(
  shape: BeveledRectangleBorder(
      side: BorderSide(width: 1, color: Colors.red),
      borderRadius: BorderRadius.circular(10)),
  child: Text('老孟'),
  onPressed: () {},
)

AjYreya.png!mobile

如果设置的半径比控件还大,就会变成 菱形

3RaisedButton(
  shape: BeveledRectangleBorder(
      side: BorderSide(width: 1, color: Colors.red),
      borderRadius: BorderRadius.circular(100)),
  child: Text('老孟'),
  onPressed: () {},
)

zUBNJ3j.png!mobile

同理,如果半径设置为0,就是矩形。

RaisedButton(
  shape: BeveledRectangleBorder(
      side: BorderSide(width: 1, color: Colors.red),
      borderRadius: BorderRadius.circular(0)),
  child: Text('老孟'),
  onPressed: () {},
)

zANn6vf.png!mobile

Border

Border允许单独设置每一个边上的线条样式.

RaisedButton(
  shape: Border(
    top: BorderSide(color: Colors.red,width: 2)
  ),
  child: Text('老孟'),
  onPressed: () {},
)

Unq2I37.png!mobile

设置全部

RaisedButton(
        shape: Border(
          top: BorderSide(color: Colors.red,width: 10),
          right: BorderSide(color: Colors.blue,width: 10),
          bottom: BorderSide(color: Colors.yellow,width: 10),
          left: BorderSide(color: Colors.green,width: 10),
        ),
        child: Text('老孟'),
        onPressed: () {},
      )

q6jmqey.png!mobile

BorderDirectional

BorderDirectional和 Border 基本一样,区别就是 BorderDirectional 带有阅读方向,大部分国家阅读是从左到右,但有的国家是从右到左的,比如阿拉伯等。

RaisedButton(
  shape: BorderDirectional(
    start: BorderSide(color: Colors.red,width: 2),
    end: BorderSide(color: Colors.blue,width: 2),
  ),
  child: Text('老孟'),
  onPressed: () {},
)

EJrMNvB.png!mobile

CircleBorder

圆形

RaisedButton(
  shape: CircleBorder(side: BorderSide(color: Colors.red)),
  child: Text('老孟'),
  onPressed: () {},
)

jEvM32q.png!mobile

ContinuousRectangleBorder

连续的圆角矩形,直线和圆角平滑连续的过渡,和RoundedRectangleBorder相比,圆角效果会小一些。

RaisedButton(
  shape: ContinuousRectangleBorder(
      side: BorderSide(color: Colors.red),
      borderRadius: BorderRadius.circular(20)),
  child: Text('老孟'),
  onPressed: () {},
)

AZJ3Qfy.png!mobile

RoundedRectangleBorder

圆角矩形

RaisedButton(
  shape: RoundedRectangleBorder(
      side: BorderSide(color: Colors.red),
      borderRadius: BorderRadius.circular(10)),
  child: Text('老孟'),
  onPressed: () {},
)

aIRz6z7.png!mobile

StadiumBorder

类似 足球场 的形状,两边圆形,中间矩形

RaisedButton(
  shape: StadiumBorder(
      side: BorderSide(color: Colors.red),),
  child: Text('老孟'),
  onPressed: () {},
)

rQRNfaI.png!mobile

OutlineInputBorder

带外边框

RaisedButton(
  shape: OutlineInputBorder(
    borderSide: BorderSide(color: Colors.red),
    borderRadius: BorderRadius.circular(10),
  ),
  child: Text('老孟'),
  onPressed: () {},
)

iuYfMfV.png!mobile

UnderlineInputBorder

下划线边框

RaisedButton(
  shape: UnderlineInputBorder(
    borderSide: BorderSide(color: Colors.red),
  ),
  child: Text('老孟'),
  onPressed: () {},
)

veMZ7je.png!mobile

ClipRect

ClipRect组件使用矩形裁剪子组件,通常情况下,ClipRect作用于 CustomPaintCustomSingleChildLayoutCustomMultiChildLayoutAlignCenterOverflowBoxSizedOverflowBox 组件,例如ClipRect作用于Align,可以仅显示上半部分,代码如下:

ClipRect(
  child: Align(
    alignment: Alignment.topCenter,
    heightFactor: 0.5,
    child: Container(
      height: 150,
      width: 150,
      child: Image.asset(
        'images/1.png',
        fit: BoxFit.cover,
      ),
    ),
  ),
)

全图效果:

<img src="https://img-blog.csdnimg.cn/20200324160500474.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21lbmdrczE5ODc=,size_16,color_FFFFFF,t_70" style="zoom:50%;" />

裁剪效果:

<img src="https://img-blog.csdnimg.cn/20200324160537832.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21lbmdrczE5ODc=,size_16,color_FFFFFF,t_70" style="zoom:50%;" />

clipper 参数定义裁剪规则,下面具体介绍。

clipBehavior 参数定义了裁剪的方式,只有子控件超出父控件的范围才有裁剪的说法,各个方式说明如下:

  • none:不裁剪,系统默认值,如果子组件不超出边界,此值没有任何性能消耗。
  • hardEdge:裁剪但不应用抗锯齿,速度比 none 慢一点,但比其他方式快。
  • antiAlias:裁剪而且抗锯齿,此方式看起来更平滑,比 antiAliasWithSaveLayer 快,比 hardEdge 慢,通常用于处理圆形和弧形裁剪。
  • antiAliasWithSaveLayer:裁剪、抗锯齿而且有一个缓冲区,此方式很慢,用到的情况比较少。

ClipRRect

ClipRRect组件可以对子组件进行圆角裁剪,默认圆角半径为0,注意ClipRRect有2个R,不是上面介绍的ClipRect。

用法如下:

ClipRRect(
  borderRadius: BorderRadius.circular(20),
  child: Container(
    height: 150,
    width: 150,
    child: Image.asset(
      'images/1.png',
      fit: BoxFit.cover,
    ),
  ),
)

效果如图:

<img src="https://img-blog.csdnimg.cn/20200324160615913.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21lbmdrczE5ODc=,size_16,color_FFFFFF,t_70" style="zoom:50%;" />

ClipOval

ClipOval裁剪为椭圆形,椭圆形的大小为正切父组件,因此如果父组件为正方形,切出来是圆形,用法如下:

ClipOval(
  child: Container(
    height: 150,
    width: 250,
    child: Image.asset(
      'images/1.png',
      fit: BoxFit.cover,
    ),
  ),
)

效果如下:

<img src="https://img-blog.csdnimg.cn/20200324160734820.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21lbmdrczE5ODc=,size_16,color_FFFFFF,t_70" style="zoom:50%;" />

ClipPath

ClipPath组件根据路径进行裁剪,我们自定义裁剪路径也可以使用系统提供的,用法如下:

ClipPath.shape(
  shape: StadiumBorder(),
  child: Container(
    height: 150,
    width: 250,
    child: Image.asset(
      'images/1.png',
      fit: BoxFit.cover,
    ),
  ),
)

shape 参数是ShapeBorder类型,系统已经定义了很多形状,介绍如下:

  • RoundedRectangleBorder:圆角矩形
  • ContinuousRectangleBorder:直线和圆角平滑连续的过渡,和RoundedRectangleBorder相比,圆角效果会小一些。
  • StadiumBorder:类似于足球场的形状,两端半圆。
  • BeveledRectangleBorder:斜角矩形。效果如图:

    <img src="https://img-blog.csdnimg.cn/20200324160806686.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21lbmdrczE5ODc=,size_16,color_FFFFFF,t_70" style="zoom:50%;" />

  • CircleBorder:圆形。

CustomClipper

CustomClipper并不是一个组件,而是一个 abstract (抽象)类,使用CustomClipper可以绘制出任何我们想要的形状,比如三角形,代码如下:

@override
Widget build(BuildContext context) {
  return Center(
    child: ClipPath(
      clipper: TrianglePath(),
      child: Container(
        height: 150,
        width: 250,
        child: Image.asset(
          'images/1.png',
          fit: BoxFit.cover,
        ),
      ),
    ),
  );
}

自定义TrianglePath代码如下:

class TrianglePath extends CustomClipper<Path>{
  @override
  Path getClip(Size size) {
    var path = Path();
    path.moveTo(size.width/2, 0);
    path.lineTo(0, size.height);
    path.lineTo(size.width, size.height);
    return path;
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) {
    return true;
  }
}

效果如下:

<img src="https://img-blog.csdnimg.cn/20200324160835511.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21lbmdrczE5ODc=,size_16,color_FFFFFF,t_70" style="zoom:50%;" />

我们还可以绘制五角星,代码如下:

class StarPath extends CustomClipper<Path> {
  StarPath({this.scale = 2.5});

  final double scale;

  double perDegree = 36;

  /// 角度转弧度公式
  double degree2Radian(double degree) {
    return (pi * degree / 180);
  }

  @override
  Path getClip(Size size) {
    var R = min(size.width / 2, size.height / 2);
    var r = R / scale;
    var x = size.width / 2;
    var y = size.height / 2;

    var path = Path();
    path.moveTo(x, y - R);
    path.lineTo(x - sin(degree2Radian(perDegree)) * r,
        y - cos(degree2Radian(perDegree)) * r);
    path.lineTo(x - sin(degree2Radian(perDegree * 2)) * R,
        y - cos(degree2Radian(perDegree * 2)) * R);
    path.lineTo(x - sin(degree2Radian(perDegree * 3)) * r,
        y - cos(degree2Radian(perDegree * 3)) * r);
    path.lineTo(x - sin(degree2Radian(perDegree * 4)) * R,
        y - cos(degree2Radian(perDegree * 4)) * R);
    path.lineTo(x - sin(degree2Radian(perDegree * 5)) * r,
        y - cos(degree2Radian(perDegree * 5)) * r);
    path.lineTo(x - sin(degree2Radian(perDegree * 6)) * R,
        y - cos(degree2Radian(perDegree * 6)) * R);
    path.lineTo(x - sin(degree2Radian(perDegree * 7)) * r,
        y - cos(degree2Radian(perDegree * 7)) * r);
    path.lineTo(x - sin(degree2Radian(perDegree * 8)) * R,
        y - cos(degree2Radian(perDegree * 8)) * R);
    path.lineTo(x - sin(degree2Radian(perDegree * 9)) * r,
        y - cos(degree2Radian(perDegree * 9)) * r);
    path.lineTo(x - sin(degree2Radian(perDegree * 10)) * R,
        y - cos(degree2Radian(perDegree * 10)) * R);
    return path;
  }

  @override
  bool shouldReclip(StarPath oldClipper) {
    return oldClipper.scale != this.scale;
  }
}

scale 参数表示间隔的点到圆心的缩放比例,五角星效果如下:

<img src="https://img-blog.csdnimg.cn/2020032416085643.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21lbmdrczE5ODc=,size_16,color_FFFFFF,t_70" style="zoom:50%;" />

下面用动画动态设置 scale ,代码如下:

class StartClip extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _StartClipState();
}

class _StartClipState extends State<StartClip>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation _animation;

  @override
  void initState() {
    _controller =
        AnimationController(duration: Duration(seconds: 2), vsync: this)
          ..addStatusListener((status) {
            if (status == AnimationStatus.completed) {
              _controller.reverse();
            } else if (status == AnimationStatus.dismissed) {
              _controller.forward();
            }
          });
    _animation = Tween(begin: 1.0, end: 4.0).animate(_controller);
    _controller.forward();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: AnimatedBuilder(
          animation: _animation,
          builder: (context, child) {
            return ClipPath(
              clipper: StarPath(scale: _animation.value),
              child: Container(
                height: 150,
                width: 150,
                color: Colors.red,
              ),
            );
          }),
    );
  }
}

效果如下:

rey2qmI.gif!mobile

交流

老孟Flutter博客地址(330个控件用法): http://laomengit.com

欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:

aem6biy.png!mobile 2YNJNfb.png!mobile

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK