import 'dart:math' as math; import 'package:flutter/material.dart'; class Wave extends StatefulWidget { final double value; final Color color; final Axis direction; const Wave({ Key? key, required this.value, required this.color, required this.direction, }) : super(key: key); @override _WaveState createState() => _WaveState(); } class _WaveState extends State with SingleTickerProviderStateMixin { late AnimationController _animationController; @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: Duration(seconds: 2), ); _animationController.repeat(); } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: CurvedAnimation( parent: _animationController, curve: Curves.easeInOut, ), builder: (context, child) => ClipPath( child: Container( color: widget.color, ), clipper: _WaveClipper( animationValue: _animationController.value, value: widget.value, direction: widget.direction, ), ), ); } } class _WaveClipper extends CustomClipper { final double animationValue; final double value; final Axis direction; _WaveClipper({ required this.animationValue, required this.value, required this.direction, }); @override Path getClip(Size size) { if (direction == Axis.horizontal) { Path path = Path() ..addPolygon(_generateHorizontalWavePath(size), false) ..lineTo(0.0, size.height) ..lineTo(0.0, 0.0) ..close(); return path; } Path path = Path() ..addPolygon(_generateVerticalWavePath(size), false) ..lineTo(size.width, size.height) ..lineTo(0.0, size.height) ..close(); return path; } List _generateHorizontalWavePath(Size size) { final waveList = []; for (int i = -2; i <= size.height.toInt() + 2; i++) { final waveHeight = (size.width / 20); final dx = math.sin((animationValue * 360 - i) % 360 * (math.pi / 180)) * waveHeight + (size.width * value); waveList.add(Offset(dx, i.toDouble())); } return waveList; } List _generateVerticalWavePath(Size size) { final waveList = []; for (int i = -2; i <= size.width.toInt() + 2; i++) { final waveHeight = (size.height / 20); final dy = math.sin((animationValue * 360 - i) % 360 * (math.pi / 180)) * waveHeight + (size.height - (size.height * value)); waveList.add(Offset(i.toDouble(), dy)); } return waveList; } @override bool shouldReclip(_WaveClipper oldClipper) => animationValue != oldClipper.animationValue; }