• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Flutter自定义View:彩色进度条

武飞扬头像
IT小码哥
帮助1

简介

在日常开发中,当Flutter提供的基础组件无法满足开发需求时,特别是产品设计的复杂交互效果,就需要我们自定义组件。 和Android系统一样,自定义组件都包括三种实现方式。

  1. 原生基础组件的多种组合
  2. 手动绘制
  3. 继承系统组件,增加功能效果。

本文主要通过圆形进度条案例来讲解在Flutter中怎么通过手动绘制实现自定义View。文章末尾附有完整源码。

先看效果:

学新通

主要参数

CustomPainter

和Android一样,Flutter实现自定义view也需要painter和canvas,即画笔和画布。

  1. 我们先来看CustomPaint的构造函数。
CustomPaint({
  Key key,
  this.painter, 
  this.foregroundPainter,
  this.size = Size.zero, 
  this.isComplex = false, 
  this.willChange = false, 
  Widget child, 
})

painter:背景画笔。

foregroundPainter:前景画笔。

size:默认代表绘制区域的大小。

isComplex:表示是否复杂绘制。当绘制的效果比较复杂时,可以开启,Flutter内部会对效果做一定的优化。

willChange:和isComplex搭配使用。

child:子view。

Canvas

  1. Canvas 表示画布,拥有很多的绘制方法。如下表所示:
方法名称 功能
drawLine 绘制直线
drawPoint 绘制圆点
drawPath 绘制路径
drawImage 绘制图片
drawRect 绘制矩形
drawCircle 绘制圆
drawOval 绘制椭圆
drawArc 绘制弧形

Paint

  1. Paint表示画笔,在Paint中,可以配置画笔的各种属性,如颜色,粗细等。
      var paint = Paint()
      ..strokeWidth = strokeWidth 
      ..strokeCap = strokeCapRound?StrokeCap.round:StrokeCap.butt
      ..color = Colors.grey
      ..isAntiAlias = true
      ..style = PaintingStyle.stroke;

strokeWidth:画笔宽度

strokeCap:画笔结尾的延伸类型,具体看图效果

color:画笔换色

isAntiAlias:是否抗锯齿

style:画笔的样式


功能实现:

绘制灰色圆弧

  1. 首先定义_GradientCircularProgressIndicator类继承CustomPainter类,并实现paint和shouldRepaint方法。注意自定义view必须继承CustomPainter类
class _GradientCircularProgressIndicator extends CustomPainter {

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

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

2 .创建画笔及属性配置

 var paint = Paint()
      ..strokeWidth = strokeWidth //宽度
      //画笔结尾形状,这里是圆形
      ..strokeCap = strokeCapRound?StrokeCap.round:StrokeCap.butt
        //颜色
      ..color = Colors.grey
        //抗锯齿
      ..isAntiAlias = true
        //样式
      ..style = PaintingStyle.stroke;
  1. 定义外边框矩形大小。
  var rect = Offset(_offset,_offset) & Size(size.width-strokeWidth, size.height - strokeWidth);
  1. 使用canvas绘制圆弧。
 canvas.drawArc(rect, _start,totalAngle, false, paint);

绘制彩色进度

  1. 画完灰色圆弧后,刚进度条增加进度时,绘制彩色进度。首先需要给画笔增加渐变着色器。这里使用的是SweepGradient。
 paint.shader = SweepGradient(colors: colors,startAngle: 0.0,endAngle: _value,stops: [0.3,0.6,0.8]).createShader(rect);

6 . 着色器更加进度值变化,然后直接绘制即可。

canvas.drawArc(rect, _start, _value, false, paint);

增加绘制动画

  1. 当进度条绘制完成后,还要给设置一个动画来完成彩色进度条的动态绘制。这里使用AnimationController动画控制器来实现模拟进度。
 _controller = AnimationController(vsync: this,lowerBound: 0,
                                   upperBound:1, duration:
                                   const Duration(seconds: 5));
   _controller.forward();//启动动画
  1. 使用
 return Center( child: GradientCircularProgressIndicator(value: _controller.value, radius: 50, strokeWidth: 6, colors: const [Colors.red,Colors.orange,Colors.yellow],strokeCapRound: true,),
         );

完整代码

import 'dart:math';
import 'package:flutter/material.dart';
class GradientCircularProgressIndicator extends StatelessWidget {
  const GradientCircularProgressIndicator({
    super.key,
    required this.value,
    required this.radius,
    required this.colors,
    this.strokeCapRound = false,
    this.backGroundColor =  Colors.grey,
    this.totalAngle = 2 * pi,
    this.strokeWidth = 2.0,
  });
  ///当前进度  取值范围[0.0->1.0]
  final double value;
  ///圆的半径
  final double radius;
  ///圆环的粗细
  final double strokeWidth;
  ///两端是否为圆角
  final bool strokeCapRound;
  final Color backGroundColor;
  /// 进度条的总弧度 2*PI为整圆
  final double totalAngle;
  /// 渐变色的数组
  final List<Color> colors;
  /// 渐变色的终点,对应colors属性
  // final List<double> stops;

  @override
  Widget build(BuildContext context) {
    print("radius:$value");
    double _offset = .0;
    if (strokeCapRound) {
      _offset = asin(strokeWidth / (radius * 2 - strokeWidth));
    }
    var _colors = colors;
    _colors ??= [
      Theme.of(context).primaryColorLight,
      Theme.of(context).cardColor
    ];
    return Transform.rotate(
      angle: -pi,
      child: CustomPaint(
        size: Size.fromRadius(radius),
        painter: _GradientCircularProgressIndicator(
            value: value,
            radius: radius,
            strokeWidth: strokeWidth,
            strokeCapRound : strokeCapRound,
            backGroundColor : backGroundColor,
            totalAngle : totalAngle,
            colors : _colors,
        ),
      ),
    );
  }
}

class _GradientCircularProgressIndicator extends CustomPainter {
  _GradientCircularProgressIndicator(
      {required this.value, required this.radius, required this.strokeWidth,
       required this.strokeCapRound , required this.backGroundColor,
        required this.totalAngle,required this.colors

      });

  final double value;
  final double radius;
  final double strokeWidth;
  ///两端是否为圆角
  final bool strokeCapRound;
  //背景色
  final Color backGroundColor;
  /// 进度条的总弧度 2*PI为整圆
  final double totalAngle;
  /// 渐变色的数组
  final List<Color> colors;
  @override
  void paint(Canvas canvas, Size size) {

    if(radius !=null){
      size = Size.fromRadius(radius);
    }
    double _offset = strokeWidth /2.0;
    double _value = (value ??.0);
    _value = _value.clamp(.0,1.0)*totalAngle;
    double _start = 0;
    if(strokeCapRound){
      _start = asin(strokeWidth/(size.width-strokeWidth));
    }
    var rect = Offset(_offset,_offset) & Size(size.width-strokeWidth, size.height - strokeWidth);
    var paint = Paint()
      ..strokeWidth = strokeWidth
      ..strokeCap = strokeCapRound?StrokeCap.round:StrokeCap.butt
      ..color = Colors.grey
      ..isAntiAlias = true
    if(backGroundColor !=Colors.transparent){
      paint.color = backGroundColor;
      canvas.drawArc(rect, _start,totalAngle, false, paint);
    }
    if(_value>0){
      paint.shader = SweepGradient(colors: colors,startAngle: 0.0,endAngle: _value,stops: [0.3,0.6,0.8]).createShader(rect);
      canvas.drawArc(rect, _start, _value, false, paint);
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

总结

Flutter中实现自定义View相对来说比较简单,当然本文介绍的目的是带大家简单入门,喜欢的小伙伴可以收藏学习。切记一定要动手实践。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgahiab
系列文章
更多 icon
同类精品
更多 icon
继续加载