| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714 |
- import 'package:flutter/material.dart';
- import 'dart:math' as math;
- import '../../utils/colors.dart';
- import '../../utils/theme_manager.dart';
- import '../../utils/theme_notifier.dart';
- class AnimatedBackground extends StatefulWidget {
- final Widget child;
- final bool isZenMode;
- final Function()? onShake;
- const AnimatedBackground({
- super.key,
- required this.child,
- this.isZenMode = false,
- this.onShake,
- });
- @override
- State<AnimatedBackground> createState() => _AnimatedBackgroundState();
- }
- class _AnimatedBackgroundState extends State<AnimatedBackground>
- with TickerProviderStateMixin {
- late AnimationController _controller1;
- late AnimationController _controller2;
- late AnimationController _controller3;
- late AnimationController _shakeController;
- late AnimationController _seasonalController;
- bool _isShaking = false;
- // Static variables to preserve animation state across navigation
- static double _preservedLayer1Value = 0.0;
- static double _preservedLayer2Value = 0.0;
- static double _preservedLayer3Value = 0.0;
- static double _preservedSeasonalValue = 0.0;
- static DateTime _lastPreservedTime = DateTime.now();
- @override
- void initState() {
- super.initState();
- // Calculate time elapsed since last preservation
- final now = DateTime.now();
- final elapsedSeconds =
- now.difference(_lastPreservedTime).inMilliseconds / 1000.0;
- // Update preserved values based on elapsed time
- _preservedLayer1Value =
- (_preservedLayer1Value + elapsedSeconds / 60.0) % 1.0;
- _preservedLayer2Value =
- (_preservedLayer2Value + elapsedSeconds / 80.0) % 1.0;
- _preservedLayer3Value =
- (_preservedLayer3Value + elapsedSeconds / 100.0) % 1.0;
- _preservedSeasonalValue =
- (_preservedSeasonalValue + elapsedSeconds / 60.0) % 1.0;
- // Create multiple animation controllers for layered effects
- _controller1 = AnimationController(
- duration: const Duration(seconds: 60),
- vsync: this,
- );
- _controller1.value = _preservedLayer1Value;
- _controller1.repeat();
- _controller2 = AnimationController(
- duration: const Duration(seconds: 80),
- vsync: this,
- );
- _controller2.value = _preservedLayer2Value;
- _controller2.repeat();
- _controller3 = AnimationController(
- duration: const Duration(seconds: 100),
- vsync: this,
- );
- _controller3.value = _preservedLayer3Value;
- _controller3.repeat();
- // Faster controller for seasonal patterns
- _seasonalController = AnimationController(
- duration: const Duration(seconds: 60), // Faster for seasonal effects
- vsync: this,
- );
- _seasonalController.value = _preservedSeasonalValue;
- _seasonalController.repeat();
- // Shake effect controller
- _shakeController = AnimationController(
- duration: const Duration(milliseconds: 800),
- vsync: this,
- );
- }
- @override
- void dispose() {
- // Preserve current animation values for next instance
- _preservedLayer1Value = _controller1.value;
- _preservedLayer2Value = _controller2.value;
- _preservedLayer3Value = _controller3.value;
- _preservedSeasonalValue = _seasonalController.value;
- _lastPreservedTime = DateTime.now();
- _controller1.dispose();
- _controller2.dispose();
- _controller3.dispose();
- _shakeController.dispose();
- _seasonalController.dispose();
- super.dispose();
- }
- void triggerShake() {
- if (!_isShaking) {
- _isShaking = true;
- _shakeController.forward().then((_) {
- _shakeController.reset();
- _isShaking = false;
- });
- widget.onShake?.call();
- }
- }
- @override
- Widget build(BuildContext context) {
- return ThemeAwareBuilder(
- builder: (context, theme) {
- final seasonalPattern = ThemeManager.effectiveTheme;
- return Stack(
- children: [
- // Base background
- Container(
- decoration: BoxDecoration(
- gradient: LinearGradient(
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- colors: [
- ZenColors.currentAppBackground,
- ZenColors.currentSecondaryBackground,
- ],
- ),
- ),
- ),
- // Seasonal pattern overlay
- if (seasonalPattern != SeasonalTheme.default_)
- _buildSeasonalPattern(seasonalPattern),
- // Animated gradient layers
- ...List.generate(2, (index) => _buildAnimatedLayer(index)),
- // Content overlay
- widget.child,
- ],
- );
- },
- );
- }
- Widget _buildSeasonalPattern(SeasonalTheme theme) {
- switch (theme) {
- case SeasonalTheme.winter:
- return _buildSnowfall();
- case SeasonalTheme.summer:
- return _buildSunshine();
- case SeasonalTheme.autumn:
- return _buildFallingLeaves();
- case SeasonalTheme.spring:
- return _buildFlowersBlooming();
- default:
- return const SizedBox.shrink();
- }
- }
- Widget _buildSnowfall() {
- return AnimatedBuilder(
- animation: _seasonalController,
- builder: (context, child) {
- return Container(
- decoration: BoxDecoration(
- gradient: RadialGradient(
- center: Alignment.topCenter,
- radius: 1.5,
- colors: [Colors.white.withValues(alpha: 0.1), Colors.transparent],
- ),
- ),
- child: CustomPaint(
- painter: SnowfallPainter(_seasonalController.value),
- size: Size.infinite,
- ),
- );
- },
- );
- }
- Widget _buildSunshine() {
- return AnimatedBuilder(
- animation: _seasonalController,
- builder: (context, child) {
- return Container(
- decoration: BoxDecoration(
- gradient: RadialGradient(
- center: Alignment.topCenter,
- radius: 1.5,
- colors: [
- Colors.yellow.withValues(alpha: 0.15),
- Colors.transparent,
- ],
- ),
- ),
- child: CustomPaint(
- painter: SunshinePainter(_seasonalController.value),
- size: Size.infinite,
- ),
- );
- },
- );
- }
- Widget _buildFallingLeaves() {
- return AnimatedBuilder(
- animation: _seasonalController,
- builder: (context, child) {
- return Container(
- decoration: BoxDecoration(
- gradient: RadialGradient(
- center: Alignment.topCenter,
- radius: 1.5,
- colors: [
- Colors.orange.withValues(alpha: 0.1),
- Colors.transparent,
- ],
- ),
- ),
- child: CustomPaint(
- painter: FallingLeavesPainter(_seasonalController.value),
- size: Size.infinite,
- ),
- );
- },
- );
- }
- Widget _buildFlowersBlooming() {
- return AnimatedBuilder(
- animation: _seasonalController,
- builder: (context, child) {
- return Container(
- decoration: BoxDecoration(
- gradient: RadialGradient(
- center: Alignment.topCenter,
- radius: 1.5,
- colors: [Colors.pink.withValues(alpha: 0.1), Colors.transparent],
- ),
- ),
- child: CustomPaint(
- painter: FlowersPainter(_seasonalController.value),
- size: Size.infinite,
- ),
- );
- },
- );
- }
- Widget _buildAnimatedLayer(int layerIndex) {
- AnimationController controller;
- List<Color> colors;
- switch (layerIndex) {
- case 0:
- controller = _controller1;
- colors =
- widget.isZenMode
- ? ZenColors.zenModeColors
- : ZenColors.animationLayer1;
- break;
- case 1:
- controller = _controller2;
- colors = ZenColors.animationLayer2;
- break;
- default:
- controller = _controller3;
- colors = ZenColors.animationLayer3;
- }
- return AnimatedBuilder(
- animation: Listenable.merge([controller, _shakeController]),
- builder: (context, child) {
- // Add shake effect
- double shakeIntensity = 0.0;
- if (_isShaking) {
- shakeIntensity =
- math.sin(_shakeController.value * math.pi * 8) *
- (1 - _shakeController.value) *
- 10;
- }
- return Transform.translate(
- offset: Offset(
- shakeIntensity * (math.Random().nextDouble() - 0.5) * 2,
- shakeIntensity * (math.Random().nextDouble() - 0.5) * 2,
- ),
- child: Transform.rotate(
- angle: controller.value * 2 * math.pi,
- child: Container(
- decoration: BoxDecoration(
- gradient: RadialGradient(
- center: Alignment(
- math.sin(controller.value * 2 * math.pi) * 0.5,
- math.cos(controller.value * 2 * math.pi) * 0.5,
- ),
- radius:
- 1.5 +
- math.sin(controller.value * math.pi) * 0.5 +
- (shakeIntensity * 0.1),
- colors: colors,
- ),
- ),
- ),
- ),
- );
- },
- );
- }
- }
- class SnowfallPainter extends CustomPainter {
- final double animationValue;
- SnowfallPainter(this.animationValue);
- @override
- void paint(Canvas canvas, Size size) {
- final paint =
- Paint()
- ..color = Colors.white.withValues(alpha: 0.3)
- ..style = PaintingStyle.fill;
- // Fixed seed for consistent positions (removed unused variable)
- // Draw 80 snowflakes with falling animation covering entire screen
- for (int i = 0; i < 80; i++) {
- // Better distribution using sine and cosine functions with different frequencies
- final angle1 =
- i * 2.39996; // Use irrational number for better distribution
- final baseX = (math.sin(angle1) * 0.5 + 0.5) * size.width;
- // Consistent falling animation starting from top
- const fallSpeed = 0.2; // Same speed for all snowflakes
- final fallOffset = animationValue * fallSpeed * size.height * 1.2;
- final animatedY =
- (fallOffset + (i * 20) % size.height) % (size.height + 100) - 100;
- final snowflakeSize = (2 + math.sin(i * 0.3) * 2) * 3; // 3x larger
- _drawDetailedSnowflake(
- canvas,
- Offset(baseX, animatedY),
- snowflakeSize,
- paint,
- );
- }
- }
- void _drawDetailedSnowflake(
- Canvas canvas,
- Offset center,
- double size,
- Paint paint,
- ) {
- // Draw a 6-pointed snowflake
- final strokePaint =
- Paint()
- ..color = paint.color
- ..strokeWidth = size * 0.1
- ..style = PaintingStyle.stroke;
- // Draw 6 main spokes
- for (int i = 0; i < 6; i++) {
- final angle = i * math.pi / 3;
- final startX = center.dx + math.cos(angle) * size * 0.2;
- final startY = center.dy + math.sin(angle) * size * 0.2;
- final endX = center.dx + math.cos(angle) * size;
- final endY = center.dy + math.sin(angle) * size;
- canvas.drawLine(Offset(startX, startY), Offset(endX, endY), strokePaint);
- // Draw small branches on each spoke
- final branchSize = size * 0.3;
- final branchX = center.dx + math.cos(angle) * size * 0.6;
- final branchY = center.dy + math.sin(angle) * size * 0.6;
- // Left branch
- final leftBranchAngle = angle - math.pi / 4;
- canvas.drawLine(
- Offset(branchX, branchY),
- Offset(
- branchX + math.cos(leftBranchAngle) * branchSize,
- branchY + math.sin(leftBranchAngle) * branchSize,
- ),
- strokePaint,
- );
- // Right branch
- final rightBranchAngle = angle + math.pi / 4;
- canvas.drawLine(
- Offset(branchX, branchY),
- Offset(
- branchX + math.cos(rightBranchAngle) * branchSize,
- branchY + math.sin(rightBranchAngle) * branchSize,
- ),
- strokePaint,
- );
- }
- // Draw center circle
- canvas.drawCircle(center, size * 0.15, paint);
- }
- @override
- bool shouldRepaint(CustomPainter oldDelegate) => true;
- }
- class SunshinePainter extends CustomPainter {
- final double animationValue;
- SunshinePainter(this.animationValue);
- @override
- void paint(Canvas canvas, Size size) {
- final center = Offset(size.width * 0.8, size.height * 0.2);
- // Draw sun rays
- final rayPaint =
- Paint()
- ..color = Colors.yellow.withValues(alpha: 0.2)
- ..strokeWidth = 2
- ..style = PaintingStyle.stroke;
- for (int i = 0; i < 12; i++) {
- final angle = (i * math.pi * 2 / 12) + (animationValue * math.pi * 2);
- final startRadius = 30;
- final endRadius = 60 + math.sin(animationValue * math.pi * 4) * 10;
- final start = Offset(
- center.dx + math.cos(angle) * startRadius,
- center.dy + math.sin(angle) * startRadius,
- );
- final end = Offset(
- center.dx + math.cos(angle) * endRadius,
- center.dy + math.sin(angle) * endRadius,
- );
- canvas.drawLine(start, end, rayPaint);
- }
- // Draw sun
- final sunPaint =
- Paint()
- ..color = Colors.yellow.withValues(alpha: 0.3)
- ..style = PaintingStyle.fill;
- canvas.drawCircle(center, 25, sunPaint);
- }
- @override
- bool shouldRepaint(CustomPainter oldDelegate) => true;
- }
- class FallingLeavesPainter extends CustomPainter {
- final double animationValue;
- FallingLeavesPainter(this.animationValue);
- @override
- void paint(Canvas canvas, Size size) {
- final paint =
- Paint()
- ..color = Colors.orange.withValues(alpha: 0.4)
- ..style = PaintingStyle.fill;
- // Fixed seed for consistent positions (removed unused variable)
- // Draw 40 leaves with slow falling animation
- for (int i = 0; i < 40; i++) {
- // Better distribution using trigonometric functions
- final angle1 = i * 1.618; // Golden ratio for better distribution
- final baseX = (math.sin(angle1) * 0.5 + 0.5) * size.width;
- // Consistent falling animation starting from top with slight swaying
- const fallSpeed = 0.2; // Same speed as snowflakes
- final swayAmount = math.sin(animationValue * math.pi * 2 + i) * 25;
- final fallOffset = animationValue * fallSpeed * size.height * 1.2;
- final animatedY =
- (fallOffset + (i * 30) % size.height) % (size.height + 100) - 100;
- final animatedX = baseX + swayAmount;
- final leafSize = (6 + math.sin(i * 0.4) * 4) * 3; // 3x larger
- _drawDetailedLeaf(
- canvas,
- Offset(animatedX, animatedY),
- leafSize,
- paint,
- i,
- );
- }
- }
- void _drawDetailedLeaf(
- Canvas canvas,
- Offset center,
- double size,
- Paint paint,
- int index,
- ) {
- // Create a more detailed leaf shape
- final leafPaint =
- Paint()
- ..color = paint.color
- ..style = PaintingStyle.fill;
- final strokePaint =
- Paint()
- ..color = paint.color.withValues(alpha: 0.8)
- ..strokeWidth = size * 0.05
- ..style = PaintingStyle.stroke;
- // Different leaf shapes based on index
- final leafType = index % 3;
- if (leafType == 0) {
- // Oak leaf shape
- _drawOakLeaf(canvas, center, size, leafPaint, strokePaint);
- } else if (leafType == 1) {
- // Maple leaf shape
- _drawMapleLeaf(canvas, center, size, leafPaint, strokePaint);
- } else {
- // Simple oval leaf with stem
- _drawSimpleLeaf(canvas, center, size, leafPaint, strokePaint);
- }
- }
- void _drawOakLeaf(
- Canvas canvas,
- Offset center,
- double size,
- Paint fillPaint,
- Paint strokePaint,
- ) {
- final path = Path();
- path.moveTo(center.dx, center.dy - size * 0.4);
- // Create wavy edges
- for (int i = 0; i < 6; i++) {
- final angle = i * math.pi / 3;
- final radius = size * (0.3 + math.sin(i * 2) * 0.1);
- path.lineTo(
- center.dx + math.cos(angle) * radius,
- center.dy + math.sin(angle) * radius,
- );
- }
- path.close();
- canvas.drawPath(path, fillPaint);
- canvas.drawPath(path, strokePaint);
- // Draw stem
- canvas.drawLine(
- center,
- Offset(center.dx, center.dy + size * 0.3),
- strokePaint,
- );
- }
- void _drawMapleLeaf(
- Canvas canvas,
- Offset center,
- double size,
- Paint fillPaint,
- Paint strokePaint,
- ) {
- final path = Path();
- // Create 5-pointed maple leaf shape
- for (int i = 0; i < 5; i++) {
- final angle = i * math.pi * 2 / 5 - math.pi / 2;
- final radius = size * (i % 2 == 0 ? 0.4 : 0.2);
- if (i == 0) {
- path.moveTo(
- center.dx + math.cos(angle) * radius,
- center.dy + math.sin(angle) * radius,
- );
- } else {
- path.lineTo(
- center.dx + math.cos(angle) * radius,
- center.dy + math.sin(angle) * radius,
- );
- }
- }
- path.close();
- canvas.drawPath(path, fillPaint);
- canvas.drawPath(path, strokePaint);
- // Draw stem
- canvas.drawLine(
- center,
- Offset(center.dx, center.dy + size * 0.3),
- strokePaint,
- );
- }
- void _drawSimpleLeaf(
- Canvas canvas,
- Offset center,
- double size,
- Paint fillPaint,
- Paint strokePaint,
- ) {
- // Draw oval leaf
- canvas.drawOval(
- Rect.fromCenter(center: center, width: size, height: size * 0.6),
- fillPaint,
- );
- // Draw center vein
- canvas.drawLine(
- Offset(center.dx, center.dy - size * 0.3),
- Offset(center.dx, center.dy + size * 0.3),
- strokePaint,
- );
- // Draw side veins
- for (int i = 0; i < 3; i++) {
- final y = center.dy + (i - 1) * size * 0.15;
- canvas.drawLine(
- Offset(center.dx, y),
- Offset(center.dx + size * 0.3, y),
- strokePaint,
- );
- canvas.drawLine(
- Offset(center.dx, y),
- Offset(center.dx - size * 0.3, y),
- strokePaint,
- );
- }
- // Draw stem
- canvas.drawLine(
- center,
- Offset(center.dx, center.dy + size * 0.4),
- strokePaint,
- );
- }
- @override
- bool shouldRepaint(CustomPainter oldDelegate) => true;
- }
- class FlowersPainter extends CustomPainter {
- final double animationValue;
- FlowersPainter(this.animationValue);
- @override
- void paint(Canvas canvas, Size size) {
- final petalPaint =
- Paint()
- ..color = Colors.pink.withValues(alpha: 0.3)
- ..style = PaintingStyle.fill;
- final centerPaint =
- Paint()
- ..color = Colors.yellow.withValues(alpha: 0.4)
- ..style = PaintingStyle.fill;
- // Fixed seed for consistent positions (removed unused variable)
- // Draw 25 flowers with gentle falling animation
- for (int i = 0; i < 25; i++) {
- // Better distribution using sine for X position
- final angle = i * 2.399963; // Golden angle in radians
- final baseX = (math.sin(angle) * 0.5 + 0.5) * size.width;
- // Consistent falling animation starting from top
- const fallSpeed = 0.2; // Same speed as other elements
- final fallOffset = animationValue * fallSpeed * size.height * 1.2;
- final animatedY =
- (fallOffset + (i * 25) % size.height) % (size.height + 100) - 100;
- final center = Offset(baseX, animatedY);
- // Blooming animation - flowers grow and shrink gently
- final bloomPhase = (animationValue + i * 0.1) % 1.0;
- final scale =
- (0.5 + math.sin(bloomPhase * math.pi * 2) * 0.3) * 2; // 2x larger
- // Draw petals
- for (int petal = 0; petal < 5; petal++) {
- final petalAngle = petal * math.pi * 2 / 5;
- final petalCenter = Offset(
- center.dx + math.cos(petalAngle) * 8 * scale,
- center.dy + math.sin(petalAngle) * 8 * scale,
- );
- canvas.drawCircle(petalCenter, 6 * scale, petalPaint);
- }
- // Draw flower center
- canvas.drawCircle(center, 4 * scale, centerPaint);
- }
- }
- @override
- bool shouldRepaint(CustomPainter oldDelegate) => true;
- }
|