flutter自定义系列之自定义时钟界面的绘制

简单粗暴的直接上代码:

import 'dart:math';

import 'package:flutter/material.dart';

class ClockPainter extends CustomPainter {
  final BuildContext context;
  final DateTime datetime;

  // 构造函数,需要传入BuildContext和DateTime
  ClockPainter(this.context, this.datetime);

  @override
  void paint(Canvas canvas, Size size) {
    // 计算中心点的坐标
    double centerX = size.width / 2;
    double centerY = size.height / 2;
    Offset center = Offset(centerX, centerY);
    
    // 计算时针、分针和秒针的长度
    double minHandLength = centerX * 0.75;
    double hourHandLength = centerX * 0.5;
    double secHandLength = centerX * 0.85;

    // 创建画分针的画笔
    Paint minHandPaint = Paint()
      ..color = Colors.orange
      ..style = PaintingStyle.stroke
      ..strokeWidth = 10;
    // 计算分针的角度
    double minDegrees = 360 / 60 * datetime.minute;
    // 计算分针的终点坐标
    double minX = centerX + minHandLength * cos(minDegrees * pi / 180);
    double minY = centerY + minHandLength * sin(minDegrees * pi / 180);
    // 画分针
    canvas.drawLine(center, Offset(minX, minY), minHandPaint);

    // 创建画时针的画笔
    Paint hourHandPaint = Paint()
      ..color = Colors.brown
      ..style = PaintingStyle.stroke
      ..strokeWidth = 16;
    // 计算时针的角度
    double hourDegrees = 360 / 12 * (datetime.hour % 12) + datetime.minute / 60 * 30;
    // 计算时针的终点坐标
    double hourX = centerX + hourHandLength * cos(hourDegrees * pi / 180);
    double hourY = centerY + hourHandLength * sin(hourDegrees * pi / 180);
    // 画时针
    canvas.drawLine(center, Offset(hourX, hourY), hourHandPaint);

    // 创建画秒针的画笔
    Paint secHandPaint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2;
    // 计算秒针的角度
    double secDegrees = 360 / 60 * datetime.second;
    // 计算秒针的终点坐标
    double secX = centerX + secHandLength * cos(radians(secDegrees - 90));
    double secY = centerY + secHandLength * sin(radians(secDegrees - 90));
    // 画秒针
    canvas.drawLine(center, Offset(secX, secY), secHandPaint);

    // 创建画刻度的画笔
    Paint dashPaint = Paint()
      ..color = Colors.grey
      ..style = PaintingStyle.stroke
      ..strokeWidth = 1;
    // 定义刻度的总数和长度
    int totalDashes = 60;
    double longDashLength = 20; // 长刻度线的长度
    double shortDashLength = 10; // 短刻度线的长度
    double dashSpace = size.width / 2 - longDashLength;
    // 画每一个刻度
    for (int i = 0; i < totalDashes; i++) {
      // 计算每一个刻度的角度
      double min = 360 / totalDashes * i;
      // 计算每一个刻度的起点坐标
      double x1 = centerX + dashSpace * cos(radians(min - 90));
      double y1 = centerY + dashSpace * sin(radians(min - 90));
      // 如果是整点,则刻度线长一些
      double dashLength = i % 5 == 0 ? longDashLength : shortDashLength;
      // 计算每一个刻度的终点坐标
      double x2 = centerX + (dashSpace + dashLength) * cos(radians(min - 90));
      double y2 = centerY + (dashSpace + dashLength) * sin(radians(min - 90));
      // 画刻度
      canvas.drawLine(Offset(x1, y1), Offset(x2, y2), dashPaint);
    }

    // 准备画数字
    TextPainter textPainter = TextPainter(
      textAlign: TextAlign.center,
      textDirection: TextDirection.ltr,
    );
    // 画每一个数字
    for (int i = 1; i <= 12; i++) {
      // 设置数字的样式
      textPainter.text = TextSpan(
        text: '$i',
        style: TextStyle(
          color: Colors.black,
          fontSize: 22,
        ),
      );
      // 这里我们需要让textPainter进行布局,否则我们无法获取到文字的宽度和高度
      textPainter.layout();

      // 计算每个数字的位置,这里我们让12个数字均匀的分布在表盘上
      double angle = 2 * pi / 12 * i;
      double x = centerX + (size.width / 2 - 40) * cos(angle - pi / 2) - textPainter.width / 2;
      double y = centerY + (size.height / 2 - 40) * sin(angle - pi / 2) - textPainter.height / 2;
      Offset offset = Offset(x, y);

      // 在计算出的位置上绘制文字
      textPainter.paint(canvas, offset);
    }
  }

  // 将角度转换为弧度
  double radians(double degree) {
    return degree * pi / 180;
  }

  // 返回true表示每次都需要重绘
  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}

注释写的很清楚了,不需要再做阐述;

使用的地方刷新界面。

DateTime _now = DateTime.now();

void _getTime() {
  setState(() {
    _now = DateTime.now();
  });
}

初始化定时器,记得在销毁的时候关闭。

@override
void initState() {
  super.initState();
  _timer = Timer.periodic(Duration(seconds: 1), (Timer t) => _getTime());
  
  }
@override
void dispose() {
  _timer.cancel();
  super.dispose();
}

图片.png

一个简单的钟表盘就绘制好了,可以根据我们具体的需要拓展。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flutter中的自定义Controller是指可以控制和管理特定组件状态的类。通过自定义Controller,我们可以实现对组件的状态进行监听、更新和控制。 在Flutter中,常用的自定义Controller是StatefulWidget的Controller,也称为StateController。StateController通常包含一个State对象,用于管理组件的状态,并提供一些方法来更新状态和通知组件重新构建。 下面是一个简单的示例,展示了如何创建一个自定义的Controller来管理一个计数器的状态: ```dart class CounterController { int _count = 0; int get count => _count; void increment() { _count++; } } ``` 在上面的示例中,CounterController包含一个私有变量_count来保存计数器的值,并提供了一个公共方法increment来增加计数器的值。通过get方法count,我们可以获取当前计数器的值。 在使用自定义Controller时,通常需要将其与StatefulWidget配合使用。下面是一个使用CounterController的示例: ```dart class CounterWidget extends StatefulWidget { @override _CounterWidgetState createState() => _CounterWidgetState(); } class _CounterWidgetState extends State<CounterWidget> { final CounterController _controller = CounterController(); @override Widget build(BuildContext context) { return Column( children: [ Text('Count: ${_controller.count}'), RaisedButton( child: Text('Increment'), onPressed: () { setState(() { _controller.increment(); }); }, ), ], ); } } ``` 在上面的示例中,CounterWidget使用CounterController来管理计数器的状态。在build方法中,我们可以通过_controller.count获取当前计数器的值,并通过_controller.increment方法来增加计数器的值。当点击按钮时,我们调用setState方法来通知Flutter框架重新构建组件。 通过自定义Controller,我们可以更好地管理和控制组件的状态,使代码更加模块化和可维护。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值