Flutter : 一个简单的九宫格抽奖实现

写在前面

用Flutter实现一个简单的九宫格抽奖
Flutter : 一个简单的九宫格抽奖实现_第1张图片

步骤

  • 实现一个九宫格的页面
  • 实现一个控制器用于控制抽奖开始
  • 实现抽奖的动画

过程

定义数据类型

class SimpleLotteryValue {
  SimpleLotteryValue(
      {this.target = 0, this.isFinish = false, this.isPlaying = false});

  /// 中奖目标
  int target = 0;

  bool isPlaying = false;
  bool isFinish = false;

  SimpleLotteryValue copyWith({
    int target = 0,
    bool isPlaying = false,
    bool isFinish = false,
  }) {
    return SimpleLotteryValue(
        target: target, isFinish: isFinish, isPlaying: isPlaying);
  }

  @override
  String toString() {
    return "target : $target , isPlaying : $isPlaying , isFinish : $isFinish";
  }
}

定义控制器

class SimpleLotteryController extends ValueNotifier {
  SimpleLotteryController() : super(SimpleLotteryValue());

  /// 开启抽奖
  ///
  /// [target] 中奖目标
  void start(int target) {
    // 九宫格抽奖里范围为0~8
    assert(target >= 0 && target <= 8);
    if (value.isPlaying) {
      return;
    }
    value = value.copyWith(target: target, isPlaying: true);
  }

  void finish() {
    value = value.copyWith(isFinish: true);
  }
}

实现九宫格页面

class SimpleLotteryWidget extends StatefulWidget {
  final SimpleLotteryController controller;
  final Function onPress;

  SimpleLotteryWidget({Key key, @required this.controller, this.onPress});

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

class _SimpleLotteryWidgetState extends State {
  
  @override
  Widget build(BuildContext context) {
    return Container(
        width: 220,
        height: 220,
        child: GridView.builder(
            itemCount: 9,
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 3, crossAxisSpacing: 4, mainAxisSpacing: 4),
            itemBuilder: (context, index) {
              if (index != 4) {
                return Container(
                  child: Stack(
                    alignment: Alignment.center,
                    children: [
                      Container(
                        width: 50,
                        height: 50,
                        color:  Colors.amber,
                      ),
                    ],
                  ),
                );
              }
              return GestureDetector(
                onTap: () => widget.onPress(),
                child: Container(
                  color: Colors.red,
                ),
              );
            }));
  }
}

实现抽奖动画


 Animation _selectedIndexTween;
  AnimationController _startAnimateController;
  int _currentSelect = -1;
  int _target = 0;

  /// 旋转的圈数
  final int repeatRound = 3;
  VoidCallback _listener;

  _SimpleLotteryWidgetState() {
    _listener = () {
      // 开启抽奖动画
      if (widget.controller.value.isPlaying) {
        _startAnimateController.reset();
        _target = widget.controller.value.target;
        _selectedIndexTween = _initSelectIndexTween(_target);

        _startAnimateController.forward();
      }
    };
  }

  /// 选中下标的映射 
  /// 因为动画的数值为0~8的增长,而宫格的实际下标顺序是0,3,6,7,8,5,2,1,因此做了映射处理
  final Map _selectMap = {
    0: 0,
    1: 3,
    2: 6,
    3: 7,
    4: 8,
    5: 5,
    6: 2,
    7: 1
  };

  @override
  void initState() {
    _startAnimateController =
        AnimationController(vsync: this, duration: Duration(seconds: 5));
    _selectedIndexTween = _initSelectIndexTween(_target);

    // 动画监听
    _startAnimateController.addListener(() {
      // 更新选中的下标
      _currentSelect = _selectMap[_selectedIndexTween.value % 8];

      if (_startAnimateController.isCompleted) {
        widget.controller.finish();
      }
      setState(() {});
    });

    // 控制监听
    widget.controller.addListener(_listener);

    super.initState();
  }

完整代码

class SimpleLotteryWidget extends StatefulWidget {
  final SimpleLotteryController controller;
  final Function onPress;

  SimpleLotteryWidget({Key key, @required this.controller, this.onPress});

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

class _SimpleLotteryWidgetState extends State
    with TickerProviderStateMixin {
  Animation _selectedIndexTween;
  AnimationController _startAnimateController;
  int _currentSelect = -1;
  int _target = 0;

  /// 旋转的圈数
  final int repeatRound = 3;
  VoidCallback _listener;

  /// 选中下标的映射
  final Map _selectMap = {
    0: 0,
    1: 3,
    2: 6,
    3: 7,
    4: 8,
    5: 5,
    6: 2,
    7: 1
  };

  _SimpleLotteryWidgetState() {
    _listener = () {
      // 开启抽奖动画
      if (widget.controller.value.isPlaying) {
        _startAnimateController.reset();
        _target = widget.controller.value.target;
        _selectedIndexTween = _initSelectIndexTween(_target);

        _startAnimateController.forward();
      }
    };
  }

  /// 初始化tween
  ///
  /// [target] 中奖的目标
  Animation _initSelectIndexTween(int target) =>
      StepTween(begin: 0, end: repeatRound * 8 + target).animate(
          CurvedAnimation(
              parent: _startAnimateController, curve: Curves.easeOutQuart));

  @override
  void initState() {
    _startAnimateController =
        AnimationController(vsync: this, duration: Duration(seconds: 5));
    _selectedIndexTween = _initSelectIndexTween(_target);

    // 动画监听
    _startAnimateController.addListener(() {
      // 更新选中的下标
      _currentSelect = _selectMap[_selectedIndexTween.value % 8];

      if (_startAnimateController.isCompleted) {
        widget.controller.finish();
      }
      setState(() {});
    });

    // 控制监听
    widget.controller.addListener(_listener);

    super.initState();
  }

  @override
  void deactivate() {
    widget.controller.removeListener(_listener);
    super.deactivate();
  }

  @override
  void dispose() {
    _startAnimateController.dispose();
    widget.controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        width: 220,
        height: 220,
        child: GridView.builder(
            itemCount: 9,
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 3, crossAxisSpacing: 4, mainAxisSpacing: 4),
            itemBuilder: (context, index) {
              if (index != 4) {
                return Container(
                  child: Stack(
                    alignment: Alignment.center,
                    children: [
                      Container(
                        width: 50,
                        height: 50,
                        color: index == _currentSelect
                            ? Colors.blue
                            : Colors.amber,
                      ),
                    ],
                  ),
                );
              }
              return GestureDetector(
                onTap: () => widget.onPress(),
                child: Container(
                  color: Colors.red,
                ),
              );
            }));
  }
}

代码地址

代码地址

你可能感兴趣的:(flutter)