Skip to content

Commit

Permalink
feat: add onTappedSquare to Chessboard widget
Browse files Browse the repository at this point in the history
  • Loading branch information
tom-anders committed Dec 26, 2024
1 parent e95e42e commit b89d3cf
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 2 deletions.
10 changes: 10 additions & 0 deletions lib/src/widgets/board.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class Chessboard extends StatefulWidget with ChessboardGeometry {
required this.fen,
this.opponentsPiecesUpsideDown = false,
this.lastMove,
this.onTappedSquare,
required this.game,
this.shapes,
this.annotations,
Expand All @@ -60,6 +61,7 @@ class Chessboard extends StatefulWidget with ChessboardGeometry {
required this.orientation,
required this.fen,
this.lastMove,
this.onTappedSquare,
this.shapes,
this.annotations,
}) : _size = size,
Expand Down Expand Up @@ -88,6 +90,12 @@ class Chessboard extends StatefulWidget with ChessboardGeometry {
/// Last move played, used to highlight corresponding squares.
final Move? lastMove;

/// Callback called after a square has been tapped.
///
/// This will be called even when the board is not interactable.
/// For a callback when a move has been made, use [GameData.onMove].
final void Function(Square)? onTappedSquare;

/// Game state of the board.
///
/// If `null`, the board cannot be interacted with.
Expand Down Expand Up @@ -549,6 +557,8 @@ class _BoardState extends State<Chessboard> {
final square = widget.offsetSquare(details.localPosition);
if (square == null) return;

widget.onTappedSquare?.call(square);

final Piece? piece = pieces[square];

if (widget.settings.drawShape.enable) {
Expand Down
41 changes: 39 additions & 2 deletions test/widgets/board_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,26 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:dartchess/dartchess.dart';
import 'package:chessground/chessground.dart';
import 'package:mocktail/mocktail.dart';

const boardSize = 200.0;
const squareSize = boardSize / 8;

class OnTappedSquareMock extends Mock {
void call(Square square);
}

void main() {
group('Non-interactive board', () {
const viewOnlyBoard = Chessboard.fixed(
final onTappedSquare = OnTappedSquareMock();
final viewOnlyBoard = Chessboard.fixed(
size: boardSize,
orientation: Side.white,
fen: kInitialFEN,
settings: ChessboardSettings(
settings: const ChessboardSettings(
drawShape: DrawShapeOptions(enable: true),
),
onTappedSquare: onTappedSquare.call,
);

testWidgets('initial position display', (WidgetTester tester) async {
Expand All @@ -29,11 +36,16 @@ void main() {
});

testWidgets('cannot select piece', (WidgetTester tester) async {
reset(onTappedSquare);

await tester.pumpWidget(viewOnlyBoard);
await tester.tap(find.byKey(const Key('e2-whitepawn')));
await tester.pump();

expect(find.byKey(const Key('e2-selected')), findsNothing);

verify(() => onTappedSquare.call(Square.e2)).called(1);
verifyNoMoreInteractions(onTappedSquare);
});

testWidgets('background is constrained to the size of the board', (
Expand Down Expand Up @@ -113,11 +125,13 @@ void main() {
border: BoardBorder(width: 16.0, color: Color(0xFF000000)),
),
]) {
final onTappedSquare = OnTappedSquareMock();
await tester.pumpWidget(
_TestApp(
initialPlayerSide: PlayerSide.both,
settings: settings,
key: ValueKey(settings.hashCode),
onTappedSquare: onTappedSquare.call,
),
);
await tester.tap(find.byKey(const Key('a2-whitepawn')));
Expand Down Expand Up @@ -156,6 +170,17 @@ void main() {
await tester.tap(find.byKey(const Key('e7-blackpawn')));
await tester.pump();
expect(find.byKey(const Key('e7-selected')), findsNothing);

verifyInOrder([
() => onTappedSquare.call(Square.a2),
() => onTappedSquare.call(Square.a2),
() => onTappedSquare.call(Square.a1),
() => onTappedSquare.call(Square.e7),
() => onTappedSquare.call(Square.a1),
() => onTappedSquare.call(Square.c4),
() => onTappedSquare.call(Square.e7),
]);
verifyNoMoreInteractions(onTappedSquare);
}
});

Expand Down Expand Up @@ -205,11 +230,13 @@ void main() {
border: BoardBorder(width: 16.0, color: Color(0xFF000000)),
),
]) {
final onTappedSquare = OnTappedSquareMock();
await tester.pumpWidget(
_TestApp(
initialPlayerSide: PlayerSide.both,
settings: settings,
key: ValueKey(settings.hashCode),
onTappedSquare: onTappedSquare.call,
),
);
await tester.tap(find.byKey(const Key('e2-whitepawn')));
Expand All @@ -229,6 +256,12 @@ void main() {
expect(find.byKey(const Key('e2-whitepawn')), findsNothing);
expect(find.byKey(const Key('e2-lastMove')), findsOneWidget);
expect(find.byKey(const Key('e4-lastMove')), findsOneWidget);

verifyInOrder([
() => onTappedSquare.call(Square.e2),
() => onTappedSquare.call(Square.e2),
]);
verifyNoMoreInteractions(onTappedSquare);
}
});

Expand Down Expand Up @@ -1555,6 +1588,7 @@ class _TestApp extends StatefulWidget {
this.enableDrawingShapes = false,
this.shouldPlayOpponentMove = false,
this.gameEventStream,
this.onTappedSquare,
super.key,
});

Expand All @@ -1573,6 +1607,8 @@ class _TestApp extends StatefulWidget {
/// A stream of game events
final Stream<GameEvent>? gameEventStream;

final void Function(Square)? onTappedSquare;

@override
State<_TestApp> createState() => _TestAppState();
}
Expand Down Expand Up @@ -1729,6 +1765,7 @@ class _TestAppState extends State<_TestApp> {
},
),
),
onTappedSquare: widget.onTappedSquare,
shapes: shapes,
),
),
Expand Down

0 comments on commit b89d3cf

Please sign in to comment.