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