news 2026/10/11 4:46:54

flutter---进度条(1)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
flutter---进度条(1)

效果图

标准蓝色进度条

进度条的禁用形态(只供观赏,不能点击)

环形进度条和仪表盘进度条

动画进度条:这个蓝色的光圈会一直变化

渐变环形进度条

标准蓝色进度条的实现步骤

1.设置变量

double _sliderValue1 = 0.3;,//进度条默认滑动的距离的比例

2.设置进度条的通用子项

// ================= 标准蓝色进度条 ================= Widget _buildSliderCard({ required String title, //进度条标题 required double value, //当前的进度值 (0.0~1.0) required ValueChanged<double> onChanged, //值改变时的回调函数 required Color color, //进度条激活时的颜色 bool showValue = false, //是否显示百分比 }) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF1E293B), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.white.withOpacity(0.1)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ //标题 Text( title, style: const TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.w500, ), ), //进度条滑动的值 if (showValue) Text( '${(value * 100).toInt()}%',//value*100转化为百分比,.toInt是取证的方法 style: const TextStyle( color: Color(0xFF94A3B8), fontSize: 12, ), ), ], ), const SizedBox(height: 12), //进度条核心部分 SliderTheme( data: SliderThemeData( trackHeight: 8, //进度条轨道高度 thumbShape: const RoundSliderThumbShape( enabledThumbRadius: 10, //滑块半径 pressedElevation: 18, //按下时阴影高度 ), overlayShape: const RoundSliderOverlayShape(overlayRadius: 16), //点击涟漪效果半径 activeTrackColor: color, //激活的颜色 inactiveTrackColor: Colors.white.withOpacity(0.1),//未激活的颜色 thumbColor: Colors.white, //滑块颜色 overlayColor: color.withOpacity(0.2), //点击涟漪效果 ), child: Slider(//自动检测和计算滑动的大小和方向 value: value, //绑定当前值 onChanged: onChanged, //绑定回调函数,当用户滑动时被调用 ), ), ], ), ); }

3.使用这个子项

_buildSliderCard( title: '标准蓝色进度条', //标题 value: _sliderValue1, //进度条当前进度值 onChanged: (newValueFromSlider) => setState(() => _sliderValue1 = newValueFromSlider), color: const Color(0xFF2178E3), //进度条激活颜色 showValue: true, //是否显示百分比 ),

完整代码执行逻辑

1. 初始构建 _sliderValue1 = 0.3 Slider.value = 0.3 显示百分比: 30% 2. 用户滑动到35%位置 Slider检测到滑动 → 调用 onChanged(0.35),newValueFromSlider = 0.35 3. 执行回调 setState(() => _sliderValue1 = 0.35) _sliderValue1 更新为 0.35 4. 触发重新构建 build() 方法再次执行 5. 传递新值给组件 _buildSliderCard(value: 0.35, ...) 6. 组件内部 - Slider.value = 0.35 - 滑块移动到35%位置 - 文字更新: '35%' 7. UI更新完成
进度条的禁用形态
SliderTheme( data: SliderThemeData( trackHeight: 8, //进度条轨道高度 thumbShape: const RoundSliderThumbShape( enabledThumbRadius: 0, //滑块半径 ), overlayShape: const RoundSliderOverlayShape(overlayRadius: 0), //去掉涟漪预留,才能让进度条占满宽度 activeTrackColor: Color(0xFF185FFF), //激活的颜色 inactiveTrackColor: Color(0xFFD8D8D8),//未激活的颜色 thumbColor: Colors.transparent, //滑块颜色 // 关键:禁用态颜色 disabledActiveTrackColor: const Color(0xFF185FFF), disabledInactiveTrackColor: const Color(0xFFD8D8D8), disabledThumbColor: Colors.transparent, ), child: Slider(//自动检测和计算滑动的大小和方向 value: _sliderValue, //绑定当前值 onChanged: null, //绑定回调函数,当用户滑动时被调用 ), ),
环形进度条和仪表盘进度条的实现步骤

1.设置变量,初始进度条值

double _sliderValue2 = 0.3;

2.设置环形进度条的UI框架的通用子项

Widget _buildCircularProgressCard({ required String title, //标题 required double value, //当前进度值 required ValueChanged<double> onChanged, //值变化的回调 }) { return Container( width: 150, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF1E293B), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.white.withOpacity(0.1)), ), child: Column( children: [ //标题 Text( title, style: const TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 12), SizedBox( height: 100, child: Stack( alignment: Alignment.center, //所有子项居中对齐,所以背景圆环和进度圆环能自定对齐圆心 children: [ // 背景圆环 Container( width: 88, height: 88, decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( color: Colors.white.withOpacity(0.1), width: 8, //边框宽度:这个边框宽度做了圆形进度条的背景 ), ), ), // 进度圆环 SizedBox( width: 80, height: 80, child: CircularProgressIndicator( //绘制环形进度条 value: value, //传入值 strokeWidth: 8, //传入圆环的粗细 backgroundColor: Colors.transparent, valueColor: AlwaysStoppedAnimation( //传入进度条激活的颜色 Color(0xFF3B82F6).withOpacity(0.7), ), ), ), // 中心文本 Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '${(value * 100).toInt()}%', style: const TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold, ), ), Text( '进度', style: TextStyle( color: Colors.white.withOpacity(0.6), fontSize: 10, ), ), ], ), ], ), ), const SizedBox(height: 8), //控制滑块 SizedBox( height: 20, //滑块区域高度 child: Slider( value: value, onChanged: onChanged, //值变化时回调 activeColor: const Color(0xFF3B82F6), //激活颜色 inactiveColor: Colors.white.withOpacity(0.1), //未激活颜色 ), ), ], ), ); }

3.设置仪表盘进度条的UI框架

Widget _buildGaugeProgressCard({ required String title, //标题 required double value, //当前进度值 required ValueChanged<double> onChanged, //值变化回调 }) { return Container( width: 150, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF1E293B), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.white.withOpacity(0.1)), ), child: Column( children: [ //标题 Text( title, style: const TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 12), //仪表盘绘制区域 SizedBox( height: 60, child: CustomPaint( size: const Size(100, 60), //绘制区域 painter: _GaugePainter(value: value),//自定义绘制器 ), ), const SizedBox(height: 48), //控制滑块 SizedBox( height: 20, child: Slider( value: value, onChanged: onChanged, activeColor: const Color(0xFF1D4ED8), inactiveColor: Colors.white.withOpacity(0.1), ), ), ], ), ); }

4.绘制仪表盘进度条

class _GaugePainter extends CustomPainter { final double value; //进度值(0.0~1.0) _GaugePainter({required this.value}); //构造函数,接收进度值 @override void paint(Canvas canvas, Size size) { const startAngle = -pi * 1.3; //起始角度 const sweepAngle = pi * 1.6; //总角度范围 final center = Offset(size.width / 2, size.height * 0.8); //圆心位置 const radius = 40.0; //圆弧半径 // 绘制背景圆弧 final backgroundPaint = Paint() //创建画笔 ..color = Colors.white.withOpacity(0.1) //背景颜色 ..style = PaintingStyle.stroke //描边模式 ..strokeWidth = 8 //线条宽度 ..strokeCap = StrokeCap.round; //线条端点圆角 canvas.drawArc( Rect.fromCircle(center: center, radius: radius), //圆弧所在矩形 startAngle, //使用起始角度 sweepAngle, //使用角度范围 false, //不填充中心,只是描边 backgroundPaint, //使用画笔 ); // 绘制进度圆弧 final progressPaint = Paint() //绘制画笔 ..color = const Color(0xFF1D4ED8) //背景颜色 ..style = PaintingStyle.stroke //描边模式 ..strokeWidth = 8 //线宽 ..strokeCap = StrokeCap.round; //线条端点圆角 canvas.drawArc( Rect.fromCircle(center: center, radius: radius), startAngle, sweepAngle * value, false, progressPaint, ); //绘制指针 //指针角度 = 起始角度 + (总角度 * 进度值) final pointerAngle = startAngle + sweepAngle * value; final pointerPaint = Paint() ..color = Colors.white ..style = PaintingStyle.fill; //填充模式 //绘制指针圆点 canvas.drawCircle( center, //圆心位置 6, //半径 pointerPaint, ); // 绘制指针线 final pointerLinePaint = Paint() ..color = Colors.white ..style = PaintingStyle.stroke ..strokeWidth = 2; //计算指针终点位置 final pointerEnd = Offset( //计算 center.dx + cos(pointerAngle) * radius * 0.8, //X坐标 center.dy + sin(pointerAngle) * radius * 0.8, //Y坐标 ); //绘制从圆心到终点的线条 canvas.drawLine(center, pointerEnd, pointerLinePaint); // 绘制刻度值 final textPainter = TextPainter( text: TextSpan( text: '${(value * 100).toInt()}', //计算百分比 style: const TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold, ), ), textDirection: TextDirection.ltr, //从左到右文本方向 ); textPainter.layout(); //布局计算文本尺寸 textPainter.paint( canvas, Offset( //位置 center.dx - textPainter.width / 2, //水平居中 center.dy - 40 //圆心上方的位置 ), ); // 绘制百分号单位 final unitPainter = TextPainter( text: const TextSpan( text: '%', style: TextStyle( color: Colors.white70, fontSize: 12, ), ), textDirection: TextDirection.ltr, ); unitPainter.layout(); unitPainter.paint( canvas, Offset(center.dx - unitPainter.width / 2, center.dy - 15), //位置 ); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => true; }

5.使用这两个进度条

// 环形和仪表盘进度条 Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ //环形进度条 _buildCircularProgressCard( title: '环形进度条', value: _sliderValue2, onChanged: (value) => setState(() => _sliderValue2 = value), ), //仪表盘进度条 _buildGaugeProgressCard( title: '仪表盘进度条', value: _sliderValue2, onChanged: (value) => setState(() => _sliderValue2 = value), ), ], ),
动画进度条的实现步骤

1.定义变量

// 进度条值 double _sliderValue3 = 0.9; // 动画控制器 late AnimationController _animationController; double _animatedValue = 0.0; //动画当前值

2.初始化加载动画

@override void initState() { super.initState(); //加载动画 _animationController = AnimationController( vsync: this, duration: const Duration(seconds: 2), //2秒周期,往复运动 )..repeat(reverse: true); //设置动画监听器 _animationController.addListener(() { setState(() { _animatedValue = _animationController.value; //更新动画值 }); }); }

3.注销监听器

@override void dispose() { _animationController.dispose(); super.dispose(); }

4.构建环形进度条和设置动画

Widget _buildAnimatedCircularProgress() { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF1E293B), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.white.withOpacity(0.1)), ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ //标题 const Text( '呼吸灯效果', style: TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.w500, ), ), //暂停/播放按钮:控制动画的播放和暂停 IconButton( icon: Icon( _animationController.isAnimating ? Icons.pause : Icons.play_arrow, //判断动画是否在运行 color: const Color(0xFF3B82F6), ), onPressed: () { if (_animationController.isAnimating) { _animationController.stop(); //停止动画 } else { _animationController.repeat(reverse: true); //开始往复动画 } setState(() {}); }, ), ], ), const SizedBox(height: 12), SizedBox( height: 120, child: Stack( alignment: Alignment.center, //中心对齐 children: [ // 进度圆环 SizedBox( width: 80, height: 80, child: CircularProgressIndicator( value: _sliderValue3, //进度值 strokeWidth: 10, //圆环粗细 backgroundColor: Colors.transparent, valueColor: AlwaysStoppedAnimation( Color.lerp( //颜色插值动画 Color(0xFF1E40AF), //起始颜色 Color(0xFF60A5FA), //结束颜色 _animatedValue, //插值因子 )!, ), ), ), // 外圈动画光环 Container( width: 100 * (0.8 + _animatedValue * 0.2), //动态宽度 height: 100 * (0.8 + _animatedValue * 0.2), //动态高度 decoration: BoxDecoration( shape: BoxShape.circle, gradient: RadialGradient( colors: [ Color(0xFF3B82F6).withOpacity(0.1), Color(0xFF3B82F6).withOpacity(0.05), Colors.transparent, ], ), ), ), // 中心文本 Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '${(_sliderValue3 * 100).toInt()}%', style: const TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold, ), ), Text( '动态进度', style: TextStyle( color: Colors.white.withOpacity(0.6), fontSize: 12, ), ), ], ), ], ), ), //控制滑块 const SizedBox(height: 12), SliderTheme( data: SliderThemeData( trackHeight: 6, //轨道高度 thumbShape: const RoundSliderThumbShape( enabledThumbRadius: 8, //滑块半径 ), activeTrackColor: const Color(0xFF60A5FA), //激活颜色 inactiveTrackColor: Colors.white.withOpacity(0.1), //未激活颜色 ), child: Slider( value: _sliderValue3, onChanged: (value) => setState(() => _sliderValue3 = value), ), ), ], ), ); }

5.使用这个动画环形进度条

_buildAnimatedCircularProgress(),
渐变环形进度条实现步骤

1.设置变量

// 进度条值 double _sliderValue4 = 0.88;

2.构建多色环形进度条

Widget _buildMultiColorCircularProgress() { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF1E293B), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.white.withOpacity(0.1)), ), child: Column( children: [ //标题 const Text( '渐变环形进度条', style: TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 12), //环形进度条 SizedBox( height: 150, child: Stack( alignment: Alignment.center, //居中对齐 children: [ // 外圈装饰 Container( width: 120, height: 120, decoration: const BoxDecoration( shape: BoxShape.circle, gradient: RadialGradient( colors: [ Color(0xFF1E293B), Color(0xFF0F172A), ], ), ), ), // 自定义绘制渐变圆环 CustomPaint( size: const Size(100, 100), painter: _GradientCircularPainter(value: _sliderValue4), ), // 中心文本 Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '${(_sliderValue4 * 100).toInt()}%', style: const TextStyle( color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( '完成度', style: TextStyle( color: Colors.white.withOpacity(0.6), fontSize: 12, ), ), ], ), ], ), ), //滑动条 const SizedBox(height: 12), SliderTheme( data: SliderThemeData( trackHeight: 8, thumbShape: const RoundSliderThumbShape( enabledThumbRadius: 10, ), activeTrackColor:const Color(0xFF3B82F6), inactiveTrackColor: Colors.white.withOpacity(0.1), ), child: Slider( value: _sliderValue4, onChanged: (value) => setState(() => _sliderValue4 = value), ), ), ], ), ); } }

3.自定义多色环形进度条绘制

class _GradientCircularPainter extends CustomPainter { final double value; //进度值 _GradientCircularPainter({required this.value}); //构造函数 @override void paint(Canvas canvas, Size size) { //计算圆心位置 final center = Offset(size.width / 2, size.height / 2); //计算半径 final radius = size.width / 2 - 5; // 绘制背景圆环 final backgroundPaint = Paint() ..color = Colors.white.withOpacity(0.1) //背景颜色 ..style = PaintingStyle.stroke //描边模式(不填充圆心) ..strokeWidth = 10; //线宽 canvas.drawCircle(center, radius, backgroundPaint); // 绘制渐变进度圆环 final progressRect = Rect.fromCircle(center: center, radius: radius); //准备绘制区域 final progressPaint = Paint() ..style = PaintingStyle.stroke //描边模式 ..strokeWidth = 10 //线宽 ..strokeCap = StrokeCap.round //线条端点圆角 ..shader = SweepGradient( //扫掠渐变 colors: const [ Color(0xFF1E40AF), Color(0xFF3B82F6), Color(0xFF60A5FA), Color(0xFF93C5FD), Color(0xFF1E40AF), ], stops: const [0.0, 0.3, 0.6, 0.9, 1.0], //颜色分布位置 ).createShader(progressRect); //创建渐变着色器 const startAngle = -pi / 2; //起始角度 final sweepAngle = 2 * pi * value; //扫过的角度 canvas.drawArc( progressRect, //绘制区域 startAngle, //起始角度 sweepAngle, //扫过的角度 false, //不填充中心 progressPaint, //渐变画笔 ); // 绘制进度端点圆点 final endAngle = startAngle + sweepAngle; //计算端点角度:终点角度 final dotX = center.dx + radius * cos(endAngle); //X坐标 final dotY = center.dy + radius * sin(endAngle); //Y坐标 final dotPaint = Paint() //绘制圆点 ..color = Colors.white ..style = PaintingStyle.fill; canvas.drawCircle(Offset(dotX, dotY), 6, dotPaint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => true; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/11 4:45:49

向量库故障下的RAG降级:三级降级链设计与工程实践

“向量库没装好&#xff0c;RAG 检索还能不能用&#xff1f;”这个问题我估计不少搞过知识库问答的人都心里犯过嘀咕。它出自我手头一个叫 AI工厂管家社区版的项目——一个部署在车间里的知识问答机器人&#xff0c;主要回答设备手册、维修 SOP、安全规程这类问题。交付客户现场…

作者头像 李华
网站建设 2026/10/11 4:45:33

工作一到三年,想系统补AI能力考什么证?

工作一到三年&#xff0c;很多人已经能独立完成手头任务&#xff0c;但也慢慢感受到AI带来的变化——同样的活&#xff0c;别人用工具半天干完&#xff0c;自己还在手动处理。这时候想系统补AI能力&#xff0c;可以结合自己当前的工作任务&#xff0c;从下面五个认证方向里选一…

作者头像 李华
网站建设 2026/10/11 4:45:30

长视频高光片段怎么智能筛选:先找价值片段,再做人工复核

长视频高光片段智能筛选的核心不是让机器随意切出片段&#xff0c;而是先明确什么样的片段对用户或观众有价值&#xff0c;再用智能工具辅助定位候选&#xff0c;最终通过人工判断确认传播价值。剪映专业版可以在导入长视频、生成字幕、识别语音内容等环节提供辅助&#xff0c;…

作者头像 李华
网站建设 2026/10/11 4:44:04

GEO生成式引擎优化服务商选型对比:三种技术架构的优劣分析

读完本文你将掌握&#xff1a;GEO&#xff08;生成式引擎优化&#xff09;的底层技术原理、AI搜索推荐系统的内容召回逻辑、三种主流GEO实现架构的对比&#xff0c;以及如何评估一家GEO优化服务商的技术能力。一、为什么传统SEO在AI搜索面前失效了&#xff1f;先说一个很多人踩…

作者头像 李华