Flutter 116: 圖解 PhysicalModel & PhysicalShape 裁切小組件

    小菜在學習過程中發現僅通過設置 decoration 是無法改變 Container 內部 Widget 樣式的,一般我們會通過 ClipX 方式進行裁剪;而一旦涉及到 Widget 的裁剪,對於基礎樣式,通常會採用 ClipOval / ClipRRect / ClipPath 等方式,對於稍加複雜的樣式,一般通過自定義 CustomClipper 也可以滿足;

    而小菜今天嘗試另一種類似的 PhysicalModel 方式;PhysicalX 方式可以設置 Widget 陰影效果,而 ClipX 方式不行;

PhysicalModel

源碼分析

const PhysicalModel({
    Key key,
    this.shape = BoxShape.rectangle,    // 裁剪樣式
    this.clipBehavior = Clip.none,      // 裁剪方式
    this.borderRadius,                  // 圓角弧度
    this.elevation = 0.0,               // 陰影高度
    @required this.color,               // 背景色
    this.shadowColor = const Color(0xFF000000),     // 陰影顏色
    Widget child,
})

    分析源碼可得,PhysicalModel 是一個樣式相對單一的裁切 Widget,其中 color 背景色是必不可少的,而真正影響陰影顏色的爲 shadowColor

案例嘗試

1. shape & borderRadius

    shape 爲裁剪樣式,包括 BoxShape.rectangleBoxShape.circle 兩種,分別對應 ClipRRectClipOval,而 borderRadius 只有在 rectangle 圓角情況下生效;其中 clipBehavior 與其他涉及到裁切方式一致;

// ClipX
return ClipRRect(
    borderRadius: BorderRadius.all(Radius.circular(20.0)),
    child: Container(width: 80.0, height: 80.0, child: Image.asset('images/icon_hzw01.jpg')));
return Container(
    width: 80.0, height: 80.0,
    child: ClipOval(child: Image.asset('images/icon_hzw01.jpg', fit: BoxFit.fill)));

// PhysicalModel
return PhysicalModel(
    color: Colors.transparent,
    shape: isCircle ? BoxShape.circle : BoxShape.rectangle,
    clipBehavior: Clip.antiAlias,
    borderRadius: BorderRadius.all(Radius.circular(20.0)),
    child: Container(width: 80.0, height: 80.0, child: Image.asset('images/icon_hzw01.jpg')));
2. color & shadowColor

    color 對應 Widget 裁切的背景色,陰影效果是根據當前背景色展示,shadowColor 可以設置陰影顏色;

return PhysicalModel(
    color: Colors.teal.withOpacity(0.6),
    shape: isCircle ? BoxShape.circle : BoxShape.rectangle,
    clipBehavior: Clip.antiAlias,
    elevation: 6.0,
    shadowColor: isCircle ? Colors.yellow : Colors.deepOrange,
    borderRadius: BorderRadius.all(Radius.circular(20.0)),
    child: Container(width: 80.0, height: 80.0, color: Colors.pink.withOpacity(0.2)));

PhysicalShape

源碼分析

const PhysicalShape({
    Key key,
    @required this.clipper,
    this.clipBehavior = Clip.none,
    this.elevation = 0.0,
    @required this.color,
    this.shadowColor = const Color(0xFF000000),
    Widget child,
})

    分析源碼可得,PhysicalShapeClipPath 類似,均是通過 clipper 方式自定義裁切方式;

案例嘗試

    小菜嘗試裁切一個多邊形圖片,通過自定義 CustomClipper 來設置裁切樣式;

return ClipPath(
    clipBehavior: Clip.antiAlias,
    clipper: PolygonClipper(10),
    child: Container(width: 80.0, height: 80.0, child: Image.asset('images/icon_hzw01.jpg')));

return PhysicalShape(
    color: Colors.transparent,
    clipBehavior: Clip.antiAlias,
    clipper: PolygonClipper(10),
    shadowColor: Colors.deepOrange,
    elevation: 6.0,
    child: Container(width: 80.0, height: 80.0, child: Image.asset('images/icon_hzw01.jpg')));

class PolygonClipper extends CustomClipper<Path> {
  final int num;
  PolygonClipper(this.num);

  @override
  Path getClip(Size size) {
    double radius = size.width * 0.5;
    Path _path = Path();
    var startX = size.width * 0.5 + radius * cos(2 * pi * 0 / num);
    var startY = size.height * 0.5 + radius * sin(2 * pi * 0 / num);
    _path.moveTo(startX, startY);
    for (var i = 1; i <= num; i++) {
      var newX = size.width * 0.5 + radius * cos(2 * pi * i / num);
      var newY = size.height * 0.5 + radius * sin(2 * pi * i / num);
      _path.lineTo(newX, newY);
    }
    _path.close();
    return _path;
  }

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

    PhysicalModel & PhysicalShape 案例源碼


    小菜對於更深入的裁切方式還不夠深入,Flutter 同時提供了對應帶有動畫效果的 AnimatedPhysicalModel;如有錯誤,請多多指導!

來源: 阿策小和尚

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章