214 lines
5.2 KiB
Dart
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,
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|