效果图

Flutter本身没有直接绘制虚线的内置组件,所以需要自定义画虚线的工具类
Dart
import 'package:flutter/material.dart';
//绘制垂直虚线的工具类
class DashPainter extends CustomPainter {
//1.定义常量
final Color color;
final double strokeWidth;
final double dashWidth;
final double dashSpace;
//2.构造函数
DashPainter({
this.color = Colors.black, //虚线颜色,默认黑色
this.strokeWidth = 1.0, //线宽,默认1像素
this.dashWidth = 3.0,//每个短划的长度,默认3像素
this.dashSpace = 2.0,//短滑之间的距离,默认2像素
});
@override
void paint(Canvas canvas, Size size) { //size(215)来自CustomPaint的size属性,size由Flutter自动传入
//3.创建画笔
final paint = Paint()
..color = color//设置颜色
..strokeWidth = strokeWidth//设置线宽
..style = PaintingStyle.stroke;//设置为描边模式
// 4.计算x坐标,让线条在容器中居中
final x = strokeWidth / 2;
double startY = 0;//起始Y坐标
//5.循环直到画满整个高度
while (startY < size.height) {
//绘制一个短划
canvas.drawLine(
Offset(x, startY), //起点坐标,X坐标固定,Y变化,所以是绘制垂直线
Offset(x, startY + dashWidth),//终点坐标,X坐标固定,Y变化,所以是绘制垂直线
paint,//使用的画笔
);
//移动到下一个短划的起始位置
startY += dashWidth + dashSpace;
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
使用方式
Dart
Positioned( //确定位置
left: 280 / 4,//水平位置
child: CustomPaint(//设置画布
size: Size(0.5, 215), // 宽度0.5,高度215
painter: DashPainter(
color: Color(0xFF878787),
strokeWidth: 0.5,//线条粗细
dashWidth: 3,//每段虚线长度
dashSpace: 2,//虚线间隔
),
),
),
数据流
Dart
Positioned → CustomPaint → DashPainter构造函数 → paint方法 → 绘制到屏幕
↓ ↓ ↓ ↓ ↓
位置定位 设置画布大小 接收绘制参数 执行绘制逻辑 显示虚线
①Positioned定位:left = 70
②CustomPaint设置画布,画布大小:宽0.5px,高215px
③DashPainter 构造函数接收参数
④paint 方法拿到参数,创建画笔和计算参数: x = 0.5 ÷ 2 = 0.25(居中位置),Y默认为0
⑤执行循环划线的方法,直到Y大于215
⑥虚线画完,显示在屏幕上
完整调用栈
-
Flutter框架构建Positioned
-
Positioned创建CustomPaint
-
CustomPaint创建DashPainter实例
-
DashPainter构造函数保存参数
-
Flutter调用DashPainter.paint()
-
paint()方法执行绘制循环
-
43次canvas.drawLine()调用
-
结果显示在屏幕上
代码实例
Dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:my_flutter/dash_painter.dart';
class HomePage extends StatefulWidget{
const HomePage({super.key});
@override
State<StatefulWidget> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>{
//UI构建
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.only(left: 20,top: 200),//内边距
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Y轴坐标标签和表格行
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Y轴标签(左侧)
Container(
width: 20,
height: 215,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('180', style: TextStyle(fontSize: 10)),
Text('120', style: TextStyle(fontSize: 10)),
Text('60', style: TextStyle(fontSize: 10)),
Text('0', style: TextStyle(fontSize: 10)),
],
),
),
// 表格主体
_buildTodayTable(),
],
),
// X轴坐标标签(底部)
Container(
padding: EdgeInsets.only(left: 20, right: 45),
width: 305,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("0时", style: TextStyle(fontSize: 10)),
Text("6时", style: TextStyle(fontSize: 10)),
Text("12时", style: TextStyle(fontSize: 10)),
Text("18时", style: TextStyle(fontSize: 10)),
],
),
),
],
),
),
);
}
//表格的主体
Widget _buildTodayTable() {
return Container(
width: 280,
height: 215,
decoration: BoxDecoration(
//边框
border: Border(
left: BorderSide(color: Color(0xFF878787), width: 0.5),
right: BorderSide(color: Color(0xFF878787), width: 0.5),
bottom: BorderSide(color: Color(0xFF878787), width: 0.5),
top: BorderSide(color: Color(0xFF878787), width: 0.5),
),
),
child: Stack(
children: [
// 第一条中间水平线
Positioned(
top: 215 / 3,
child: Container(
width: 305,
height: 0.5,
color: Color(0xFF878787),
),
),
// 第二条中间水平线
Positioned(
top: 215 * 2 / 3,
child: Container(
width: 305,
height: 0.5,
color: Color(0xFF878787),
),
),
// 垂直线(全虚线)
Positioned( //确定位置
left: 280 / 4,//水平位置
child: CustomPaint(//设置画布
size: Size(0.5, 215), // 宽度0.5,高度215
painter: DashPainter(
color: Color(0xFF878787),
strokeWidth: 0.5,//线条粗细
dashWidth: 3,//每段虚线长度
dashSpace: 2,//虚线间隔
),
),
),
Positioned(
left: 280 / 2,
child: CustomPaint(
size: Size(0.5, 215),
painter: DashPainter(
color: Color(0xFF878787),
strokeWidth: 0.5,
dashWidth: 3,
dashSpace: 2,
),
),
),
Positioned(
left: 280 * 3 / 4,
child: CustomPaint(
size: Size(0.5, 215),
painter: DashPainter(
color: Color(0xFF878787),
strokeWidth: 0.5,
dashWidth: 3,
dashSpace: 2,
),
),
),
],
),
);
}
}