You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			383 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Dart
		
	
			
		
		
	
	
			383 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Dart
		
	
| 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<SplashAnimationScreen> 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<AnimatedScreen> createState() => _AnimatedScreenState();
 | |
| }
 | |
| 
 | |
| class _AnimatedScreenState extends State<AnimatedScreen> with TickerProviderStateMixin {
 | |
|   late AnimationController _moveController;
 | |
|   late Animation<Offset> _positionAnimation;
 | |
|   late AnimationController _expandController;
 | |
|   late Animation<double> _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<Offset>(
 | |
|       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<double>(
 | |
|       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<MoveObjectDemo> createState() => _MoveObjectDemoState();
 | |
| }
 | |
| 
 | |
| class _MoveObjectDemoState extends State<MoveObjectDemo> with SingleTickerProviderStateMixin {
 | |
|   late AnimationController _controller;
 | |
|   late Animation<Alignment> _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<MoveOnClickDemo> createState() => _MoveOnClickDemoState();
 | |
| }
 | |
| 
 | |
| class _MoveOnClickDemoState extends State<MoveOnClickDemo> with TickerProviderStateMixin {
 | |
|   late AnimationController _controller;
 | |
|   late Animation<Alignment> _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),
 | |
|                   ),
 | |
|                 ),
 | |
|               ),
 | |
|             );
 | |
|           },
 | |
|         ),
 | |
|       ),
 | |
|     );
 | |
|   }
 | |
| }
 |