import 'dart:async'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/presentation/authentication/login.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:lottie/lottie.dart'; class SplashAnimationScreen extends StatefulWidget { final Widget? routeWidget; SplashAnimationScreen({super.key, this.routeWidget}); @override _SplashAnimationScreenState createState() { return _SplashAnimationScreenState(); } } class _SplashAnimationScreenState extends State with SingleTickerProviderStateMixin { late final AnimationController _controller; @override void initState() { super.initState(); _controller = AnimationController(vsync: this); _controller.addListener(() { if (_controller.status == AnimationStatus.completed) { Navigator.of(context).pushReplacement(FadePage(page: widget.routeWidget ?? LoginScreen())); } }); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.whiteColor, body: Stack( children: [ Lottie.asset(AppAnimations.splashLaunching, controller: _controller, onLoaded: (composition) { _controller ..duration = composition.duration ..forward(); // Start the animation }, repeat: false, reverse: false, frameRate: FrameRate(60), fit: BoxFit.fill) .center, ], ), ); } } // todo: do-not remove this code,as animation need to test on multiple screen sizes class AnimatedScreen extends StatefulWidget { const AnimatedScreen({super.key}); @override State createState() => _AnimatedScreenState(); } class _AnimatedScreenState extends State with TickerProviderStateMixin { late AnimationController _moveController; late Animation _positionAnimation; late AnimationController _expandController; late Animation _expandAnimation; bool isRipple = false; late final AnimationController _controller; @override void initState() { super.initState(); _controller = AnimationController(vsync: this); _controller.addListener(() { if (_controller.status == AnimationStatus.completed) { Navigator.of(context).pushReplacement( FadePage( page: LoginScreen(), ), ); } }); // Step 1: Move circle from bottom-left to top-right _moveController = AnimationController(vsync: this, duration: const Duration(seconds: 1)); _positionAnimation = Tween( begin: const Offset(-1, 1), end: const Offset(1, -1), ).animate( CurvedAnimation(parent: _moveController, curve: const Cubic(0.82, -0.01, 0.58, 1)), ); // Step 2: Expand white circle from center _expandController = AnimationController(vsync: this, duration: const Duration(milliseconds: 1000)); _expandAnimation = Tween( begin: 0.0, end: 4.0, ).animate(CurvedAnimation(parent: _expandController, curve: Curves.easeOut)); // Trigger the animations in sequence _moveController.forward().whenComplete(() { setState(() { isRipple = true; }); _expandController.forward().whenComplete(() { setState(() { isRipple = false; }); }); }); } @override void dispose() { _controller.dispose(); _moveController.dispose(); _expandController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final screenSize = MediaQuery.of(context).size; return Scaffold( backgroundColor: AppColors.whiteColor, body: Stack( children: [ // Moving rotated ellipse Lottie.asset(AppAnimations.splashLaunching, controller: _controller, onLoaded: (composition) { _controller ..duration = composition.duration ..forward(); // Start the animation }, repeat: false, reverse: false, frameRate: FrameRate(60), fit: BoxFit.fill) .center, // Lottie.asset(AppAnimations.loadingAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 80.h, height: 80.h, fit: BoxFit.fill).center, // // AnimatedContainer( // duration: Duration(milliseconds: 500), // width: screenSize.width, // height: screenSize.height, // color: isRipple ? AppColors.primaryRedColor : AppColors.whiteColor, // ), // // AnimatedBuilder( // animation: _moveController, // builder: (context, child) { // final pos = _positionAnimation.value; // return Positioned( // left: (screenSize.width * .75) * (pos.dx), // top: (screenSize.height * 0.75) * (pos.dy), // child: Transform.rotate( // angle: -120 * 3.1415927 / 150, // convert degrees to radians // child: Container( // width: 400, // height: 653, // decoration: BoxDecoration( // color: Color(0xffED1C2B), // borderRadius: BorderRadius.circular(330), // ), // ), // ), // ); // }, // ), // // Expanding white circle // AnimatedBuilder( // animation: _expandController, // builder: (context, child) { // return Center( // child: Transform.scale( // scale: _expandAnimation.value, // child: Opacity( // opacity: 1.0, //- _expandAnimation.value.clamp(0.0, 1.0), // child: Container( // decoration: const BoxDecoration( // color: Colors.white, // shape: BoxShape.circle, // // border: Border.fromBorderSide(BorderSide( // // width: 0, // // color: Color(0xffED1C2B), // // ) // )), // ), // // ), // ), // ); // }, // ), // AnimatedBuilder( // animation: _expandController, // builder: (context, child) { // final screenSize = MediaQuery.of(context).size; // final maxDiameter = // (screenSize.width > screenSize.height ? screenSize.width : screenSize.height) * 2; // // return Center( // child: Transform.scale( // scale: _expandAnimation.value * maxDiameter / 100, // scale up to fill screen // child: Opacity( // opacity: (1.0 - _expandAnimation.value).clamp(0.0, 1.0), // child: Container( // decoration: const BoxDecoration( // color: Colors.white, // shape: BoxShape.circle, // ), // ), // ), // ), // ); // }, // ), ], ), ); } } class MoveObjectDemo extends StatefulWidget { const MoveObjectDemo({super.key}); @override State createState() => _MoveObjectDemoState(); } class _MoveObjectDemoState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _alignmentAnimation; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(seconds: 1), ); _alignmentAnimation = AlignmentTween( begin: Alignment(-2.0, 2.5), end: Alignment(2.5, -2), ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut)); _controller.forward(); // start animation } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: AnimatedBuilder( animation: _alignmentAnimation, builder: (context, child) { return Align( alignment: _alignmentAnimation.value, child: Transform.rotate( angle: -120 * 3.1415927 / 180, child: Container( width: 200, height: 375, decoration: BoxDecoration( color: const Color(0xffED1C2B), borderRadius: BorderRadius.circular(330), ), ), ), // Transform.rotate( // angle: -120 * 3.1415927 / 180, // convert to radians // child: Container( // width: 400, // height: 653, // decoration: BoxDecoration( // color: const Color(0xffED1C2B), // borderRadius: BorderRadius.circular(330), // ), // ), // ), ); }, ), ); } } class MoveOnClickDemo extends StatefulWidget { const MoveOnClickDemo({super.key}); @override State createState() => _MoveOnClickDemoState(); } class _MoveOnClickDemoState extends State with TickerProviderStateMixin { late AnimationController _controller; late Animation _alignmentAnimation; @override void initState() { super.initState(); init(); } init() { _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 1000), // Figma duration ); _alignmentAnimation = AlignmentTween( // begin: Alignment(-10.0, 5), // end: Alignment(5, -2), begin: Alignment.bottomLeft, end: Alignment.topRight, ).animate(CurvedAnimation( parent: _controller, curve: const Cubic(0.82, -0.01, 0.58, 1), // Figma cubic-bezier )); _animate(); } @override void dispose() { _controller.dispose(); super.dispose(); } void _animate() { if (_controller.isCompleted) { _controller.reverse(); } else { _controller.forward(); } } @override Widget build(BuildContext context) { return Scaffold( body: GestureDetector( onTap: _animate, // trigger on click onDoubleTap: () { _controller.dispose(); init(); }, child: AnimatedBuilder( animation: _alignmentAnimation, builder: (context, child) { print(_alignmentAnimation.value); return Align( alignment: _alignmentAnimation.value, child: Transform.rotate( angle: -120 * 3.1415927 / 145, // -120 deg child: Container( width: 100, height: 150, decoration: BoxDecoration( color: const Color(0xffED1C2B), borderRadius: BorderRadius.circular(330), ), ), ), ); }, ), ), ); } }