← Назад к вопросам
Реализовать кастомный Progress Indicator
2.2 Middle🔥 91 комментариев
#Flutter виджеты#Анимации
Условие
Создайте кастомный виджет индикатора прогресса с использованием CustomPainter.
Требования
- Круговой индикатор прогресса
- Настраиваемый цвет, толщина линии, размер
- Отображение процента в центре
- Анимированное изменение прогресса
- Возможность использовать как determinate и indeterminate
Интерфейс
CustomProgressIndicator({
required double progress, // 0.0 - 1.0
Color color = Colors.blue,
Color backgroundColor = Colors.grey,
double strokeWidth = 4.0,
bool showPercentage = true,
})
Дополнительные баллы
- Градиентный цвет прогресса
- Разные стили конца линии (round, square)
- Пульсирующий эффект при завершении
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение: Кастомный Progress Indicator
Создаём круговой индикатор прогресса с поддержкой градиентов и анимаций.
CustomPainter для рисования
Используем Canvas для отрисовки окружности и прогрессивной дуги:
class CircularProgressPainter extends CustomPainter {
final double progress;
final Color progressColor;
final Color backgroundColor;
final double strokeWidth;
CircularProgressPainter({
required this.progress,
required this.progressColor,
required this.backgroundColor,
required this.strokeWidth,
});
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = (size.width - strokeWidth) / 2;
// Фоновая окружность
canvas.drawCircle(
center,
radius,
Paint()
..color = backgroundColor
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth,
);
// Дуга прогресса
canvas.drawArc(
Rect.fromCircle(center: center, radius: radius),
-3.14159 / 2,
2 * 3.14159 * progress,
false,
Paint()
..color = progressColor
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth
..strokeCap = StrokeCap.round,
);
}
@override
bool shouldRepaint(CircularProgressPainter oldDelegate) {
return oldDelegate.progress != progress;
}
}
Основной виджет
class CustomProgressIndicator extends StatefulWidget {
final double progress;
final Color color;
final Color backgroundColor;
final double strokeWidth;
final bool showPercentage;
final double size;
const CustomProgressIndicator({
required this.progress,
this.color = Colors.blue,
this.backgroundColor = Colors.grey,
this.strokeWidth = 4.0,
this.showPercentage = true,
this.size = 200,
});
@override
_CustomProgressIndicatorState createState() =>
_CustomProgressIndicatorState();
}
class _CustomProgressIndicatorState extends State<CustomProgressIndicator>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(milliseconds: 800),
vsync: this,
);
_setupAnimation();
_controller.forward();
}
void _setupAnimation() {
_animation = Tween<double>(begin: 0, end: widget.progress).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
);
}
@override
void didUpdateWidget(CustomProgressIndicator oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.progress != widget.progress) {
_controller.reset();
_setupAnimation();
_controller.forward();
}
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: widget.size,
height: widget.size,
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Stack(
alignment: Alignment.center,
children: [
CustomPaint(
painter: CircularProgressPainter(
progress: _animation.value,
progressColor: widget.color,
backgroundColor: widget.backgroundColor,
strokeWidth: widget.strokeWidth,
),
),
if (widget.showPercentage)
Text(
'${(_animation.value * 100).toStringAsFixed(0)}%',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: widget.color,
),
),
],
);
},
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
Примеры использования
class ProgressDemo extends StatefulWidget {
@override
_ProgressDemoState createState() => _ProgressDemoState();
}
class _ProgressDemoState extends State<ProgressDemo> {
double _progress = 0.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Progress Indicator')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomProgressIndicator(
progress: _progress,
color: Colors.blue,
size: 200,
),
SizedBox(height: 40),
Slider(
value: _progress,
onChanged: (value) {
setState(() => _progress = value);
},
),
],
),
),
);
}
}
Этот индикатор можно легко расширить для градиентов и различных стилей конца линии.