Skip to content
Snippets Groups Projects
Commit db163e64 authored by Benoît Harrault's avatar Benoît Harrault
Browse files

Improve game architecture

parent 64bb7efd
No related branches found
No related tags found
1 merge request!19Resolve "Improve game architecture"
Pipeline #5668 passed
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:solitaire/cubit/game_cubit.dart';
import 'package:solitaire/models/board.dart';
import 'package:solitaire/ui/widgets/game/tile_widget.dart';
class Tileset extends StatelessWidget {
const Tileset({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<GameCubit, GameState>(
builder: (BuildContext context, GameState gameState) {
final Board board = gameState.currentGame.board;
final BoardCells cells = board.cells;
final boardSize = gameState.currentGame.boardSize;
final double tileSize = (MediaQuery.of(context).size.width - 40) / boardSize;
return Column(
children: [
Table(
defaultColumnWidth: const IntrinsicColumnWidth(),
children: [
for (int row = 0; row < cells.length; row++)
TableRow(
children: [
for (int col = 0; col < cells[row].length; col++)
TableCell(
child: TileWidget(
tile: cells[row][col],
tileSize: tileSize,
),
),
],
),
],
),
],
);
},
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:solitaire/config/menu.dart';
import 'package:solitaire/cubit/game_cubit.dart';
import 'package:solitaire/cubit/nav_cubit.dart';
import 'package:solitaire/models/game.dart';
import 'package:solitaire/ui/widgets/helpers/app_title.dart';
class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget {
const GlobalAppBar({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<GameCubit, GameState>(
builder: (BuildContext context, GameState gameState) {
return BlocBuilder<NavCubit, int>(
builder: (BuildContext context, int pageIndex) {
final Game currentGame = gameState.currentGame;
final List<Widget> menuActions = [];
if (currentGame.isRunning) {
menuActions.add(TextButton(
child: const Image(
image: AssetImage('assets/icons/button_back.png'),
fit: BoxFit.fill,
),
onPressed: () {},
onLongPress: () {
final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
gameCubit.quitGame();
},
));
} else {
if (pageIndex == Menu.indexGame) {
// go to Settings page
menuActions.add(ElevatedButton(
onPressed: () {
context.read<NavCubit>().goToSettingsPage();
},
style: ElevatedButton.styleFrom(
shape: const CircleBorder(),
),
child: Menu.menuItemSettings.icon,
));
// go to About page
menuActions.add(ElevatedButton(
onPressed: () {
context.read<NavCubit>().goToAboutPage();
},
style: ElevatedButton.styleFrom(
shape: const CircleBorder(),
),
child: Menu.menuItemAbout.icon,
));
} else {
// back to Home page
menuActions.add(ElevatedButton(
onPressed: () {
context.read<NavCubit>().goToGamePage();
},
style: ElevatedButton.styleFrom(
shape: const CircleBorder(),
),
child: Menu.menuItemGame.icon,
));
}
}
return AppBar(
title: const AppTitle(text: 'app_name'),
actions: menuActions,
);
},
);
},
);
}
@override
Size get preferredSize => const Size.fromHeight(50);
}
......@@ -8,15 +8,16 @@ class AppHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
tr(text),
textAlign: TextAlign.start,
style: Theme.of(context).textTheme.headlineMedium!.apply(fontWeightDelta: 2),
style: Theme.of(context).textTheme.headlineSmall!.apply(fontWeightDelta: 2),
),
const SizedBox(height: 8),
],
);
}
......
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
class AppTitle extends StatelessWidget {
const AppTitle({super.key, required this.text});
final String text;
@override
Widget build(BuildContext context) {
return Text(
tr(text),
textAlign: TextAlign.start,
style: Theme.of(context).textTheme.headlineLarge!.apply(fontWeightDelta: 2),
);
}
}
import 'package:flutter/material.dart';
import 'package:solitaire/provider/data.dart';
import 'package:solitaire/utils/game_utils.dart';
class RestartGameButton extends StatelessWidget {
const RestartGameButton({super.key, required this.myProvider});
final Data myProvider;
@override
Widget build(BuildContext context) {
return TextButton(
child: const Image(
image: AssetImage('assets/icons/button_back.png'),
fit: BoxFit.fill,
),
onPressed: () => GameUtils.quitAndDeleteCurrentGame(myProvider),
);
}
}
import 'package:flutter/material.dart';
import 'package:solitaire/ui/layout/parameters.dart';
import 'package:solitaire/utils/game_utils.dart';
class ResumeGameButton extends Parameters {
const ResumeGameButton({super.key, required super.myProvider});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(Parameters.blockMargin),
padding: const EdgeInsets.all(Parameters.blockPadding),
child: Table(
defaultColumnWidth: const IntrinsicColumnWidth(),
children: [
TableRow(
children: [
Column(
children: [
TextButton(
child: Parameters.buildImageContainerWidget('button_delete_saved_game'),
onPressed: () => GameUtils.deleteSavedGame(myProvider),
),
],
),
Column(
children: [
TextButton(
child: Parameters.buildImageContainerWidget('button_resume_game'),
onPressed: () => GameUtils.resumeSavedGame(myProvider),
),
],
),
Parameters.buildDecorationImageWidget(),
],
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:solitaire/provider/data.dart';
import 'package:solitaire/ui/layout/parameters.dart';
import 'package:solitaire/utils/game_utils.dart';
class StartNewGameButton extends StatelessWidget {
const StartNewGameButton({super.key, required this.myProvider});
final Data myProvider;
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(Parameters.blockMargin),
padding: const EdgeInsets.all(Parameters.blockPadding),
child: Table(
defaultColumnWidth: const IntrinsicColumnWidth(),
children: [
TableRow(
children: [
Parameters.buildDecorationImageWidget(),
Column(
children: [
TextButton(
child: Parameters.buildImageContainerWidget('button_start'),
onPressed: () => GameUtils.startNewGame(myProvider),
),
],
),
Parameters.buildDecorationImageWidget(),
],
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:solitaire/config/default_game_settings.dart';
import 'package:solitaire/config/default_global_settings.dart';
import 'package:solitaire/cubit/settings_game_cubit.dart';
import 'package:solitaire/cubit/settings_global_cubit.dart';
import 'package:solitaire/ui/painters/parameter_painter.dart';
import 'package:solitaire/ui/widgets/button_game_start_new.dart';
class Parameters extends StatelessWidget {
const Parameters({super.key});
final double separatorHeight = 8.0;
@override
Widget build(BuildContext context) {
final List<Widget> lines = [];
// Game settings
for (String code in DefaultGameSettings.availableParameters) {
lines.add(Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: buildParametersLine(
code: code,
isGlobal: false,
),
));
lines.add(SizedBox(height: separatorHeight));
}
lines.add(SizedBox(height: separatorHeight));
lines.add(const Expanded(child: StartNewGameButton()));
lines.add(SizedBox(height: separatorHeight));
// Global settings
for (String code in DefaultGlobalSettings.availableParameters) {
lines.add(Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: buildParametersLine(
code: code,
isGlobal: true,
),
));
lines.add(SizedBox(height: separatorHeight));
}
return Column(
children: lines,
);
}
List<Widget> buildParametersLine({
required String code,
required bool isGlobal,
}) {
final List<Widget> parameterButtons = [];
final List<String> availableValues = isGlobal
? DefaultGlobalSettings.getAvailableValues(code)
: DefaultGameSettings.getAvailableValues(code);
if (availableValues.length <= 1) {
return [];
}
for (String value in availableValues) {
final Widget parameterButton = BlocBuilder<GameSettingsCubit, GameSettingsState>(
builder: (BuildContext context, GameSettingsState gameSettingsState) {
return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>(
builder: (BuildContext context, GlobalSettingsState globalSettingsState) {
final GameSettingsCubit gameSettingsCubit =
BlocProvider.of<GameSettingsCubit>(context);
final GlobalSettingsCubit globalSettingsCubit =
BlocProvider.of<GlobalSettingsCubit>(context);
final String currentValue = isGlobal
? globalSettingsCubit.getParameterValue(code)
: gameSettingsCubit.getParameterValue(code);
final bool isActive = (value == currentValue);
final double displayWidth = MediaQuery.of(context).size.width;
final double itemWidth = displayWidth / availableValues.length - 26;
return TextButton(
child: CustomPaint(
size: Size(itemWidth, itemWidth),
willChange: false,
painter: ParameterPainter(
code: code,
value: value,
isSelected: isActive,
gameSettings: gameSettingsState.settings,
globalSettings: globalSettingsState.settings,
),
isComplex: true,
),
onPressed: () => isGlobal
? globalSettingsCubit.setParameterValue(code, value)
: gameSettingsCubit.setParameterValue(code, value),
);
},
);
},
);
parameterButtons.add(parameterButton);
}
return parameterButtons;
}
}
import 'dart:math';
import 'package:solitaire/entities/tile.dart';
import 'package:solitaire/provider/data.dart';
import 'package:solitaire/utils/tools.dart';
class BoardUtils {
static printGrid(List cells) {
String textBoard = ' ';
String textHole = '·';
String textPeg = 'o';
printlog('');
printlog('-------');
for (int rowIndex = 0; rowIndex < cells.length; rowIndex++) {
String row = '';
for (int colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) {
String textCell = textBoard;
Tile? tile = cells[rowIndex][colIndex];
if (tile != null) {
textCell = tile.hasPeg ? textPeg : textHole;
}
row += textCell;
}
printlog(row);
}
printlog('-------');
printlog('');
}
static Board createBoardFromSavedState(Data myProvider, String savedBoard) {
Board board = [];
int boardSize = pow((savedBoard.length), 1 / 2).round();
myProvider.updateBoardSize(boardSize);
String textBoard = ' ';
String textPeg = 'o';
int index = 0;
for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
List<Tile?> row = [];
for (int colIndex = 0; colIndex < boardSize; colIndex++) {
String stringValue = savedBoard[index++];
if (stringValue == textBoard) {
row.add(null);
} else {
row.add(Tile(rowIndex, colIndex, (stringValue == textPeg)));
}
}
board.add(row);
}
return board;
}
static createNewBoard(Data myProvider) {
Map<String, List<String>> templates = {
'french': [
' ooo ',
' ooooo ',
'ooo·ooo',
'ooooooo',
'ooooooo',
' ooooo ',
' ooo ',
],
'german': [
' ooo ',
' ooo ',
' ooo ',
'ooooooooo',
'oooo·oooo',
'ooooooooo',
' ooo ',
' ooo ',
' ooo ',
],
'english': [
' ooo ',
' ooo ',
'ooooooo',
'ooo·ooo',
'ooooooo',
' ooo ',
' ooo ',
],
'diamond': [
' o ',
' ooo ',
' ooooo ',
' ooooooo ',
'oooo·oooo',
' ooooooo ',
' ooooo ',
' ooo ',
' o ',
]
};
List<String>? template = templates[myProvider.parameterLayout];
Board grid = [];
int row = 0;
template?.forEach((String line) {
List<Tile?> gridLine = [];
int col = 0;
line.split("").forEach((String tileCode) {
gridLine.add(tileCode == ' ' ? null : Tile(row, col, (tileCode == 'o')));
col++;
});
row++;
grid.add(gridLine);
});
printGrid(grid);
myProvider.resetGame();
myProvider.updateBoard(grid);
}
}
......@@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: args
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
version: "2.5.0"
async:
dependency: transitive
description:
......@@ -21,10 +21,10 @@ packages:
dependency: transitive
description:
name: bloc
sha256: f53a110e3b48dcd78136c10daa5d51512443cea5e1348c9d80a320095fa2db9e
sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
url: "https://pub.dev"
source: hosted
version: "8.1.3"
version: "8.1.4"
characters:
dependency: transitive
description:
......@@ -61,10 +61,10 @@ packages:
dependency: "direct main"
description:
name: easy_localization
sha256: c145aeb6584aedc7c862ab8c737c3277788f47488bfdf9bae0fe112bd0a4789c
sha256: fa59bcdbbb911a764aa6acf96bbb6fa7a5cf8234354fc45ec1a43a0349ef0201
url: "https://pub.dev"
source: hosted
version: "3.0.5"
version: "3.0.7"
easy_logger:
dependency: transitive
description:
......@@ -106,18 +106,18 @@ packages:
dependency: "direct main"
description:
name: flutter_bloc
sha256: "87325da1ac757fcc4813e6b34ed5dd61169973871fdf181d6c2109dd6935ece1"
sha256: f0ecf6e6eb955193ca60af2d5ca39565a86b8a142452c5b24d96fb477428f4d2
url: "https://pub.dev"
source: hosted
version: "8.1.4"
version: "8.1.5"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
version: "4.0.0"
flutter_localizations:
dependency: transitive
description: flutter
......@@ -140,10 +140,10 @@ packages:
dependency: transitive
description:
name: http
sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba
sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
version: "1.2.1"
http_parser:
dependency: transitive
description:
......@@ -156,10 +156,10 @@ packages:
dependency: "direct main"
description:
name: hydrated_bloc
sha256: "00a2099680162e74b5a836b8a7f446e478520a9cae9f6032e028ad8129f4432d"
sha256: af35b357739fe41728df10bec03aad422cdc725a1e702e03af9d2a41ea05160c
url: "https://pub.dev"
source: hosted
version: "9.1.4"
version: "9.1.5"
intl:
dependency: transitive
description:
......@@ -172,10 +172,10 @@ packages:
dependency: transitive
description:
name: lints
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
version: "4.0.0"
material_color_utilities:
dependency: transitive
description:
......@@ -200,32 +200,24 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0"
overlay_support:
dependency: "direct main"
description:
name: overlay_support
sha256: fc39389bfd94e6985e1e13b2a88a125fc4027608485d2d4e2847afe1b2bb339c
url: "https://pub.dev"
source: hosted
version: "2.1.0"
package_info_plus:
dependency: "direct main"
description:
name: package_info_plus
sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79"
sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0
url: "https://pub.dev"
source: hosted
version: "5.0.1"
version: "8.0.0"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6"
sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.0"
path:
dependency: "direct main"
dependency: transitive
description:
name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
......@@ -236,26 +228,26 @@ packages:
dependency: "direct main"
description:
name: path_provider
sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
url: "https://pub.dev"
source: hosted
version: "2.1.2"
version: "2.1.3"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d
url: "https://pub.dev"
source: hosted
version: "2.2.2"
version: "2.2.4"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
url: "https://pub.dev"
source: hosted
version: "2.3.2"
version: "2.4.0"
path_provider_linux:
dependency: transitive
description:
......@@ -297,7 +289,7 @@ packages:
source: hosted
version: "2.1.8"
provider:
dependency: "direct main"
dependency: transitive
description:
name: provider
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
......@@ -305,29 +297,29 @@ packages:
source: hosted
version: "6.1.2"
shared_preferences:
dependency: "direct main"
dependency: transitive
description:
name: shared_preferences
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
url: "https://pub.dev"
source: hosted
version: "2.2.2"
version: "2.2.3"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
version: "2.2.2"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7"
url: "https://pub.dev"
source: hosted
version: "2.3.5"
version: "2.4.0"
shared_preferences_linux:
dependency: transitive
description:
......@@ -348,10 +340,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_web
sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21"
sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a"
url: "https://pub.dev"
source: hosted
version: "2.2.2"
version: "2.3.0"
shared_preferences_windows:
dependency: transitive
description:
......@@ -425,18 +417,18 @@ packages:
dependency: transitive
description:
name: web
sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05"
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
url: "https://pub.dev"
source: hosted
version: "0.4.2"
version: "0.5.1"
win32:
dependency: transitive
description:
name: win32
sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480"
sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
url: "https://pub.dev"
source: hosted
version: "5.3.0"
version: "5.5.0"
xdg_directories:
dependency: transitive
description:
......@@ -447,4 +439,4 @@ packages:
version: "1.0.4"
sdks:
dart: ">=3.3.0 <4.0.0"
flutter: ">=3.16.0"
flutter: ">=3.19.0"
name: solitaire
description: Solitaire Game
publish_to: 'none'
version: 0.0.17+17
publish_to: "none"
version: 0.0.18+18
environment:
sdk: '^3.0.0'
sdk: "^3.0.0"
dependencies:
flutter:
......@@ -16,16 +17,12 @@ dependencies:
flutter_bloc: ^8.1.1
hive: ^2.2.3
hydrated_bloc: ^9.0.0
overlay_support: ^2.1.0
provider: ^6.0.5
shared_preferences: ^2.2.1
package_info_plus: ^5.0.1
path: ^1.9.0
package_info_plus: ^8.0.0
path_provider: ^2.0.11
unicons: ^2.1.1
dev_dependencies:
flutter_lints: ^3.0.1
flutter_lints: ^4.0.0
flutter:
uses-material-design: true
......@@ -45,4 +42,3 @@ flutter:
weight: 400
- asset: assets/fonts/Nunito-Light.ttf
weight: 300
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment