8c3c2ae9a9
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
74 lines
1.9 KiB
Dart
74 lines
1.9 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../../state/game_session_notifier.dart';
|
|
import 'board_painter.dart';
|
|
|
|
/// The board with clear-flash and combo-text effects. Pure display: drag
|
|
/// orchestration lives in the game screen.
|
|
class BoardWidget extends StatefulWidget {
|
|
const BoardWidget({super.key, required this.view, required this.ghost});
|
|
|
|
final GameViewState view;
|
|
final GhostSpec? ghost;
|
|
|
|
@override
|
|
State<BoardWidget> createState() => _BoardWidgetState();
|
|
}
|
|
|
|
class _BoardWidgetState extends State<BoardWidget>
|
|
with SingleTickerProviderStateMixin {
|
|
late final AnimationController _flash = AnimationController(
|
|
vsync: this,
|
|
duration: const Duration(milliseconds: 350),
|
|
);
|
|
|
|
List<int> _flashRows = const [];
|
|
List<int> _flashCols = const [];
|
|
|
|
@override
|
|
void didUpdateWidget(BoardWidget old) {
|
|
super.didUpdateWidget(old);
|
|
final placement = widget.view.lastPlacement;
|
|
if (widget.view.fxTick != old.view.fxTick &&
|
|
placement != null &&
|
|
placement.linesCleared > 0) {
|
|
_flashRows = placement.clearedRows;
|
|
_flashCols = placement.clearedCols;
|
|
_flash.forward(from: 0);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_flash.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return AspectRatio(
|
|
aspectRatio: 1,
|
|
child: AnimatedBuilder(
|
|
animation: _flash,
|
|
builder: (context, _) {
|
|
final fading = _flash.isAnimating ? 1 - _flash.value : 0.0;
|
|
return Stack(
|
|
fit: StackFit.expand,
|
|
children: [
|
|
CustomPaint(
|
|
painter: BoardPainter(
|
|
grid: widget.view.grid,
|
|
ghost: widget.ghost,
|
|
flashProgress: fading,
|
|
flashRows: _flashRows,
|
|
flashCols: _flashCols,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|