![Flutter开发实例解析](https://wfqqreader-1252317822.image.myqcloud.com/cover/665/41398665/b_41398665.jpg)
3.2 基于Canvas和CustomPaint绘制表盘
有了前面理论知识的铺垫,接下来正式开始拟物时钟的开发实战。首先创建一个新的工程,并修改lib/main.dart的代码与3.1.1小节中的初始代码相同。
本节进行表盘的绘制,表盘采用单独的组件进行封装。在lib下创建components目录,用来容纳项目中的组件,在components下创建ClockPanel.dart。
表盘由3部分组成,分别是外表盘、内表盘及表盘刻度。
3.2.1 使用Container绘制外表盘
外表盘基于Container组件实现,通过decoration设置形状为原型,并通过多个BoxShadow设置新拟物的阴影效果。
![](https://epubservercos.yuewen.com/411DE0/21570843401308606/epubprivate/OEBPS/Images/82_02.jpg?sign=1739338901-h4Mnei6miEVwrSTIzzzyqtYhUILT8aov-0-0a8a03150844235f441a0a9b2f38d64e)
![](https://epubservercos.yuewen.com/411DE0/21570843401308606/epubprivate/OEBPS/Images/83_01.jpg?sign=1739338901-S37XMOIx0bhqqE9l1EkI4QF1L1kMnWJn-0-51a49aef70485617d61ef6e9a6adc9ec)
其中,ClockPanel的根布局是一个Stack组件,是一种层级布局组件,外表盘位于最底层,刻度位于最上层。在Stack的children属性中,各个部分的布局都通过函数封装起来,以提高代码的清晰性和可读性。getOuterPanel方法即绘制外表盘。ClockPanel接受一个size属性,表示表盘的大小,在getOuterPanel中以size作为Container容器的大小。
回到lib/main.dart中对MyHomePage进行修改。将Scaffold的body组件替换为Stack,用于逐层摆放表盘与指针。同时将ClockPanel作为Stack的第一个元素。在build方法中,首先通过MediaQuery获取到屏幕的宽高,并封装为Size对象传入ClockPanel。这里对宽高均乘以0.9的因子,在屏幕两侧留取余量,使其显示更加美观。具体代码如下:
![](https://epubservercos.yuewen.com/411DE0/21570843401308606/epubprivate/OEBPS/Images/83_02.jpg?sign=1739338901-ue1XpQAmM9wII4w6uH0VNAlrNHb93pYn-0-c4b8053147833745e61c4b41c6964070)
![](https://epubservercos.yuewen.com/411DE0/21570843401308606/epubprivate/OEBPS/Images/84_01.jpg?sign=1739338901-P9OpyIDdoqNj4tSscJ488yr1qJPkI1cu-0-d908cd36e1db8cee8b8104450747b2af)
运行代码,显示效果如图3-16所示,可以看出新拟物效果非常赏心悦目。
![](https://epubservercos.yuewen.com/411DE0/21570843401308606/epubprivate/OEBPS/Images/84_02.jpg?sign=1739338901-TzFO3Aqv3Bp8PVKg6390NlHbkPglf4S2-0-a1aa08683df65af2352b9341a01bf757)
图3-16 外表盘运行效果
3.2.2 使用Container绘制内表盘
外表盘通过Container的BoxShadow营造出凸起效果,内表盘则要营造出凹陷的效果。内表盘同样也基于Container实现,主要通过BoxDecoration的RadialGradient来设置渐变色。具体实现如下:
![](https://epubservercos.yuewen.com/411DE0/21570843401308606/epubprivate/OEBPS/Images/84_03.jpg?sign=1739338901-frZFIadXrc3bUq3d409qPd5yezl5h8Sp-0-92f0a9979b594713b5859a15732474a0)
![](https://epubservercos.yuewen.com/411DE0/21570843401308606/epubprivate/OEBPS/Images/85_01.jpg?sign=1739338901-iw0YFh0ZaUTN5L2pggA9Q6kRexBJPrk8-0-b238a3f07d4b2bf439864adc9ddb9b6b)
![](https://epubservercos.yuewen.com/411DE0/21570843401308606/epubprivate/OEBPS/Images/86_01.jpg?sign=1739338901-ilVrIwyQPpc1eSqXer78ha78NIdVN2lF-0-4b9eeaa173092977747053c1b7d1f318)
其中,getInnerPanel为内表盘绘制方法,内表盘由两个Container通过Stack叠加而成。每个Container分别带有一个渐变效果。两个Container的长和宽再次乘以0.9的因子,作为表框的宽度。
运行代码效果如图3-17所示,可以看出表盘已经基本成型。
![](https://epubservercos.yuewen.com/411DE0/21570843401308606/epubprivate/OEBPS/Images/86_02.jpg?sign=1739338901-g4zXQxsjXHXxi3SYjI5ppvXBJE35f4QS-0-63f2faa19c38ecefae66737cd32c954f)
图3-17 内表盘运行效果
3.2.3 使用CustomPaint绘制表盘刻度
完成表盘之后,接下来进行表盘刻度的绘制。有了刻度能够更加精确地显示时间。刻度通过CustomPaint组件绘制而成。
在ClockPanel中继续创建一个getScale方法,用于绘制表盘刻度,代码实现如下:
![](https://epubservercos.yuewen.com/411DE0/21570843401308606/epubprivate/OEBPS/Images/86_03.jpg?sign=1739338901-biNrsHroh2ZbyF29YDr4ZKYdK4F5IaAC-0-d1d419fc960846a09a0aa2fb94add8bd)
其中,ClockScalePainter类包含了刻度的具体绘制方法,接下来创建这个类。将ClockScale-Painter类的代码写在ClockPanel类之后即可。在ClockScalePainter的paint方法中,首先创建一个Paint画笔,指定线条的颜色与粗细。之后通过canvas的drawLine方法进行画线操作,根据12、3、6、9点的位置坐标进行画线。代码实现如下:
![](https://epubservercos.yuewen.com/411DE0/21570843401308606/epubprivate/OEBPS/Images/86_04.jpg?sign=1739338901-rWgdMS4sJ7zYYuUNI88kq6ffMqj096Lh-0-a1875dc25c1b7db8981297fce5127d49)
![](https://epubservercos.yuewen.com/411DE0/21570843401308606/epubprivate/OEBPS/Images/87_01.jpg?sign=1739338901-MsT0MUZ2IzisgECLHx3HtjgcFmYC2n71-0-8d186fc7a2c30f615fa3002e828c5072)
接下来,在ClockPanel类的build方法中,将getScale方法添加进去:
![](https://epubservercos.yuewen.com/411DE0/21570843401308606/epubprivate/OEBPS/Images/87_02.jpg?sign=1739338901-KYOFDAPmIeB7Ug7dVtYRNZ80NQsTSYVB-0-46abb66915cf3d62f78d7fedbc417b0f)
再次运行代码,显示效果如图3-18所示。
![](https://epubservercos.yuewen.com/411DE0/21570843401308606/epubprivate/OEBPS/Images/88_01.jpg?sign=1739338901-EnbttarU5w7yaVSkoM7kZm9FFJ65lgXO-0-ac0909482d906167ba12ee237c4fdfc4)
图3-18 表盘添加刻度后效果
至此,表盘部分开发完成。