ShakeImage.dart 6.81 KB
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<ShakeImage>
    with SingleTickerProviderStateMixin, WidgetsBindingObserver, RouteAware {
  late AnimationController _controller;
  late Animation<double> _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<bool> didPushRouteInformation(RouteInformation routeInformation) {
    printLog('--didPushRouteInformation routeInformation=$routeInformation');
    return super.didPushRouteInformation(routeInformation);
  }

  ///系统导航栏上的后退按钮(Android)或物理返回按钮被按下时调用,这个方法允许你拦截和处理系统的返回操作。
  @override
  Future<bool> 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<AppExitResponse> onExitRequested() async {
    printLog('---onExitRequested');
    return AppExitResponse.exit;
  }

  void printLog(String msg) {
    // Log.d('$TAG $msg');
  }
}