Flutterにおける水の波紋アニメーション
さあ始めましょう。
まずはボタンのあるレイアウトを作ります。
import 'package:circular_animation/animation.dart';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
State createState() => _HomePageState();
}
class _HomePageState extends State {
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: const CircleAvatar(
radius: 50,
backgroundColor: Colors.deepPurple,
child: Icon(Icons.waves_rounded),
),
),
);
}
}
次に本命のロジックの部分です。
これを別ファイル、または同じファイルに追加してください。
// ignore_for_file: must_be_immutable
import 'dart:async';
import 'package:flutter/material.dart';
class WaveAnimation extends StatefulWidget {
Widget child;
double outerMostCircleStartRadius;
double outerMostCircleEndRadius;
double startOpacity;
Duration animationTime;
int numberOfCircles;
double borderWidth;
Color borderColor;
double gap;
Duration delay;
WaveAnimation({
super.key,
required this.child,
required this.borderColor,
required this.outerMostCircleEndRadius,
required this.outerMostCircleStartRadius,
this.startOpacity = .1,
this.numberOfCircles = 2,
this.delay = const Duration(seconds: 2),
this.animationTime = const Duration(seconds: 1),
this.borderWidth = 8.0,
this.gap = 15.0,
}) {
assert(numberOfCircles > 0);
assert(gap > 0);
assert(outerMostCircleStartRadius > 0);
assert(outerMostCircleEndRadius > 0);
assert(startOpacity > 0);
assert(delay >= animationTime);
}
_WaveAnimationState createState() => _WaveAnimationState();
}
class _WaveAnimationState extends State<WaveAnimation>
with TickerProviderStateMixin {
AnimationController? _radiusOpacityController;
Animation? _radiusAnimation;
Animation? _opacityAnimation;
void initState() {
super.initState();
_setupAnimation();
}
void _setupAnimation() {
_radiusOpacityController = AnimationController(
vsync: this,
duration: widget.animationTime,
)..addListener(() {
setState(() {});
});
Timer(widget.delay, () {
_radiusOpacityController?.reset();
_radiusOpacityController?.forward();
});
_radiusAnimation = Tween(
begin: widget.outerMostCircleStartRadius,
end: widget.outerMostCircleEndRadius)
.animate(
CurvedAnimation(
parent: _radiusOpacityController!,
curve: Curves.linear,
),
);
_opacityAnimation = Tween(begin: widget.startOpacity, end: 0.0).animate(
CurvedAnimation(
parent: _radiusOpacityController!, curve: Curves.linear));
}
void dispose() {
super.dispose();
_radiusOpacityController?.dispose();
}
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: [
Opacity(
opacity: _opacityAnimation!.value,
child: CustomPaint(
painter: _CustomPainter(
numberOfCircles: widget.numberOfCircles,
borderWidth: widget.borderWidth,
borderColor: widget.borderColor,
gap: widget.gap,
outermostCircleRadius: _radiusAnimation!.value,
),
),
),
widget.child,
],
);
}
}
class _CustomPainter extends CustomPainter {
int? _numberOfCircles;
double? _borderWidth;
Color? _borderColor;
double? _gap;
double? _outermostCircleRadius;
_CustomPainter({
required int numberOfCircles,
required double borderWidth,
required Color borderColor,
required double gap,
required double outermostCircleRadius,
}) {
_numberOfCircles = numberOfCircles;
_borderWidth = borderWidth;
_borderColor = borderColor;
_gap = gap;
_outermostCircleRadius = outermostCircleRadius;
}
void paint(Canvas canvas, Size size) {
final borderPaint = Paint()
..color = _borderColor!
..strokeWidth = _borderWidth!
..style = PaintingStyle.stroke;
var center = Offset(size.width / 2, size.height / 2);
for (int i = 0; i < _numberOfCircles! && _outermostCircleRadius! > 0; i++) {
final radius = _outermostCircleRadius! - _gap! * i;
if (radius > 0) {
canvas.drawCircle(center, radius, borderPaint);
}
}
}
bool shouldRepaint(_CustomPainter oldDelegate) {
return oldDelegate._outermostCircleRadius != _outermostCircleRadius;
}
}
このアニメーションを使い方:
import 'package:circular_animation/animation.dart';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: WaveAnimation(
borderColor: Colors.black,
outerMostCircleEndRadius: 100,
outerMostCircleStartRadius: 50,
numberOfCircles: 4,
child: const CircleAvatar(
radius: 50,
backgroundColor: Colors.deepPurple,
child: Icon(Icons.waves_rounded),
),
),
),
);
}
}
何をしましたか?
- 半径と不透明度のアニメーションのためのコントローラーを作成しました
- アニメーションの時間に応じて、不透明度を減少させ、半径を増加させます
- 与えられた時間後にアニメーションを再開するためにタイマーを使用します
- レイアウトにスタックを使用します。私たちの円は、渡された子ウィジェットの下にあります
- _CustomPainterは円を描くために使用されます
- 最も外側の円から始めて、内側の円に進み、半径が0より大きい間描き続けます
出力:
このアニメーションでさらに多くの組み合わせを作り、LinkedInで私にタグを付けてください。
LinkedIn
こちらの記事はdev.toの良い記事を日本人向けに翻訳しています。
https://dev.to/raman04byte/water-wave-animation-in-flutter-io5