import 'package:flutter/material.dart'; import 'dart:math' as math; class AudioWaveformProgress extends StatelessWidget { final double progress; final bool isActive; final ValueChanged? onChanged; const AudioWaveformProgress({ super.key, required this.progress, this.isActive = true, this.onChanged, }); @override Widget build(BuildContext context) { return GestureDetector( onTapDown: onChanged != null ? (details) { final box = context.findRenderObject() as RenderBox; final localPosition = details.localPosition; final value = localPosition.dx / box.size.width; onChanged?.call(value.clamp(0.0, 1.0)); } : null, onHorizontalDragUpdate: onChanged != null ? (details) { final box = context.findRenderObject() as RenderBox; final localPosition = details.localPosition; final value = localPosition.dx / box.size.width; onChanged?.call(value.clamp(0.0, 1.0)); } : null, child: Container( height: 40, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), ), child: CustomPaint( painter: WaveformPainter( progress: progress.clamp(0.0, 1.0), isActive: isActive, ), size: Size.infinite, ), ), ); } } class WaveformPainter extends CustomPainter { final double progress; final bool isActive; WaveformPainter({ required this.progress, required this.isActive, }); @override void paint(Canvas canvas, Size size) { final random = math.Random(42); // استفاده از seed ثابت برای consistency const barCount = 60; // تعداد میله‌ها final barWidth = (size.width / barCount) * 0.45; // عرض هر میله final spacing = size.width / barCount; final inactivePaint = Paint() ..color = const Color.fromARGB(255, 102, 102, 102) ..strokeWidth = barWidth ..strokeCap = StrokeCap.round; final activePaint = Paint() ..color = const Color.fromARGB(255, 0, 126, 167) ..strokeWidth = barWidth ..strokeCap = StrokeCap.round; // رسم میله‌های موج صدا for (int i = 0; i < barCount; i++) { final x = i * spacing + spacing / 2; // ارتفاع تصادفی برای هر میله (شبیه‌سازی موج صدا) final randomHeight = random.nextDouble(); final minHeight = size.height * 0.2; final maxHeight = size.height * 0.9; final barHeight = minHeight + (randomHeight * (maxHeight - minHeight)); final y1 = (size.height - barHeight) / 2; final y2 = y1 + barHeight; // تعیین رنگ بر اساس progress // استفاده از (i + 1) تا میله اول از 1/60 شروع بشه نه 0/60 final barProgress = (i + 1) / barCount; final paint = barProgress <= progress && isActive ? activePaint : inactivePaint; canvas.drawLine( Offset(x, y1), Offset(x, y2), paint, ); } // // رسم thumb (دایره کوچک) در محل progress // if (isActive && progress > 0) { // final thumbX = progress * size.width; // final thumbPaint = Paint() // ..color = const Color(0xFF3B82F6) // ..style = PaintingStyle.fill; // canvas.drawCircle( // Offset(thumbX, size.height / 2), // 6, // thumbPaint, // ); // // سایه برای thumb // final shadowPaint = Paint() // ..color = const Color(0xFF3B82F6).withOpacity(0.3) // ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 4); // canvas.drawCircle( // Offset(thumbX, size.height / 2), // 8, // shadowPaint, // ); // } } @override bool shouldRepaint(WaveformPainter oldDelegate) { return oldDelegate.progress != progress || oldDelegate.isActive != isActive; } }