MoodCanvas/lib/widgets/animated_background.dart
fengshengxiong 91b7eebbf2 接入TopON
2026-01-22 16:34:55 +08:00

214 lines
5.2 KiB
Dart

import 'dart:math';
import 'package:flutter/material.dart';
class AnimatedBackground extends StatefulWidget {
final Widget child;
final Color primaryColor;
final Color secondaryColor;
const AnimatedBackground({
super.key,
required this.child,
this.primaryColor = Colors.pinkAccent,
this.secondaryColor = Colors.purpleAccent,
});
@override
State<AnimatedBackground> createState() => _AnimatedBackgroundState();
}
class _AnimatedBackgroundState extends State<AnimatedBackground>
with TickerProviderStateMixin {
late AnimationController _controller;
late List<Particle> particles;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 10),
vsync: this,
)..repeat();
particles = List.generate(20, (index) => Particle());
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
// 渐变背景
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.black, Colors.grey[900]!, Colors.black],
),
),
),
// 粒子效果
AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return CustomPaint(
painter: ParticlePainter(
particles: particles,
animationValue: _controller.value,
primaryColor: widget.primaryColor,
secondaryColor: widget.secondaryColor,
),
size: Size.infinite,
);
},
),
// 主要内容
widget.child,
],
);
}
}
class Particle {
late double x;
late double y;
late double size;
late double speed;
late double opacity;
late Color color;
Particle() {
reset();
}
void reset() {
x = Random().nextDouble();
y = Random().nextDouble();
size = Random().nextDouble() * 3 + 1;
speed = Random().nextDouble() * 0.02 + 0.01;
opacity = Random().nextDouble() * 0.5 + 0.1;
}
void update() {
y -= speed;
if (y < 0) {
y = 1;
x = Random().nextDouble();
}
}
}
class ParticlePainter extends CustomPainter {
final List<Particle> particles;
final double animationValue;
final Color primaryColor;
final Color secondaryColor;
ParticlePainter({
required this.particles,
required this.animationValue,
required this.primaryColor,
required this.secondaryColor,
});
@override
void paint(Canvas canvas, Size size) {
for (var particle in particles) {
particle.update();
final paint = Paint()
..color = (Random().nextBool() ? primaryColor : secondaryColor)
.withValues(alpha: particle.opacity)
..style = PaintingStyle.fill;
final center = Offset(particle.x * size.width, particle.y * size.height);
// 绘制粒子
canvas.drawCircle(center, particle.size, paint);
// 添加发光效果
final glowPaint = Paint()
..color = paint.color.withValues(alpha: particle.opacity * 0.3)
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 3);
canvas.drawCircle(center, particle.size * 2, glowPaint);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
// 底部导航栏的发光效果组件
class GlowingBottomNavBar extends StatefulWidget {
final Widget child;
const GlowingBottomNavBar({super.key, required this.child});
@override
State<GlowingBottomNavBar> createState() => _GlowingBottomNavBarState();
}
class _GlowingBottomNavBarState extends State<GlowingBottomNavBar>
with SingleTickerProviderStateMixin {
late AnimationController _glowController;
late Animation<double> _glowAnimation;
@override
void initState() {
super.initState();
_glowController = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..repeat(reverse: true);
_glowAnimation = Tween<double>(begin: 0.3, end: 0.8).animate(
CurvedAnimation(parent: _glowController, curve: Curves.easeInOut),
);
}
@override
void dispose() {
_glowController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _glowAnimation,
builder: (context, child) {
return Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.pinkAccent.withValues(
alpha: _glowAnimation.value * 0.3,
),
blurRadius: 20,
spreadRadius: 2,
offset: const Offset(0, -5),
),
BoxShadow(
color: Colors.purpleAccent.withValues(
alpha: _glowAnimation.value * 0.2,
),
blurRadius: 30,
spreadRadius: 1,
offset: const Offset(0, -8),
),
],
),
child: widget.child,
);
},
);
}
}