import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:gobang/controllers/game_controller.dart'; import 'package:gobang/models/position.dart'; import 'package:gobang/widgets/board_painter.dart'; /// 可点击的五子棋棋盘;最后一手带落子动画与高亮标记。 class GobangBoard extends StatefulWidget { const GobangBoard({super.key, required this.size}); final double size; @override State createState() => _GobangBoardState(); } class _GobangBoardState extends State with SingleTickerProviderStateMixin { late final AnimationController _controller; late final Animation _scale; late final Animation _pulse; Worker? _paintWorker; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 520), ); _scale = CurvedAnimation( parent: _controller, curve: const Interval(0, 0.55, curve: Curves.easeOutBack), ); _pulse = TweenSequence([ TweenSequenceItem(tween: Tween(begin: 0.0, end: 1.0), weight: 1), TweenSequenceItem(tween: Tween(begin: 1.0, end: 0.35), weight: 1), TweenSequenceItem(tween: Tween(begin: 0.35, end: 0.8), weight: 1), TweenSequenceItem(tween: Tween(begin: 0.8, end: 0.0), weight: 1), ]).animate( CurvedAnimation( parent: _controller, curve: const Interval(0.2, 1.0, curve: Curves.easeInOut), ), ); final c = Get.find(); _paintWorker = ever(c.paintVersion, (_) { if (c.board.state.isEmpty) { _controller.value = 0; return; } _controller.forward(from: 0); }); } @override void dispose() { _paintWorker?.dispose(); _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final c = Get.find(); return Obx(() { // 必须在 Obx 作用域内直接读取 .obs,不能放进 AnimatedBuilder final version = c.paintVersion.value; final pieces = List.unmodifiable(c.board.state); return GestureDetector( onTapDown: (d) => c.onBoardTap(d.localPosition, widget.size), child: AnimatedBuilder( animation: _controller, builder: (context, _) { return CustomPaint( size: Size.square(widget.size), painter: BoardPainter( pieces: pieces, version: version, lastMoveScale: _scale.value, lastMovePulse: _pulse.value, ), ); }, ), ); }); } }