Flutter 3.x+Multi-Layer WavesCustomPainter & ClipPath
Flutter Wave Generator
Design flowing multi-layer organic waves, curved dividers, and animated liquid transitions with copy-ready Dart code.
Start
End
BG
Flutter CustomPainter Code
// Widget usage
// Dynamic organic animated waves
SizedBox(
width: double.infinity,
height: 300.0,
child: const AnimatedWavesWidget(
waveType: 'layered',
layersCount: 3,
amplitude: 40.0,
frequency: 1.80,
phase: 0.00,
positionY: 150.0,
smoothness: 0.35,
isBottom: true,
isGradient: true,
primaryColor: const Color(0xFF8B5CF6),
secondaryColor: const Color(0xFFEC4899),
),
)
// โโโ Animated Waves Widget โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
import 'dart:math' as math;
import 'package:flutter/material.dart';
class AnimatedWavesWidget extends StatefulWidget {
final double waveHeight;
final String waveType;
final int layersCount;
final double amplitude;
final double frequency;
final double phase;
final double positionY;
final double smoothness;
final bool isBottom;
final bool isGradient;
final Color primaryColor;
final Color secondaryColor;
const AnimatedWavesWidget({
Key? key,
this.waveHeight = 300.0,
this.waveType = 'layered',
this.layersCount = 3,
this.amplitude = 40.0,
this.frequency = 1.80,
this.phase = 0.00,
this.positionY = 150.0,
this.smoothness = 0.35,
this.isBottom = true,
this.isGradient = true,
this.primaryColor = const Color(0xFF8B5CF6),
this.secondaryColor = const Color(0xFFEC4899),
}) : super(key: key);
@override
State<AnimatedWavesWidget> createState() => _AnimatedWavesWidgetState();
}
class _AnimatedWavesWidgetState extends State<AnimatedWavesWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 5),
)..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return CustomPaint(
size: Size(double.infinity, widget.waveHeight),
painter: WavePainter(
time: _controller.value * math.pi * 2,
waveType: widget.waveType,
layersCount: widget.layersCount,
amplitude: widget.amplitude,
frequency: widget.frequency,
phase: widget.phase,
positionY: widget.positionY,
smoothness: widget.smoothness,
isBottom: widget.isBottom,
isGradient: widget.isGradient,
primaryColor: widget.primaryColor,
secondaryColor: widget.secondaryColor,
),
);
},
);
}
}
class WavePainter extends CustomPainter {
final double time;
final String waveType;
final int layersCount;
final double amplitude;
final double frequency;
final double phase;
final double positionY;
final double smoothness;
final bool isBottom;
final bool isGradient;
final Color primaryColor;
final Color secondaryColor;
WavePainter({
required this.time,
required this.waveType,
required this.layersCount,
required this.amplitude,
required this.frequency,
required this.phase,
required this.positionY,
required this.smoothness,
required this.isBottom,
required this.isGradient,
required this.primaryColor,
required this.secondaryColor,
});
@override
void paint(Canvas canvas, Size size) {
final W = size.width;
final H = size.height;
// Scale baseline 300 units to layout dimensions
final scaleY = H / 300.0;
final scaleX = W / 300.0;
final boundaryY = isBottom ? H : 0.0;
for (int layer = 0; layer < layersCount; layer++) {
final amp = amplitude * (1 - layer * 0.15) * scaleY;
final freq = frequency * (1 + layer * 0.1);
final ph = phase + layer * 1.5 + time * (1 + layer * 0.15);
final posY = (positionY + (isBottom ? layer * 15 : -layer * 15)) * scaleY;
final int numPoints = waveType == 'curved' ? 6 : 20;
final List<Offset> pts = [];
for (int i = 0; i < numPoints; i++) {
final x = (i * 300.0) / (numPoints - 1) * scaleX;
double y = posY;
if (waveType == 'liquid') {
y += math.sin(x * (freq / W * math.pi * 2) + ph) * amp * 0.7;
y += math.cos(x * (freq * 2.0 / W * math.pi * 2) - ph * 0.5) * amp * 0.3;
} else {
y += math.sin(x * (freq / W * math.pi * 2) + ph) * amp;
}
pts.add(Offset(x, y.clamp(0.0, H)));
}
final path = Path();
path.moveTo(0.0, boundaryY);
path.lineTo(pts[0].dx, pts[0].dy);
final tension = waveType == 'sine' ? 0.35 : smoothness;
for (int i = 0; i < numPoints - 1; i++) {
final prev = pts[i == 0 ? 0 : i - 1];
final curr = pts[i];
final next = pts[i + 1];
final next2 = pts[i + 2 >= numPoints ? numPoints - 1 : i + 2];
final c1 = Offset(
curr.dx + (next.dx - prev.dx) * tension,
curr.dy + (next.dy - prev.dy) * tension,
);
final c2 = Offset(
next.dx - (next2.dx - curr.dx) * tension,
next.dy - (next2.dy - curr.dy) * tension,
);
path.cubicTo(c1.dx, c1.dy, c2.dx, c2.dy, next.dx, next.dy);
}
path.lineTo(W, boundaryY);
path.close();
final opacity = (1.0 - (layer * 0.2)).clamp(0.1, 1.0);
final layerColor1 = primaryColor.withOpacity(primaryColor.opacity * opacity);
final layerColor2 = secondaryColor.withOpacity(secondaryColor.opacity * opacity);
final paint = Paint()..style = PaintingStyle.fill;
if (isGradient) {
paint.shader = LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [layerColor1, layerColor2],
).createShader(Rect.fromLTRB(0, posY - amp, W, boundaryY));
} else {
paint.color = layerColor1;
}
canvas.drawPath(path, paint);
}
}
@override
bool shouldRepaint(covariant WavePainter oldDelegate) => true;
}Animated waves use single state AnimationControllers to render high performance floating wave shapes.
Flutter Generator Suite SwitcherView All Tools Suite โ