解决 Flutter 内置进度条无法使用渐变色等高级需求的问题
我们在某些场景下会有展示某个业务点进度的需求,而Flutter内置的进度条非常简单,因此我们需要造一个。
效果图先上:
内置进度条的不足
其构造函数如下:
const ProgressIndicator({
Key? key,
this.value,
this.backgroundColor,
this.color,
this.valueColor,
this.semanticsLabel,
this.semanticsValue,
}) : super(key: key);
如上,我们发现:
- 只能设置单色背景和进度,无法设置渐变色
- 不能设置圆角(但可以通过圆角裁剪
ClipRRect
解决) - 无法设置内边距(背景和进度之间的边距)
- 其他更高级的需求也无法满足
自制进度条
源码如下:
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(),
),
),
);
}
}
这里面用到了 Container
、FractionallySizedBox
、DecoratedBox
三个内置组件。
其中,Container这个组件相关我们都已经很熟悉了
FractionallySizedBox
: 它可以将其子Widget按比例的填充到其可用的空间内DecoratedBox
: 它用于为其子Widget绘制一个装饰层。
其实,在实际的产品中,由于我们的页面需要根据UI图进行多端适配,因此,上部分源码中FractionallySizedBox
、DecoratedBox
部分,可以合并为一个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自制进度条的一些方案。
给个关注吧,让我们共同研究互联网新技术!
转载自:https://juejin.cn/post/7087781074469650439