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

Merge branch '21-normalize-game-architecture' into 'master'

Resolve "Normalize game architecture"

Closes #21

See merge request !20
parents 00eb251f 9451f9d5
Branches 22-improve-app-metadata
Tags Release_0.1.0_19
1 merge request!20Resolve "Normalize game architecture"
Pipeline #5809 passed
Showing
with 160 additions and 74 deletions
...@@ -2,8 +2,9 @@ import 'package:flutter/material.dart'; ...@@ -2,8 +2,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:solitaire/cubit/game_cubit.dart'; import 'package:solitaire/cubit/game_cubit.dart';
import 'package:solitaire/ui/widgets/game/game_widget.dart'; import 'package:solitaire/models/game/game.dart';
import 'package:solitaire/ui/widgets/parameters.dart'; import 'package:solitaire/ui/layouts/game_layout.dart';
import 'package:solitaire/ui/layouts/parameters_layout.dart';
class PageGame extends StatelessWidget { class PageGame extends StatelessWidget {
const PageGame({super.key}); const PageGame({super.key});
...@@ -12,7 +13,11 @@ class PageGame extends StatelessWidget { ...@@ -12,7 +13,11 @@ class PageGame extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<GameCubit, GameState>( return BlocBuilder<GameCubit, GameState>(
builder: (BuildContext context, GameState gameState) { builder: (BuildContext context, GameState gameState) {
return gameState.currentGame.isRunning ? const GameWidget() : const Parameters(); final Game currentGame = gameState.currentGame;
return currentGame.isRunning
? const GameLayout()
: ParametersLayout(canResume: currentGame.canBeResumed);
}, },
); );
} }
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:solitaire/ui/widgets/helpers/app_header.dart'; import 'package:solitaire/ui/helpers/app_titles.dart';
import 'package:solitaire/ui/widgets/settings/settings_form.dart'; import 'package:solitaire/ui/settings/settings_form.dart';
class PageSettings extends StatelessWidget { class PageSettings extends StatelessWidget {
const PageSettings({super.key}); const PageSettings({super.key});
...@@ -16,7 +16,7 @@ class PageSettings extends StatelessWidget { ...@@ -16,7 +16,7 @@ class PageSettings extends StatelessWidget {
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
children: <Widget>[ children: <Widget>[
SizedBox(height: 8), SizedBox(height: 8),
AppHeader(text: 'settings_title'), AppTitle(text: 'settings_title'),
SizedBox(height: 8), SizedBox(height: 8),
SettingsForm(), SettingsForm(),
], ],
......
...@@ -2,7 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; ...@@ -2,7 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:unicons/unicons.dart'; import 'package:unicons/unicons.dart';
import 'package:solitaire/ui/widgets/settings/theme_card.dart'; import 'package:solitaire/ui/settings/theme_card.dart';
class SettingsForm extends StatefulWidget { class SettingsForm extends StatefulWidget {
const SettingsForm({super.key}); const SettingsForm({super.key});
......
...@@ -14,7 +14,7 @@ class SkeletonScreen extends StatelessWidget { ...@@ -14,7 +14,7 @@ class SkeletonScreen extends StatelessWidget {
appBar: const GlobalAppBar(), appBar: const GlobalAppBar(),
extendBodyBehindAppBar: false, extendBodyBehindAppBar: false,
body: Material( body: Material(
color: Theme.of(context).colorScheme.background, color: Theme.of(context).colorScheme.surface,
child: BlocBuilder<NavCubit, int>( child: BlocBuilder<NavCubit, int>(
builder: (BuildContext context, int pageIndex) { builder: (BuildContext context, int pageIndex) {
return Padding( return Padding(
...@@ -28,7 +28,7 @@ class SkeletonScreen extends StatelessWidget { ...@@ -28,7 +28,7 @@ class SkeletonScreen extends StatelessWidget {
}, },
), ),
), ),
backgroundColor: Theme.of(context).colorScheme.background, backgroundColor: Theme.of(context).colorScheme.surface,
); );
} }
} }
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:solitaire/cubit/game_cubit.dart';
class DeleteSavedGameButton extends StatelessWidget {
const DeleteSavedGameButton({super.key});
@override
Widget build(BuildContext context) {
return TextButton(
child: const Image(
image: AssetImage('assets/ui/button_delete_saved_game.png'),
fit: BoxFit.fill,
),
onPressed: () {
BlocProvider.of<GameCubit>(context).deleteSavedGame();
},
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:solitaire/cubit/game_cubit.dart';
class QuitGameButton extends StatelessWidget {
const QuitGameButton({super.key});
@override
Widget build(BuildContext context) {
return TextButton(
child: const Image(
image: AssetImage('assets/ui/button_back.png'),
fit: BoxFit.fill,
),
onPressed: () {
BlocProvider.of<GameCubit>(context).quitGame();
},
);
}
}
...@@ -14,17 +14,17 @@ class StartNewGameButton extends StatelessWidget { ...@@ -14,17 +14,17 @@ class StartNewGameButton extends StatelessWidget {
builder: (BuildContext context, GameSettingsState gameSettingsState) { builder: (BuildContext context, GameSettingsState gameSettingsState) {
return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>( return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>(
builder: (BuildContext context, GlobalSettingsState globalSettingsState) { builder: (BuildContext context, GlobalSettingsState globalSettingsState) {
final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
return TextButton( return TextButton(
child: const Image( child: const Image(
image: AssetImage('assets/icons/button_start.png'), image: AssetImage('assets/ui/button_start.png'),
fit: BoxFit.fill, fit: BoxFit.fill,
), ),
onPressed: () => gameCubit.startNewGame( onPressed: () {
BlocProvider.of<GameCubit>(context).startNewGame(
gameSettings: gameSettingsState.settings, gameSettings: gameSettingsState.settings,
globalSettings: globalSettingsState.settings, globalSettings: globalSettingsState.settings,
), );
},
); );
}, },
); );
......
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:solitaire/cubit/game_cubit.dart';
class ResumeSavedGameButton extends StatelessWidget {
const ResumeSavedGameButton({super.key});
@override
Widget build(BuildContext context) {
return TextButton(
child: const Image(
image: AssetImage('assets/ui/button_resume_game.png'),
fit: BoxFit.fill,
),
onPressed: () {
BlocProvider.of<GameCubit>(context).resumeSavedGame();
},
);
}
}
...@@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; ...@@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:solitaire/cubit/game_cubit.dart'; import 'package:solitaire/cubit/game_cubit.dart';
import 'package:solitaire/models/board.dart'; import 'package:solitaire/models/game/board.dart';
import 'package:solitaire/ui/widgets/game/tile_widget.dart'; import 'package:solitaire/ui/widgets/game/tile_widget.dart';
class Tileset extends StatelessWidget { class GameBoardWidget extends StatelessWidget {
const Tileset({super.key}); const GameBoardWidget({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
......
...@@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; ...@@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:solitaire/cubit/game_cubit.dart'; import 'package:solitaire/cubit/game_cubit.dart';
import 'package:solitaire/models/cell.dart'; import 'package:solitaire/models/game/cell.dart';
import 'package:solitaire/models/game.dart'; import 'package:solitaire/models/game/game.dart';
class TileWidget extends StatelessWidget { class TileWidget extends StatelessWidget {
const TileWidget({ const TileWidget({
...@@ -21,12 +21,10 @@ class TileWidget extends StatelessWidget { ...@@ -21,12 +21,10 @@ class TileWidget extends StatelessWidget {
builder: (BuildContext context, GameState gameState) { builder: (BuildContext context, GameState gameState) {
final Game currentGame = gameState.currentGame; final Game currentGame = gameState.currentGame;
final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
if (tile.hasHole) { if (tile.hasHole) {
return render( return render(
currentGame: currentGame, currentGame: currentGame,
gameCubit: gameCubit, gameCubit: BlocProvider.of<GameCubit>(context),
); );
} else { } else {
return Image( return Image(
......
...@@ -4,8 +4,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; ...@@ -4,8 +4,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:solitaire/config/menu.dart'; import 'package:solitaire/config/menu.dart';
import 'package:solitaire/cubit/game_cubit.dart'; import 'package:solitaire/cubit/game_cubit.dart';
import 'package:solitaire/cubit/nav_cubit.dart'; import 'package:solitaire/cubit/nav_cubit.dart';
import 'package:solitaire/models/game.dart'; import 'package:solitaire/models/game/game.dart';
import 'package:solitaire/ui/widgets/helpers/app_title.dart'; import 'package:solitaire/ui/helpers/app_titles.dart';
class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget { class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget {
const GlobalAppBar({super.key}); const GlobalAppBar({super.key});
...@@ -20,16 +20,15 @@ class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget { ...@@ -20,16 +20,15 @@ class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget {
final List<Widget> menuActions = []; final List<Widget> menuActions = [];
if (currentGame.isRunning) { if (currentGame.isRunning && !currentGame.isFinished) {
menuActions.add(TextButton( menuActions.add(TextButton(
child: const Image( child: const Image(
image: AssetImage('assets/icons/button_back.png'), image: AssetImage('assets/ui/button_back.png'),
fit: BoxFit.fill, fit: BoxFit.fill,
), ),
onPressed: () {}, onPressed: () {},
onLongPress: () { onLongPress: () {
final GameCubit gameCubit = BlocProvider.of<GameCubit>(context); BlocProvider.of<GameCubit>(context).quitGame();
gameCubit.quitGame();
}, },
)); ));
} else { } else {
...@@ -70,7 +69,7 @@ class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget { ...@@ -70,7 +69,7 @@ class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget {
} }
return AppBar( return AppBar(
title: const AppTitle(text: 'app_name'), title: const AppHeader(text: 'app_name'),
actions: menuActions, actions: menuActions,
); );
}, },
......
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
class AppHeader extends StatelessWidget {
const AppHeader({super.key, required this.text});
final String text;
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
tr(text),
textAlign: TextAlign.start,
style: Theme.of(context).textTheme.headlineSmall!.apply(fontWeightDelta: 2),
),
const SizedBox(height: 8),
],
);
}
}
import 'dart:ui';
extension ColorExtension on Color {
Color darken([int percent = 40]) {
assert(1 <= percent && percent <= 100);
final value = 1 - percent / 100;
return Color.fromARGB(
alpha,
(red * value).round(),
(green * value).round(),
(blue * value).round(),
);
}
Color lighten([int percent = 40]) {
assert(1 <= percent && percent <= 100);
final value = percent / 100;
return Color.fromARGB(
alpha,
(red + ((255 - red) * value)).round(),
(green + ((255 - green) * value)).round(),
(blue + ((255 - blue) * value)).round(),
);
}
Color avg(Color other) {
final red = (this.red + other.red) ~/ 2;
final green = (this.green + other.green) ~/ 2;
final blue = (this.blue + other.blue) ~/ 2;
final alpha = (this.alpha + other.alpha) ~/ 2;
return Color.fromARGB(alpha, red, green, blue);
}
}
...@@ -106,10 +106,10 @@ packages: ...@@ -106,10 +106,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_bloc name: flutter_bloc
sha256: f0ecf6e6eb955193ca60af2d5ca39565a86b8a142452c5b24d96fb477428f4d2 sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.1.5" version: "8.1.6"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
...@@ -164,10 +164,10 @@ packages: ...@@ -164,10 +164,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: intl name: intl
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.18.1" version: "0.19.0"
lints: lints:
dependency: transitive dependency: transitive
description: description:
...@@ -188,10 +188,10 @@ packages: ...@@ -188,10 +188,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.12.0"
nested: nested:
dependency: transitive dependency: transitive
description: description:
...@@ -236,10 +236,10 @@ packages: ...@@ -236,10 +236,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: path_provider_android name: path_provider_android
sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d sha256: "9c96da072b421e98183f9ea7464898428e764bc0ce5567f27ec8693442e72514"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.4" version: "2.2.5"
path_provider_foundation: path_provider_foundation:
dependency: transitive dependency: transitive
description: description:
...@@ -276,10 +276,10 @@ packages: ...@@ -276,10 +276,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: platform name: platform
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.4" version: "3.1.5"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
...@@ -308,10 +308,10 @@ packages: ...@@ -308,10 +308,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_android name: shared_preferences_android
sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2" sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.2" version: "2.2.3"
shared_preferences_foundation: shared_preferences_foundation:
dependency: transitive dependency: transitive
description: description:
...@@ -425,10 +425,10 @@ packages: ...@@ -425,10 +425,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.5.0" version: "5.5.1"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
...@@ -438,5 +438,5 @@ packages: ...@@ -438,5 +438,5 @@ packages:
source: hosted source: hosted
version: "1.0.4" version: "1.0.4"
sdks: sdks:
dart: ">=3.3.0 <4.0.0" dart: ">=3.4.0 <4.0.0"
flutter: ">=3.19.0" flutter: ">=3.22.0"
...@@ -3,7 +3,7 @@ description: Solitaire Game ...@@ -3,7 +3,7 @@ description: Solitaire Game
publish_to: "none" publish_to: "none"
version: 0.0.18+18 version: 0.1.0+19
environment: environment:
sdk: "^3.0.0" sdk: "^3.0.0"
...@@ -12,6 +12,7 @@ dependencies: ...@@ -12,6 +12,7 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
# base
easy_localization: ^3.0.1 easy_localization: ^3.0.1
equatable: ^2.0.5 equatable: ^2.0.5
flutter_bloc: ^8.1.1 flutter_bloc: ^8.1.1
...@@ -21,14 +22,17 @@ dependencies: ...@@ -21,14 +22,17 @@ dependencies:
path_provider: ^2.0.11 path_provider: ^2.0.11
unicons: ^2.1.1 unicons: ^2.1.1
# specific
# (none)
dev_dependencies: dev_dependencies:
flutter_lints: ^4.0.0 flutter_lints: ^4.0.0
flutter: flutter:
uses-material-design: true uses-material-design: true
assets: assets:
- assets/icons/
- assets/skins/ - assets/skins/
- assets/ui/
- assets/translations/ - assets/translations/
fonts: fonts:
...@@ -42,3 +46,4 @@ flutter: ...@@ -42,3 +46,4 @@ flutter:
weight: 400 weight: 400
- asset: assets/fonts/Nunito-Light.ttf - asset: assets/fonts/Nunito-Light.ttf
weight: 300 weight: 300
...@@ -6,7 +6,7 @@ command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not ins ...@@ -6,7 +6,7 @@ command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not ins
command -v optipng >/dev/null 2>&1 || { echo >&2 "I require optipng but it's not installed. Aborting."; exit 1; } command -v optipng >/dev/null 2>&1 || { echo >&2 "I require optipng but it's not installed. Aborting."; exit 1; }
CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
BASE_DIR="$(dirname "${CURRENT_DIR}")" BASE_DIR="$(dirname "$(dirname "${CURRENT_DIR}")")"
SOURCE_ICON="${CURRENT_DIR}/icon.svg" SOURCE_ICON="${CURRENT_DIR}/icon.svg"
SOURCE_FASTLANE="${CURRENT_DIR}/featureGraphic.svg" SOURCE_FASTLANE="${CURRENT_DIR}/featureGraphic.svg"
......
File moved
File moved
#! /bin/bash
CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
${CURRENT_DIR}/app/build_application_resources.sh
${CURRENT_DIR}/ui/build_ui_resources.sh
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment