解决 Flutter 内置进度条无法使用渐变色等高级需求的问题

5,687 阅读2分钟

我们在某些场景下会有展示某个业务点进度的需求,而Flutter内置的进度条非常简单,因此我们需要造一个。

效果图先上:

效果图.png

内置进度条的不足

其构造函数如下:

const ProgressIndicator({
    Key? key,
    this.value,
    this.backgroundColor,
    this.color,
    this.valueColor,
    this.semanticsLabel,
    this.semanticsValue,
  }) : super(key: key);

如上,我们发现:

  1. 只能设置单色背景和进度,无法设置渐变色
  2. 不能设置圆角(但可以通过圆角裁剪 ClipRRect 解决)
  3. 无法设置内边距(背景和进度之间的边距)
  4. 其他更高级的需求也无法满足

自制进度条

源码如下:

class _Progressbar extends StatelessWidget {
  final double? width;
  final double? height;
  final Axis? direction;
  final double value;
  final EdgeInsets? padding;
  final EdgeInsets? margin;
  final BoxDecoration? outerDecoration;
  final BoxDecoration? innerDecoration;

  const _Progressbar({
    Key? key,
    required this.value,
    this.direction,
    this.width,
    this.height,
    this.padding,
    this.margin,
    this.outerDecoration,
    this.innerDecoration,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final isVertical = (direction ?? Axis.horizontal) == Axis.vertical;
    return Container(
      height: height,
      width: width,
      decoration: outerDecoration ??
          BoxDecoration(
            borderRadius: BorderRadius.circular(100),
            color: Colors.black12,
          ),
      padding: padding,
      margin: margin,
      alignment: isVertical ? Alignment.bottomCenter : Alignment.centerLeft,
      child: FractionallySizedBox(
        widthFactor: isVertical ? 1 : value,
        heightFactor: isVertical ? value : 1,
        child: DecoratedBox(
          decoration: innerDecoration ?? const BoxDecoration(),
        ),
      ),
    );
  }
}

这里面用到了 ContainerFractionallySizedBoxDecoratedBox 三个内置组件。

其中,Container这个组件相关我们都已经很熟悉了

  • FractionallySizedBox: 它可以将其子Widget按比例的填充到其可用的空间内
  • DecoratedBox: 它用于为其子Widget绘制一个装饰层。

其实,在实际的产品中,由于我们的页面需要根据UI图进行多端适配,因此,上部分源码中FractionallySizedBoxDecoratedBox部分,可以合并为一个Container组件。

合并之后源码如下:

class _Progressbar2 extends StatelessWidget {
 final double width;
 final double height;
 final Axis? direction;
 final double value;
 final EdgeInsets? padding;
 final EdgeInsets? margin;
 final BoxDecoration? outerDecoration;
 final BoxDecoration? innerDecoration;

 const _Progressbar2({
   Key? key,
   required this.value,
   required this.width,
   required this.height,
   this.direction,
   this.padding,
   this.margin,
   this.outerDecoration,
   this.innerDecoration,
 }) : super(key: key);

 @override
 Widget build(BuildContext context) {
   final isVertical = (direction ?? Axis.horizontal) == Axis.vertical;
   final fnPadding = padding ?? EdgeInsets.zero;
   return Container(
     height: height,
     width: width,
     decoration: outerDecoration ??
         BoxDecoration(
           borderRadius: BorderRadius.circular(100),
           color: Colors.black12,
         ),
     padding: fnPadding,
     margin: margin,
     alignment: isVertical ? Alignment.bottomCenter : Alignment.centerLeft,
     child: Container(
       width: isVertical ? null : value * (width - fnPadding.left - fnPadding.right),
       height: isVertical ? value * (height - fnPadding.top - fnPadding.bottom) : null,
       decoration: innerDecoration ?? const BoxDecoration(),
     ),
   );
 }
}

需要注意的是,此时的宽和高需要指定才行,不过对于需要适配的应用来说,UI图里面是会说明其尺寸的

另一种方法:自绘

前面的这种解决方案,不得不说是相对简单的一种了。当然如果我们觉得其层级还是不够简练,那此时我们可用通过自绘去实现

大致源码如下:

class _Progressbar3 extends StatelessWidget {
  final double width;
  final double height;
  final Axis? direction;
  final double value;
  // 定义其他我们需要的属性

  const _Progressbar3({
    Key? key,
    required this.value,
    required this.width,
    required this.height,
    this.direction,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return CustomPaint(painter: _ProgressPainter());
  }
}

// 我们自定义的进度条绘制代码
class _ProgressPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // 在此处绘制我们的进度条
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    // 当我们需要重绘的时候返回 true 即可
    return false;
  }
}

至此,我们介绍了Flutter自制进度条的一些方案。

给个关注吧,让我们共同研究互联网新技术!

以下为我公司开发的Windows自动暗色、主题管理软件,欢迎各位前去体验 爱今天灵眸运行截图