shake_widget.dart 2.77 KB
import 'package:flutter/material.dart';

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

///左右晃动组件,用于答错场景
class ShakeWidget extends StatefulWidget {
  final Widget child;
  final int shakeCount;
  final double shakeOffset;
  final Duration duration;
  final bool shouldShake;

  const ShakeWidget({
    Key? key,
    required this.child,
    this.shakeCount = 2,
    this.shakeOffset = 10.0,
    this.duration = const Duration(milliseconds: 500),
    this.shouldShake = false,
  }) : super(key: key);

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

class _ShakeWidgetState extends State<ShakeWidget> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;
  static const String TAG = "ShakeWidget";

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: widget.duration,
    );

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

  @override
  void didUpdateWidget(covariant ShakeWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    Log.d("$TAG didUpdateWidget widget.shouldShake=${widget.shouldShake} oldWidget.shouldShake=${oldWidget.shouldShake} isAnimating=${_controller.isAnimating}");
    // if (widget.shouldShake && !oldWidget.shouldShake) {
    if (widget.shouldShake && !_controller.isAnimating) {
      _controller.forward(from: 0.0);
    } else if (!widget.shouldShake && _controller.isAnimating) {
      _controller.stop();
    }
  }

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

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animation,
      builder: (context, child) {
        return Transform.translate(
          offset: Offset(_animation.value, 0),
          child: widget.child,
        );
      },
      child: widget.child,
    );
  }
}