前言
原生项目应业务要求现在要用Flutter实现,项目中自定义的控件需要用flutter重绘,如图:步阶进度条。
效果图
思路很简单,底层绘制灰色矩形,根据步阶总数计算出每段宽度绘制渐变矩形,最后再在每段末尾的位置绘制上分割线。 Flutter的绘制部分需要继承CusomPainter并实现paint(Canvas canvas, Size size)和shouldRepaint(WindSpeedPainter oldDelegate),使用Canvas、Paint绘制具体图形,最后通过CustomPaint展示。
代码
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
///静音运转组件
class MuteWorkSlider extends StatefulWidget {
const MuteWorkSlider({
Key? key,
required this.width,
required this.height,
required this.initialValue,
this.minValue = 1,
this.maxValue = 6,
required this.onChanged,
this.dividerWidth = 1,
}) : super(key: key);
final int initialValue; //初始值
final int minValue; //最小值
final int maxValue; //最大值
final double width; //控件总体宽度
final double height; //控件总体高度
final ValueChanged<int> onChanged;
final double dividerWidth; //分割线宽度
@override
State<MuteWorkSlider> createState() => _WindSpeedSliderState();
}
class _WindSpeedSliderState extends State<MuteWorkSlider> {
late int divisions = widget.maxValue; //共有多少个值
late int value = 0; //内部使用的数值 从0开始
late int resultValue; //最终输出的数值
@override
void initState() {
super.initState();
value = widget.initialValue - 1;
}
@override
Widget build(BuildContext context) {
void dealTouch(dynamic e) {
final newValue =
(e.localPosition.dx / (widget.width / divisions)).floor();
if (value != newValue && newValue >= 0 && newValue < divisions) {
value = newValue;
resultValue = value + 1;
setState(() {});
}
}
return ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(widget.height / 2)),
child: Container(
color: const Color(0xFFDDDDDD),
width: widget.width,
height: widget.height,
child: LayoutBuilder(
builder: (context, constraints) {
final rect = Rect.fromLTRB(
0,
0,
widget.width,
widget.height,
);
return GestureDetector(
child: CustomPaint(
painter: WindSpeedPainter(
divisions: divisions,
value: value,
rect: rect,
dividerWidth: widget.dividerWidth,
),
),
onPanStart: (e) {
dealTouch(e);
},
onPanUpdate: (e) {
dealTouch(e);
},
onPanEnd: (e) {
widget.onChanged(resultValue);
},
);
},
),
),
);
}
}
class WindSpeedPainter extends CustomPainter {
WindSpeedPainter({
required this.divisions,
required this.value,
required this.rect,
required this.dividerWidth,
});
final int divisions;
final int value;
final Rect rect;
final double dividerWidth;
@override
void paint(Canvas canvas, Size size) {
// 每个进度的宽度
final itemWidth = rect.right / divisions;
//当前步阶对应的宽度
final progressWidth = (value + 1) * itemWidth;
//渐变色
Gradient gradient = const LinearGradient(
colors: [Color(0xFF91C1FE), Color(0xFF495FE9)],
begin: Alignment.centerLeft,
end: Alignment.centerRight);
//画进度
canvas.drawRect(
Rect.fromLTRB(rect.left, rect.top, progressWidth, rect.bottom),
Paint()
..style = PaintingStyle.fill
..shader = gradient.createShader(rect)
..isAntiAlias = true
..strokeCap = StrokeCap.butt);
//画分割线
for (var i = 1; i < divisions; i++) {
final dividerX = i * itemWidth;
canvas.drawLine(
Offset(dividerX, rect.top),
Offset(dividerX, rect.bottom),
Paint()
..style = PaintingStyle.stroke
..color = const Color(0xFFFFFFFF).withOpacity(0.5)
..strokeWidth = dividerWidth
..strokeCap = StrokeCap.round,
);
}
}
@override
bool shouldRepaint(WindSpeedPainter oldDelegate) =>
divisions != oldDelegate.divisions || value != oldDelegate.value;
}