Naveen Kumar
Naveen Kumar

Reputation: 3

How to create a waveform using custom painter - Flutter

In the below code i need to show the wave form when the user speaks, but i'm not sure about the code, it does't gets me positive

Reference image Expected Output

Also it needs to be varies based on the amplitude change and color of the wave also to be changed based on the resultant amplitude

///
class Waveform extends StatelessWidget {
  final List<double> amplitudes;

  const Waveform(this.amplitudes, {super.key});

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 100.0,
      child: CustomPaint(
        painter: WaveformPainter(amplitudes),
      ),
    );
  }
}

///
class WaveformPainter extends CustomPainter {
  final List<double> amplitudes;

  WaveformPainter(this.amplitudes);

  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = Colors.blue
      ..strokeCap = StrokeCap.round
      ..strokeWidth = 4.0;

    final Path path = Path();

    for (int i = 0; i < amplitudes.length; i++) {
      final double x = i * size.width / (amplitudes.length - 1);
      final double y = (1 - amplitudes[i]) * size.height / 2;

      if (i == 0) {
        path.moveTo(x, y);
      } else {
        path.lineTo(x, y);
      }
    }

    // Adding gradient
    paint.shader = LinearGradient(
      begin: Alignment.topCenter,
      end: Alignment.bottomCenter,
      colors: [
        Colors.blue.withOpacity(0.5),
        Colors.blue.withOpacity(0.0),
      ],
    ).createShader(Rect.fromPoints(Offset(0, 0), Offset(0, size.height)));

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

Upvotes: 0

Views: 297

Answers (1)

Andrei Bucurei
Andrei Bucurei

Reputation: 2458

Your approach looks good but I assume it results in sharp lines instead of smooth curves at peaks. One way to achieve the smooth wave is by using the CatmullRomSpline. For this, you will need at least 4 values in your input list of amplitues. If you don't have 4 points you will have to generate them.

@override
void paint(Canvas canvas, Size size) {
  // normalize amplitude values based on the size
  final points = normalize(amplitudes, size);
  // ensure that at least 4 points are available;
  final spline = CatmullRomSpline(points);
  final samples = spline.generateSamples().toList();
  final path = Path();
  final startOffset = samples.first.value;
  path.moveTo(startOffset.dx, startOffset.dy);

  for (final sample in samples) {
    path.lineTo(sample.value.dx, sample.value.dy);
  }

  final paint = /* config your paint */;
  canvas.drawPath(path, paint);
}

Upvotes: 0

Related Questions