import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; void main() { runApp(const MemorialApp()); } class MemorialApp extends StatelessWidget { const MemorialApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'Pet Memorial', theme: ThemeData.dark().copyWith( scaffoldBackgroundColor: const Color(0xFF0B0B0E), ), home: const SplashScreen(), ); } } /* ----------------------------------------------------------- SPLASH SCREEN – 呼吸光点 ------------------------------------------------------------ */ class SplashScreen extends StatefulWidget { const SplashScreen({super.key}); @override State createState() => _SplashScreenState(); } class _SplashScreenState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(seconds: 3), )..repeat(reverse: true); Future.delayed(const Duration(seconds: 4), () { Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => const HomeScreen()), ); }); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: ScaleTransition( scale: Tween(begin: 0.8, end: 1.2).animate( CurvedAnimation(parent: _controller, curve: Curves.easeInOut), ), child: Container( width: 20, height: 20, decoration: BoxDecoration( shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.white.withOpacity(0.6), blurRadius: 30, ) ], ), ), ), ), ); } } /* ----------------------------------------------------------- HOME – 悬浮星球 + 故事切换 ------------------------------------------------------------ */ class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State with SingleTickerProviderStateMixin { late AnimationController _planetController; int currentIndex = 0; final List> stories = [ { 'name': 'Luna', 'story': 'She waited by the door every night.', }, { 'name': 'Charlie', 'story': 'Loved the sun more than treats.', }, { 'name': 'Momo', 'story': 'Always slept beside my shoes.', }, ]; @override void initState() { super.initState(); _planetController = AnimationController( vsync: this, duration: const Duration(seconds: 40), )..repeat(); } void nextStory() { setState(() { currentIndex = (currentIndex + 1) % stories.length; }); } void previousStory() { setState(() { currentIndex = (currentIndex - 1 + stories.length) % stories.length; }); } @override Widget build(BuildContext context) { final story = stories[currentIndex]; return Scaffold( body: GestureDetector( onHorizontalDragEnd: (details) { if (details.primaryVelocity! < 0) { nextStory(); } else { previousStory(); } }, onLongPress: () { Navigator.push( context, MaterialPageRoute( builder: (_) => StoryDetailScreen(story: story), ), ); }, child: Stack( children: [ Center( child: RotationTransition( turns: _planetController, child: Container( width: 260, height: 260, decoration: BoxDecoration( shape: BoxShape.circle, gradient: RadialGradient( colors: [ Colors.white.withOpacity(0.15), Colors.black, ], ), boxShadow: [ BoxShadow( color: Colors.white.withOpacity(0.2), blurRadius: 40, ), ], ), ), ), ), Positioned( bottom: 120, left: 0, right: 0, child: Column( children: [ Text( story['name']!, style: const TextStyle( fontSize: 22, fontWeight: FontWeight.w300, ), ), const SizedBox(height: 8), Text( story['story']!, style: TextStyle( fontSize: 14, color: Colors.white.withOpacity(0.7), ), ), ], ), ), Positioned( right: 20, bottom: 40, child: TextButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (_) => const BreathingScreen(), ), ); }, child: const Text( 'My', style: TextStyle(color: Colors.white54), ), ), ) ], ), ), ); } } /* ----------------------------------------------------------- STORY DETAIL ------------------------------------------------------------ */ class StoryDetailScreen extends StatelessWidget { final Map story; const StoryDetailScreen({super.key, required this.story}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(backgroundColor: Colors.transparent), body: Padding( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( story['name']!, style: const TextStyle(fontSize: 28), ), const SizedBox(height: 20), Text( story['story']!, style: const TextStyle(fontSize: 16), ), const Spacer(), Center( child: TextButton( onPressed: () => Navigator.pop(context), child: const Text('Return to the Planet'), ), ) ], ), ), ); } } /* ----------------------------------------------------------- BREATHING MODE ------------------------------------------------------------ */ class BreathingScreen extends StatefulWidget { const BreathingScreen({super.key}); @override State createState() => _BreathingScreenState(); } class _BreathingScreenState extends State with SingleTickerProviderStateMixin { late AnimationController _breathController; @override void initState() { super.initState(); _breathController = AnimationController( vsync: this, duration: const Duration(seconds: 14), )..repeat(); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: ScaleTransition( scale: Tween(begin: 0.6, end: 1.2).animate( CurvedAnimation( parent: _breathController, curve: Curves.easeInOut, ), ), child: Container( width: 180, height: 180, decoration: BoxDecoration( shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.white.withOpacity(0.4), blurRadius: 60, ) ], ), child: const Center( child: Text( 'Luna', style: TextStyle( fontSize: 22, fontWeight: FontWeight.w300, ), ), ), ), ), ), ); } }