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