Compare commits
No commits in common. "master" and "fix-routing-again" have entirely different histories.
master
...
fix-routin
@ -1 +0,0 @@
|
||||
extensions:
|
@ -1,65 +0,0 @@
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class GameInfo {
|
||||
final UuidValue? playerID;
|
||||
final String? passphrase;
|
||||
|
||||
const GameInfo({
|
||||
required this.playerID,
|
||||
required this.passphrase,
|
||||
});
|
||||
|
||||
factory GameInfo.empty() {
|
||||
return const GameInfo(playerID: null, passphrase: null);
|
||||
}
|
||||
|
||||
factory GameInfo.fromJson(Map<String, dynamic> json) {
|
||||
final playerid = UuidValue.fromString(json['playerID']);
|
||||
final passphrase = json['passphrase'];
|
||||
|
||||
return GameInfo(playerID: playerid, passphrase: passphrase);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
String? pid;
|
||||
|
||||
if (playerID != null) {
|
||||
pid = playerID.toString();
|
||||
}
|
||||
|
||||
return {
|
||||
'playerID': pid,
|
||||
'passphrase': passphrase,
|
||||
};
|
||||
}
|
||||
|
||||
void store() async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
|
||||
await prefs.setString(passphrase!, playerID.toString());
|
||||
}
|
||||
|
||||
static Future<GameInfo?> get(String phrase) async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var playerID = prefs.getString(phrase);
|
||||
|
||||
if (playerID == null) return null;
|
||||
|
||||
return GameInfo(
|
||||
playerID: UuidValue.fromString(playerID), passphrase: phrase);
|
||||
}
|
||||
}
|
||||
|
||||
class WebsocketMessageIdentifyPlayer {
|
||||
final String playerID;
|
||||
final String? passphrase;
|
||||
|
||||
const WebsocketMessageIdentifyPlayer({
|
||||
required this.playerID,
|
||||
required this.passphrase,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() =>
|
||||
{'playerID': playerID, 'passphrase': passphrase};
|
||||
}
|
80
lib/api/register.dart
Normal file
80
lib/api/register.dart
Normal file
@ -0,0 +1,80 @@
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class PlayerInfo {
|
||||
final UuidValue? playerID;
|
||||
final UuidValue? lobbyID;
|
||||
final String? passphrase;
|
||||
|
||||
const PlayerInfo({
|
||||
required this.playerID,
|
||||
required this.lobbyID,
|
||||
required this.passphrase,
|
||||
});
|
||||
|
||||
factory PlayerInfo.fromJson(Map<String, dynamic> json) {
|
||||
final playerid = UuidValue.fromString(json['playerID']);
|
||||
final lobbyid = UuidValue.fromString(json['lobbyID']);
|
||||
final passphrase = json['passphrase'];
|
||||
|
||||
return PlayerInfo(
|
||||
playerID: playerid, lobbyID: lobbyid, passphrase: passphrase);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'playerID': playerID,
|
||||
'lobbyID': lobbyID,
|
||||
'passphrase': passphrase,
|
||||
};
|
||||
|
||||
void store() async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
|
||||
await prefs.setBool("contains", true);
|
||||
await prefs.setString("playerID", playerID.toString());
|
||||
await prefs.setString("lobbyID", lobbyID.toString());
|
||||
await prefs.setString("passphrase", passphrase.toString());
|
||||
}
|
||||
|
||||
void delete() async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
|
||||
await prefs.setBool("contains", false);
|
||||
}
|
||||
|
||||
Future<PlayerInfo?> get() async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var contains = prefs.getBool("contains");
|
||||
var playerID = prefs.getString("playerID");
|
||||
var lobbyID = prefs.getString("lobbyID");
|
||||
var passphrase = prefs.getString("passphrase");
|
||||
|
||||
if (contains == null ||
|
||||
!contains ||
|
||||
playerID == null ||
|
||||
lobbyID == null ||
|
||||
passphrase == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return PlayerInfo(
|
||||
playerID: UuidValue.fromString(playerID),
|
||||
lobbyID: UuidValue.fromString(lobbyID),
|
||||
passphrase: passphrase);
|
||||
}
|
||||
}
|
||||
|
||||
class WebsocketMessageIdentifyPlayer {
|
||||
final String playerID;
|
||||
final String lobbyID;
|
||||
final String? passphrase;
|
||||
|
||||
const WebsocketMessageIdentifyPlayer({
|
||||
required this.playerID,
|
||||
required this.lobbyID,
|
||||
required this.passphrase,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() =>
|
||||
{'lobbyID': lobbyID, 'playerID': playerID, 'passphrase': passphrase};
|
||||
}
|
@ -4,8 +4,7 @@ enum MessageType {
|
||||
boardState,
|
||||
move,
|
||||
invalidMove,
|
||||
colorDetermined,
|
||||
gameEnded;
|
||||
colorDetermined;
|
||||
|
||||
String toJson() => name;
|
||||
static MessageType fromJson(String json) => values.byName(json);
|
||||
@ -83,16 +82,6 @@ class ApiWebsocketMessage {
|
||||
squareInCheck: json['squareInCheck'],
|
||||
playerColor: null,
|
||||
);
|
||||
case MessageType.gameEnded:
|
||||
ret = ApiWebsocketMessage(
|
||||
type: type,
|
||||
move: null,
|
||||
turnColor: null,
|
||||
reason: json['reason'],
|
||||
position: null,
|
||||
squareInCheck: null,
|
||||
playerColor: null,
|
||||
);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:mchess/chess_bloc/chess_bloc.dart';
|
||||
import 'package:mchess/chess_bloc/promotion_bloc.dart';
|
||||
import 'package:mchess/chess_bloc/tap_bloc.dart';
|
||||
import 'package:mchess/connection_cubit/connection_cubit.dart';
|
||||
import 'package:mchess/utils/chess_router.dart';
|
||||
|
||||
@ -17,21 +16,18 @@ class ChessApp extends StatelessWidget {
|
||||
create: (_) => ConnectionCubit.getInstance(),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (_) => ChessBloc.getInstance(),
|
||||
create: (context) => ChessBloc.getInstance(),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (_) => PromotionBloc.getInstance(),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (_) => TapBloc.getInstance(),
|
||||
),
|
||||
create: (context) => PromotionBloc.getInstance(),
|
||||
)
|
||||
],
|
||||
child: MaterialApp.router(
|
||||
theme: ThemeData.dark(
|
||||
useMaterial3: true,
|
||||
),
|
||||
routerConfig: ChessAppRouter.getInstance().router,
|
||||
title: 'mChess 1.0.8',
|
||||
title: 'mChess 0.1.1340',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -13,8 +13,7 @@ class ChessBoard extends StatelessWidget {
|
||||
|
||||
const ChessBoard._({required this.bState, required this.squares});
|
||||
|
||||
factory ChessBoard(
|
||||
{required ChessBoardState boardState, ChessCoordinate? tappedSquare}) {
|
||||
factory ChessBoard({required ChessBoardState boardState}) {
|
||||
List<ChessSquare> squares = List.empty(growable: true);
|
||||
for (int i = 0; i < 64; i++) {
|
||||
var column = (i % 8) + 1;
|
||||
@ -25,16 +24,16 @@ class ChessBoard extends StatelessWidget {
|
||||
bool squareWasPartOfLastMove = false;
|
||||
if ((boardState.lastMove.to == ChessCoordinate(column, row) ||
|
||||
boardState.lastMove.from == ChessCoordinate(column, row)) &&
|
||||
boardState.colorLastMove) {
|
||||
boardState.positionAckdByServer) {
|
||||
squareWasPartOfLastMove = true;
|
||||
}
|
||||
|
||||
squares.add(
|
||||
ChessSquare(
|
||||
ChessCoordinate(column, row),
|
||||
piece,
|
||||
squareWasPartOfLastMove,
|
||||
tappedSquare == ChessCoordinate(column, row)),
|
||||
ChessCoordinate(column, row),
|
||||
piece,
|
||||
squareWasPartOfLastMove,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:mchess/chess/chess_square_outer_dragtarget.dart';
|
||||
import 'package:mchess/chess_bloc/chess_bloc.dart';
|
||||
import 'package:mchess/chess_bloc/tap_bloc.dart';
|
||||
import '../utils/chess_utils.dart';
|
||||
|
||||
class ChessSquare extends StatefulWidget {
|
||||
@ -18,18 +18,13 @@ class ChessSquare extends StatefulWidget {
|
||||
required this.color,
|
||||
});
|
||||
|
||||
factory ChessSquare(ChessCoordinate coord, ChessPiece? piece,
|
||||
bool wasPartOfLastMove, bool wasTapped) {
|
||||
factory ChessSquare(
|
||||
ChessCoordinate coord, ChessPiece? piece, bool wasPartOfLastMove) {
|
||||
Color lightSquaresColor =
|
||||
wasPartOfLastMove ? Colors.green.shade200 : Colors.brown.shade50;
|
||||
Color darkSquaresColor =
|
||||
wasPartOfLastMove ? Colors.green.shade300 : Colors.brown.shade400;
|
||||
|
||||
if (wasTapped) {
|
||||
lightSquaresColor = Colors.red.shade200;
|
||||
darkSquaresColor = Colors.red.shade300;
|
||||
}
|
||||
|
||||
Color squareColor;
|
||||
|
||||
if (coord.row % 2 == 0) {
|
||||
@ -58,26 +53,31 @@ class ChessSquare extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ChessSquareState extends State<ChessSquare> {
|
||||
late Color squareColor;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
squareColor = widget.color;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var dragTarget = Container(
|
||||
color: widget.color,
|
||||
child: ChessSquareOuterDragTarget(
|
||||
coordinate: widget.coordinate,
|
||||
containedPiece: widget.containedPiece ?? const ChessPiece.none()),
|
||||
return BlocListener<ChessBloc, ChessBoardState>(
|
||||
listenWhen: (previous, current) {
|
||||
return true;
|
||||
},
|
||||
listener: (context, state) {
|
||||
setState(() {
|
||||
squareColor = Colors.red;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
color: widget.color,
|
||||
child: ChessSquareOuterDragTarget(
|
||||
coordinate: widget.coordinate,
|
||||
containedPiece: widget.containedPiece ?? const ChessPiece.none()),
|
||||
),
|
||||
);
|
||||
|
||||
if (widget.containedPiece == null ||
|
||||
widget.containedPiece!.color != ChessBloc.getMyColor()) {
|
||||
return GestureDetector(
|
||||
child: dragTarget,
|
||||
onTap: () {
|
||||
TapBloc().add(SquareTappedEvent(
|
||||
tapped: widget.coordinate, pieceOnSquare: widget.containedPiece));
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return dragTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mchess/chess/chess_square.dart';
|
||||
import 'package:mchess/chess_bloc/chess_bloc.dart';
|
||||
import 'package:mchess/chess_bloc/tap_bloc.dart';
|
||||
import 'package:mchess/utils/chess_utils.dart';
|
||||
|
||||
class ChessSquareInnerDraggable extends StatelessWidget {
|
||||
@ -49,10 +48,7 @@ class ChessSquareInnerDraggable extends StatelessWidget {
|
||||
dragAnchorStrategy: pointerDragAnchorStrategy,
|
||||
child: containedPiece ?? Container(),
|
||||
onDragCompleted: () {},
|
||||
onDragStarted: () {
|
||||
TapBloc().add(SquareTappedEvent(
|
||||
tapped: coordinate, pieceOnSquare: containedPiece));
|
||||
},
|
||||
onDragStarted: () {},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ import 'package:mchess/chess/chess_square_inner_draggable.dart';
|
||||
import 'package:mchess/chess_bloc/chess_bloc.dart';
|
||||
import 'package:mchess/chess_bloc/chess_events.dart';
|
||||
import 'package:mchess/chess_bloc/promotion_bloc.dart';
|
||||
import 'package:mchess/chess_bloc/tap_bloc.dart';
|
||||
import 'package:mchess/utils/chess_utils.dart';
|
||||
|
||||
class ChessSquareOuterDragTarget extends StatelessWidget {
|
||||
@ -16,31 +15,26 @@ class ChessSquareOuterDragTarget extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DragTarget<PieceDragged>(
|
||||
onWillAcceptWithDetails: (details) {
|
||||
if (details.data.fromSquare == coordinate) {
|
||||
onWillAccept: (move) {
|
||||
if (move?.fromSquare == coordinate) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
onAcceptWithDetails: (details) {
|
||||
onAccept: (pieceDragged) {
|
||||
// Replace the dummy value with the actual target of the move.
|
||||
details.data.toSquare = coordinate;
|
||||
pieceDragged.toSquare = coordinate;
|
||||
|
||||
TapBloc().add(CancelTapEvent());
|
||||
|
||||
if (isPromotionMove(
|
||||
details.data.movedPiece!.pieceClass,
|
||||
ChessBloc.getMyColor(),
|
||||
details.data.toSquare,
|
||||
)) {
|
||||
if (isPromotionMove(pieceDragged)) {
|
||||
var move = ChessMove(
|
||||
from: details.data.fromSquare, to: details.data.toSquare);
|
||||
PromotionBloc().add(PawnMovedToPromotionField(
|
||||
move: move, colorMoved: ChessBloc.myColor));
|
||||
} else if (coordinate != details.data.fromSquare) {
|
||||
ChessBloc().add(OwnPieceMoved(
|
||||
startSquare: details.data.fromSquare,
|
||||
endSquare: details.data.toSquare));
|
||||
from: pieceDragged.fromSquare, to: pieceDragged.toSquare);
|
||||
PromotionBloc.getInstance().add(PawnMovedToPromotionField(
|
||||
move: move, colorMoved: ChessBloc.myColor!));
|
||||
} else if (coordinate != pieceDragged.fromSquare) {
|
||||
ChessBloc.getInstance().add(OwnPieceMoved(
|
||||
startSquare: pieceDragged.fromSquare,
|
||||
endSquare: pieceDragged.toSquare,
|
||||
piece: pieceDragged.movedPiece!));
|
||||
}
|
||||
},
|
||||
builder: (context, candidateData, rejectedData) {
|
||||
@ -51,4 +45,28 @@ class ChessSquareOuterDragTarget extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
bool isPromotionMove(PieceDragged move) {
|
||||
bool isPromotion = false;
|
||||
if (move.movedPiece!.pieceClass != ChessPieceClass.pawn) {
|
||||
return isPromotion;
|
||||
}
|
||||
|
||||
switch (ChessBloc.myColor) {
|
||||
case ChessColor.black:
|
||||
if (move.toSquare.row == 1) {
|
||||
isPromotion = true;
|
||||
}
|
||||
break;
|
||||
case ChessColor.white:
|
||||
if (move.toSquare.row == 8) {
|
||||
isPromotion = true;
|
||||
}
|
||||
break;
|
||||
case null:
|
||||
break;
|
||||
}
|
||||
|
||||
return isPromotion;
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,9 @@ import 'package:mchess/utils/chess_utils.dart';
|
||||
class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
||||
static final ChessBloc _instance = ChessBloc._internal();
|
||||
static ChessColor turnColor = ChessColor.white;
|
||||
static ChessColor myColor = ChessColor.white;
|
||||
static ChessColor? myColor = ChessColor.white;
|
||||
|
||||
static ChessColor getMyColor() {
|
||||
static ChessColor? getSidesColor() {
|
||||
return myColor;
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
||||
on<OwnPieceMoved>(ownMoveHandler);
|
||||
on<OwnPromotionPlayed>(ownPromotionHandler);
|
||||
on<InvalidMovePlayed>(invalidMoveHandler);
|
||||
on<BoardStatusReceived>(boardStatusHandler);
|
||||
}
|
||||
|
||||
factory ChessBloc.getInstance() {
|
||||
@ -51,12 +52,10 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
||||
|
||||
void flipBoard(ColorDetermined event, Emitter<ChessBoardState> emit) {
|
||||
log("My Color is $myColor");
|
||||
|
||||
myColor = event.playerColor;
|
||||
|
||||
myColor = event.myColor;
|
||||
emit(
|
||||
ChessBoardState(
|
||||
event.playerColor,
|
||||
event.myColor,
|
||||
state.newTurnColor,
|
||||
state.position,
|
||||
ChessMove.none(),
|
||||
@ -76,13 +75,12 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
||||
.recordMove(event.startSquare, event.endSquare, event.position);
|
||||
}
|
||||
|
||||
myColor = event.playerColor;
|
||||
turnColor = event.turnColor;
|
||||
|
||||
emit(
|
||||
ChessBoardState(
|
||||
myColor,
|
||||
event.turnColor,
|
||||
state.bottomColor,
|
||||
turnColor,
|
||||
event.position,
|
||||
move,
|
||||
true,
|
||||
@ -114,14 +112,8 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
||||
tempPosition[move.from] = const ChessPiece.none();
|
||||
|
||||
emit(
|
||||
ChessBoardState(
|
||||
state.bottomColor,
|
||||
turnColor,
|
||||
tempPosition,
|
||||
ChessPositionManager.getInstance().lastMove,
|
||||
true,
|
||||
ChessCoordinate.none(),
|
||||
),
|
||||
ChessBoardState(state.bottomColor, turnColor, tempPosition, move, false,
|
||||
ChessCoordinate.none()),
|
||||
);
|
||||
}
|
||||
|
||||
@ -129,7 +121,7 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
||||
OwnPromotionPlayed event, Emitter<ChessBoardState> emit) {
|
||||
var apiMove = event.move.toApiMove();
|
||||
var shorNameForPiece = chessPiecesShortName[
|
||||
ChessPieceAssetKey(pieceClass: event.pieceClass, color: myColor)]!;
|
||||
ChessPieceAssetKey(pieceClass: event.pieceClass, color: myColor!)]!;
|
||||
apiMove.promotionToPiece = shorNameForPiece;
|
||||
var message = ApiWebsocketMessage(
|
||||
type: MessageType.move,
|
||||
@ -146,18 +138,31 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
||||
|
||||
void invalidMoveHandler(
|
||||
InvalidMovePlayed event, Emitter<ChessBoardState> emit) {
|
||||
var move = ChessPositionManager.getInstance().lastMove;
|
||||
emit(
|
||||
ChessBoardState(
|
||||
state.bottomColor,
|
||||
turnColor,
|
||||
ChessPositionManager.getInstance().currentPosition,
|
||||
move,
|
||||
true,
|
||||
ChessMove.none(),
|
||||
false,
|
||||
event.squareInCheck,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void boardStatusHandler(
|
||||
BoardStatusReceived event, Emitter<ChessBoardState> emit) {
|
||||
emit(
|
||||
ChessBoardState(
|
||||
event.myColor,
|
||||
event.whoseTurn,
|
||||
event.pos,
|
||||
ChessMove.none(),
|
||||
false,
|
||||
ChessCoordinate.none(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChessBoardState {
|
||||
@ -165,7 +170,7 @@ class ChessBoardState {
|
||||
final ChessColor newTurnColor;
|
||||
final ChessPosition position;
|
||||
final ChessMove lastMove;
|
||||
final bool colorLastMove;
|
||||
final bool positionAckdByServer;
|
||||
final ChessCoordinate squareInCheck;
|
||||
|
||||
ChessBoardState._(
|
||||
@ -173,7 +178,7 @@ class ChessBoardState {
|
||||
this.newTurnColor,
|
||||
this.position,
|
||||
this.lastMove,
|
||||
this.colorLastMove,
|
||||
this.positionAckdByServer,
|
||||
this.squareInCheck,
|
||||
);
|
||||
|
||||
|
@ -9,7 +9,6 @@ class ReceivedBoardState extends ChessEvent {
|
||||
final ChessPosition position;
|
||||
final ChessCoordinate squareInCheck;
|
||||
final ChessColor turnColor;
|
||||
final ChessColor playerColor;
|
||||
|
||||
ReceivedBoardState({
|
||||
required this.startSquare,
|
||||
@ -17,15 +16,18 @@ class ReceivedBoardState extends ChessEvent {
|
||||
required this.position,
|
||||
required this.squareInCheck,
|
||||
required this.turnColor,
|
||||
required this.playerColor,
|
||||
});
|
||||
}
|
||||
|
||||
class OwnPieceMoved extends ChessEvent {
|
||||
final ChessCoordinate startSquare;
|
||||
final ChessCoordinate endSquare;
|
||||
final ChessPiece piece;
|
||||
|
||||
OwnPieceMoved({required this.startSquare, required this.endSquare});
|
||||
OwnPieceMoved(
|
||||
{required this.startSquare,
|
||||
required this.endSquare,
|
||||
required this.piece});
|
||||
}
|
||||
|
||||
class OwnPromotionPlayed extends ChessEvent {
|
||||
@ -40,9 +42,9 @@ class InitBoard extends ChessEvent {
|
||||
}
|
||||
|
||||
class ColorDetermined extends ChessEvent {
|
||||
final ChessColor playerColor;
|
||||
final ChessColor myColor;
|
||||
|
||||
ColorDetermined({required this.playerColor});
|
||||
ColorDetermined({required this.myColor});
|
||||
}
|
||||
|
||||
class InvalidMovePlayed extends ChessEvent {
|
||||
@ -54,3 +56,15 @@ class InvalidMovePlayed extends ChessEvent {
|
||||
required this.squareInCheck,
|
||||
});
|
||||
}
|
||||
|
||||
class BoardStatusReceived extends ChessEvent {
|
||||
final ChessPosition pos;
|
||||
final ChessColor myColor;
|
||||
final ChessColor whoseTurn;
|
||||
|
||||
BoardStatusReceived({
|
||||
required this.pos,
|
||||
required this.myColor,
|
||||
required this.whoseTurn,
|
||||
});
|
||||
}
|
||||
|
@ -1,109 +0,0 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:mchess/chess_bloc/chess_bloc.dart';
|
||||
import 'package:mchess/chess_bloc/chess_events.dart';
|
||||
import 'package:mchess/chess_bloc/promotion_bloc.dart';
|
||||
import 'package:mchess/utils/chess_utils.dart';
|
||||
|
||||
class TapBloc extends Bloc<TapEvent, TapState> {
|
||||
static final TapBloc _instance = TapBloc._internal();
|
||||
|
||||
static TapBloc getInstance() {
|
||||
return _instance;
|
||||
}
|
||||
|
||||
factory TapBloc() {
|
||||
return _instance;
|
||||
}
|
||||
|
||||
TapBloc._internal() : super(TapState.init()) {
|
||||
on<SquareTappedEvent>(handleTap);
|
||||
on<CancelTapEvent>(cancelTap);
|
||||
}
|
||||
|
||||
void handleTap(SquareTappedEvent event, Emitter<TapState> emit) {
|
||||
ChessCoordinate? firstTappedSquare, secondTappedSquare;
|
||||
ChessPiece? piece;
|
||||
|
||||
if (ChessBloc.myColor != ChessBloc.turnColor) return;
|
||||
|
||||
if (state.firstSquareTapped == null) {
|
||||
//first tap
|
||||
if (event.pieceOnSquare == null) return;
|
||||
if (event.pieceOnSquare != null &&
|
||||
ChessBloc.myColor != event.pieceOnSquare!.color) return;
|
||||
firstTappedSquare = event.tapped;
|
||||
piece = event.pieceOnSquare;
|
||||
} else {
|
||||
//second tap
|
||||
if (event.pieceOnSquare?.color == ChessBloc.myColor) {
|
||||
emit(TapState(
|
||||
firstSquareTapped: event.tapped,
|
||||
pieceOnFirstTappedSquare: event.pieceOnSquare,
|
||||
secondSquareTapped: null));
|
||||
return;
|
||||
}
|
||||
secondTappedSquare = event.tapped;
|
||||
}
|
||||
|
||||
if (state.firstSquareTapped != null &&
|
||||
state.firstSquareTapped != event.tapped) {
|
||||
if (isPromotionMove(
|
||||
state.pieceOnFirstTappedSquare!.pieceClass,
|
||||
ChessBloc.myColor,
|
||||
event.tapped,
|
||||
)) {
|
||||
PromotionBloc().add(PawnMovedToPromotionField(
|
||||
move: ChessMove(from: state.firstSquareTapped!, to: event.tapped),
|
||||
colorMoved: ChessBloc.myColor));
|
||||
emit(TapState.init());
|
||||
return;
|
||||
} else {
|
||||
ChessBloc().add(OwnPieceMoved(
|
||||
startSquare: state.firstSquareTapped!, endSquare: event.tapped));
|
||||
emit(TapState.init());
|
||||
return;
|
||||
}
|
||||
}
|
||||
log("handleTap() in TapBloc is called");
|
||||
emit(TapState(
|
||||
firstSquareTapped: firstTappedSquare,
|
||||
pieceOnFirstTappedSquare: piece,
|
||||
secondSquareTapped: secondTappedSquare));
|
||||
}
|
||||
|
||||
void cancelTap(CancelTapEvent event, Emitter<TapState> emit) {
|
||||
emit(TapState.init());
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TapEvent {}
|
||||
|
||||
class SquareTappedEvent extends TapEvent {
|
||||
ChessCoordinate tapped;
|
||||
ChessPiece? pieceOnSquare;
|
||||
|
||||
SquareTappedEvent({required this.tapped, required this.pieceOnSquare});
|
||||
}
|
||||
|
||||
class CancelTapEvent extends TapEvent {}
|
||||
|
||||
class TapState {
|
||||
ChessCoordinate? firstSquareTapped;
|
||||
ChessPiece? pieceOnFirstTappedSquare;
|
||||
|
||||
ChessCoordinate? secondSquareTapped;
|
||||
|
||||
TapState(
|
||||
{required this.firstSquareTapped,
|
||||
required this.pieceOnFirstTappedSquare,
|
||||
required this.secondSquareTapped});
|
||||
|
||||
factory TapState.init() {
|
||||
return TapState(
|
||||
firstSquareTapped: null,
|
||||
pieceOnFirstTappedSquare: null,
|
||||
secondSquareTapped: null);
|
||||
}
|
||||
}
|
@ -1,21 +1,19 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:mchess/api/move.dart';
|
||||
import 'package:mchess/api/websocket_message.dart';
|
||||
import 'package:mchess/chess_bloc/chess_bloc.dart';
|
||||
import 'package:mchess/chess_bloc/chess_events.dart';
|
||||
import 'package:mchess/api/game_info.dart';
|
||||
import 'package:mchess/api/register.dart';
|
||||
import 'package:mchess/chess_bloc/chess_position.dart';
|
||||
import 'package:mchess/connection_cubit/connection_cubit.dart';
|
||||
import 'package:mchess/utils/chess_router.dart';
|
||||
import 'package:mchess/utils/chess_utils.dart';
|
||||
import 'package:mchess/utils/config.dart' as config;
|
||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||
|
||||
class ServerConnection {
|
||||
WebSocketChannel? channel;
|
||||
late bool wasConnected = false;
|
||||
Stream broadcast = const Stream.empty();
|
||||
|
||||
static final ServerConnection _instance = ServerConnection._internal();
|
||||
@ -40,36 +38,33 @@ class ServerConnection {
|
||||
channel!.sink.add(message);
|
||||
}
|
||||
|
||||
Future? connect(String playerID, String? passphrase) {
|
||||
if (channel != null) return null;
|
||||
void connect(String playerID, lobbyID, String? passphrase) {
|
||||
String url;
|
||||
if (kDebugMode) {
|
||||
url = 'ws://localhost:8080/api/ws';
|
||||
} else {
|
||||
url = 'wss://chess.sw-gross.de:9999/api/ws';
|
||||
}
|
||||
channel = WebSocketChannel.connect(Uri.parse(url));
|
||||
|
||||
channel = WebSocketChannel.connect(Uri.parse(config.getWebsocketURL()));
|
||||
|
||||
channel!.ready.then((val) {
|
||||
send(
|
||||
jsonEncode(
|
||||
WebsocketMessageIdentifyPlayer(
|
||||
playerID: (playerID),
|
||||
passphrase: (passphrase),
|
||||
),
|
||||
send(
|
||||
jsonEncode(
|
||||
WebsocketMessageIdentifyPlayer(
|
||||
playerID: (playerID),
|
||||
lobbyID: (lobbyID),
|
||||
passphrase: (passphrase),
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
|
||||
log(channel!.closeCode.toString());
|
||||
broadcast = channel!.stream.asBroadcastStream();
|
||||
broadcast.listen(handleIncomingData);
|
||||
});
|
||||
|
||||
return channel!.ready;
|
||||
log(channel!.closeCode.toString());
|
||||
broadcast = channel!.stream.asBroadcastStream();
|
||||
broadcast.listen(handleIncomingData);
|
||||
}
|
||||
|
||||
Future disconnectExistingConnection() async {
|
||||
void disconnectExistingConnection() {
|
||||
if (channel == null) return;
|
||||
|
||||
await channel!.sink.close();
|
||||
|
||||
channel = null;
|
||||
broadcast = const Stream.empty();
|
||||
channel!.sink.close();
|
||||
}
|
||||
|
||||
void handleIncomingData(dynamic data) {
|
||||
@ -92,8 +87,6 @@ class ServerConnection {
|
||||
|
||||
case MessageType.invalidMove:
|
||||
handleInvalidMoveMessage(apiMessage);
|
||||
case MessageType.gameEnded:
|
||||
handleGameEndedMessage(apiMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,15 +99,14 @@ class ServerConnection {
|
||||
if (apiMessage.position != null) {
|
||||
ChessBloc.getInstance().add(
|
||||
ReceivedBoardState(
|
||||
startSquare: move?.from,
|
||||
endSquare: move?.to,
|
||||
position: ChessPositionManager.getInstance()
|
||||
.fromPGNString(apiMessage.position!),
|
||||
squareInCheck: ChessCoordinate.fromApiCoordinate(
|
||||
apiMessage.squareInCheck ?? const ApiCoordinate(col: 0, row: 0)),
|
||||
turnColor: ChessColor.fromApiColor(apiMessage.turnColor!),
|
||||
playerColor: ChessColor.fromApiColor(apiMessage.playerColor!),
|
||||
),
|
||||
startSquare: move?.from,
|
||||
endSquare: move?.to,
|
||||
position: ChessPositionManager.getInstance()
|
||||
.fromPGNString(apiMessage.position!),
|
||||
squareInCheck: ChessCoordinate.fromApiCoordinate(
|
||||
apiMessage.squareInCheck ??
|
||||
const ApiCoordinate(col: 0, row: 0)),
|
||||
turnColor: ChessColor.fromApiColor(apiMessage.turnColor!)),
|
||||
);
|
||||
} else {
|
||||
log('Error: no position received');
|
||||
@ -125,7 +117,7 @@ class ServerConnection {
|
||||
ConnectionCubit.getInstance().opponentConnected();
|
||||
ChessBloc.getInstance().add(InitBoard());
|
||||
ChessBloc.getInstance().add(ColorDetermined(
|
||||
playerColor: ChessColor.fromApiColor(apiMessage.playerColor!)));
|
||||
myColor: ChessColor.fromApiColor(apiMessage.playerColor!)));
|
||||
}
|
||||
|
||||
void handleInvalidMoveMessage(ApiWebsocketMessage apiMessage) {
|
||||
@ -138,29 +130,4 @@ class ServerConnection {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void handleGameEndedMessage(ApiWebsocketMessage apiMessage) {
|
||||
showDialog(
|
||||
context: navigatorKey.currentContext!,
|
||||
builder: (context) {
|
||||
String msg = '';
|
||||
if (apiMessage.reason == 'whiteIsCheckmated') {
|
||||
msg = 'Black won! White is checkmated';
|
||||
} else if (apiMessage.reason == 'blackIsCheckmated') {
|
||||
msg = 'White won! Black is checkmated';
|
||||
}
|
||||
return AlertDialog(
|
||||
title: const Text('Game ended'),
|
||||
content: Text(msg),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: const Text('Home'),
|
||||
onPressed: () {
|
||||
navigatorKey.currentContext!.goNamed('lobbySelector');
|
||||
},
|
||||
),
|
||||
]);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -15,64 +15,21 @@ class ConnectionCubit extends Cubit<ConnectionCubitState> {
|
||||
return _instance;
|
||||
}
|
||||
|
||||
void connect(String playerID, String? passphrase) {
|
||||
var connectedFuture =
|
||||
ServerConnection.getInstance().connect(playerID, passphrase);
|
||||
|
||||
connectedFuture?.then((val) {
|
||||
emit(ConnectionCubitState(
|
||||
iAmConnected: true,
|
||||
connectedToPhrase: passphrase,
|
||||
opponentConnected: false));
|
||||
});
|
||||
}
|
||||
|
||||
void connectIfNotConnected(String playerID, String? passphrase) {
|
||||
if (state.iAmConnected && state.connectedToPhrase == passphrase) {
|
||||
return;
|
||||
}
|
||||
if (state.iAmConnected && state.connectedToPhrase != passphrase) {
|
||||
disonnect().then((val) {
|
||||
connect(playerID, passphrase);
|
||||
});
|
||||
}
|
||||
|
||||
connect(playerID, passphrase);
|
||||
}
|
||||
|
||||
Future disonnect() async {
|
||||
var disconnectFuture =
|
||||
ServerConnection.getInstance().disconnectExistingConnection();
|
||||
|
||||
disconnectFuture.then(
|
||||
(val) {
|
||||
emit(ConnectionCubitState.init());
|
||||
},
|
||||
);
|
||||
|
||||
return disconnectFuture;
|
||||
void connect(String playerID, lobbyID, String? passphrase) {
|
||||
ServerConnection.getInstance().connect(playerID, lobbyID, passphrase);
|
||||
}
|
||||
|
||||
void opponentConnected() {
|
||||
emit(ConnectionCubitState(
|
||||
iAmConnected: state.iAmConnected,
|
||||
connectedToPhrase: state.connectedToPhrase,
|
||||
opponentConnected: true));
|
||||
emit(ConnectionCubitState(true));
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectionCubitState {
|
||||
final bool iAmConnected;
|
||||
final String? connectedToPhrase;
|
||||
final bool opponentConnected;
|
||||
|
||||
ConnectionCubitState(
|
||||
{required this.iAmConnected,
|
||||
required this.connectedToPhrase,
|
||||
required this.opponentConnected});
|
||||
ConnectionCubitState(this.opponentConnected);
|
||||
|
||||
factory ConnectionCubitState.init() {
|
||||
return ConnectionCubitState(
|
||||
iAmConnected: false, connectedToPhrase: null, opponentConnected: false);
|
||||
return ConnectionCubitState(false);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_web_plugins/url_strategy.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mchess/chess/chess_app.dart';
|
||||
|
||||
void main() {
|
||||
GoRouter.optionURLReflectsImperativeAPIs = true;
|
||||
setUrlStrategy(const PathUrlStrategy());
|
||||
|
||||
runApp(const ChessApp());
|
||||
}
|
||||
|
@ -5,21 +5,30 @@ import 'package:mchess/chess_bloc/chess_bloc.dart';
|
||||
|
||||
import 'package:mchess/chess/chess_board.dart';
|
||||
import 'package:mchess/chess_bloc/promotion_bloc.dart';
|
||||
import 'package:mchess/chess_bloc/tap_bloc.dart';
|
||||
import 'package:mchess/utils/chess_utils.dart';
|
||||
import 'package:mchess/utils/widgets/promotion_dialog.dart';
|
||||
import 'package:universal_platform/universal_platform.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class ChessGame extends StatefulWidget {
|
||||
const ChessGame({super.key});
|
||||
final UuidValue playerID;
|
||||
final UuidValue lobbyID;
|
||||
final String? passphrase;
|
||||
const ChessGame(
|
||||
{required this.playerID,
|
||||
required this.lobbyID,
|
||||
required this.passphrase,
|
||||
super.key});
|
||||
|
||||
@override
|
||||
State<ChessGame> createState() => _ChessGameState();
|
||||
}
|
||||
|
||||
class _ChessGameState extends State<ChessGame> {
|
||||
ChessCoordinate? tappedSquare;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -39,26 +48,18 @@ class _ChessGameState extends State<ChessGame> {
|
||||
body: Center(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(10),
|
||||
child: BlocListener<TapBloc, TapState>(
|
||||
listener: (context, state) {
|
||||
setState(() {
|
||||
tappedSquare = state.firstSquareTapped;
|
||||
});
|
||||
child: BlocListener<PromotionBloc, PromotionState>(
|
||||
listener: (listenerContext, state) {
|
||||
if (state.showPromotionDialog) {
|
||||
promotionDialogBuilder(listenerContext, state.colorMoved);
|
||||
}
|
||||
},
|
||||
child: BlocListener<PromotionBloc, PromotionState>(
|
||||
listener: (listenerContext, state) {
|
||||
if (state.showPromotionDialog) {
|
||||
promotionDialogBuilder(listenerContext, state.colorMoved);
|
||||
}
|
||||
child: BlocBuilder<ChessBloc, ChessBoardState>(
|
||||
builder: (context, state) {
|
||||
return ChessBoard(
|
||||
boardState: state,
|
||||
);
|
||||
},
|
||||
child: BlocBuilder<ChessBloc, ChessBoardState>(
|
||||
builder: (context, state) {
|
||||
return ChessBoard(
|
||||
boardState: state,
|
||||
tappedSquare: tappedSquare,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -1,161 +0,0 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:mchess/api/game_info.dart';
|
||||
import 'package:mchess/connection_cubit/connection_cubit.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:mchess/pages/chess_game.dart';
|
||||
import 'package:mchess/utils/config.dart' as config;
|
||||
import 'package:mchess/utils/passphrase.dart';
|
||||
import 'package:universal_platform/universal_platform.dart';
|
||||
|
||||
class CreateGameWidget extends StatefulWidget {
|
||||
const CreateGameWidget({super.key});
|
||||
|
||||
@override
|
||||
State<CreateGameWidget> createState() => _CreateGameWidgetState();
|
||||
}
|
||||
|
||||
class _CreateGameWidgetState extends State<CreateGameWidget> {
|
||||
late Future<GameInfo?> registerResponse;
|
||||
late Future disconnectFuture;
|
||||
late ChessGameArguments chessGameArgs;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
disconnectFuture = ConnectionCubit().disonnect();
|
||||
disconnectFuture.then((val) {
|
||||
registerResponse = createPrivateGame();
|
||||
registerResponse.then((val) {
|
||||
ConnectionCubit().connectIfNotConnected(
|
||||
val!.playerID.toString(),
|
||||
val.passphrase,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
FloatingActionButton? fltnBtn;
|
||||
if (UniversalPlatform.isLinux ||
|
||||
UniversalPlatform.isMacOS ||
|
||||
UniversalPlatform.isWindows) {
|
||||
fltnBtn = FloatingActionButton(
|
||||
child: const Icon(Icons.cancel),
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
floatingActionButton: fltnBtn,
|
||||
body: Center(
|
||||
child: FutureBuilder(
|
||||
future: disconnectFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState != ConnectionState.done) {
|
||||
return Container();
|
||||
} else {
|
||||
return FutureBuilder<GameInfo?>(
|
||||
future: registerResponse,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState != ConnectionState.done) {
|
||||
return const SizedBox(
|
||||
height: 100,
|
||||
width: 100,
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
} else {
|
||||
var passphrase =
|
||||
snapshot.data?.passphrase ?? "no passphrase";
|
||||
return BlocListener<ConnectionCubit,
|
||||
ConnectionCubitState>(
|
||||
listener: (context, state) {
|
||||
// We wait for our opponent to connect
|
||||
if (state.opponentConnected) {
|
||||
//TODO: is goNamed the correct way to navigate?
|
||||
context.goNamed('game', pathParameters: {
|
||||
'phrase': passphrase.toURL(),
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Give this phrase to your friend and sit tight:',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SelectableText(
|
||||
passphrase,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.copy),
|
||||
onPressed: () async {
|
||||
await Clipboard.setData(
|
||||
ClipboardData(text: passphrase));
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
const CircularProgressIndicator()
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<GameInfo?> createPrivateGame() async {
|
||||
Response response;
|
||||
|
||||
try {
|
||||
response = await http.get(Uri.parse(config.getCreateGameURL()),
|
||||
headers: {"Accept": "application/json"});
|
||||
} catch (e) {
|
||||
log('Exception: ${e.toString()}');
|
||||
|
||||
const snackBar = SnackBar(
|
||||
backgroundColor: Colors.amberAccent,
|
||||
content: Text("mChess server is not responding. Try again or give up"),
|
||||
);
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
if (!mounted) return null;
|
||||
|
||||
ScaffoldMessenger.of(context).clearSnackBars();
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
context.goNamed('lobbySelector'); // We go back to the lobby selector
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var info = GameInfo.fromJson(jsonDecode(response.body));
|
||||
info.store();
|
||||
return info;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
136
lib/pages/host_game.dart
Normal file
136
lib/pages/host_game.dart
Normal file
@ -0,0 +1,136 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:mchess/api/register.dart';
|
||||
import 'package:mchess/connection_cubit/connection_cubit.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:mchess/pages/chess_game.dart';
|
||||
|
||||
class HostGameWidget extends StatefulWidget {
|
||||
const HostGameWidget({super.key});
|
||||
|
||||
@override
|
||||
State<HostGameWidget> createState() => _HostGameWidgetState();
|
||||
}
|
||||
|
||||
class _HostGameWidgetState extends State<HostGameWidget> {
|
||||
late Future<PlayerInfo?> registerResponse;
|
||||
late ChessGameArguments chessGameArgs;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
registerResponse = hostPrivateGame();
|
||||
registerResponse.then((value) {
|
||||
value?.store();
|
||||
});
|
||||
connectToWebsocket(registerResponse);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void connectToWebsocket(Future<PlayerInfo?> resp) {
|
||||
resp.then((value) {
|
||||
if (value == null) return;
|
||||
|
||||
chessGameArgs = ChessGameArguments(
|
||||
lobbyID: value.lobbyID!,
|
||||
playerID: value.playerID!,
|
||||
passphrase: value.passphrase);
|
||||
|
||||
ConnectionCubit.getInstance().connect(
|
||||
value.playerID!.uuid,
|
||||
value.lobbyID!.uuid,
|
||||
value.passphrase,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: FutureBuilder<PlayerInfo?>(
|
||||
future: registerResponse,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState != ConnectionState.done) {
|
||||
return const SizedBox(
|
||||
height: 100,
|
||||
width: 100,
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
} else {
|
||||
String passphrase = snapshot.data?.passphrase ?? "no passphrase";
|
||||
return BlocListener<ConnectionCubit, ConnectionCubitState>(
|
||||
listener: (context, state) {
|
||||
// We wait for our opponent to connect
|
||||
if (state.opponentConnected) {
|
||||
context.pushReplacement('/game', extra: chessGameArgs);
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Give this phrase to your friend and sit tight:',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
SelectableText(
|
||||
passphrase,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
const CircularProgressIndicator()
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<PlayerInfo?> hostPrivateGame() async {
|
||||
String addr;
|
||||
Response response;
|
||||
|
||||
if (kDebugMode) {
|
||||
addr = 'http://localhost:8080/api/hostPrivate';
|
||||
} else {
|
||||
addr = 'https://chess.sw-gross.de:9999/api/hostPrivate';
|
||||
}
|
||||
|
||||
try {
|
||||
response = await http
|
||||
.get(Uri.parse(addr), headers: {"Accept": "application/json"});
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
|
||||
if (!context.mounted) return null;
|
||||
|
||||
const snackBar = SnackBar(
|
||||
backgroundColor: Colors.amberAccent,
|
||||
content: Text("mChess server is not responding. Try again or give up"),
|
||||
);
|
||||
Future.delayed(const Duration(seconds: 2), () {
|
||||
ScaffoldMessenger.of(context).clearSnackBars();
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
context.goNamed('lobbySelector'); // We go back to the lobby selector
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
log(response.body);
|
||||
return PlayerInfo.fromJson(jsonDecode(response.body));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mchess/api/game_info.dart';
|
||||
import 'package:mchess/connection_cubit/connection_cubit.dart';
|
||||
import 'package:mchess/pages/chess_game.dart';
|
||||
import 'package:mchess/utils/config.dart' as config;
|
||||
import 'package:universal_platform/universal_platform.dart';
|
||||
|
||||
class JoinGameHandleWidget extends StatefulWidget {
|
||||
final String passphrase;
|
||||
const JoinGameHandleWidget({required this.passphrase, super.key});
|
||||
|
||||
@override
|
||||
State<JoinGameHandleWidget> createState() => _JoinGameHandleWidgetState();
|
||||
}
|
||||
|
||||
class _JoinGameHandleWidgetState extends State<JoinGameHandleWidget> {
|
||||
late Future<GameInfo?> joinGameFuture;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
joinGameFuture = joinPrivateGame(widget.passphrase);
|
||||
joinGameFuture.then((val) {
|
||||
if (val == null) return;
|
||||
|
||||
ConnectionCubit.getInstance().connectIfNotConnected(
|
||||
val.playerID!.uuid,
|
||||
val.passphrase,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
FloatingActionButton? fltnBtn;
|
||||
if (UniversalPlatform.isLinux ||
|
||||
UniversalPlatform.isMacOS ||
|
||||
UniversalPlatform.isWindows) {
|
||||
fltnBtn = FloatingActionButton(
|
||||
child: const Icon(Icons.cancel),
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
var loadingIndicator = const SizedBox(
|
||||
height: 100,
|
||||
width: 100,
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
floatingActionButton: fltnBtn,
|
||||
body: Center(
|
||||
child: FutureBuilder(
|
||||
future: joinGameFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState != ConnectionState.done) {
|
||||
return loadingIndicator;
|
||||
} else {
|
||||
return BlocBuilder<ConnectionCubit, ConnectionCubitState>(
|
||||
builder: (context, state) {
|
||||
if (state.iAmConnected) {
|
||||
return const ChessGame();
|
||||
} else {
|
||||
return loadingIndicator;
|
||||
}
|
||||
});
|
||||
}
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<GameInfo?> joinPrivateGame(String phrase) async {
|
||||
http.Response response;
|
||||
|
||||
var existingInfo = await GameInfo.get(phrase);
|
||||
log('playerID: ${existingInfo?.playerID} and passphrase: "${existingInfo?.passphrase}"');
|
||||
|
||||
GameInfo info;
|
||||
if (existingInfo?.passphrase == phrase) {
|
||||
// We have player info for this exact passphrase
|
||||
info = GameInfo(playerID: existingInfo?.playerID, passphrase: phrase);
|
||||
} else {
|
||||
info = GameInfo(playerID: null, passphrase: phrase);
|
||||
}
|
||||
|
||||
try {
|
||||
response = await http.post(Uri.parse(config.getJoinGameURL()),
|
||||
body: jsonEncode(info), headers: {"Accept": "application/json"});
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
|
||||
if (!context.mounted) return null;
|
||||
|
||||
const snackBar = SnackBar(
|
||||
backgroundColor: Colors.amberAccent,
|
||||
content: Text("mChess server is not responding. Try again or give up"),
|
||||
);
|
||||
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
if (!mounted) return null;
|
||||
ScaffoldMessenger.of(context).clearSnackBars();
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
context.goNamed('lobbySelector'); // We go back to the lobby selector
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
if (response.statusCode == HttpStatus.notFound) {
|
||||
const snackBar = SnackBar(
|
||||
backgroundColor: Colors.amberAccent,
|
||||
content: Text("Passphrase could not be found."),
|
||||
);
|
||||
|
||||
if (!mounted) return null;
|
||||
|
||||
ScaffoldMessenger.of(context).clearSnackBars();
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
context.goNamed('lobbySelector');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (response.statusCode == HttpStatus.ok) {
|
||||
var info = GameInfo.fromJson(jsonDecode(response.body));
|
||||
info.store();
|
||||
log('Player info received from server: ');
|
||||
log('playerID: ${info.playerID}');
|
||||
log('passphrase: ${info.passphrase}');
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,6 +1,14 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mchess/utils/passphrase.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mchess/api/register.dart';
|
||||
import 'package:mchess/connection_cubit/connection_cubit.dart';
|
||||
import 'package:mchess/pages/chess_game.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class LobbySelector extends StatefulWidget {
|
||||
const LobbySelector({super.key});
|
||||
@ -10,19 +18,26 @@ class LobbySelector extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _LobbySelectorState extends State<LobbySelector> {
|
||||
final buttonStyle = const ButtonStyle();
|
||||
final phraseController = TextEditingController();
|
||||
late Future<PlayerInfo?> joinGameFuture;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SharedPreferences.getInstance().then((prefs) {
|
||||
final playerID = prefs.getString("playerID");
|
||||
final lobbyID = prefs.getString("lobbyID");
|
||||
final passphrase = prefs.getString("passphrase");
|
||||
log("lobbyID: $lobbyID and playerID: $playerID and passphrase: $passphrase");
|
||||
});
|
||||
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
context.goNamed('createGame');
|
||||
},
|
||||
onPressed: () => buildJoinOrHostDialog(context),
|
||||
child: const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@ -30,23 +45,7 @@ class _LobbySelectorState extends State<LobbySelector> {
|
||||
SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Text('Create private game')
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
buildEnterPassphraseDialog(context);
|
||||
},
|
||||
child: const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.mail),
|
||||
SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Text('Join private game')
|
||||
Text('Private game')
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -56,45 +55,141 @@ class _LobbySelectorState extends State<LobbySelector> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> buildEnterPassphraseDialog(BuildContext context) {
|
||||
Future<void> buildJoinOrHostDialog(BuildContext context) {
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return ScaffoldMessenger(
|
||||
child: Builder(builder: (builderContext) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
body: AlertDialog(
|
||||
title: const Text('Enter the passphrase here:'),
|
||||
content: TextField(
|
||||
controller: phraseController,
|
||||
onSubmitted: (val) => submitAction(val),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Enter passphrase here',
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () => submitAction(phraseController.text),
|
||||
icon: const Icon(Icons.check),
|
||||
)),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: const Text('Cancel'),
|
||||
onPressed: () {
|
||||
builderContext.pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
return Scaffold(
|
||||
body: AlertDialog(
|
||||
title: const Text('Host or join?'),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: const Text('Cancel'),
|
||||
onPressed: () => context.pop(),
|
||||
),
|
||||
);
|
||||
}),
|
||||
TextButton(
|
||||
child: const Text('Host'),
|
||||
onPressed: () {
|
||||
context.pop(); //close dialog before going to host
|
||||
context.goNamed('host');
|
||||
}),
|
||||
TextButton(
|
||||
child: const Text('Join'),
|
||||
onPressed: () {
|
||||
context.pop(); //close dialog before going to next dialog
|
||||
buildEnterPassphraseDialog(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void submitAction(String phrase) {
|
||||
context.pop();
|
||||
context.goNamed('game', pathParameters: {'phrase': phrase.toURL()});
|
||||
phraseController.clear();
|
||||
Future<void> buildEnterPassphraseDialog(BuildContext context) {
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Enter the passphrase here:'),
|
||||
content: TextField(
|
||||
controller: phraseController,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Enter passphrase here',
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
joinGameFuture = joinPrivateGame();
|
||||
joinGameFuture.then((value) {
|
||||
if (value != null) {
|
||||
phraseController.clear();
|
||||
context.pop();
|
||||
switchToGame(value);
|
||||
}
|
||||
});
|
||||
},
|
||||
icon: const Icon(Icons.check),
|
||||
)),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: const Text('Cancel'),
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void switchToGame(PlayerInfo info) {
|
||||
var chessGameArgs = ChessGameArguments(
|
||||
lobbyID: info.lobbyID!,
|
||||
playerID: info.playerID!,
|
||||
passphrase: info.passphrase);
|
||||
|
||||
ConnectionCubit.getInstance().connect(
|
||||
info.playerID!.uuid,
|
||||
info.lobbyID!.uuid,
|
||||
info.passphrase,
|
||||
);
|
||||
|
||||
if (!chessGameArgs.isValid()) {
|
||||
context.goNamed('lobbySelector');
|
||||
const snackBar = SnackBar(
|
||||
backgroundColor: Colors.amberAccent,
|
||||
content: Text("Game information is corrupted"),
|
||||
);
|
||||
ScaffoldMessenger.of(context).clearSnackBars();
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
}
|
||||
|
||||
context.goNamed('game', extra: chessGameArgs);
|
||||
}
|
||||
|
||||
Future<PlayerInfo?> joinPrivateGame() async {
|
||||
String addr;
|
||||
http.Response response;
|
||||
|
||||
// server expects us to send the passphrase
|
||||
var info = PlayerInfo(
|
||||
playerID: null, lobbyID: null, passphrase: phraseController.text);
|
||||
|
||||
if (kDebugMode) {
|
||||
addr = 'http://localhost:8080/api/joinPrivate';
|
||||
} else {
|
||||
addr = 'https://chess.sw-gross.de:9999/api/joinPrivate';
|
||||
}
|
||||
|
||||
try {
|
||||
response = await http.post(Uri.parse(addr),
|
||||
body: jsonEncode(info), headers: {"Accept": "application/json"});
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
|
||||
if (!context.mounted) return null;
|
||||
|
||||
const snackBar = SnackBar(
|
||||
backgroundColor: Colors.amberAccent,
|
||||
content: Text("mChess server is not responding. Try again or give up"),
|
||||
);
|
||||
ScaffoldMessenger.of(context).clearSnackBars();
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var info = PlayerInfo.fromJson(jsonDecode(response.body));
|
||||
log('Player info received from server: ');
|
||||
log('lobbyID: ${info.lobbyID}');
|
||||
log('playerID: ${info.playerID}');
|
||||
log('passphrase: ${info.passphrase}');
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,7 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mchess/pages/join_game_handle_widget.dart';
|
||||
import 'package:mchess/pages/chess_game.dart';
|
||||
import 'package:mchess/pages/lobby_selector.dart';
|
||||
import 'package:mchess/pages/create_game_widget.dart';
|
||||
import 'package:mchess/utils/passphrase.dart';
|
||||
|
||||
final navigatorKey = GlobalKey<NavigatorState>();
|
||||
import 'package:mchess/pages/host_game.dart';
|
||||
|
||||
class ChessAppRouter {
|
||||
static final ChessAppRouter _instance = ChessAppRouter._internal();
|
||||
@ -19,7 +13,6 @@ class ChessAppRouter {
|
||||
}
|
||||
|
||||
final router = GoRouter(
|
||||
navigatorKey: navigatorKey,
|
||||
debugLogDiagnostics: true,
|
||||
routes: [
|
||||
GoRoute(
|
||||
@ -30,23 +23,22 @@ class ChessAppRouter {
|
||||
},
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'createGame',
|
||||
name: 'createGame',
|
||||
path: 'host',
|
||||
name: 'host',
|
||||
builder: (context, state) {
|
||||
return const CreateGameWidget();
|
||||
return const HostGameWidget();
|
||||
}),
|
||||
GoRoute(
|
||||
path: 'game/:phrase',
|
||||
path: 'game',
|
||||
name: 'game',
|
||||
builder: (context, state) {
|
||||
var urlPhrase = state.pathParameters['phrase'];
|
||||
if (urlPhrase == null) {
|
||||
log('in /game route builder: url phrase null');
|
||||
return const LobbySelector();
|
||||
}
|
||||
var args = state.extra as ChessGameArguments;
|
||||
|
||||
return JoinGameHandleWidget(
|
||||
passphrase: urlPhrase.toPhraseWithSpaces());
|
||||
return ChessGame(
|
||||
lobbyID: args.lobbyID,
|
||||
playerID: args.playerID,
|
||||
passphrase: args.passphrase,
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
|
@ -363,26 +363,3 @@ class PieceDragged {
|
||||
|
||||
PieceDragged(this.fromSquare, this.toSquare, this.movedPiece);
|
||||
}
|
||||
|
||||
bool isPromotionMove(
|
||||
ChessPieceClass pieceMoved, ChessColor myColor, ChessCoordinate toSquare) {
|
||||
bool isPromotion = false;
|
||||
if (pieceMoved != ChessPieceClass.pawn) {
|
||||
return isPromotion;
|
||||
}
|
||||
|
||||
switch (myColor) {
|
||||
case ChessColor.black:
|
||||
if (toSquare.row == 1) {
|
||||
isPromotion = true;
|
||||
}
|
||||
break;
|
||||
case ChessColor.white:
|
||||
if (toSquare.row == 8) {
|
||||
isPromotion = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return isPromotion;
|
||||
}
|
||||
|
@ -1,34 +0,0 @@
|
||||
const prodURL = 'chess.sw-gross.de:9999';
|
||||
const debugURL = 'localhost:8080';
|
||||
|
||||
const useDbgUrl = false;
|
||||
|
||||
String getCreateGameURL() {
|
||||
var prot = 'https';
|
||||
var domain = prodURL;
|
||||
if (useDbgUrl) {
|
||||
prot = 'http';
|
||||
domain = debugURL;
|
||||
}
|
||||
return '$prot://$domain/api/hostPrivate';
|
||||
}
|
||||
|
||||
String getJoinGameURL() {
|
||||
var prot = 'https';
|
||||
var domain = prodURL;
|
||||
if (useDbgUrl) {
|
||||
prot = 'http';
|
||||
domain = debugURL;
|
||||
}
|
||||
return '$prot://$domain/api/joinPrivate';
|
||||
}
|
||||
|
||||
String getWebsocketURL() {
|
||||
var prot = 'wss';
|
||||
var domain = prodURL;
|
||||
if (useDbgUrl) {
|
||||
prot = 'ws';
|
||||
domain = debugURL;
|
||||
}
|
||||
return '$prot://$domain/api/ws';
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
extension PassphaseURL on String {
|
||||
String capitalize() {
|
||||
return "${this[0].toUpperCase()}${substring(1).toLowerCase()}";
|
||||
}
|
||||
|
||||
String toURL() {
|
||||
var words = split(' ');
|
||||
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
words[i] = words[i].capitalize();
|
||||
}
|
||||
|
||||
return words.join();
|
||||
}
|
||||
|
||||
String toPhraseWithSpaces() {
|
||||
var phrase = '';
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
if (this[i] == this[i].toUpperCase()) {
|
||||
phrase += ' ';
|
||||
}
|
||||
phrase += this[i].toLowerCase();
|
||||
}
|
||||
|
||||
phrase = phrase.trim();
|
||||
|
||||
return phrase.toLowerCase();
|
||||
}
|
||||
}
|
184
pubspec.lock
184
pubspec.lock
@ -5,10 +5,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
|
||||
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.0"
|
||||
version: "2.4.2"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -21,10 +21,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: bloc
|
||||
sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
|
||||
sha256: "3820f15f502372d979121de1f6b97bfcf1630ebff8fe1d52fb2b0bfa49be5b49"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.4"
|
||||
version: "8.1.2"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -69,10 +69,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cupertino_icons
|
||||
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
|
||||
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.8"
|
||||
version: "1.0.6"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -85,10 +85,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
|
||||
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.0"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -97,14 +97,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@ -114,26 +106,26 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_bloc
|
||||
sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a
|
||||
sha256: e74efb89ee6945bcbce74a5b3a5a3376b088e5f21f55c263fc38cbdc6237faae
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.6"
|
||||
version: "8.1.3"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
|
||||
sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
version: "3.0.1"
|
||||
flutter_svg:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_svg
|
||||
sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2"
|
||||
sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.10+1"
|
||||
version: "2.0.9"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@ -148,18 +140,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: go_router
|
||||
sha256: cdae1b9c8bd7efadcef6112e81c903662ef2ce105cbd220a04bbb7c3425b5554
|
||||
sha256: ca7e4a2249f96773152f1853fa25933ac752495cdd7fdf5dafb9691bd05830fd
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.2.0"
|
||||
version: "13.0.0"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
|
||||
sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
version: "1.1.2"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -168,38 +160,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.5"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.5"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
|
||||
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
version: "3.0.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -212,26 +180,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16+1"
|
||||
version: "0.12.16"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
version: "0.5.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "25dfcaf170a0190f47ca6355bdd4552cb8924b430512ff0cafb8db9bd41fe33b"
|
||||
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.14.0"
|
||||
version: "1.10.0"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -244,10 +212,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
version: "1.8.3"
|
||||
path_parsing:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -268,10 +236,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.1"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -292,26 +260,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
|
||||
sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.5"
|
||||
version: "3.1.3"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
version: "2.1.7"
|
||||
provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: provider
|
||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
||||
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.2"
|
||||
version: "6.1.1"
|
||||
quiver:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -324,26 +292,26 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
|
||||
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
version: "2.2.2"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577"
|
||||
sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
version: "2.2.1"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7"
|
||||
sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
version: "2.3.4"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -356,18 +324,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
|
||||
sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
version: "2.3.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a"
|
||||
sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
version: "2.2.2"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -433,10 +401,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "2419f20b0c8677b2d67c8ac4d1ac7372d862dc6c460cdbb052b40155408cd794"
|
||||
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.1"
|
||||
version: "0.6.1"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -449,42 +417,42 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: universal_platform
|
||||
sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec"
|
||||
sha256: d315be0f6641898b280ffa34e2ddb14f3d12b1a37882557869646e0cc363d0cc
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.0.0+1"
|
||||
uuid:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: uuid
|
||||
sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8"
|
||||
sha256: "22c94e5ad1e75f9934b766b53c742572ee2677c56bc871d850a57dad0f82127f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.4.0"
|
||||
version: "4.2.2"
|
||||
vector_graphics:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_graphics
|
||||
sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3"
|
||||
sha256: "0f0c746dd2d6254a0057218ff980fc7f5670fd0fcf5e4db38a490d31eed4ad43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.11+1"
|
||||
version: "1.1.9+1"
|
||||
vector_graphics_codec:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_graphics_codec
|
||||
sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da
|
||||
sha256: "0edf6d630d1bfd5589114138ed8fada3234deacc37966bec033d3047c29248b7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.11+1"
|
||||
version: "1.1.9+1"
|
||||
vector_graphics_compiler:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_graphics_compiler
|
||||
sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81"
|
||||
sha256: d24333727332d9bd20990f1483af4e09abdb9b1fc7c3db940b56ab5c42790c26
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.11+1"
|
||||
version: "1.1.9+1"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -493,54 +461,38 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "7475cb4dd713d57b6f7464c0e13f06da0d535d8b2067e188962a59bac2cf280b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.2.2"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
||||
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket
|
||||
sha256: "24301d8c293ce6fe327ffe6f59d8fd8834735f0ec36e4fd383ec7ff8a64aa078"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.5"
|
||||
version: "0.3.0"
|
||||
web_socket_channel:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: a2d56211ee4d35d9b344d9d4ce60f362e4f5d1aafb988302906bd732bc731276
|
||||
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
version: "2.4.0"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
|
||||
sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.5.1"
|
||||
version: "5.1.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
|
||||
sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
version: "1.0.3"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -550,5 +502,5 @@ packages:
|
||||
source: hosted
|
||||
version: "6.5.0"
|
||||
sdks:
|
||||
dart: ">=3.4.0 <4.0.0"
|
||||
flutter: ">=3.22.0"
|
||||
dart: ">=3.2.0 <4.0.0"
|
||||
flutter: ">=3.16.0"
|
||||
|
@ -40,8 +40,8 @@ dependencies:
|
||||
cupertino_icons: ^1.0.2
|
||||
flutter_bloc: ^8.1.3
|
||||
quiver: ^3.1.0
|
||||
web_socket_channel: ^3.0.0
|
||||
go_router: ^14.0.2
|
||||
web_socket_channel: ^2.2.0
|
||||
go_router: ^13.0.0
|
||||
http: ^1.0.0
|
||||
uuid: ^4.0.0
|
||||
shared_preferences: ^2.2.2
|
||||
@ -56,7 +56,7 @@ dev_dependencies:
|
||||
# activated in the `analysis_options.yaml` file located at the root of your
|
||||
# package. See that file for information about deactivating specific lint
|
||||
# rules and activating additional ones.
|
||||
flutter_lints: ^4.0.0
|
||||
flutter_lints: ^3.0.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
@ -8,11 +8,16 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mchess/pages/chess_game.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(const ChessGame());
|
||||
await tester.pumpWidget(ChessGame(
|
||||
playerID: UuidValue.fromString("test"),
|
||||
lobbyID: UuidValue.fromString("testLobbyId"),
|
||||
passphrase: 'test',
|
||||
));
|
||||
|
||||
// Verify that our counter starts at 0.
|
||||
expect(find.text('0'), findsOneWidget);
|
||||
|
Loading…
Reference in New Issue
Block a user