import 'dart:async'; import 'dart:ui'; import 'package:flutter/cupertino.dart'; import 'package:flutter/scheduler.dart'; import 'package:wow_english/common/extension/string_extension.dart'; import '../../../app/app.dart'; import '../../../utils/log_util.dart'; ///带左右摇晃的wow封面 class ShakeImage extends StatefulWidget { const ShakeImage({super.key}); @override _ShakeImageState createState() => _ShakeImageState(); } class _ShakeImageState extends State with SingleTickerProviderStateMixin, WidgetsBindingObserver, RouteAware { late AnimationController _controller; late Animation _animation; late Timer _timer; late final AppLifecycleListener appLifecycleListener; static const TAG = 'ShakeImage'; @override void initState() { var lifecycleState = SchedulerBinding.instance.lifecycleState; printLog( 'lifecycleState:$lifecycleState'); // lifecycleState:AppLifecycleState.resumed appLifecycleListener = AppLifecycleListener( onStateChange: onStateChange, onResume: onResume, onInactive: onInactive, onHide: onHide, onShow: onShow, onPause: onPause, onRestart: onRestart, onDetach: onDetach, onExitRequested: onExitRequested, ); super.initState(); WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addPostFrameCallback((_) { final route = ModalRoute.of(context); if (route is PageRoute) { customerRouteObserver.subscribe(this, route); } }); _controller = AnimationController( duration: const Duration(seconds: 2), vsync: this, ); _animation = TweenSequence([ TweenSequenceItem( tween: Tween(begin: 0.0, end: 0.05) .chain(CurveTween(curve: Curves.easeInOut)), weight: 1), TweenSequenceItem( tween: Tween(begin: 0.05, end: -0.1) .chain(CurveTween(curve: Curves.easeInOut)), weight: 1), TweenSequenceItem( tween: Tween(begin: -0.1, end: 0.2) .chain(CurveTween(curve: Curves.easeInOut)), weight: 1), TweenSequenceItem( tween: Tween(begin: 0.2, end: -0.2) .chain(CurveTween(curve: Curves.easeInOut)), weight: 1), TweenSequenceItem( tween: Tween(begin: -0.2, end: 0.1) .chain(CurveTween(curve: Curves.easeInOut)), weight: 1), TweenSequenceItem( tween: Tween(begin: 0.1, end: -0.05) .chain(CurveTween(curve: Curves.easeInOut)), weight: 1), TweenSequenceItem( tween: Tween(begin: -0.05, end: 0.0) .chain(CurveTween(curve: Curves.easeInOut)), weight: 1), ]).animate(_controller); printLog("--initState"); // _controller.addStatusListener((status) { // if (status == AnimationStatus.completed) { // _controller.reverse(); // } else if (status == AnimationStatus.dismissed) { // _controller.forward(); // } // }); } void _startAnimation() { Timer(const Duration(seconds: 1), () { _controller.forward(from: 0.0); _timer = Timer.periodic(const Duration(seconds: 4), (Timer timer) { _controller.forward(from: 0.0); }); }); printLog('_startAnimation'); } void _stopAnimation() { _timer.cancel(); printLog('_stopAnimation'); } ///进下一页 @override void didPushNext() { super.didPushNext(); printLog('--didPushNext'); _stopAnimation(); } ///下一个页面退回到当前页 @override void didPopNext() { printLog('--didPopNext'); super.didPopNext(); _startAnimation(); } ///进入到当前页,在initState之后执行 @override void didPush() { super.didPush(); printLog('--didPush'); _startAnimation(); } ///退出当前页到上一页,监听导航栈中的路由被弹出(即当前页面被移除)的事件 @override void didPop() { super.didPop(); printLog('--didPop'); _stopAnimation(); } @override Future didPushRouteInformation(RouteInformation routeInformation) { printLog('--didPushRouteInformation routeInformation=$routeInformation'); return super.didPushRouteInformation(routeInformation); } ///系统导航栏上的后退按钮(Android)或物理返回按钮被按下时调用,这个方法允许你拦截和处理系统的返回操作。 @override Future didPopRoute() { printLog('--didPopRoute'); return super.didPopRoute(); } ///前后台切换 @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.paused) { _controller.stop(); } else if (state == AppLifecycleState.resumed) { _controller.forward(); } } @override void dispose() { printLog('dispose'); customerRouteObserver.unsubscribe(this); WidgetsBinding.instance.removeObserver(this); _controller.dispose(); _timer.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return Center( child: AnimatedBuilder( animation: _animation, builder: (context, child) { return Transform.rotate( angle: _animation.value, child: child, ); }, child: Image.asset('xe_shop'.assetPng, width: 153), ), ); } /// 监听状态 onStateChange(AppLifecycleState state) { printLog('app_state:$state'); } // =============================== 根据App状态的产生的各种回调 =============================== /// 可见,并且可以响应用户操作时的回调 /// 比如应用从后台调度到前台时,在 onShow() 后面 执行 /// 注意:这个回调,初始化时 不执行 onResume() { printLog('---onResume'); _startAnimation(); } /// 可见,但无法响应用户操作时的回调 onInactive() { printLog('---onInactive'); } /// 隐藏时的回调 onHide() { printLog('---onHide'); } /// 显示时的回调,应用从后台调度到前台时 onShow() { printLog('---onShow'); } /// 暂停时的回调(后台) onPause() { printLog('---onPause'); _stopAnimation(); } /// 暂停后恢复时的回调 onRestart() { printLog('---onRestart'); } /// 这两个回调,不是所有平台都支持, /// 当退出 并将所有视图与引擎分离时的回调(IOS 支持,Android 不支持) onDetach() { printLog('---onDetach'); } /// 在退出程序时,发出询问的回调(IOS、Android 都不支持) /// 响应 [AppExitResponse.exit] 将继续终止,响应 [AppExitResponse.cancel] 将取消终止。 Future onExitRequested() async { printLog('---onExitRequested'); return AppExitResponse.exit; } void printLog(String msg) { // Log.d('$TAG $msg'); } }