option_widget.dart 3.62 KB
import 'package:flutter/material.dart';
import 'package:wow_english/common/widgets/throttledGesture_gesture_detector.dart';

import '../../utils/log_util.dart';

/// 选择题选项组件,具备答错摇晃、答对缩放动效能力
class OptionWidget extends StatefulWidget {
  final Widget child;

  //正确缩放动画;错误摇晃动画;为空即非选中选项,无操作
  final bool? isCorrect;
  final bool isClickable;
  final VoidCallback? onTap;

  const OptionWidget({
    Key? key,
    required this.child,
    this.isCorrect,
    this.isClickable = true,
    this.onTap,
  }) : super(key: key);

  @override
  _OptionWidgetState createState() => _OptionWidgetState();
}

class _OptionWidgetState extends State<OptionWidget>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _shakeAnimation;
  late Animation<double> _scaleAnimation;
  static const String TAG = 'OptionWidget';

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 500),
    );

    _shakeAnimation = TweenSequence([
      TweenSequenceItem(
        tween: Tween(begin: 0.0, end: 10.0)
            .chain(CurveTween(curve: Curves.easeInOut)),
        weight: 1,
      ),
      TweenSequenceItem(
        tween: Tween(begin: 10.0, end: -10.0)
            .chain(CurveTween(curve: Curves.easeInOut)),
        weight: 1,
      ),
      TweenSequenceItem(
        tween: Tween(begin: -10.0, end: 10.0)
            .chain(CurveTween(curve: Curves.easeInOut)),
        weight: 1,
      ),
      TweenSequenceItem(
        tween: Tween(begin: 10.0, end: -10.0)
            .chain(CurveTween(curve: Curves.easeInOut)),
        weight: 1,
      ),
      TweenSequenceItem(
        tween: Tween(begin: -10.0, end: 0.0)
            .chain(CurveTween(curve: Curves.easeInOut)),
        weight: 1,
      ),
    ]).animate(_controller);

    _scaleAnimation = TweenSequence<double>([
      TweenSequenceItem(tween: Tween(begin: 1.0, end: 0.8), weight: 1),
      TweenSequenceItem(tween: Tween(begin: 0.8, end: 1.25), weight: 1),
      TweenSequenceItem(tween: Tween(begin: 1.25, end: 1.0), weight: 1),
    ]).animate(CurvedAnimation(
      parent: _controller,
      curve: Curves.easeInOut,
    ));
  }

  @override
  void didUpdateWidget(OptionWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    Log.d(
        '$TAG didUpdateWidget widget.isCorrect=${widget.isCorrect} oldWidget.isCorrect=${oldWidget.isCorrect} isAnimation=${_controller.isAnimating} status=${_controller.status}');
    if (widget.isCorrect != oldWidget.isCorrect) {
      _controller.reset();
      if (widget.isCorrect == true) {
        _controller.forward();
      } else if (widget.isCorrect == false) {
        _controller.forward();
      }
    }
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, child) {
        double offsetX = widget.isCorrect == false ? _shakeAnimation.value : 0;
        double scale = widget.isCorrect == true ? _scaleAnimation.value : 1.0;

        return ThrottledGestureDetector(
          onTap: widget.isClickable ? widget.onTap : null,
          child: Transform.translate(
            offset: Offset(offsetX, 0),
            child: Transform.scale(
              scale: scale,
              child: Opacity(
                opacity: widget.isClickable ? 1.0 : 0.5,
                child: widget.child,
              ),
            ),
          ),
        );
      },
    );
  }
}