Flutter---坐标网格图标

效果图

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

⑥虚线画完,显示在屏幕上

完整调用栈

  1. Flutter框架构建Positioned

  2. Positioned创建CustomPaint

  3. CustomPaint创建DashPainter实例

  4. DashPainter构造函数保存参数

  5. Flutter调用DashPainter.paint()

  6. paint()方法执行绘制循环

  7. 43次canvas.drawLine()调用

  8. 结果显示在屏幕上

代码实例

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,
              ),
            ),
          ),
    ],
    ),
    );
  }
}
相关推荐
wuhen_n2 小时前
网络请求在Vite层的代理与Mock:告别跨域和后端依赖
前端·javascript·vue.js
用户69371750013847 小时前
Google 正在“收紧侧加载”:陌生 APK 安装或需等待 24 小时
android·前端
蓝帆傲亦7 小时前
Web 前端搜索文字高亮实现方法汇总
前端
用户69371750013847 小时前
Room 3.0:这次不是升级,是重来
android·前端·google
漫随流水8 小时前
旅游推荐系统(view.py)
前端·数据库·python·旅游
踩着两条虫9 小时前
VTJ.PRO 核心架构全公开!从设计稿到代码,揭秘AI智能体如何“听懂人话”
前端·vue.js·ai编程
jzlhll12310 小时前
kotlin Flow first() last()总结
开发语言·前端·kotlin
用头发抵命11 小时前
Vue 3 中优雅地集成 Video.js 播放器:从组件封装到功能定制
开发语言·javascript·ecmascript
蓝冰凌11 小时前
Vue 3 中 defineExpose 的行为【defineExpose暴露ref变量】详解:自动解包、响应性与实际使用
前端·javascript·vue.js
奔跑的呱呱牛11 小时前
generate-route-vue基于文件系统的 Vue Router 动态路由生成工具
前端·javascript·vue.js