Flutter 實現「斑馬紋」背景(需要變換顏色)

2022-06-17 21:02:26

Flutter 實現「斑馬紋」背景

由於工作中專案需求,需要將H5轉換為Flutter程式碼。
其中的斑馬紋背景需要根據介面返回的顏色來渲染,所以不能只是圖片形式,無法通過decoration屬性設定圖片背景板。

樓主這邊想到的方法就是通過 實現一個canvas繪製斑馬紋類。使用Stack佈局,將斑馬紋放在下方作為背景板,需要展示的內容在上方。
實現 「斑馬紋」背景(需要變換顏色)

文章主要分為 效果圖、實現思維、程式碼、計算過程解釋。希望對大家有所幫助

最終效果圖

實現思維

斑馬紋(45°角,向左傾斜)

使用CustomPaint(size: Size(width, height), painter: 畫筆)

CustomPaint(
   size: Size(widget.width, widget.height),
   painter: 畫筆,
)

畫筆

繼承 CustomPainter類,實現paint(Canvas canvas, Size size)方法,根據 寬度、高度、畫筆寬度、間距 計算出各個點位。使用canvas. drawLine方法 繪製出斑馬紋。

@override
void paint(Canvas canvas, Size size) {
  …
  canvas. drawLine();
}

斑馬紋座標位置計算

2.82 = 2倍根號2
1.41 = 根號二

填充個數= 最大高度 / (畫筆寬度1.41+間距) 向上取整。(從0, 0開始,左側會露出空位,所以需要填充)
條紋個數 = 寬度/(畫筆寬度
1.41+間距) 向上取整。
(x軸y軸) 偏移量 =畫筆寬度 / 2.82 (畫筆起始點、結束點會露出一小節,需要計算x,y偏移量。將左上角x,y減去偏移量,右下角x,y加上偏移量,補充此部分)
起點座標 =((畫筆寬度1.41+間距) * 條紋index – 偏移量,– 偏移量)
終點座標 =((畫筆寬度
1.41+間距) * 條紋index - 偏移量+高度, 高度+偏移量)

圓角裁剪(如果需要)

由於畫筆繪製的是直角的,所以作為背景板會超出,需要裁剪掉四個角。使用

ClipRRect(
   borderRadius: BorderRadius.all(Radius.circular(10)),
   child: xxx
)

作為背景

使用Stack佈局,實現斑馬紋在下方作為背景板,需要展示的內容在上方

Stack(
  children: [
  	buildZebraBack(…), 
  	需要展示的內容
  ]
)

程式碼

使用處 main_page.dart

Stack(
  children: [
    Positioned(
      child: ZebraStripesBack(
          width: 335,
          height: 44,
          lineWidth: 10,
          spacing: 10,
          borderRaduis: 10,
          lineColor: Colors.blue),
      top: 0,
      left: 0,
    ),
    Container(
      width: 335,
      height: 44,
      alignment: Alignment.center,
      padding: EdgeInsets.only(
          top: 10,
          left: 12,
          bottom: 10,
          right: 12),
      child: Text(
              "英語",
              style: TextStyle(
                color: Color(0xFFFFFFFF),
                fontSize: 14.sp,
                fontWeight: FontWeight.w500),
            )
      )
  ]
)

斑馬紋具體實現類 zebra_stripes_back.dart

import 'dart:math';
import 'package:flutter/material.dart';

// 斑馬紋具體實現類
class ZebraStripesBack extends StatefulWidget {
  ZebraStripesBack({
    this.width: 0,
    this.height: 0,
    this.spacing: 4,
    this.lineWidth: 4,
    this.lineColor: Colors.transparent,
    this.borderRaduis: 0,
  });

  final double width; // 容器寬度
  final double height; // 容器高度 
  final double lineWidth; // 斑馬紋寬度
  final double spacing; // 間距
  final double borderRaduis; // 容器圓角
  final Color lineColor; // 斑馬紋顏色

  @override
  State<StatefulWidget> createState() => _ZebraStripesBackState();
}

class _ZebraStripesBackState extends State<ZebraStripesBack> {
  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ClipRRect(
        borderRadius: BorderRadius.all(Radius.circular(widget.borderRaduis)),
        child: CustomPaint(
          size: Size(widget.width, widget.height),
          painter: _ZebraStripesBackPainter(
            maxWidth: widget.width,
            maxHeight: widget.height,
            spacing: widget.spacing,
            lineWidth: widget.lineWidth,
            lineColor: widget.lineColor,
            borderRaduis: widget.borderRaduis,
          ),
        ));
  }
}

class _ZebraStripesBackPainter extends CustomPainter {
  _ZebraStripesBackPainter({
    this.maxWidth: 0,
    this.maxHeight: 0,
    this.spacing: 4,
    this.lineWidth: 4,
    this.lineColor: Colors.black12,
    this.borderRaduis: 0,
  });

  final double maxWidth;
  final double maxHeight;
  final double spacing;
  final double lineWidth;
  final Color lineColor;
  final double borderRaduis;

  @override
  void paint(Canvas canvas, Size size) {

    var paint = Paint()
      ..isAntiAlias = true
      ..style = PaintingStyle.fill
      ..color = lineColor
      ..strokeWidth = lineWidth;

    int number = 0; // 個數
    int fillNumber = 0; // 填充個數
    double lineAndSpace = lineWidth *1.41 + spacing; // 單個條紋寬 + 間距寬
    if (lineWidth > 0) {
      number = (maxWidth / lineAndSpace).ceil();
      fillNumber = (maxHeight / lineAndSpace).ceil(); // 填充個數
    }

    double deviation = lineWidth / 2.82; // x y軸偏移量 = width / 2倍根號2
    for (int i = -fillNumber; i < number; i++) {
      var left = lineAndSpace * i - deviation;
      double dx = left;
      double dy = -deviation;
      double dx1 = left + maxHeight;
      double dy1 = maxHeight + deviation;
      canvas.drawLine(
        Offset(dx, dy),
        Offset(dx1, dy1),
        paint,
      );
    }
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}

計算過程解釋

偏移量計算過程

填充個數計算過程

為什麼畫筆寬度需要乘 根號二?

缺少-填充

缺少-偏移量