FLUTTER组件学习之进度指示器

Flutter 进度指示器组件学习

进度指示器是向用户显示任务正在进行中的视觉反馈组件。Flutter 提供了多种类型的进度指示器,适用于不同的场景。

1. 基础进度指示器

1.1 CircularProgressIndicator - 圆形进度指示器

dart 复制代码
import 'package:flutter/material.dart';

class ProgressIndicatorExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('进度指示器')),
body: _buildContent(),
);
}

Widget _buildContent() {
return ListView(
padding: EdgeInsets.all(20),
children: [
_buildSectionTitle('基本圆形进度指示器'),
_buildCircularProgressIndicators(),

_buildSectionTitle('线性进度指示器'),
_buildLinearProgressIndicators(),

_buildSectionTitle('自定义进度指示器'),
_buildCustomProgressIndicators(),

_buildSectionTitle('进度指示器应用场景'),
_buildProgressIndicatorScenarios(),
],
);
}

Widget _buildSectionTitle(String title) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 16),
child: Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.blue[700],
),
),
);
}
}

2. 圆形进度指示器详解

2.1 基本圆形进度指示器

dart 复制代码
Widget _buildCircularProgressIndicators() {
return Card(
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: [
ListTile(
title: Text('不确定进度(默认)'),
subtitle: Text('用于无法确定完成时间的任务'),
trailing: CircularProgressIndicator(),
),
Divider(),
ListTile(
title: Text('确定进度(50%)'),
subtitle: Text('value: 0.5'),
trailing: CircularProgressIndicator(
value: 0.5,
),
),
Divider(),
ListTile(
title: Text('自定义颜色'),
subtitle: Text('color: Colors.red'),
trailing: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.red),
),
),
Divider(),
ListTile(
title: Text('自定义大小和描边'),
subtitle: Text('strokeWidth: 5, backgroundColor: Colors.grey[200]'),
trailing: SizedBox(
width: 40,
height: 40,
child: CircularProgressIndicator(
strokeWidth: 5,
backgroundColor: Colors.grey[200],
),
),
),
],
),
),
);
}

2.2 圆形进度指示器属性详解

dart 复制代码
Widget _buildCircularProgressIndicatorDetails() {
return ExpansionTile(
title: Text('CircularProgressIndicator 属性详解'),
children: [
DataTable(
columns: [
DataColumn(label: Text('属性')),
DataColumn(label: Text('类型')),
DataColumn(label: Text('说明')),
],
rows: [
DataRow(cells: [
DataCell(Text('value')),
DataCell(Text('double')),
DataCell(Text('进度值,0.0-1.0,null表示不确定进度')),
]),
DataRow(cells: [
DataCell(Text('valueColor')),
DataCell(Text('Animation<Color>')),
DataCell(Text('进度条颜色,可使用AlwaysStoppedAnimation')),
]),
DataRow(cells: [
DataCell(Text('backgroundColor')),
DataCell(Text('Color')),
DataCell(Text('背景颜色')),
]),
DataRow(cells: [
DataCell(Text('strokeWidth')),
DataCell(Text('double')),
DataCell(Text('线条宽度,默认4.0')),
]),
DataRow(cells: [
DataCell(Text('semanticsLabel')),
DataCell(Text('String')),
DataCell(Text('无障碍标签')),
]),
DataRow(cells: [
DataCell(Text('semanticsValue')),
DataCell(Text('String')),
DataCell(Text('无障碍值,如"50%"')),
]),
],
),
],
);
}

3. 线性进度指示器详解

3.1 基本线性进度指示器

dart 复制代码
Widget _buildLinearProgressIndicators() {
return Card(
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: [
ListTile(
title: Text('不确定进度(默认)'),
subtitle: Text('用于无法确定完成时间的任务'),
trailing: SizedBox(
width: 100,
child: LinearProgressIndicator(),
),
),
Divider(),
ListTile(
title: Text('确定进度(70%)'),
subtitle: Text('value: 0.7'),
trailing: SizedBox(
width: 100,
child: LinearProgressIndicator(
value: 0.7,
),
),
),
Divider(),
ListTile(
title: Text('自定义颜色'),
subtitle: Text('valueColor: Colors.green'),
trailing: SizedBox(
width: 100,
child: LinearProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.green),
backgroundColor: Colors.grey[200],
),
),
),
Divider(),
ListTile(
title: Text('最小高度'),
subtitle: Text('minHeight: 10'),
trailing: SizedBox(
width: 100,
child: LinearProgressIndicator(
minHeight: 10,
borderRadius: BorderRadius.circular(5),
),
),
),
],
),
),
);
}

3.2 线性进度指示器属性详解

dart 复制代码
Widget _buildLinearProgressIndicatorDetails() {
return ExpansionTile(
title: Text('LinearProgressIndicator 属性详解'),
children: [
DataTable(
columns: [
DataColumn(label: Text('属性')),
DataColumn(label: Text('类型')),
DataColumn(label: Text('说明')),
],
rows: [
DataRow(cells: [
DataCell(Text('value')),
DataCell(Text('double')),
DataCell(Text('进度值,0.0-1.0,null表示不确定进度')),
]),
DataRow(cells: [
DataCell(Text('valueColor')),
DataCell(Text('Animation<Color>')),
DataCell(Text('进度条颜色')),
]),
DataRow(cells: [
DataCell(Text('backgroundColor')),
DataCell(Text('Color')),
DataCell(Text('背景颜色')),
]),
DataRow(cells: [
DataCell(Text('minHeight')),
DataCell(Text('double')),
DataCell(Text('最小高度,默认4.0')),
]),
DataRow(cells: [
DataCell(Text('borderRadius')),
DataCell(Text('BorderRadius')),
DataCell(Text('圆角边框')),
]),
],
),
],
);
}

4. 自定义进度指示器

4.1 自定义圆形进度指示器

dart 复制代码
class CustomCircularProgressIndicator extends StatelessWidget {
final double value;
final Color color;
final double size;
final double strokeWidth;

const CustomCircularProgressIndicator({
Key? key,
this.value = 0.0,
this.color = Colors.blue,
this.size = 50.0,
this.strokeWidth = 5.0,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return CustomPaint(
size: Size(size, size),
painter: _CustomCircularProgressPainter(
value: value,
color: color,
strokeWidth: strokeWidth,
),
);
}
}

class _CustomCircularProgressPainter extends CustomPainter {
final double value;
final Color color;
final double strokeWidth;

_CustomCircularProgressPainter({
required this.value,
required this.color,
required this.strokeWidth,
});

@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = size.width / 2 - strokeWidth / 2;
final paint = Paint()
..color = color
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;

// 绘制背景
canvas.drawCircle(center, radius, paint..color = Colors.grey[300]!);

// 绘制进度弧
final sweepAngle = 2 * 3.1415926 * value;
canvas.drawArc(
Rect.fromCircle(center: center, radius: radius),
-3.1415926 / 2, // 从顶部开始
sweepAngle,
false,
paint..color = color,
);

// 绘制中心文本
if (value > 0) {
final textPainter = TextPainter(
text: TextSpan(
text: '${(value * 100).toInt()}%',
style: TextStyle(
fontSize: size.width / 5,
color: color,
fontWeight: FontWeight.bold,
),
),
textAlign: TextAlign.center,
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(
center.dx - textPainter.width / 2,
center.dy - textPainter.height / 2,
),
);
}
}

@override
bool shouldRepaint(_CustomCircularProgressPainter oldDelegate) {
return value != oldDelegate.value ||
color != oldDelegate.color ||
strokeWidth != oldDelegate.strokeWidth;
}
}

4.2 自定义线性进度指示器

dart 复制代码
class GradientLinearProgressIndicator extends StatelessWidget {
final double value;
final double height;
final List<Color> colors;

const GradientLinearProgressIndicator({
Key? key,
required this.value,
this.height = 8.0,
this.colors = const [Colors.blue, Colors.green],
}) : super(key: key);

@override
Widget build(BuildContext context) {
return Container(
height: height,
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(height / 2),
),
child: LayoutBuilder(
builder: (context, constraints) {
return Stack(
children: [
Container(
width: constraints.maxWidth * value.clamp(0.0, 1.0),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: colors,
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
borderRadius: BorderRadius.circular(height / 2),
),
),
if (value > 0)
Positioned(
right: 8,
top: 0,
bottom: 0,
child: Center(
child: Text(
'${(value * 100).toInt()}%',
style: TextStyle(
fontSize: height * 0.8,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
],
);
},
),
);
}
}

4.3 波浪形进度指示器

dart 复制代码
class WaveProgressIndicator extends StatefulWidget {
final double value;
final double size;
final Color color;

const WaveProgressIndicator({
Key? key,
required this.value,
this.size = 150.0,
this.color = Colors.blue,
}) : super(key: key);

@override
_WaveProgressIndicatorState createState() => _WaveProgressIndicatorState();
}

class _WaveProgressIndicatorState extends State<WaveProgressIndicator>
with SingleTickerProviderStateMixin {
late AnimationController _controller;

@override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
)..repeat();
}

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

@override
Widget build(BuildContext context) {
return Container(
width: widget.size,
height: widget.size,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.grey[300]!, width: 4),
),
child: Stack(
children: [
Positioned.fill(
child: ClipOval(
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return CustomPaint(
painter: _WavePainter(
progress: widget.value,
waveValue: _controller.value,
color: widget.color,
),
);
},
),
),
),
Center(
child: Text(
'${(widget.value * 100).toInt()}%',
style: TextStyle(
fontSize: widget.size * 0.2,
fontWeight: FontWeight.bold,
color: Colors.blue[700],
),
),
),
],
),
);
}
}

class _WavePainter extends CustomPainter {
final double progress;
final double waveValue;
final Color color;

_WavePainter({
required this.progress,
required this.waveValue,
required this.color,
});

@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color.withOpacity(0.7);

final waveCount = 3;
final waveAmplitude = 10.0;

final path = Path();
path.moveTo(0, size.height * (1 - progress));

for (double i = 0; i < size.width; i++) {
final x = i;
final y = size.height * (1 - progress) +
waveAmplitude *
sin((i / size.width * 2 * 3.1416 * waveCount) + waveValue * 2 * 3.1416);
path.lineTo(x, y);
}

path.lineTo(size.width, size.height);
path.lineTo(0, size.height);
path.close();

canvas.drawPath(path, paint);
}

@override
bool shouldRepaint(_WavePainter oldDelegate) {
return progress != oldDelegate.progress ||
waveValue != oldDelegate.waveValue ||
color != oldDelegate.color;
}
}

5. 进度指示器应用场景

5.1 加载页面

dart 复制代码
Widget _buildLoadingPage() {
return Card(
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'加载页面示例',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 20),
Container(
height: 150,
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(10),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text(
'加载中,请稍候...',
style: TextStyle(color: Colors.grey[600]),
),
],
),
),
),
],
),
),
);
}

5.2 按钮加载状态

dart 复制代码
class LoadingButton extends StatefulWidget {
@override
_LoadingButtonState createState() => _LoadingButtonState();
}

class _LoadingButtonState extends State<LoadingButton> {
bool _isLoading = false;

void _simulateLoading() async {
setState(() => _isLoading = true);
await Future.delayed(Duration(seconds: 2));
setState(() => _isLoading = false);
}

@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'按钮加载状态',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _isLoading ? null : _simulateLoading,
child: _isLoading
? Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
SizedBox(width: 8),
Text('处理中...'),
],
)
: Text('点击模拟加载'),
),
],
),
),
);
}
}

5.3 文件上传进度

dart 复制代码
class FileUploadProgress extends StatefulWidget {
@override
_FileUploadProgressState createState() => _FileUploadProgressState();
}

class _FileUploadProgressState extends State<FileUploadProgress> {
double _progress = 0.0;

void _simulateUpload() async {
setState(() => _progress = 0.0);

// 模拟上传进度
for (int i = 0; i <= 100; i += 10) {
await Future.delayed(Duration(milliseconds: 300));
setState(() => _progress = i / 100);
}

// 完成后重置
await Future.delayed(Duration(seconds: 1));
setState(() => _progress = 0.0);
}

@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'文件上传进度',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 20),

// 进度条
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('上传进度'),
Text('${(_progress * 100).toInt()}%'),
],
),
SizedBox(height: 8),
LinearProgressIndicator(
value: _progress,
backgroundColor: Colors.grey[200],
minHeight: 10,
borderRadius: BorderRadius.circular(5),
),
SizedBox(height: 8),
if (_progress > 0 && _progress < 1)
Text(
'正在上传文件...',
style: TextStyle(color: Colors.grey[600], fontSize: 12),
),
if (_progress == 1)
Row(
children: [
Icon(Icons.check_circle, color: Colors.green, size: 16),
SizedBox(width: 4),
Text('上传完成', style: TextStyle(color: Colors.green)),
],
),
],
),

SizedBox(height: 20),
ElevatedButton(
onPressed: _simulateUpload,
child: Text('模拟文件上传'),
),
],
),
),
);
}
}

6. 综合示例:完整进度指示器应用

dart 复制代码
class CompleteProgressExample extends StatefulWidget {
@override
_CompleteProgressExampleState createState() => _CompleteProgressExampleState();
}

class _CompleteProgressExampleState extends State<CompleteProgressExample>
with SingleTickerProviderStateMixin {
double _progressValue = 0.0;
late AnimationController _animationController;

@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: Duration(seconds: 3),
vsync: this,
);
}

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

void _startProgress() {
_progressValue = 0.0;

// 使用动画控制进度
final animation = Tween<double>(begin: 0, end: 1).animate(
CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
),
);

_animationController.forward(from: 0);

animation.addListener(() {
setState(() {
_progressValue = animation.value;
});
});
}

void _resetProgress() {
_animationController.reset();
setState(() => _progressValue = 0.0);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('进度指示器综合示例'),
actions: [
IconButton(
icon: Icon(Icons.refresh),
onPressed: _resetProgress,
tooltip: '重置进度',
),
],
),
body: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: [
// 顶部进度展示
Card(
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: [
Text(
'当前进度: ${(_progressValue * 100).toStringAsFixed(1)}%',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 20),
LinearProgressIndicator(
value: _progressValue,
minHeight: 20,
borderRadius: BorderRadius.circular(10),
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation<Color>(
_getProgressColor(_progressValue),
),
),
],
),
),
),

SizedBox(height: 30),

// 各种进度指示器展示
Expanded(
child: GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
children: [
_buildProgressCard(
title: '基础圆形',
child: CircularProgressIndicator(value: _progressValue),
),
_buildProgressCard(
title: '基础线性',
child: LinearProgressIndicator(value: _progressValue),
),
_buildProgressCard(
title: '自定义圆形',
child: CustomCircularProgressIndicator(
value: _progressValue,
size: 60,
color: _getProgressColor(_progressValue),
),
),
_buildProgressCard(
title: '渐变色',
child: GradientLinearProgressIndicator(
value: _progressValue,
height: 10,
colors: [Colors.blue, Colors.purple],
),
),
_buildProgressCard(
title: '波浪效果',
child: Container(
width: 80,
height: 80,
child: WaveProgressIndicator(
value: _progressValue,
size: 80,
color: _getProgressColor(_progressValue),
),
),
),
_buildProgressCard(
title: '带文字',
child: Stack(
alignment: Alignment.center,
children: [
CircularProgressIndicator(
value: _progressValue,
strokeWidth: 8,
),
Text(
'${(_progressValue * 100).toInt()}%',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
),

SizedBox(height: 20),

// 控制按钮
Row(
children: [
Expanded(
child: ElevatedButton.icon(
icon: Icon(Icons.play_arrow),
label: Text('开始进度'),
onPressed: _startProgress,
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 16),
),
),
),
SizedBox(width: 16),
Expanded(
child: ElevatedButton.icon(
icon: Icon(Icons.stop),
label: Text('重置进度'),
onPressed: _resetProgress,
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 16),
backgroundColor: Colors.grey,
),
),
),
],
),
],
),
),
);
}

Widget _buildProgressCard({required String title, required Widget child}) {
return Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 12),
child,
],
),
),
);
}

Color _getProgressColor(double value) {
if (value < 0.3) return Colors.red;
if (value < 0.7) return Colors.orange;
return Colors.green;
}
}

7. 性能优化与最佳实践

7.1 性能注意事项

dart 复制代码
class ProgressPerformanceTips extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'性能优化提示',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 16),
ListTile(
leading: Icon(Icons.check, color: Colors.green),
title: Text('使用const构造函数'),
subtitle: Text('对于静态进度指示器,使用const构造函数优化性能'),
),
ListTile(
leading: Icon(Icons.check, color: Colors.green),
title: Text('避免不必要的重绘'),
subtitle: Text('使用shouldRepaint精确控制CustomPaint的重绘条件'),
),
ListTile(
leading: Icon(Icons.check, color: Colors.green),
title: Text('控制动画频率'),
subtitle: Text('对于频繁更新的进度,考虑使用动画控制器而不是setState'),
),
ListTile(
leading: Icon(Icons.check, color: Colors.green),
title: Text('复用动画控制器'),
subtitle: Text('避免频繁创建和销毁AnimationController'),
),
],
),
),
);
}
}

7.2 最佳实践

  1. 选择合适的指示器类型
  • 短时间等待:使用不确定进度指示器
  • 可预测进度的任务:使用确定进度指示器
  • 需要精确百分比显示:使用带文字的进度指示器
  1. 提供状态反馈
  • 显示明确的进度百分比
  • 进度完成后提供完成状态反馈
  • 进度失败时提供错误状态
  1. 用户体验优化
  • 避免在快速操作时显示进度指示器
  • 长时间操作时提供取消选项
  • 使用动画增强用户体验
  1. 无障碍支持
  • 为进度指示器添加语义标签
  • 确保颜色对比度符合无障碍标准
  • 提供文本替代方案

8. 第三方库推荐

8.1 percent_indicator - 百分比进度指示器

yaml 复制代码
dependencies:
percent_indicator: ^4.2.2

使用示例:

dart 复制代码
import 'package:percent_indicator/percent_indicator.dart';

CircularPercentIndicator(
radius: 60.0,
lineWidth: 5.0,
percent: 0.8,
center: Text("80%"),
progressColor: Colors.green,
)

LinearPercentIndicator(
width: 200.0,
lineHeight: 14.0,
percent: 0.7,
backgroundColor: Colors.grey,
progressColor: Colors.blue,
)

8.2 flutter_spinkit - 加载动画集合

yaml 复制代码
dependencies:
flutter_spinkit: ^5.1.0

总结

Flutter的进度指示器组件非常丰富,从基础的CircularProgressIndicator和LinearProgressIndicator到自定义的进度指示器,可以满足各种应用场景的需求。关键点总结:

  1. 选择正确的指示器类型:根据场景选择圆形或线性进度指示器
  2. 合理使用进度值:可预测进度使用value,不可预测使用不确定模式
  3. 注重用户体验:提供清晰的进度反馈和状态指示
  4. 性能优化:避免不必要的重绘,合理使用动画
  5. 自定义扩展:通过CustomPaint可以创建独特的进度指示器

通过合理使用进度指示器,可以显著提升应用的用户体验,让用户清晰了解当前操作的状态。

相关推荐
happymaker06262 小时前
web前端学习日记——DAY08(jQuery,json文件格式,bootstrap)
前端·学习·jquery
be to FPGAer2 小时前
ASIC设计与综合-1
学习
大迪deblog2 小时前
系统架构设计-学习建议
学习·系统架构
黑眼圈子2 小时前
牛客刷题记录5
java·开发语言·学习·算法
炽烈小老头2 小时前
【每天学习一点算法 2026/03/26】合并区间
学习·算法
始持2 小时前
第十九讲 深度布局原理与优化
前端·flutter
人月神话Lee2 小时前
一个iOS开发者对Flutter中Widget、Element和RenderObject的理解
前端·flutter·ios
始持2 小时前
第二十讲 权限与设备能力
前端·flutter
鹭天2 小时前
目标检测学习笔记
笔记·学习·目标检测