Compare commits
	
		
			No commits in common. '9b33745a57ae419be948130ba1bf7e26713dd1aa' and '47ce5022d3f54eae135a280953d43e466a221ce8' have entirely different histories. 
		
	
	
		
			9b33745a57
			...
			47ce5022d3
		
	
		
	
											
												
													File diff suppressed because one or more lines are too long
												
											
										
									
								
											
												
													File diff suppressed because one or more lines are too long
												
											
										
									
								| @ -1 +0,0 @@ | ||||
| {"nm":"Splash Launch 1","ddd":0,"h":927,"w":430,"meta":{"g":"LottieFiles Figma v92"},"layers":[{"ty":4,"nm":"Ellipse 54","sr":1,"st":0,"op":121.06,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[19.5,19.5],"t":0},{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[19.5,19.5],"t":60},{"s":[628.5,628.5],"t":120}]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[216,464],"t":0},{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[216.5,464.5],"t":60},{"s":[225.5,473.5],"t":120}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[0],"t":0},{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[0],"t":60},{"s":[100],"t":120}]}},"shapes":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":1,"k":[{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[{"c":true,"i":[[0,0],[10.493846153846153,0],[0,10.493846153846153],[-10.493846153846153,0],[0,-10.493846153846153]],"o":[[0,10.493846153846153],[-10.493846153846153,0],[0,-10.493846153846153],[10.493846153846153,0],[0,0]],"v":[[38,19],[19,38],[0,19],[19,0],[38,19]]}],"t":0},{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[{"c":true,"i":[[0,0],[10.493846153846153,0],[0,10.493846153846153],[-10.493846153846153,0],[0,-10.493846153846153]],"o":[[0,10.493846153846153],[-10.493846153846153,0],[0,-10.493846153846153],[10.493846153846153,0],[0,0]],"v":[[38,19],[19,38],[0,19],[19,0],[38,19]]}],"t":60},{"s":[{"c":true,"i":[[0,0],[341.5871678599841,0],[0,341.5871678599841],[-341.5871678599841,0],[0,-341.5871678599841]],"o":[[0,341.5871678599841],[-341.5871678599841,0],[0,-341.5871678599841],[341.5871678599841,0],[0,0]],"v":[[1237,618.5],[618.5,1237],[0,618.5],[618.5,0],[1237,618.5]]}],"t":120}]}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":1,"lj":1,"ml":4,"o":{"a":1,"k":[{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[20],"t":0},{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[20],"t":60},{"s":[100],"t":120}]},"w":{"a":1,"k":[{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[1],"t":0},{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[1],"t":60},{"s":[20],"t":120}]},"c":{"a":0,"k":[0.9295,0.1098,0.1687]}},{"ty":"fl","bm":0,"hd":false,"nm":"","c":{"a":0,"k":[1,1,1]},"r":1,"o":{"a":0,"k":100}}],"ind":1},{"ty":4,"nm":"Group 8232","sr":1,"st":0,"op":121.06,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[155,404]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":0},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":60.06},{"s":[100],"t":120}]}},"shapes":[],"ind":2},{"ty":4,"nm":"Rectangle 17364","sr":1,"st":0,"op":121.06,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[200,326.5]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[-207.54,1178.76],"t":0},{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[565.46,-310.24],"t":60},{"s":[565.46,-310.24],"t":120.12}]},"r":{"a":0,"k":30},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[100],"t":0},{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[100],"t":60},{"s":[0],"t":120.12}]}},"shapes":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,-110.38],[0,0],[110.38,0],[0,0],[0,110.38],[0,0],[-110.38,0],[0,0]],"o":[[0,0],[0,110.38],[0,0],[-110.38,0],[0,0],[0,-110.38],[0,0],[110.38,0]],"v":[[400,200],[400,453],[200,653],[200,653],[0,453],[0,200],[200,0],[200,0]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"","c":{"a":0,"k":[0.9295,0.1098,0.1687]},"r":1,"o":{"a":0,"k":100}}],"ind":3},{"ty":4,"nm":"Symptoms Checker Bg","sr":1,"st":0,"op":121.06,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[215,463.5]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[215,463.5]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[430,0],[430,927],[0,927],[0,0]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"","c":{"a":1,"k":[{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[1,1,1],"t":0},{"o":{"x":0.82,"y":-0.01},"i":{"x":0.58,"y":1},"s":[1,1,1],"t":60},{"s":[0.9295,0.1098,0.1687],"t":120}]},"r":1,"o":{"a":0,"k":100}}],"ind":4}],"v":"5.7.0","fr":60,"op":120.06,"ip":0,"assets":[]} | ||||
| @ -1,173 +0,0 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hmg_patient_app_new/core/app_assets.dart'; | ||||
| import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/int_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||
| import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; | ||||
| import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; | ||||
| import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; | ||||
| import 'package:lottie/lottie.dart'; | ||||
| 
 | ||||
| class OnboardingScreen extends StatefulWidget { | ||||
|   OnboardingScreen({Key? key}) : super(key: key); | ||||
| 
 | ||||
|   @override | ||||
|   _OnboardingScreenState createState() { | ||||
|     return _OnboardingScreenState(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class _OnboardingScreenState extends State<OnboardingScreen> { | ||||
|   int selectedIndex = 0; | ||||
| 
 | ||||
|   late PageController pageController; | ||||
| 
 | ||||
|   void goToHomePage() => Navigator.of(context).pushReplacement(FadePage(page: LandingNavigation())); | ||||
| 
 | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     pageController = PageController(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   void dispose() { | ||||
|     super.dispose(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       backgroundColor: AppColors.whiteColor, | ||||
|       body: SafeArea( | ||||
|         top: false, | ||||
|         left: false, | ||||
|         right: false, | ||||
|         child: Column( | ||||
|           spacing: 24, | ||||
|           children: [ | ||||
|             PageView( | ||||
|               controller: pageController, | ||||
|               children: [ | ||||
|                 onboardingView( | ||||
|                   AppAnimations.onboarding_1, | ||||
|                   "Booking appointment has never been easy".needTranslation, | ||||
|                   "In few clicks find yourself having consultation with the doctor of your choice.".needTranslation, | ||||
|                 ), | ||||
|                 onboardingView( | ||||
|                   AppAnimations.onboarding_2, | ||||
|                   "Access the medical history on finger tips".needTranslation, | ||||
|                   "Keep track on your medical history including labs, prescription, insurance, etc".needTranslation, | ||||
|                 ), | ||||
|               ], | ||||
|               onPageChanged: (int index) { | ||||
|                 selectedIndex = index; | ||||
|                 setState(() {}); | ||||
|               }, | ||||
|             ).expanded, | ||||
|             Row( | ||||
|               spacing: 4.h, | ||||
|               children: [ | ||||
|                 AnimatedContainer( | ||||
|                   duration: const Duration(milliseconds: 250), | ||||
|                   height: 6.h, | ||||
|                   width: selectedIndex == 0 ? 18.h : 6.h, | ||||
|                   decoration: BoxDecoration(color: selectedIndex == 0 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30)), | ||||
|                 ), | ||||
|                 AnimatedContainer( | ||||
|                   duration: const Duration(milliseconds: 250), | ||||
|                   height: 6.h, | ||||
|                   width: selectedIndex == 1 ? 18.h : 6.h, | ||||
|                   decoration: BoxDecoration(color: selectedIndex == 1 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30)), | ||||
|                 ), | ||||
|               ], | ||||
|             ).paddingOnly(left: 24, right: 24), | ||||
|             Row( | ||||
|               children: [ | ||||
|                 AnimatedSwitcher( | ||||
|                   duration: const Duration(milliseconds: 250), | ||||
|                   transitionBuilder: (child, anim) => FadeTransition(opacity: anim, child: child), | ||||
|                   child: selectedIndex == 0 | ||||
|                       ? CustomButton( | ||||
|                           text: "Skip".needTranslation, | ||||
|                           onPressed: () => goToHomePage(), | ||||
|                           width: 86.h, | ||||
|                           height: 56.h, | ||||
|                           borderRadius: 12.h, | ||||
|                           backgroundColor: Color(0xffFEE9EA), | ||||
|                           textColor: AppColors.primaryRedColor, | ||||
|                           borderWidth: 0, | ||||
|                           borderColor: Colors.transparent, | ||||
|                         ).paddingOnly(left: 24) | ||||
|                       : const SizedBox.shrink(), | ||||
|                 ), | ||||
|                 const Spacer(), | ||||
|                 AnimatedContainer( | ||||
|                   duration: const Duration(milliseconds: 400), | ||||
|                   curve: Curves.easeInOut, | ||||
|                   width: selectedIndex == 0 ? 86 : MediaQuery.of(context).size.width - 48, | ||||
|                   margin: EdgeInsets.only(left: 24, right: 24), | ||||
|                   decoration: BoxDecoration( | ||||
|                     color: AppColors.primaryRedColor, | ||||
|                     borderRadius: BorderRadius.circular(12), | ||||
|                   ), | ||||
|                   child: AnimatedSwitcher( | ||||
|                     duration: const Duration(milliseconds: 250), | ||||
|                     transitionBuilder: (child, anim) => FadeTransition(opacity: anim, child: child), | ||||
|                     child: selectedIndex == 0 | ||||
|                         ? CustomButton( | ||||
|                             icon: AppAssets.arrow_forward, | ||||
|                             iconSize: 32.h, | ||||
|                             width: 86.h, | ||||
|                             height: 56.h, | ||||
|                             borderRadius: 12.h, | ||||
|                             text: "".needTranslation, | ||||
|                             backgroundColor: Colors.transparent, | ||||
|                             onPressed: () { | ||||
|                               pageController.animateToPage(1, duration: Duration(milliseconds: 400), curve: Curves.easeInOut); | ||||
|                             }) | ||||
|                         : CustomButton( | ||||
|                             text: "Get Started".needTranslation, | ||||
|                             fontWeight: FontWeight.w500, | ||||
|                             fontSize: 16.h, | ||||
|                             height: 56.h, | ||||
|                             borderRadius: 16.h, | ||||
|                             textOverflow: TextOverflow.ellipsis, | ||||
|                             backgroundColor: Colors.transparent, | ||||
|                             onPressed: () => goToHomePage(), | ||||
|                           ), | ||||
|                   ), | ||||
|                 ), | ||||
|               ], | ||||
|             ), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   Widget onboardingView(String icon, String heading, String body) { | ||||
|     return Column( | ||||
|       mainAxisAlignment: MainAxisAlignment.end, | ||||
|       crossAxisAlignment: CrossAxisAlignment.start, | ||||
|       spacing: 12, | ||||
|       children: [ | ||||
|         Align( | ||||
|                 alignment: Alignment.bottomCenter, | ||||
|                 child: Lottie.asset(icon, repeat: true, reverse: false, frameRate: FrameRate(60), width: MediaQuery.sizeOf(context).width - 50, height: MediaQuery.sizeOf(context).width - 50)) | ||||
|             .expanded, | ||||
|         // 12.height, | ||||
|         Text( | ||||
|           heading, | ||||
|           style: TextStyle(fontSize: 36.h, fontWeight: FontWeight.w600, color: AppColors.textColor, letterSpacing: -0.4, height: 1), | ||||
|         ), | ||||
|         Text( | ||||
|           body, | ||||
|           style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w500, color: AppColors.greyTextColor, letterSpacing: 0, height: 26 / 16), | ||||
|         ), | ||||
|       ], | ||||
|     ).paddingOnly(left: 24, right: 24); | ||||
|   } | ||||
| } | ||||
| @ -1,383 +0,0 @@ | ||||
| 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/presentation/home/navigation_screen.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 ?? LandingNavigation())); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   @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, width: double.infinity, height: double.infinity, 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)).h, | ||||
|                 top: ((screenSize.height * 0.75) * (pos.dy)).h, | ||||
|                 child: Transform.rotate( | ||||
|                   angle: -120 * 3.1415927 / 150, // convert degrees to radians | ||||
|                   child: Container( | ||||
|                     width: 400.h, | ||||
|                     height: 653.h, | ||||
|                     decoration: BoxDecoration( | ||||
|                       color: Color(0xffED1C2B), | ||||
|                       borderRadius: BorderRadius.circular(330.h), | ||||
|                     ), | ||||
|                   ), | ||||
|                 ), | ||||
|               ); | ||||
|             }, | ||||
|           ), | ||||
|           // 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), | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|             ); | ||||
|           }, | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
					Loading…
					
					
				
		Reference in New Issue