优化动画效果
This commit is contained in:
parent
e24be85f04
commit
d950901497
@ -66,6 +66,7 @@ class FirstTimeDialog extends StatelessWidget {
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: AnimatedVisibilityWidget(
|
||||
animationWidgetBuilder: AnimatedVisibilityWidget.fadeAnimationWidgetBuilder,
|
||||
isVisible: isTextShown.value,
|
||||
child: const Center(
|
||||
child: TouchHintWidget(),
|
||||
|
@ -68,6 +68,15 @@ class SelectController extends GetxController {
|
||||
occupations.add(Occupation(
|
||||
id: 4,
|
||||
gender: 2,
|
||||
name: '运动员',
|
||||
enName: 'athletes',
|
||||
vImage: 'btn_male_pic_athletes',
|
||||
hImage: 'pic_male_athletes',
|
||||
selected: false,
|
||||
));
|
||||
occupations.add(Occupation(
|
||||
id: 5,
|
||||
gender: 2,
|
||||
name: '画家',
|
||||
enName: 'artist',
|
||||
vImage: 'btn_male_pic_artist',
|
||||
@ -75,16 +84,6 @@ class SelectController extends GetxController {
|
||||
selected: true,
|
||||
enable: true,
|
||||
));
|
||||
occupations.add(Occupation(
|
||||
id: 5,
|
||||
gender: 2,
|
||||
name: '运动员',
|
||||
enName: 'athletes',
|
||||
vImage: 'btn_male_pic_athletes',
|
||||
hImage: 'pic_male_athletes',
|
||||
selected: false,
|
||||
));
|
||||
|
||||
occupations.add(Occupation(
|
||||
id: 6,
|
||||
gender: 2,
|
||||
|
@ -168,6 +168,10 @@ class PartnerWidget extends HookWidget {
|
||||
return HookBuilder(builder: (context) {
|
||||
final isDialogShown = useState(false);
|
||||
final isTextShown = useState(false);
|
||||
final fadeOutAnimationController = useAnimationController(
|
||||
initialValue: 1.0,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
);
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
@ -180,82 +184,94 @@ class PartnerWidget extends HookWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(top: 42.h, left: 149.w, child: Images.ip),
|
||||
Positioned(
|
||||
top: 42.h,
|
||||
left: 149.w,
|
||||
child: FadeTransition(
|
||||
opacity: fadeOutAnimationController,
|
||||
child: Images.ip,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 76.h,
|
||||
left: 451.w,
|
||||
child: AnimatedVisibilityWidget(
|
||||
isVisible: true,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
animationWidgetBuilder:
|
||||
AnimatedVisibilityWidget.fadeAnimationWidgetBuilder,
|
||||
isInitAnimated: true,
|
||||
onDone: (visible) {
|
||||
if (visible) {
|
||||
isDialogShown.value = true;
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
width: 464.w,
|
||||
height: 270.h,
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: Images.questionDialog,
|
||||
fit: BoxFit.fill,
|
||||
child: FadeTransition(
|
||||
opacity: fadeOutAnimationController,
|
||||
child: AnimatedVisibilityWidget(
|
||||
isVisible: true,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
animationWidgetBuilder:
|
||||
AnimatedVisibilityWidget.fadeAnimationWidgetBuilder,
|
||||
isInitAnimated: true,
|
||||
onDone: (visible) {
|
||||
if (visible) {
|
||||
isDialogShown.value = true;
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
width: 464.w,
|
||||
height: 270.h,
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: Images.questionDialog,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
),
|
||||
padding: REdgeInsets.only(top: 45.0, left: 45.0, right: 45.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Visibility(
|
||||
visible: isDialogShown.value,
|
||||
child: AnimatedTextKit(
|
||||
totalRepeatCount: 1,
|
||||
animatedTexts: [
|
||||
TypewriterAnimatedText(
|
||||
'每个来到建木之境的探梦者,都需要先选定自己的【梦想职业】,才能继续探索哦。准备好了吗?',
|
||||
textStyle: TextStyles.mediumWhiteShadow18_034,
|
||||
textAlign: TextAlign.start,
|
||||
speed: const Duration(milliseconds: 150),
|
||||
cursor: '',
|
||||
),
|
||||
],
|
||||
onFinished: () {
|
||||
isTextShown.value = true;
|
||||
},
|
||||
padding: REdgeInsets.only(top: 45.0, left: 45.0, right: 45.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Visibility(
|
||||
visible: isDialogShown.value,
|
||||
child: AnimatedTextKit(
|
||||
totalRepeatCount: 1,
|
||||
pause: Duration.zero,
|
||||
animatedTexts: [
|
||||
TypewriterAnimatedText(
|
||||
'每个来到建木之境的探梦者,都需要先选定自己的【梦想职业】,才能继续探索哦。准备好了吗?',
|
||||
textStyle: TextStyles.mediumWhiteShadow18_034,
|
||||
textAlign: TextAlign.start,
|
||||
speed: const Duration(milliseconds: 150),
|
||||
cursor: '',
|
||||
),
|
||||
],
|
||||
onFinished: () {
|
||||
isTextShown.value = true;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const RSizedBox(
|
||||
height: 36.0,
|
||||
),
|
||||
AnimatedVisibilityWidget(
|
||||
isVisible: isTextShown.value,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
animationWidgetBuilder:
|
||||
AnimatedVisibilityWidget.fadeAnimationWidgetBuilder,
|
||||
child: ImageTxtButton(
|
||||
text: '准备好了',
|
||||
imgName: 'question/btn_icon_begin',
|
||||
textStyle: TextStyles.mediumWhite20_014,
|
||||
width: 186.0,
|
||||
height: 54.0,
|
||||
onPressed: () async {
|
||||
await controller.confirm();
|
||||
await Get.offAllNamed(Routes.SELECT);
|
||||
},
|
||||
const RSizedBox(
|
||||
height: 36.0,
|
||||
),
|
||||
),
|
||||
const RSizedBox(
|
||||
height: 64.0,
|
||||
),
|
||||
],
|
||||
AnimatedVisibilityWidget(
|
||||
isVisible: isTextShown.value,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
animationWidgetBuilder:
|
||||
AnimatedVisibilityWidget.fadeAnimationWidgetBuilder,
|
||||
child: ImageTxtButton(
|
||||
text: '准备好了',
|
||||
imgName: 'question/btn_icon_begin',
|
||||
textStyle: TextStyles.mediumWhite20_014,
|
||||
width: 186.0,
|
||||
height: 54.0,
|
||||
onPressed: () async {
|
||||
await controller.confirm();
|
||||
await fadeOutAnimationController.reverse();
|
||||
await Get.offAllNamed(Routes.SELECT);
|
||||
},
|
||||
),
|
||||
),
|
||||
const RSizedBox(
|
||||
height: 64.0,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -41,6 +41,7 @@ class WelcomeView extends GetView<WelcomeController> {
|
||||
: AnimatedVisibilityWidget(
|
||||
isVisible: !finishInput.value,
|
||||
duration: const Duration(milliseconds: 800),
|
||||
curve: Curves.fastOutSlowIn,
|
||||
animationWidgetBuilder: AnimatedVisibilityWidget
|
||||
.fadeAnimationWidgetBuilder,
|
||||
onDone: (visible) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
import '../utils/image_utils.dart';
|
||||
@ -17,10 +18,13 @@ class Images {
|
||||
width: 35.w,
|
||||
);
|
||||
|
||||
static Widget up = LoadAssetImage(
|
||||
'btn_icon_up',
|
||||
height: 47.w,
|
||||
width: 47.w,
|
||||
static Widget up = _UpAndDown(
|
||||
child: LoadAssetImage(
|
||||
'btn_icon_up',
|
||||
fit: BoxFit.contain,
|
||||
height: 47.w,
|
||||
width: 47.w,
|
||||
),
|
||||
);
|
||||
|
||||
static Widget ip = LoadAssetImage(
|
||||
@ -43,16 +47,20 @@ class Images {
|
||||
fit: BoxFit.fill,
|
||||
);
|
||||
|
||||
static Widget splashBegin = LoadAssetImage(
|
||||
'splash/btn_icon_begin',
|
||||
height: 105.h,
|
||||
width: 378.w,
|
||||
static Widget splashBegin = _FadeInAndOut(
|
||||
child: LoadAssetImage(
|
||||
'splash/btn_icon_begin',
|
||||
height: 105.h,
|
||||
width: 378.w,
|
||||
),
|
||||
);
|
||||
|
||||
static Widget welcomeBegin = LoadAssetImage(
|
||||
'welcome/btn_label_bg_up',
|
||||
height: 114.h,
|
||||
width: 270.w,
|
||||
static Widget welcomeBegin = _FadeInAndOut(
|
||||
child: LoadAssetImage(
|
||||
'welcome/btn_label_bg_up',
|
||||
height: 114.h,
|
||||
width: 270.w,
|
||||
),
|
||||
);
|
||||
|
||||
static Widget pullDown = LoadAssetImage(
|
||||
@ -61,10 +69,12 @@ class Images {
|
||||
width: 21.w,
|
||||
);
|
||||
|
||||
static Widget pullUp = LoadAssetImage(
|
||||
'welcome/btn_icon_up',
|
||||
height: 21.w,
|
||||
width: 21.w,
|
||||
static Widget pullUp = _UpAndDown(
|
||||
child: LoadAssetImage(
|
||||
'welcome/btn_icon_up',
|
||||
height: 21.w,
|
||||
width: 21.w,
|
||||
),
|
||||
);
|
||||
|
||||
static Widget selectPre = LoadAssetImage(
|
||||
@ -79,16 +89,20 @@ class Images {
|
||||
width: 24.w,
|
||||
);
|
||||
|
||||
static Widget questionRecommended = LoadAssetImage(
|
||||
'question/label_recommended',
|
||||
height: 105.h,
|
||||
width: 390.w,
|
||||
static Widget questionRecommended = _FadeInAndOut(
|
||||
child: LoadAssetImage(
|
||||
'question/label_recommended',
|
||||
height: 105.h,
|
||||
width: 390.w,
|
||||
),
|
||||
);
|
||||
|
||||
static Widget homeCreate = LoadAssetImage(
|
||||
'home/label_label_create',
|
||||
height: 105.h,
|
||||
width: 378.w,
|
||||
static Widget homeCreate = _FadeInAndOut(
|
||||
child: LoadAssetImage(
|
||||
'home/label_label_create',
|
||||
height: 105.h,
|
||||
width: 378.w,
|
||||
),
|
||||
);
|
||||
|
||||
static Widget homeReset = LoadAssetImage(
|
||||
@ -319,3 +333,83 @@ class Images {
|
||||
static const AssetImage avatarUser =
|
||||
AssetImage("assets/images/label_avatar_user.png");
|
||||
}
|
||||
|
||||
class _FadeInAndOut extends HookWidget {
|
||||
const _FadeInAndOut({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final controller = useAnimationController(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
reverseDuration: const Duration(milliseconds: 500),
|
||||
);
|
||||
useEffect(() {
|
||||
void onStateChange(AnimationStatus state) {
|
||||
if (state == AnimationStatus.completed) {
|
||||
controller.reverse();
|
||||
} else if (state == AnimationStatus.dismissed) {
|
||||
controller.forward();
|
||||
}
|
||||
}
|
||||
|
||||
controller.addStatusListener(onStateChange);
|
||||
controller.forward();
|
||||
return () {
|
||||
controller.removeStatusListener(onStateChange);
|
||||
};
|
||||
}, [controller]);
|
||||
final opacityTween = Tween(
|
||||
begin: 0.7,
|
||||
end: 1.0,
|
||||
);
|
||||
return FadeTransition(
|
||||
opacity: opacityTween.animate(controller),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _UpAndDown extends HookWidget {
|
||||
const _UpAndDown({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final controller = useAnimationController(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
reverseDuration: const Duration(milliseconds: 500),
|
||||
);
|
||||
useEffect(() {
|
||||
void onStateChange(AnimationStatus state) {
|
||||
if (state == AnimationStatus.completed) {
|
||||
controller.reverse();
|
||||
} else if (state == AnimationStatus.dismissed) {
|
||||
controller.forward();
|
||||
}
|
||||
}
|
||||
|
||||
controller.addStatusListener(onStateChange);
|
||||
controller.forward();
|
||||
return () {
|
||||
controller.removeStatusListener(onStateChange);
|
||||
};
|
||||
}, [controller]);
|
||||
final offsetTween = Tween(
|
||||
begin: const Offset(0, 0.2),
|
||||
end: const Offset(0, 0),
|
||||
);
|
||||
return SlideTransition(
|
||||
position: offsetTween.animate(controller),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ class SwipeNextPageHandler extends StatelessWidget {
|
||||
}
|
||||
},
|
||||
onPanCancel: () {
|
||||
controller.animateTo(controller.lowerBound, curve: Curves.easeIn);
|
||||
|
||||
},
|
||||
onPanUpdate: (details) {
|
||||
controller.value -= details.delta.dy;
|
||||
|
Loading…
Reference in New Issue
Block a user