import 'package:animated_text_kit/animated_text_kit.dart'; import 'package:dreampad/app/routes/app_pages.dart'; import 'package:dreampad/app/shared/constants/constants.dart'; import 'package:dreampad/app/shared/shared.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import '../controllers/welcome_controller.dart'; class PartnerWidget extends HookWidget { const PartnerWidget({super.key}); WelcomeController get controller => Get.find(); @override Widget build(BuildContext context) { final isPartnerDone = useState(false); return !isPartnerDone.value ? buildPartner(context, isPartnerDone) : buildConfirm(context); } Widget buildPartner(BuildContext context, ValueNotifier isPartnerDone) { return LayoutBuilder(builder: (context, constraint) { return HookBuilder(builder: (context) { final isIpShown = useState(false); final isDialogShown = useState(false); final isTextShown = useState(false); final animationController = useAnimationController( duration: const Duration(milliseconds: 500), lowerBound: 0, upperBound: constraint.maxHeight); final fadeoutAnimation = (1 - useListenableSelector( animationController, () => animationController.value / animationController.upperBound, )) .toDouble(); useEffect(() { void onAnimationStateChange(AnimationStatus state) { SchedulerBinding.instance.addPostFrameCallback((timeStamp) { if (state == AnimationStatus.completed) { isPartnerDone.value = true; } }); } animationController.addStatusListener(onAnimationStateChange); return () { animationController.removeStatusListener(onAnimationStateChange); }; }, [animationController]); return SwipePageHandlerReceiver( controller: animationController, currentIndex: useState(0), child: AnimatedBuilder( animation: animationController, builder: (context, _) => Stack( children: [ const _Background(), Positioned( bottom: 5.h + animationController.value, left: 452.w, child: Center( child: AnimatedVisibilityWidget( duration: const Duration(milliseconds: 500), isVisible: isTextShown.value, animationWidgetBuilder: AnimatedVisibilityWidget.fadeAnimationWidgetBuilder, child: IgnorePointer( ignoring: !isTextShown.value, child: SwipeNextPageHandler( child: Column( children: [ Images.up, Images.welcomeBegin, ], ), ), ), ), )), Stack( children: [ Positioned( top: 42.h, left: 149.w, child: AnimatedVisibilityWidget( isVisible: true, duration: const Duration(milliseconds: 500), animationWidgetBuilder: AnimatedVisibilityWidget.fadeAnimationWidgetBuilder, isInitAnimated: true, onDone: (visible) { if (visible) { isIpShown.value = true; } }, child: Images.ip, ), ), Positioned( top: 76.h, left: 451.w, child: Opacity( opacity: fadeoutAnimation, child: AnimatedVisibilityWidget( isVisible: isIpShown.value, duration: const Duration(milliseconds: 500), animationWidgetBuilder: AnimatedVisibilityWidget .fadeAnimationWidgetBuilder, onDone: (visible) { if (visible) { isDialogShown.value = true; } }, child: Container( width: 465.w, height: 221.h, decoration: const BoxDecoration( image: DecorationImage( image: Images.welcomeDialog, fit: BoxFit.fill, ), ), padding: REdgeInsets.only( top: 45.0, left: 45.0, right: 45.0), child: DefaultTextStyle( style: TextStyles.mediumWhiteShadowHeight18_034, child: Visibility( visible: isDialogShown.value, child: AnimatedTextKit( totalRepeatCount: 1, animatedTexts: [ TypewriterAnimatedText( '${controller.textController.text}你好! 我是一只天马。我们天马族一直生活在这里。与探梦者为伴。接下来让我来做你探索的伙伴吧。', speed: const Duration(milliseconds: 150), cursor: '', ), ], onFinished: () { isTextShown.value = true; }, ), ), ), ), ), ), ), ], ), ], ), ), ); }); }); } Widget buildConfirm(BuildContext context) { 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( child: Container( decoration: const BoxDecoration( image: DecorationImage( image: Images.selectBg, fit: BoxFit.fill, ), ), ), ), Positioned( top: 42.h, left: 149.w, child: FadeTransition( opacity: fadeOutAnimationController, child: Images.ip, ), ), Positioned( top: 76.h, left: 451.w, 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, 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 fadeOutAnimationController.reverse(); await Get.offAllNamed(Routes.SELECT); }, ), ), const RSizedBox( height: 64.0, ), ], ), ), ), ), ), ], ); }); } } class _Background extends StatelessWidget { const _Background(); @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraint) => HookBuilder(builder: (context) { final controller = SwipeNextPageContainer.of(context)!.controller; return AnimatedBuilder( animation: controller, builder: (context, _) => HookBuilder(builder: (context) { final hasFinished = useState(false); final animationValue = hasFinished.value ? controller.upperBound : controller.value; useEffect(() { void onAnimationStateChange(AnimationStatus state) { if (state == AnimationStatus.completed) { SchedulerBinding.instance .addPostFrameCallback((timeStamp) { hasFinished.value = true; }); } } controller.addStatusListener(onAnimationStateChange); return () { controller .removeStatusListener(onAnimationStateChange); }; }, [controller]); return Stack( children: [ Positioned( top: 0 - animationValue, left: 0, right: 0, child: Container( width: constraint.maxWidth, height: constraint.maxHeight, decoration: const BoxDecoration( image: DecorationImage( image: Images.welcomeBg, fit: BoxFit.fill, ), ), ), ), Positioned( top: controller.upperBound - animationValue - 1, left: 0, right: 0, child: Container( width: constraint.maxWidth, height: constraint.maxHeight, decoration: const BoxDecoration( image: DecorationImage( image: Images.selectBg, fit: BoxFit.fill, ), ), ), ), ], ); })); })); } }