Merge branch 'dev_aamir' of http://34.17.52.180/Haroon6138/HMG_Patient_App_New into dev_sultan
# Conflicts: # lib/features/medical_file/medical_file_view_model.dartpull/76/head
						commit
						3642523fdf
					
				
											
												
													File diff suppressed because one or more lines are too long
												
											
										
									
								
											
												
													File diff suppressed because one or more lines are too long
												
											
										
									
								| @ -0,0 +1 @@ | ||||
| {"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":[]} | ||||
| @ -0,0 +1,173 @@ | ||||
| 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); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,383 @@ | ||||
| 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