gobang_board.dart 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import 'package:flutter/material.dart';
  2. import 'package:get/get.dart';
  3. import 'package:gobang/controllers/game_controller.dart';
  4. import 'package:gobang/models/position.dart';
  5. import 'package:gobang/widgets/board_painter.dart';
  6. /// 可点击的五子棋棋盘;最后一手带落子动画与高亮标记。
  7. class GobangBoard extends StatefulWidget {
  8. const GobangBoard({super.key, required this.size});
  9. final double size;
  10. @override
  11. State<GobangBoard> createState() => _GobangBoardState();
  12. }
  13. class _GobangBoardState extends State<GobangBoard>
  14. with SingleTickerProviderStateMixin {
  15. late final AnimationController _controller;
  16. late final Animation<double> _scale;
  17. late final Animation<double> _pulse;
  18. Worker? _paintWorker;
  19. @override
  20. void initState() {
  21. super.initState();
  22. _controller = AnimationController(
  23. vsync: this,
  24. duration: const Duration(milliseconds: 520),
  25. );
  26. _scale = CurvedAnimation(
  27. parent: _controller,
  28. curve: const Interval(0, 0.55, curve: Curves.easeOutBack),
  29. );
  30. _pulse = TweenSequence<double>([
  31. TweenSequenceItem(tween: Tween(begin: 0.0, end: 1.0), weight: 1),
  32. TweenSequenceItem(tween: Tween(begin: 1.0, end: 0.35), weight: 1),
  33. TweenSequenceItem(tween: Tween(begin: 0.35, end: 0.8), weight: 1),
  34. TweenSequenceItem(tween: Tween(begin: 0.8, end: 0.0), weight: 1),
  35. ]).animate(
  36. CurvedAnimation(
  37. parent: _controller,
  38. curve: const Interval(0.2, 1.0, curve: Curves.easeInOut),
  39. ),
  40. );
  41. final c = Get.find<GameController>();
  42. _paintWorker = ever<int>(c.paintVersion, (_) {
  43. if (c.board.state.isEmpty) {
  44. _controller.value = 0;
  45. return;
  46. }
  47. _controller.forward(from: 0);
  48. });
  49. }
  50. @override
  51. void dispose() {
  52. _paintWorker?.dispose();
  53. _controller.dispose();
  54. super.dispose();
  55. }
  56. @override
  57. Widget build(BuildContext context) {
  58. final c = Get.find<GameController>();
  59. return Obx(() {
  60. // 必须在 Obx 作用域内直接读取 .obs,不能放进 AnimatedBuilder
  61. final version = c.paintVersion.value;
  62. final pieces = List<Position>.unmodifiable(c.board.state);
  63. return GestureDetector(
  64. onTapDown: (d) => c.onBoardTap(d.localPosition, widget.size),
  65. child: AnimatedBuilder(
  66. animation: _controller,
  67. builder: (context, _) {
  68. return CustomPaint(
  69. size: Size.square(widget.size),
  70. painter: BoardPainter(
  71. pieces: pieces,
  72. version: version,
  73. lastMoveScale: _scale.value,
  74. lastMovePulse: _pulse.value,
  75. ),
  76. );
  77. },
  78. ),
  79. );
  80. });
  81. }
  82. }