写在前面

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

步骤

实现一个九宫格的页面

实现一个控制器用于控制抽奖开始

实现抽奖的动画

过程

定义数据类型

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 Statewith 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,

),

);

}));

}

}

代码地址

代码地址

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐