diff --git a/assets/translations/en.json b/assets/translations/en.json index 7c987554a0784ef90fd559894b614c4f8855a791..39acf877f4d1cd69c8d73f4702c895e0af421258 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -1,6 +1,9 @@ { "app_name": "Jigsaw puzzle", + "page_home": "Home", + "page_game": "Game", + "settings_title": "Settings", "settings_label_theme": "Theme mode", diff --git a/assets/translations/fr.json b/assets/translations/fr.json index 70581d6a87ccf7a95f62d1abfefe721f0f7b52ca..e567f39a4c9582bf2f207498623f7b5597a33e3f 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -1,6 +1,9 @@ { "app_name": "Puzzle", + "page_home": "Accueil", + "page_game": "Jeu", + "settings_title": "Réglages", "settings_label_theme": "Thème de couleurs", diff --git a/fastlane/metadata/android/en-US/changelogs/68.txt b/fastlane/metadata/android/en-US/changelogs/68.txt new file mode 100644 index 0000000000000000000000000000000000000000..ac2c90e221caf3cf01ebba168fb16a8f31c0253a --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/68.txt @@ -0,0 +1 @@ +Normalize Activity application architecture. diff --git a/fastlane/metadata/android/fr-FR/changelogs/68.txt b/fastlane/metadata/android/fr-FR/changelogs/68.txt new file mode 100644 index 0000000000000000000000000000000000000000..1d6843d89ba84ea4147528bc3d62d9ecf1a4a762 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/68.txt @@ -0,0 +1 @@ +Harmonisation des applications en Activity. diff --git a/lib/common/config/activity_page.dart b/lib/common/config/activity_page.dart new file mode 100644 index 0000000000000000000000000000000000000000..f27238f502b2b6a3d89f98a6d48ba1a30f540cf8 --- /dev/null +++ b/lib/common/config/activity_page.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; + +import 'package:puzzlegame/common/ui/pages/game.dart'; +import 'package:puzzlegame/common/ui/pages/parameters.dart'; + +class ActivityPageItem { + final String code; + final Icon icon; + final Widget page; + + const ActivityPageItem({ + required this.code, + required this.icon, + required this.page, + }); +} + +class ActivityPage { + static const bool displayBottomNavBar = false; + + static const indexHome = 0; + static const pageHome = ActivityPageItem( + code: 'page_home', + icon: Icon(UniconsLine.home), + page: PageParameters(), + ); + + static const indexGame = 1; + static const pageGame = ActivityPageItem( + code: 'page_game', + icon: Icon(UniconsLine.star), + page: PageGame(), + ); + + static const Map<int, ActivityPageItem> items = { + indexHome: pageHome, + indexGame: pageGame, + }; + + static int defaultPageIndex = indexHome; + + static bool isIndexAllowed(int pageIndex) { + return items.keys.contains(pageIndex); + } + + static Widget getWidget(int pageIndex) { + return items[pageIndex]?.page ?? pageHome.page; + } +} diff --git a/lib/common/config/screen.dart b/lib/common/config/screen.dart new file mode 100644 index 0000000000000000000000000000000000000000..459e8280a3ecb6d3ec79a20baf4ce7eab9e1a9ee --- /dev/null +++ b/lib/common/config/screen.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; + +import 'package:puzzlegame/common/ui/screens/about.dart'; +import 'package:puzzlegame/common/ui/screens/activity.dart'; +import 'package:puzzlegame/common/ui/screens/settings.dart'; + +class ScreenItem { + final String code; + final Icon icon; + final Widget screen; + + const ScreenItem({ + required this.code, + required this.icon, + required this.screen, + }); +} + +class Screen { + static const indexActivity = 0; + static const screenActivity = ScreenItem( + code: 'screen_activity', + icon: Icon(UniconsLine.home), + screen: ScreenActivity(), + ); + + static const indexSettings = 1; + static const screenSettings = ScreenItem( + code: 'screen_settings', + icon: Icon(UniconsLine.setting), + screen: ScreenSettings(), + ); + + static const indexAbout = 2; + static const screenAbout = ScreenItem( + code: 'screen_about', + icon: Icon(UniconsLine.info_circle), + screen: ScreenAbout(), + ); + + static Map<int, ScreenItem> items = { + indexActivity: screenActivity, + indexSettings: screenSettings, + indexAbout: screenAbout, + }; + + static bool isIndexAllowed(int screenIndex) { + return items.keys.contains(screenIndex); + } + + static Widget getWidget(int screenIndex) { + return items[screenIndex]?.screen ?? screenActivity.screen; + } +} diff --git a/lib/common/cubit/nav/nav_cubit_pages.dart b/lib/common/cubit/nav/nav_cubit_pages.dart new file mode 100644 index 0000000000000000000000000000000000000000..20b21cd2c596ae235982fea5caabea9fcb1db5c9 --- /dev/null +++ b/lib/common/cubit/nav/nav_cubit_pages.dart @@ -0,0 +1,33 @@ +import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; + +import 'package:puzzlegame/common/config/activity_page.dart'; + +class NavCubitPage extends HydratedCubit<int> { + NavCubitPage() : super(0); + + void updateIndex(int index) { + if (ActivityPage.isIndexAllowed(index)) { + emit(index); + } else { + emit(ActivityPage.indexHome); + } + } + + void goToPageHome() { + updateIndex(ActivityPage.indexHome); + } + + void goToPageGame() { + updateIndex(ActivityPage.indexGame); + } + + @override + int fromJson(Map<String, dynamic> json) { + return ActivityPage.indexHome; + } + + @override + Map<String, dynamic>? toJson(int state) { + return <String, int>{'index': state}; + } +} diff --git a/lib/common/cubit/nav/nav_cubit_screens.dart b/lib/common/cubit/nav/nav_cubit_screens.dart new file mode 100644 index 0000000000000000000000000000000000000000..b65d03f362fd3064aae4a47f1b92d209311ea90d --- /dev/null +++ b/lib/common/cubit/nav/nav_cubit_screens.dart @@ -0,0 +1,37 @@ +import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; + +import 'package:puzzlegame/common/config/screen.dart'; + +class NavCubitScreen extends HydratedCubit<int> { + NavCubitScreen() : super(0); + + void updateIndex(int index) { + if (Screen.isIndexAllowed(index)) { + emit(index); + } else { + goToScreenActivity(); + } + } + + void goToScreenActivity() { + emit(Screen.indexActivity); + } + + void goToScreenSettings() { + emit(Screen.indexSettings); + } + + void goToScreenAbout() { + emit(Screen.indexAbout); + } + + @override + int fromJson(Map<String, dynamic> json) { + return Screen.indexActivity; + } + + @override + Map<String, dynamic>? toJson(int state) { + return <String, int>{'index': state}; + } +} diff --git a/lib/common/ui/nav/bottom_nav_bar.dart b/lib/common/ui/nav/bottom_nav_bar.dart new file mode 100644 index 0000000000000000000000000000000000000000..1cf151cd2be5191759d1ef99a53a61a2355bde41 --- /dev/null +++ b/lib/common/ui/nav/bottom_nav_bar.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; + +import 'package:puzzlegame/common/config/activity_page.dart'; +import 'package:puzzlegame/common/cubit/nav/nav_cubit_pages.dart'; + +class BottomNavBar extends StatelessWidget { + const BottomNavBar({super.key}); + + @override + Widget build(BuildContext context) { + return Card( + margin: const EdgeInsets.only(top: 1, right: 4, left: 4), + elevation: 4, + shadowColor: Theme.of(context).colorScheme.shadow, + color: Theme.of(context).colorScheme.surfaceContainerHighest, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), + ), + ), + child: BlocBuilder<NavCubitPage, int>(builder: (BuildContext context, int state) { + final List<BottomNavigationBarItem> items = []; + + ActivityPage.items.forEach((int pageIndex, ActivityPageItem item) { + items.add(BottomNavigationBarItem( + icon: item.icon, + label: tr(item.code), + )); + }); + + return BottomNavigationBar( + currentIndex: state, + onTap: (int index) => BlocProvider.of<NavCubitPage>(context).updateIndex(index), + type: BottomNavigationBarType.fixed, + elevation: 0, + backgroundColor: Colors.transparent, + selectedItemColor: Theme.of(context).colorScheme.primary, + unselectedItemColor: Theme.of(context).textTheme.bodySmall!.color, + items: items, + ); + }), + ); + } +} diff --git a/lib/ui/widgets/global_app_bar.dart b/lib/common/ui/nav/global_app_bar.dart similarity index 60% rename from lib/ui/widgets/global_app_bar.dart rename to lib/common/ui/nav/global_app_bar.dart index 108b301d03e5eec02d914d2f1240dee85a441a76..d7589f2550b8cba2b30181b0f89d28dd53e6eb60 100644 --- a/lib/ui/widgets/global_app_bar.dart +++ b/lib/common/ui/nav/global_app_bar.dart @@ -1,30 +1,33 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/config/menu.dart'; -import 'package:puzzlegame/cubit/game_cubit.dart'; -import 'package:puzzlegame/cubit/nav_cubit.dart'; -import 'package:puzzlegame/models/game/game.dart'; +import 'package:puzzlegame/common/config/screen.dart'; +import 'package:puzzlegame/common/cubit/nav/nav_cubit_pages.dart'; +import 'package:puzzlegame/common/cubit/nav/nav_cubit_screens.dart'; + +import 'package:puzzlegame/cubit/activity/activity_cubit.dart'; +import 'package:puzzlegame/models/activity/activity.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>( + return BlocBuilder<ActivityCubit, ActivityState>( + builder: (BuildContext context, ActivityState activityState) { + return BlocBuilder<NavCubitScreen, int>( builder: (BuildContext context, int pageIndex) { - final Game currentGame = gameState.currentGame; + final Activity currentActivity = activityState.currentActivity; final List<Widget> menuActions = []; - if (currentGame.isRunning && !currentGame.isFinished) { + if (currentActivity.isRunning && !currentActivity.isFinished) { menuActions.add(StyledButton( color: Colors.red, onPressed: () {}, onLongPress: () { - BlocProvider.of<GameCubit>(context).quitGame(); + BlocProvider.of<ActivityCubit>(context).quitActivity(); + BlocProvider.of<NavCubitPage>(context).goToPageHome(); }, child: const Image( image: AssetImage('assets/ui/button_back.png'), @@ -32,38 +35,38 @@ class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget { ), )); } else { - if (pageIndex == Menu.indexGame) { + if (pageIndex == Screen.indexActivity) { // go to Settings page menuActions.add(ElevatedButton( onPressed: () { - BlocProvider.of<NavCubit>(context).goToSettingsPage(); + BlocProvider.of<NavCubitScreen>(context).goToScreenSettings(); }, style: ElevatedButton.styleFrom( shape: const CircleBorder(), ), - child: Menu.menuItemSettings.icon, + child: Screen.screenSettings.icon, )); // go to About page menuActions.add(ElevatedButton( onPressed: () { - BlocProvider.of<NavCubit>(context).goToAboutPage(); + BlocProvider.of<NavCubitScreen>(context).goToScreenAbout(); }, style: ElevatedButton.styleFrom( shape: const CircleBorder(), ), - child: Menu.menuItemAbout.icon, + child: Screen.screenAbout.icon, )); } else { // back to Home page menuActions.add(ElevatedButton( onPressed: () { - BlocProvider.of<NavCubit>(context).goToGamePage(); + BlocProvider.of<NavCubitScreen>(context).goToScreenActivity(); }, style: ElevatedButton.styleFrom( shape: const CircleBorder(), ), - child: Menu.menuItemGame.icon, + child: Screen.screenActivity.icon, )); } } diff --git a/lib/ui/layouts/game_layout.dart b/lib/common/ui/pages/game.dart similarity index 66% rename from lib/ui/layouts/game_layout.dart rename to lib/common/ui/pages/game.dart index 81c2ad4e221d08e2109ac60146c6aa3718ef72a4..0ea1c315a05aaace0975bbefe713db930c3afc89 100644 --- a/lib/ui/layouts/game_layout.dart +++ b/lib/common/ui/pages/game.dart @@ -1,21 +1,21 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/cubit/game_cubit.dart'; -import 'package:puzzlegame/models/game/game.dart'; +import 'package:puzzlegame/cubit/activity/activity_cubit.dart'; +import 'package:puzzlegame/models/activity/activity.dart'; import 'package:puzzlegame/ui/game/game_bottom.dart'; import 'package:puzzlegame/ui/game/game_end.dart'; import 'package:puzzlegame/ui/game/game_top.dart'; import 'package:puzzlegame/ui/widgets/game/game_board.dart'; -class GameLayout extends StatelessWidget { - const GameLayout({super.key}); +class PageGame extends StatelessWidget { + const PageGame({super.key}); @override Widget build(BuildContext context) { - return BlocBuilder<GameCubit, GameState>( - builder: (BuildContext context, GameState gameState) { - final Game currentGame = gameState.currentGame; + return BlocBuilder<ActivityCubit, ActivityState>( + builder: (BuildContext context, ActivityState activityState) { + final Activity currentActivity = activityState.currentActivity; return Container( alignment: AlignmentDirectional.topCenter, @@ -30,7 +30,7 @@ class GameLayout extends StatelessWidget { const SizedBox(height: 8), const GameBottomWidget(), const Expanded(child: SizedBox.shrink()), - currentGame.isFinished ? const GameEndWidget() : const SizedBox.shrink(), + currentActivity.isFinished ? const GameEndWidget() : const SizedBox.shrink(), ], ), ); diff --git a/lib/common/ui/pages/parameters.dart b/lib/common/ui/pages/parameters.dart new file mode 100644 index 0000000000000000000000000000000000000000..4f76a4f5959ccca45362cda2e3e0ce5375709738 --- /dev/null +++ b/lib/common/ui/pages/parameters.dart @@ -0,0 +1,148 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; + +import 'package:puzzlegame/common/ui/parameters/parameter_widget.dart'; + +import 'package:puzzlegame/config/default_activity_settings.dart'; +import 'package:puzzlegame/config/default_global_settings.dart'; +import 'package:puzzlegame/cubit/activity/activity_cubit.dart'; +import 'package:puzzlegame/cubit/settings/settings_activity_cubit.dart'; +import 'package:puzzlegame/cubit/settings/settings_global_cubit.dart'; +import 'package:puzzlegame/models/activity/activity.dart'; +import 'package:puzzlegame/ui/widgets/actions/button_delete_saved_game.dart'; +import 'package:puzzlegame/ui/widgets/actions/button_game_start_new.dart'; +import 'package:puzzlegame/ui/widgets/actions/button_resume_saved_game.dart'; + +class PageParameters extends StatelessWidget { + const PageParameters({super.key}); + + final double separatorHeight = 8.0; + + @override + Widget build(BuildContext context) { + return BlocBuilder<ActivityCubit, ActivityState>( + builder: (BuildContext context, ActivityState activityState) { + final Activity currentActivity = activityState.currentActivity; + + final List<Widget> lines = []; + + // Game settings + for (String code in DefaultActivitySettings.availableParameters) { + lines.add(Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: buildParametersLine( + code: code, + isGlobal: false, + ), + )); + + lines.add(SizedBox(height: separatorHeight)); + } + + lines.add(Expanded( + child: SizedBox(height: separatorHeight), + )); + + if (currentActivity.canBeResumed == false) { + // Start new game + lines.add( + const AspectRatio( + aspectRatio: 3, + child: StartNewGameButton(), + ), + ); + } else { + // Resume game + lines.add(const AspectRatio( + aspectRatio: 3, + child: ResumeSavedGameButton(), + )); + // Delete saved game + lines.add(SizedBox.square( + dimension: MediaQuery.of(context).size.width / 5, + child: const DeleteSavedGameButton(), + )); + } + + 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) + : DefaultActivitySettings.getAvailableValues(code); + + if (availableValues.length <= 1) { + return []; + } + + for (String value in availableValues) { + final Widget parameterButton = BlocBuilder<ActivitySettingsCubit, ActivitySettingsState>( + builder: (BuildContext context, ActivitySettingsState activitySettingsState) { + return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>( + builder: (BuildContext context, GlobalSettingsState globalSettingsState) { + final ActivitySettingsCubit activitySettingsCubit = + BlocProvider.of<ActivitySettingsCubit>(context); + final GlobalSettingsCubit globalSettingsCubit = + BlocProvider.of<GlobalSettingsCubit>(context); + + final String currentValue = isGlobal + ? globalSettingsCubit.getParameterValue(code) + : activitySettingsCubit.getParameterValue(code); + + final bool isSelected = (value == currentValue); + + final double displayWidth = MediaQuery.of(context).size.width; + final double itemWidth = displayWidth / availableValues.length - 4; + + return SizedBox.square( + dimension: itemWidth, + child: ParameterWidget( + code: code, + value: value, + isSelected: isSelected, + size: itemWidth, + activitySettings: activitySettingsState.settings, + globalSettings: globalSettingsState.settings, + onPressed: () { + isGlobal + ? globalSettingsCubit.setParameterValue(code, value) + : activitySettingsCubit.setParameterValue(code, value); + }, + ), + ); + }, + ); + }, + ); + + parameterButtons.add(parameterButton); + } + + return parameterButtons; + } +} diff --git a/lib/ui/parameters/parameter_painter.dart b/lib/common/ui/parameters/parameter_painter.dart similarity index 91% rename from lib/ui/parameters/parameter_painter.dart rename to lib/common/ui/parameters/parameter_painter.dart index abd8f42c4304e12c90ae6a05d103b8026ecccf87..015eb0143a693e88f5073123f9f332afcd6f172d 100644 --- a/lib/ui/parameters/parameter_painter.dart +++ b/lib/common/ui/parameters/parameter_painter.dart @@ -3,21 +3,21 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/config/default_game_settings.dart'; -import 'package:puzzlegame/models/settings/settings_game.dart'; +import 'package:puzzlegame/config/default_activity_settings.dart'; +import 'package:puzzlegame/models/settings/settings_activity.dart'; import 'package:puzzlegame/models/settings/settings_global.dart'; class ParameterPainter extends CustomPainter { const ParameterPainter({ required this.code, required this.value, - required this.gameSettings, + required this.activitySettings, required this.globalSettings, }); final String code; final String value; - final GameSettings gameSettings; + final ActivitySettings activitySettings; final GlobalSettings globalSettings; @override @@ -27,7 +27,7 @@ class ParameterPainter extends CustomPainter { // content switch (code) { - case DefaultGameSettings.parameterCodeTilesetSize: + case DefaultActivitySettings.parameterCodeTilesetSize: paintTilesetSizeParameterItem(canvas, canvasSize); break; default: diff --git a/lib/ui/parameters/parameter_widget.dart b/lib/common/ui/parameters/parameter_widget.dart similarity index 83% rename from lib/ui/parameters/parameter_widget.dart rename to lib/common/ui/parameters/parameter_widget.dart index 08c7afa63c92fe3fe9100df40a682fed694b537c..0202092ed0679056b39cdfb4c12dc942e8363322 100644 --- a/lib/ui/parameters/parameter_widget.dart +++ b/lib/common/ui/parameters/parameter_widget.dart @@ -1,11 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/config/default_game_settings.dart'; +import 'package:puzzlegame/common/ui/parameters/parameter_painter.dart'; + +import 'package:puzzlegame/config/default_activity_settings.dart'; import 'package:puzzlegame/config/default_global_settings.dart'; -import 'package:puzzlegame/models/settings/settings_game.dart'; +import 'package:puzzlegame/models/settings/settings_activity.dart'; import 'package:puzzlegame/models/settings/settings_global.dart'; -import 'package:puzzlegame/ui/parameters/parameter_painter.dart'; class ParameterWidget extends StatelessWidget { const ParameterWidget({ @@ -14,7 +15,7 @@ class ParameterWidget extends StatelessWidget { required this.value, required this.isSelected, required this.size, - required this.gameSettings, + required this.activitySettings, required this.globalSettings, required this.onPressed, }); @@ -23,7 +24,7 @@ class ParameterWidget extends StatelessWidget { final String value; final bool isSelected; final double size; - final GameSettings gameSettings; + final ActivitySettings activitySettings; final GlobalSettings globalSettings; final VoidCallback onPressed; @@ -37,7 +38,7 @@ class ParameterWidget extends StatelessWidget { Widget content = const SizedBox.shrink(); switch (code) { - case DefaultGameSettings.parameterCodeTilesetSize: + case DefaultActivitySettings.parameterCodeTilesetSize: content = getTilesetSizeParameterItem(); break; case DefaultGlobalSettings.parameterCodeSkin: @@ -76,13 +77,13 @@ class ParameterWidget extends StatelessWidget { Color backgroundColor = Colors.grey; switch (value) { - case DefaultGameSettings.tilesetSizeValueSmall: + case DefaultActivitySettings.tilesetSizeValueSmall: backgroundColor = Colors.green; break; - case DefaultGameSettings.tilesetSizeValueMedium: + case DefaultActivitySettings.tilesetSizeValueMedium: backgroundColor = Colors.orange; break; - case DefaultGameSettings.tilesetSizeValueLarge: + case DefaultActivitySettings.tilesetSizeValueLarge: backgroundColor = Colors.red; break; default: @@ -98,7 +99,7 @@ class ParameterWidget extends StatelessWidget { painter: ParameterPainter( code: code, value: value, - gameSettings: gameSettings, + activitySettings: activitySettings, globalSettings: globalSettings, ), isComplex: true, diff --git a/lib/ui/screens/page_about.dart b/lib/common/ui/screens/about.dart similarity index 93% rename from lib/ui/screens/page_about.dart rename to lib/common/ui/screens/about.dart index ab73e304b8138295f6669330b928e0d1d0263565..f7a14a9a7e574a7b6f9ed181f38d08ba8c2285fe 100644 --- a/lib/ui/screens/page_about.dart +++ b/lib/common/ui/screens/about.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -class PageAbout extends StatelessWidget { - const PageAbout({super.key}); +class ScreenAbout extends StatelessWidget { + const ScreenAbout({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/common/ui/screens/activity.dart b/lib/common/ui/screens/activity.dart new file mode 100644 index 0000000000000000000000000000000000000000..1f1fcd1e56d2710cf068b917ea5b2b6c93283a83 --- /dev/null +++ b/lib/common/ui/screens/activity.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; + +import 'package:puzzlegame/common/config/activity_page.dart'; +import 'package:puzzlegame/common/cubit/nav/nav_cubit_pages.dart'; + +class ScreenActivity extends StatelessWidget { + const ScreenActivity({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder<NavCubitPage, int>( + builder: (BuildContext context, int pageIndex) { + return ActivityPage.getWidget(pageIndex); + }, + ); + } +} diff --git a/lib/ui/screens/page_settings.dart b/lib/common/ui/screens/settings.dart similarity index 87% rename from lib/ui/screens/page_settings.dart rename to lib/common/ui/screens/settings.dart index 50964ef462b6e3e411347cab4e966afb0f505008..7981b1c6becc4f5b925c3058b05423750d80f62e 100644 --- a/lib/ui/screens/page_settings.dart +++ b/lib/common/ui/screens/settings.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -class PageSettings extends StatelessWidget { - const PageSettings({super.key}); +class ScreenSettings extends StatelessWidget { + const ScreenSettings({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/config/application_config.dart b/lib/config/application_config.dart new file mode 100644 index 0000000000000000000000000000000000000000..c803b3cbd1ae639cae89be9cc5cc1848ba59cdb3 --- /dev/null +++ b/lib/config/application_config.dart @@ -0,0 +1,3 @@ +class ApplicationConfig { + static const String appTitle = 'Puzzle'; +} diff --git a/lib/config/default_game_settings.dart b/lib/config/default_activity_settings.dart similarity index 89% rename from lib/config/default_game_settings.dart rename to lib/config/default_activity_settings.dart index 46d85ed209a0741cebdde6f45555a0364727a9bf..f0c25632edadac6fd73a337b64aebcb91736e5be 100644 --- a/lib/config/default_game_settings.dart +++ b/lib/config/default_activity_settings.dart @@ -1,6 +1,6 @@ import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -class DefaultGameSettings { +class DefaultActivitySettings { // available game parameters codes static const String parameterCodeTilesetSize = 'tilesetSize'; static const String parameterCodeImageName = 'imageName'; @@ -33,9 +33,9 @@ class DefaultGameSettings { static List<String> getAvailableValues(String parameterCode) { switch (parameterCode) { case parameterCodeTilesetSize: - return DefaultGameSettings.allowedTilesetSizeValues; + return DefaultActivitySettings.allowedTilesetSizeValues; case parameterCodeImageName: - return DefaultGameSettings.allowedImageNameS; + return DefaultActivitySettings.allowedImageNameS; } printlog('Did not find any available value for game parameter "$parameterCode".'); diff --git a/lib/config/menu.dart b/lib/config/menu.dart deleted file mode 100644 index 93caa82c2242f177b38a6990764c01aa4621e174..0000000000000000000000000000000000000000 --- a/lib/config/menu.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; - -import 'package:puzzlegame/ui/screens/page_about.dart'; -import 'package:puzzlegame/ui/screens/page_game.dart'; -import 'package:puzzlegame/ui/screens/page_settings.dart'; - -class MenuItem { - final Icon icon; - final Widget page; - - const MenuItem({ - required this.icon, - required this.page, - }); -} - -class Menu { - static const indexGame = 0; - static const menuItemGame = MenuItem( - icon: Icon(UniconsLine.home), - page: PageGame(), - ); - - static const indexSettings = 1; - static const menuItemSettings = MenuItem( - icon: Icon(UniconsLine.setting), - page: PageSettings(), - ); - - static const indexAbout = 2; - static const menuItemAbout = MenuItem( - icon: Icon(UniconsLine.info_circle), - page: PageAbout(), - ); - - static Map<int, MenuItem> items = { - indexGame: menuItemGame, - indexSettings: menuItemSettings, - indexAbout: menuItemAbout, - }; - - static bool isIndexAllowed(int pageIndex) { - return items.keys.contains(pageIndex); - } - - static Widget getPageWidget(int pageIndex) { - return items[pageIndex]?.page ?? menuItemGame.page; - } - - static int itemsCount = Menu.items.length; -} diff --git a/lib/cubit/activity/activity_cubit.dart b/lib/cubit/activity/activity_cubit.dart new file mode 100644 index 0000000000000000000000000000000000000000..f527ca9138a7453eb9b869cb08aa003570d5508d --- /dev/null +++ b/lib/cubit/activity/activity_cubit.dart @@ -0,0 +1,243 @@ +import 'dart:async'; +import 'dart:math'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show rootBundle; +import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; +import 'package:image/image.dart' as imglib; + +import 'package:puzzlegame/models/activity/activity.dart'; +import 'package:puzzlegame/models/activity/moving_tile.dart'; +import 'package:puzzlegame/models/settings/settings_activity.dart'; +import 'package:puzzlegame/models/settings/settings_global.dart'; + +part 'activity_state.dart'; + +class ActivityCubit extends HydratedCubit<ActivityState> { + ActivityCubit() + : super(ActivityState( + currentActivity: Activity.createEmpty(), + )); + + void updateState(Activity activity) { + emit(ActivityState( + currentActivity: activity, + )); + } + + void refresh() { + final Activity activity = Activity( + // Settings + activitySettings: state.currentActivity.activitySettings, + globalSettings: state.currentActivity.globalSettings, + // State + isRunning: state.currentActivity.isRunning, + isStarted: state.currentActivity.isStarted, + isFinished: state.currentActivity.isFinished, + animationInProgress: state.currentActivity.animationInProgress, + shufflingInProgress: state.currentActivity.shufflingInProgress, + // Base data + image: state.currentActivity.image, + tiles: state.currentActivity.tiles, + // Game data + movesCount: state.currentActivity.movesCount, + displayTip: state.currentActivity.displayTip, + tileSize: state.currentActivity.tileSize, + ); + // game.dump(); + + updateState(activity); + } + + void startNewActivity({ + required ActivitySettings activitySettings, + required GlobalSettings globalSettings, + }) { + final Activity newActivity = Activity.createNew( + // Settings + activitySettings: activitySettings, + globalSettings: globalSettings, + ); + + newActivity.dump(); + + updateState(newActivity); + refresh(); + + state.currentActivity.isRunning = true; + state.currentActivity.shufflingInProgress = true; + refresh(); + + Timer(const Duration(seconds: 1), () { + splitImageInTiles(); + }); + } + + void quitActivity() { + state.currentActivity.isRunning = false; + refresh(); + } + + void resumeSavedActivity() { + state.currentActivity.isRunning = true; + refresh(); + } + + void deleteSavedActivity() { + state.currentActivity.isRunning = false; + state.currentActivity.isFinished = true; + refresh(); + } + + void updateTileSize(double tileSize) { + if (tileSize != state.currentActivity.tileSize) { + state.currentActivity.tileSize = tileSize; + for (var i = 0; i < state.currentActivity.tiles.length; i++) { + state.currentActivity.tiles[i].size = tileSize; + } + refresh(); + } + } + + void toggleDisplayTipImage() { + state.currentActivity.displayTip = !state.currentActivity.displayTip; + refresh(); + } + + void incrementMovesCount() { + state.currentActivity.movesCount++; + refresh(); + } + + bool checkTilesetIsCleared() { + for (MovingTile tile in state.currentActivity.tiles) { + if (!tile.isCorrect()) { + return false; + } + } + return true; + } + + void swapTiles(List<int> tile1, List<int> tile2) { + state.currentActivity.isStarted = true; + + final int indexTile1 = state.currentActivity.tiles.indexWhere( + (tile) => ((tile.currentCol == tile1[0]) && (tile.currentRow == tile1[1]))); + final int indexTile2 = state.currentActivity.tiles.indexWhere( + (tile) => ((tile.currentCol == tile2[0]) && (tile.currentRow == tile2[1]))); + + final MovingTile swap = state.currentActivity.tiles[indexTile1]; + state.currentActivity.tiles[indexTile1] = state.currentActivity.tiles[indexTile2]; + state.currentActivity.tiles[indexTile2] = swap; + + final int swapCol = state.currentActivity.tiles[indexTile1].currentCol; + state.currentActivity.tiles[indexTile1].currentCol = + state.currentActivity.tiles[indexTile2].currentCol; + state.currentActivity.tiles[indexTile2].currentCol = swapCol; + + final int swapRow = state.currentActivity.tiles[indexTile1].currentRow; + state.currentActivity.tiles[indexTile1].currentRow = + state.currentActivity.tiles[indexTile2].currentRow; + state.currentActivity.tiles[indexTile2].currentRow = swapRow; + + incrementMovesCount(); + if (checkTilesetIsCleared()) { + state.currentActivity.isFinished = true; + } + + refresh(); + } + + Future<void> splitImageInTiles() async { + final String imageAsset = 'assets/images/${state.currentActivity.image}.png'; + final Uint8List imageData = (await rootBundle.load(imageAsset)).buffer.asUint8List(); + + final int tilesCount = state.currentActivity.activitySettings.tilesetSizeValue; + + final imglib.Image image = imglib.decodeImage(imageData) ?? + imglib.Image.fromBytes( + height: 1, + width: 1, + bytes: Uint8List.fromList([]).buffer, + ); + + int x = 0, y = 0; + final int width = (image.width / tilesCount).round(); + final int height = (image.height / tilesCount).round(); + + final List<MovingTile> tiles = []; + for (int i = 0; i < tilesCount; i++) { + for (int j = 0; j < tilesCount; j++) { + final Uint8List tileData = Uint8List.fromList(imglib.encodeJpg(imglib.copyCrop( + image, + x: x, + y: y, + width: width, + height: height, + ))); + + tiles.add(MovingTile( + currentCol: j, + currentRow: i, + image: Image.memory(tileData), + size: state.currentActivity.tileSize, + originalCol: j, + originalRow: i, + )); + + x += width; + } + x = 0; + y += height; + } + + state.currentActivity.tiles = tiles; + shuffleTiles(); + + state.currentActivity.shufflingInProgress = false; + refresh(); + } + + void shuffleTiles() { + final Random random = Random(); + + final List<MovingTile> tiles = state.currentActivity.tiles; + final int tilesCount = tiles.length; + + for (int i = 0; i < (10 * tilesCount); i++) { + final int indexTile1 = random.nextInt(tilesCount); + final int indexTile2 = random.nextInt(tilesCount); + + final MovingTile swap = tiles[indexTile1]; + tiles[indexTile1] = tiles[indexTile2]; + tiles[indexTile2] = swap; + + final int swapCol = tiles[indexTile1].currentCol; + tiles[indexTile1].currentCol = tiles[indexTile2].currentCol; + tiles[indexTile2].currentCol = swapCol; + + final int swapRow = tiles[indexTile1].currentRow; + tiles[indexTile1].currentRow = tiles[indexTile2].currentRow; + tiles[indexTile2].currentRow = swapRow; + } + + state.currentActivity.tiles = tiles; + } + + @override + ActivityState? fromJson(Map<String, dynamic> json) { + final Activity currentActivity = json['currentActivity'] as Activity; + + return ActivityState( + currentActivity: currentActivity, + ); + } + + @override + Map<String, dynamic>? toJson(ActivityState state) { + return <String, dynamic>{ + 'currentActivity': state.currentActivity.toJson(), + }; + } +} diff --git a/lib/cubit/activity/activity_state.dart b/lib/cubit/activity/activity_state.dart new file mode 100644 index 0000000000000000000000000000000000000000..887b45e4255fd7de1cc7744569d82a38a66602f2 --- /dev/null +++ b/lib/cubit/activity/activity_state.dart @@ -0,0 +1,15 @@ +part of 'activity_cubit.dart'; + +@immutable +class ActivityState extends Equatable { + const ActivityState({ + required this.currentActivity, + }); + + final Activity currentActivity; + + @override + List<dynamic> get props => <dynamic>[ + currentActivity, + ]; +} diff --git a/lib/cubit/game_cubit.dart b/lib/cubit/game_cubit.dart deleted file mode 100644 index 885595a2ef01f77c5be20678ad09c7f64ae6978b..0000000000000000000000000000000000000000 --- a/lib/cubit/game_cubit.dart +++ /dev/null @@ -1,243 +0,0 @@ -import 'dart:async'; -import 'dart:math'; -import 'dart:typed_data'; - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart' show rootBundle; -import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:image/image.dart' as imglib; - -import 'package:puzzlegame/models/game/game.dart'; -import 'package:puzzlegame/models/game/moving_tile.dart'; -import 'package:puzzlegame/models/settings/settings_game.dart'; -import 'package:puzzlegame/models/settings/settings_global.dart'; - -part 'game_state.dart'; - -class GameCubit extends HydratedCubit<GameState> { - GameCubit() - : super(GameState( - currentGame: Game.createEmpty(), - )); - - void updateState(Game game) { - emit(GameState( - currentGame: game, - )); - } - - void refresh() { - final Game game = Game( - // Settings - gameSettings: state.currentGame.gameSettings, - globalSettings: state.currentGame.globalSettings, - // State - isRunning: state.currentGame.isRunning, - isStarted: state.currentGame.isStarted, - isFinished: state.currentGame.isFinished, - animationInProgress: state.currentGame.animationInProgress, - shufflingInProgress: state.currentGame.shufflingInProgress, - // Base data - image: state.currentGame.image, - tiles: state.currentGame.tiles, - // Game data - movesCount: state.currentGame.movesCount, - displayTip: state.currentGame.displayTip, - tileSize: state.currentGame.tileSize, - ); - // game.dump(); - - updateState(game); - } - - void startNewGame({ - required GameSettings gameSettings, - required GlobalSettings globalSettings, - }) { - final Game newGame = Game.createNew( - // Settings - gameSettings: gameSettings, - globalSettings: globalSettings, - ); - - newGame.dump(); - - updateState(newGame); - refresh(); - - state.currentGame.isRunning = true; - state.currentGame.shufflingInProgress = true; - refresh(); - - Timer(const Duration(seconds: 1), () { - splitImageInTiles(); - }); - } - - void quitGame() { - state.currentGame.isRunning = false; - refresh(); - } - - void resumeSavedGame() { - state.currentGame.isRunning = true; - refresh(); - } - - void deleteSavedGame() { - state.currentGame.isRunning = false; - state.currentGame.isFinished = true; - refresh(); - } - - void updateTileSize(double tileSize) { - if (tileSize != state.currentGame.tileSize) { - state.currentGame.tileSize = tileSize; - for (var i = 0; i < state.currentGame.tiles.length; i++) { - state.currentGame.tiles[i].size = tileSize; - } - refresh(); - } - } - - void toggleDisplayTipImage() { - state.currentGame.displayTip = !state.currentGame.displayTip; - refresh(); - } - - void incrementMovesCount() { - state.currentGame.movesCount++; - refresh(); - } - - bool checkTilesetIsCleared() { - for (MovingTile tile in state.currentGame.tiles) { - if (!tile.isCorrect()) { - return false; - } - } - return true; - } - - void swapTiles(List<int> tile1, List<int> tile2) { - state.currentGame.isStarted = true; - - final int indexTile1 = state.currentGame.tiles.indexWhere( - (tile) => ((tile.currentCol == tile1[0]) && (tile.currentRow == tile1[1]))); - final int indexTile2 = state.currentGame.tiles.indexWhere( - (tile) => ((tile.currentCol == tile2[0]) && (tile.currentRow == tile2[1]))); - - final MovingTile swap = state.currentGame.tiles[indexTile1]; - state.currentGame.tiles[indexTile1] = state.currentGame.tiles[indexTile2]; - state.currentGame.tiles[indexTile2] = swap; - - final int swapCol = state.currentGame.tiles[indexTile1].currentCol; - state.currentGame.tiles[indexTile1].currentCol = - state.currentGame.tiles[indexTile2].currentCol; - state.currentGame.tiles[indexTile2].currentCol = swapCol; - - final int swapRow = state.currentGame.tiles[indexTile1].currentRow; - state.currentGame.tiles[indexTile1].currentRow = - state.currentGame.tiles[indexTile2].currentRow; - state.currentGame.tiles[indexTile2].currentRow = swapRow; - - incrementMovesCount(); - if (checkTilesetIsCleared()) { - state.currentGame.isFinished = true; - } - - refresh(); - } - - Future<void> splitImageInTiles() async { - final String imageAsset = 'assets/images/${state.currentGame.image}.png'; - final Uint8List imageData = (await rootBundle.load(imageAsset)).buffer.asUint8List(); - - final int tilesCount = state.currentGame.gameSettings.tilesetSizeValue; - - final imglib.Image image = imglib.decodeImage(imageData) ?? - imglib.Image.fromBytes( - height: 1, - width: 1, - bytes: Uint8List.fromList([]).buffer, - ); - - int x = 0, y = 0; - final int width = (image.width / tilesCount).round(); - final int height = (image.height / tilesCount).round(); - - final List<MovingTile> tiles = []; - for (int i = 0; i < tilesCount; i++) { - for (int j = 0; j < tilesCount; j++) { - final Uint8List tileData = Uint8List.fromList(imglib.encodeJpg(imglib.copyCrop( - image, - x: x, - y: y, - width: width, - height: height, - ))); - - tiles.add(MovingTile( - currentCol: j, - currentRow: i, - image: Image.memory(tileData), - size: state.currentGame.tileSize, - originalCol: j, - originalRow: i, - )); - - x += width; - } - x = 0; - y += height; - } - - state.currentGame.tiles = tiles; - shuffleTiles(); - - state.currentGame.shufflingInProgress = false; - refresh(); - } - - void shuffleTiles() { - final Random random = Random(); - - final List<MovingTile> tiles = state.currentGame.tiles; - final int tilesCount = tiles.length; - - for (int i = 0; i < (10 * tilesCount); i++) { - final int indexTile1 = random.nextInt(tilesCount); - final int indexTile2 = random.nextInt(tilesCount); - - final MovingTile swap = tiles[indexTile1]; - tiles[indexTile1] = tiles[indexTile2]; - tiles[indexTile2] = swap; - - final int swapCol = tiles[indexTile1].currentCol; - tiles[indexTile1].currentCol = tiles[indexTile2].currentCol; - tiles[indexTile2].currentCol = swapCol; - - final int swapRow = tiles[indexTile1].currentRow; - tiles[indexTile1].currentRow = tiles[indexTile2].currentRow; - tiles[indexTile2].currentRow = swapRow; - } - - state.currentGame.tiles = tiles; - } - - @override - GameState? fromJson(Map<String, dynamic> json) { - final Game currentGame = json['currentGame'] as Game; - - return GameState( - currentGame: currentGame, - ); - } - - @override - Map<String, dynamic>? toJson(GameState state) { - return <String, dynamic>{ - 'currentGame': state.currentGame.toJson(), - }; - } -} diff --git a/lib/cubit/game_state.dart b/lib/cubit/game_state.dart deleted file mode 100644 index 00e211668c3269255926939324355792abd61c41..0000000000000000000000000000000000000000 --- a/lib/cubit/game_state.dart +++ /dev/null @@ -1,15 +0,0 @@ -part of 'game_cubit.dart'; - -@immutable -class GameState extends Equatable { - const GameState({ - required this.currentGame, - }); - - final Game currentGame; - - @override - List<dynamic> get props => <dynamic>[ - currentGame, - ]; -} diff --git a/lib/cubit/nav_cubit.dart b/lib/cubit/nav_cubit.dart deleted file mode 100644 index 86c427025fe95b93fa4ae10962f2e011cc636ea8..0000000000000000000000000000000000000000 --- a/lib/cubit/nav_cubit.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; - -import 'package:puzzlegame/config/menu.dart'; - -class NavCubit extends HydratedCubit<int> { - NavCubit() : super(0); - - void updateIndex(int index) { - if (Menu.isIndexAllowed(index)) { - emit(index); - } else { - goToGamePage(); - } - } - - void goToGamePage() { - emit(Menu.indexGame); - } - - void goToSettingsPage() { - emit(Menu.indexSettings); - } - - void goToAboutPage() { - emit(Menu.indexAbout); - } - - @override - int fromJson(Map<String, dynamic> json) { - return Menu.indexGame; - } - - @override - Map<String, dynamic>? toJson(int state) { - return <String, int>{'pageIndex': state}; - } -} diff --git a/lib/cubit/settings/settings_activity_cubit.dart b/lib/cubit/settings/settings_activity_cubit.dart new file mode 100644 index 0000000000000000000000000000000000000000..4ff017a5ec0fb85c63939ff760e60065b11ee74c --- /dev/null +++ b/lib/cubit/settings/settings_activity_cubit.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; + +import 'package:puzzlegame/config/default_activity_settings.dart'; +import 'package:puzzlegame/models/settings/settings_activity.dart'; + +part 'settings_activity_state.dart'; + +class ActivitySettingsCubit extends HydratedCubit<ActivitySettingsState> { + ActivitySettingsCubit() + : super(ActivitySettingsState(settings: ActivitySettings.createDefault())); + + void setValues({ + String? tilesetSize, + String? imageName, + }) { + emit( + ActivitySettingsState( + settings: ActivitySettings( + tilesetSize: tilesetSize ?? state.settings.tilesetSize, + imageName: imageName ?? state.settings.imageName, + ), + ), + ); + } + + String getParameterValue(String code) { + switch (code) { + case DefaultActivitySettings.parameterCodeTilesetSize: + return ActivitySettings.getTilesetSizeValueFromUnsafe(state.settings.tilesetSize); + case DefaultActivitySettings.parameterCodeImageName: + return ActivitySettings.getImageNameFromUnsafe(state.settings.imageName); + } + + return ''; + } + + void setParameterValue(String code, String value) { + final String tilesetSize = code == DefaultActivitySettings.parameterCodeTilesetSize + ? value + : getParameterValue(DefaultActivitySettings.parameterCodeTilesetSize); + final String imageName = code == DefaultActivitySettings.parameterCodeImageName + ? value + : getParameterValue(DefaultActivitySettings.parameterCodeImageName); + + setValues( + tilesetSize: tilesetSize, + imageName: imageName, + ); + } + + @override + ActivitySettingsState? fromJson(Map<String, dynamic> json) { + final String tilesetSize = + json[DefaultActivitySettings.parameterCodeTilesetSize] as String; + final String imageName = json[DefaultActivitySettings.parameterCodeImageName] as String; + + return ActivitySettingsState( + settings: ActivitySettings( + tilesetSize: tilesetSize, + imageName: imageName, + ), + ); + } + + @override + Map<String, dynamic>? toJson(ActivitySettingsState state) { + return <String, dynamic>{ + DefaultActivitySettings.parameterCodeTilesetSize: state.settings.tilesetSize, + DefaultActivitySettings.parameterCodeImageName: state.settings.imageName, + }; + } +} diff --git a/lib/cubit/settings/settings_activity_state.dart b/lib/cubit/settings/settings_activity_state.dart new file mode 100644 index 0000000000000000000000000000000000000000..2b2de42011634e81ae9e6f8bcaa1577f239c778b --- /dev/null +++ b/lib/cubit/settings/settings_activity_state.dart @@ -0,0 +1,15 @@ +part of 'settings_activity_cubit.dart'; + +@immutable +class ActivitySettingsState extends Equatable { + const ActivitySettingsState({ + required this.settings, + }); + + final ActivitySettings settings; + + @override + List<dynamic> get props => <dynamic>[ + settings, + ]; +} diff --git a/lib/cubit/settings_global_cubit.dart b/lib/cubit/settings/settings_global_cubit.dart similarity index 100% rename from lib/cubit/settings_global_cubit.dart rename to lib/cubit/settings/settings_global_cubit.dart diff --git a/lib/cubit/settings_global_state.dart b/lib/cubit/settings/settings_global_state.dart similarity index 100% rename from lib/cubit/settings_global_state.dart rename to lib/cubit/settings/settings_global_state.dart diff --git a/lib/cubit/settings_game_cubit.dart b/lib/cubit/settings_game_cubit.dart deleted file mode 100644 index f3bbf6365c34aac864e1659a27423d33b92855aa..0000000000000000000000000000000000000000 --- a/lib/cubit/settings_game_cubit.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; - -import 'package:puzzlegame/config/default_game_settings.dart'; -import 'package:puzzlegame/models/settings/settings_game.dart'; - -part 'settings_game_state.dart'; - -class GameSettingsCubit extends HydratedCubit<GameSettingsState> { - GameSettingsCubit() : super(GameSettingsState(settings: GameSettings.createDefault())); - - void setValues({ - String? tilesetSize, - String? imageName, - }) { - emit( - GameSettingsState( - settings: GameSettings( - tilesetSize: tilesetSize ?? state.settings.tilesetSize, - imageName: imageName ?? state.settings.imageName, - ), - ), - ); - } - - String getParameterValue(String code) { - switch (code) { - case DefaultGameSettings.parameterCodeTilesetSize: - return GameSettings.getTilesetSizeValueFromUnsafe(state.settings.tilesetSize); - case DefaultGameSettings.parameterCodeImageName: - return GameSettings.getImageNameFromUnsafe(state.settings.imageName); - } - - return ''; - } - - void setParameterValue(String code, String value) { - final String tilesetSize = code == DefaultGameSettings.parameterCodeTilesetSize - ? value - : getParameterValue(DefaultGameSettings.parameterCodeTilesetSize); - final String imageName = code == DefaultGameSettings.parameterCodeImageName - ? value - : getParameterValue(DefaultGameSettings.parameterCodeImageName); - - setValues( - tilesetSize: tilesetSize, - imageName: imageName, - ); - } - - @override - GameSettingsState? fromJson(Map<String, dynamic> json) { - final String tilesetSize = json[DefaultGameSettings.parameterCodeTilesetSize] as String; - final String imageName = json[DefaultGameSettings.parameterCodeImageName] as String; - - return GameSettingsState( - settings: GameSettings( - tilesetSize: tilesetSize, - imageName: imageName, - ), - ); - } - - @override - Map<String, dynamic>? toJson(GameSettingsState state) { - return <String, dynamic>{ - DefaultGameSettings.parameterCodeTilesetSize: state.settings.tilesetSize, - DefaultGameSettings.parameterCodeImageName: state.settings.imageName, - }; - } -} diff --git a/lib/cubit/settings_game_state.dart b/lib/cubit/settings_game_state.dart deleted file mode 100644 index 5acd85b44ba541e1c5e9c26af1c4be26a385b9ed..0000000000000000000000000000000000000000 --- a/lib/cubit/settings_game_state.dart +++ /dev/null @@ -1,15 +0,0 @@ -part of 'settings_game_cubit.dart'; - -@immutable -class GameSettingsState extends Equatable { - const GameSettingsState({ - required this.settings, - }); - - final GameSettings settings; - - @override - List<dynamic> get props => <dynamic>[ - settings, - ]; -} diff --git a/lib/main.dart b/lib/main.dart index 21f19aa75d2ada9fdb032a468f3bd8eda6e18252..d1ba4ad7f4da35e64cf29cb929bfd32bcd3a5977 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,10 +4,13 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/cubit/game_cubit.dart'; -import 'package:puzzlegame/cubit/nav_cubit.dart'; -import 'package:puzzlegame/cubit/settings_game_cubit.dart'; -import 'package:puzzlegame/cubit/settings_global_cubit.dart'; +import 'package:puzzlegame/common/cubit/nav/nav_cubit_pages.dart'; +import 'package:puzzlegame/common/cubit/nav/nav_cubit_screens.dart'; + +import 'package:puzzlegame/config/application_config.dart'; +import 'package:puzzlegame/cubit/activity/activity_cubit.dart'; +import 'package:puzzlegame/cubit/settings/settings_activity_cubit.dart'; +import 'package:puzzlegame/cubit/settings/settings_global_cubit.dart'; import 'package:puzzlegame/ui/skeleton.dart'; void main() async { @@ -45,17 +48,30 @@ class MyApp extends StatelessWidget { return MultiBlocProvider( providers: [ - BlocProvider<NavCubit>(create: (context) => NavCubit()), + // default providers + BlocProvider<NavCubitPage>( + create: (context) => NavCubitPage(), + ), + BlocProvider<NavCubitScreen>( + create: (context) => NavCubitScreen(), + ), BlocProvider<ApplicationThemeModeCubit>( - create: (context) => ApplicationThemeModeCubit()), - BlocProvider<GameCubit>(create: (context) => GameCubit()), - BlocProvider<GlobalSettingsCubit>(create: (context) => GlobalSettingsCubit()), - BlocProvider<GameSettingsCubit>(create: (context) => GameSettingsCubit()), + create: (context) => ApplicationThemeModeCubit(), + ), + BlocProvider<ActivityCubit>( + create: (context) => ActivityCubit(), + ), + BlocProvider<GlobalSettingsCubit>( + create: (context) => GlobalSettingsCubit(), + ), + BlocProvider<ActivitySettingsCubit>( + create: (context) => ActivitySettingsCubit(), + ), ], child: BlocBuilder<ApplicationThemeModeCubit, ApplicationThemeModeState>( builder: (BuildContext context, ApplicationThemeModeState state) { return MaterialApp( - title: 'Puzzle', + title: ApplicationConfig.appTitle, home: const SkeletonScreen(), // Theme stuff diff --git a/lib/models/game/game.dart b/lib/models/activity/activity.dart similarity index 77% rename from lib/models/game/game.dart rename to lib/models/activity/activity.dart index fd799649beaebeb34f9ab9c86372a97acedb974f..5cb54ee0bd2a146a677acc6ce9eca680d9049cc0 100644 --- a/lib/models/game/game.dart +++ b/lib/models/activity/activity.dart @@ -1,14 +1,14 @@ import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; import 'package:puzzlegame/data/fetch_data_helper.dart'; -import 'package:puzzlegame/models/game/moving_tile.dart'; -import 'package:puzzlegame/models/settings/settings_game.dart'; +import 'package:puzzlegame/models/activity/moving_tile.dart'; +import 'package:puzzlegame/models/settings/settings_activity.dart'; import 'package:puzzlegame/models/settings/settings_global.dart'; -class Game { - Game({ +class Activity { + Activity({ // Settings - required this.gameSettings, + required this.activitySettings, required this.globalSettings, // State @@ -29,7 +29,7 @@ class Game { }); // Settings - final GameSettings gameSettings; + final ActivitySettings activitySettings; final GlobalSettings globalSettings; // State @@ -48,27 +48,28 @@ class Game { bool displayTip; double tileSize; - factory Game.createEmpty() { - return Game( - gameSettings: GameSettings.createDefault(), + factory Activity.createEmpty() { + return Activity( + activitySettings: ActivitySettings.createDefault(), globalSettings: GlobalSettings.createDefault(), image: '', tiles: [], ); } - factory Game.createNew({ - GameSettings? gameSettings, + factory Activity.createNew({ + ActivitySettings? activitySettings, GlobalSettings? globalSettings, }) { - final GameSettings newGameSettings = gameSettings ?? GameSettings.createDefault(); + final ActivitySettings newActivitySettings = + activitySettings ?? ActivitySettings.createDefault(); final GlobalSettings newGlobalSettings = globalSettings ?? GlobalSettings.createDefault(); final String image = FetchDataHelper().getRandomItem(); - return Game( + return Activity( // Settings - gameSettings: newGameSettings, + activitySettings: newActivitySettings, globalSettings: newGlobalSettings, // Base data image: image, @@ -81,9 +82,9 @@ class Game { printlog(''); printlog('## Current game dump:'); printlog(''); - printlog('$Game:'); + printlog('$Activity:'); printlog(' Settings'); - gameSettings.dump(); + activitySettings.dump(); globalSettings.dump(); printlog(' State'); printlog(' isRunning: $isRunning'); @@ -102,13 +103,13 @@ class Game { @override String toString() { - return '$Game(${toJson()})'; + return '$Activity(${toJson()})'; } Map<String, dynamic>? toJson() { return <String, dynamic>{ // Settings - 'gameSettings': gameSettings.toJson(), + 'activitySettings': activitySettings.toJson(), 'globalSettings': globalSettings.toJson(), // State 'isRunning': isRunning, diff --git a/lib/models/game/moving_tile.dart b/lib/models/activity/moving_tile.dart similarity index 92% rename from lib/models/game/moving_tile.dart rename to lib/models/activity/moving_tile.dart index 3ef918c9a8e29e04746bb95f6b976546e4fd0d0a..6aa4b0fa38470d362e08b3b2b582b41a6d09c5c9 100644 --- a/lib/models/game/moving_tile.dart +++ b/lib/models/activity/moving_tile.dart @@ -1,4 +1,4 @@ -import 'package:puzzlegame/models/game/tile.dart'; +import 'package:puzzlegame/models/activity/tile.dart'; class MovingTile extends Tile { int currentCol; diff --git a/lib/models/game/tile.dart b/lib/models/activity/tile.dart similarity index 100% rename from lib/models/game/tile.dart rename to lib/models/activity/tile.dart diff --git a/lib/models/settings/settings_activity.dart b/lib/models/settings/settings_activity.dart new file mode 100644 index 0000000000000000000000000000000000000000..c825a3cf9f8691071cc708e44c8285a036b297ea --- /dev/null +++ b/lib/models/settings/settings_activity.dart @@ -0,0 +1,57 @@ +import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; + +import 'package:puzzlegame/config/default_activity_settings.dart'; + +class ActivitySettings { + final String tilesetSize; + final String imageName; + + ActivitySettings({ + required this.tilesetSize, + required this.imageName, + }); + + static String getTilesetSizeValueFromUnsafe(String tilesetSize) { + if (DefaultActivitySettings.allowedTilesetSizeValues.contains(tilesetSize)) { + return tilesetSize; + } + + return DefaultActivitySettings.defaultTilesetSizeValue; + } + + static String getImageNameFromUnsafe(String imageName) { + if (DefaultActivitySettings.allowedImageNameS.contains(imageName)) { + return imageName; + } + + return DefaultActivitySettings.defaultImageName; + } + + factory ActivitySettings.createDefault() { + return ActivitySettings( + tilesetSize: DefaultActivitySettings.defaultTilesetSizeValue, + imageName: DefaultActivitySettings.defaultImageName, + ); + } + + int get tilesetSizeValue => int.parse(tilesetSize.split('x')[0]); + + void dump() { + printlog('$ActivitySettings:'); + printlog(' ${DefaultActivitySettings.parameterCodeTilesetSize}: $tilesetSize'); + printlog(' ${DefaultActivitySettings.parameterCodeImageName}: $imageName'); + printlog(''); + } + + @override + String toString() { + return '$ActivitySettings(${toJson()})'; + } + + Map<String, dynamic>? toJson() { + return <String, dynamic>{ + DefaultActivitySettings.parameterCodeTilesetSize: tilesetSize, + DefaultActivitySettings.parameterCodeImageName: imageName, + }; + } +} diff --git a/lib/models/settings/settings_game.dart b/lib/models/settings/settings_game.dart deleted file mode 100644 index 989d6ab1e3d482c214ba0f81419ac35ef4b2df1a..0000000000000000000000000000000000000000 --- a/lib/models/settings/settings_game.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; - -import 'package:puzzlegame/config/default_game_settings.dart'; - -class GameSettings { - final String tilesetSize; - final String imageName; - - GameSettings({ - required this.tilesetSize, - required this.imageName, - }); - - static String getTilesetSizeValueFromUnsafe(String tilesetSize) { - if (DefaultGameSettings.allowedTilesetSizeValues.contains(tilesetSize)) { - return tilesetSize; - } - - return DefaultGameSettings.defaultTilesetSizeValue; - } - - static String getImageNameFromUnsafe(String imageName) { - if (DefaultGameSettings.allowedImageNameS.contains(imageName)) { - return imageName; - } - - return DefaultGameSettings.defaultImageName; - } - - factory GameSettings.createDefault() { - return GameSettings( - tilesetSize: DefaultGameSettings.defaultTilesetSizeValue, - imageName: DefaultGameSettings.defaultImageName, - ); - } - - int get tilesetSizeValue => int.parse(tilesetSize.split('x')[0]); - - void dump() { - printlog('$GameSettings:'); - printlog(' ${DefaultGameSettings.parameterCodeTilesetSize}: $tilesetSize'); - printlog(' ${DefaultGameSettings.parameterCodeImageName}: $imageName'); - printlog(''); - } - - @override - String toString() { - return '$GameSettings(${toJson()})'; - } - - Map<String, dynamic>? toJson() { - return <String, dynamic>{ - DefaultGameSettings.parameterCodeTilesetSize: tilesetSize, - DefaultGameSettings.parameterCodeImageName: imageName, - }; - } -} diff --git a/lib/ui/game/game_bottom.dart b/lib/ui/game/game_bottom.dart index 4727a9fc8dded881e8c6f946bd3376f38272ce31..cd9d93f653293d9a794ad322ec66f7830e5b87fe 100644 --- a/lib/ui/game/game_bottom.dart +++ b/lib/ui/game/game_bottom.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/cubit/game_cubit.dart'; -import 'package:puzzlegame/models/game/game.dart'; +import 'package:puzzlegame/cubit/activity/activity_cubit.dart'; +import 'package:puzzlegame/models/activity/activity.dart'; import 'package:puzzlegame/ui/widgets/game/game_tip.dart'; class GameBottomWidget extends StatelessWidget { @@ -10,11 +10,11 @@ class GameBottomWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder<GameCubit, GameState>( - builder: (BuildContext context, GameState gameState) { - final Game currentGame = gameState.currentGame; + return BlocBuilder<ActivityCubit, ActivityState>( + builder: (BuildContext context, ActivityState activityState) { + final Activity currentActivity = activityState.currentActivity; - return (!currentGame.isFinished && !currentGame.shufflingInProgress) + return (!currentActivity.isFinished && !currentActivity.shufflingInProgress) ? const GameTipWidget() : const SizedBox.shrink(); }, diff --git a/lib/ui/game/game_end.dart b/lib/ui/game/game_end.dart index e0a55fc754779ec0b1a0f987181b06cdd3c5961f..dc42e01f555c04878a3c7c7d91fd92e88af5290d 100644 --- a/lib/ui/game/game_end.dart +++ b/lib/ui/game/game_end.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/cubit/game_cubit.dart'; -import 'package:puzzlegame/models/game/game.dart'; +import 'package:puzzlegame/cubit/activity/activity_cubit.dart'; +import 'package:puzzlegame/models/activity/activity.dart'; import 'package:puzzlegame/ui/widgets/actions/button_game_quit.dart'; class GameEndWidget extends StatelessWidget { @@ -10,9 +10,9 @@ class GameEndWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder<GameCubit, GameState>( - builder: (BuildContext context, GameState gameState) { - final Game currentGame = gameState.currentGame; + return BlocBuilder<ActivityCubit, ActivityState>( + builder: (BuildContext context, ActivityState activityState) { + final Activity currentActivity = activityState.currentActivity; const Image decorationImage = Image( image: AssetImage('assets/ui/game_win.png'), @@ -33,7 +33,7 @@ class GameEndWidget extends StatelessWidget { ), Column( children: [ - currentGame.animationInProgress == true + currentActivity.animationInProgress == true ? decorationImage : const QuitGameButton() ], diff --git a/lib/ui/game/game_top.dart b/lib/ui/game/game_top.dart index 9554cae7fb9f2f7798893fe472048f29ad43d45f..209527a0f42844eb640b5fa25e5740dc81656256 100644 --- a/lib/ui/game/game_top.dart +++ b/lib/ui/game/game_top.dart @@ -1,22 +1,22 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/cubit/game_cubit.dart'; -import 'package:puzzlegame/models/game/game.dart'; +import 'package:puzzlegame/cubit/activity/activity_cubit.dart'; +import 'package:puzzlegame/models/activity/activity.dart'; class GameTopWidget extends StatelessWidget { const GameTopWidget({super.key}); @override Widget build(BuildContext context) { - return BlocBuilder<GameCubit, GameState>( - builder: (BuildContext context, GameState gameState) { - final Game currentGame = gameState.currentGame; + return BlocBuilder<ActivityCubit, ActivityState>( + builder: (BuildContext context, ActivityState activityState) { + final Activity currentActivity = activityState.currentActivity; - return currentGame.shufflingInProgress + return currentActivity.shufflingInProgress ? const SizedBox.shrink() : Text( - '${currentGame.movesCount}', + '${currentActivity.movesCount}', style: const TextStyle( fontSize: 45, fontWeight: FontWeight.w600, diff --git a/lib/ui/layouts/parameters_layout.dart b/lib/ui/layouts/parameters_layout.dart deleted file mode 100644 index 554a08619c3158d6bc5b33eeab2c1e536f537485..0000000000000000000000000000000000000000 --- a/lib/ui/layouts/parameters_layout.dart +++ /dev/null @@ -1,141 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; - -import 'package:puzzlegame/config/default_game_settings.dart'; -import 'package:puzzlegame/config/default_global_settings.dart'; -import 'package:puzzlegame/cubit/settings_game_cubit.dart'; -import 'package:puzzlegame/cubit/settings_global_cubit.dart'; -import 'package:puzzlegame/ui/parameters/parameter_widget.dart'; -import 'package:puzzlegame/ui/widgets/actions/button_delete_saved_game.dart'; -import 'package:puzzlegame/ui/widgets/actions/button_game_start_new.dart'; -import 'package:puzzlegame/ui/widgets/actions/button_resume_saved_game.dart'; - -class ParametersLayout extends StatelessWidget { - const ParametersLayout({super.key, required this.canResume}); - - final bool canResume; - - 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(Expanded( - child: SizedBox(height: separatorHeight), - )); - - if (canResume == false) { - // Start new game - lines.add( - const AspectRatio( - aspectRatio: 3, - child: StartNewGameButton(), - ), - ); - } else { - // Resume game - lines.add(const AspectRatio( - aspectRatio: 3, - child: ResumeSavedGameButton(), - )); - // Delete saved game - lines.add(SizedBox.square( - dimension: MediaQuery.of(context).size.width / 5, - child: const DeleteSavedGameButton(), - )); - } - - 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 isSelected = (value == currentValue); - - final double displayWidth = MediaQuery.of(context).size.width; - final double itemWidth = displayWidth / availableValues.length - 4; - - return SizedBox.square( - dimension: itemWidth, - child: ParameterWidget( - code: code, - value: value, - isSelected: isSelected, - size: itemWidth, - gameSettings: gameSettingsState.settings, - globalSettings: globalSettingsState.settings, - onPressed: () { - isGlobal - ? globalSettingsCubit.setParameterValue(code, value) - : gameSettingsCubit.setParameterValue(code, value); - }, - ), - ); - }, - ); - }, - ); - - parameterButtons.add(parameterButton); - } - - return parameterButtons; - } -} diff --git a/lib/ui/screens/page_game.dart b/lib/ui/screens/page_game.dart deleted file mode 100644 index 6f280a9ede833433336c7b2422f23b5c731b074b..0000000000000000000000000000000000000000 --- a/lib/ui/screens/page_game.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; - -import 'package:puzzlegame/cubit/game_cubit.dart'; -import 'package:puzzlegame/models/game/game.dart'; -import 'package:puzzlegame/ui/layouts/game_layout.dart'; -import 'package:puzzlegame/ui/layouts/parameters_layout.dart'; - -class PageGame extends StatelessWidget { - const PageGame({super.key}); - - @override - Widget build(BuildContext context) { - return BlocBuilder<GameCubit, GameState>( - builder: (BuildContext context, GameState gameState) { - final Game currentGame = gameState.currentGame; - - return currentGame.isRunning - ? const GameLayout() - : ParametersLayout(canResume: currentGame.canBeResumed); - }, - ); - } -} diff --git a/lib/ui/skeleton.dart b/lib/ui/skeleton.dart index bcb33b5bb5bd83f46985b6db74881092b75c45c6..ab3c2d919563c0060bce0d249ff021af067bb556 100644 --- a/lib/ui/skeleton.dart +++ b/lib/ui/skeleton.dart @@ -1,34 +1,37 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/config/menu.dart'; -import 'package:puzzlegame/cubit/nav_cubit.dart'; -import 'package:puzzlegame/ui/widgets/global_app_bar.dart'; +import 'package:puzzlegame/common/config/activity_page.dart'; +import 'package:puzzlegame/common/config/screen.dart'; +import 'package:puzzlegame/common/cubit/nav/nav_cubit_screens.dart'; +import 'package:puzzlegame/common/ui/nav/global_app_bar.dart'; +import 'package:puzzlegame/common/ui/nav/bottom_nav_bar.dart'; class SkeletonScreen extends StatelessWidget { const SkeletonScreen({super.key}); @override Widget build(BuildContext context) { - return Scaffold( - appBar: const GlobalAppBar(), - extendBodyBehindAppBar: false, - body: Material( - color: Theme.of(context).colorScheme.surface, - child: BlocBuilder<NavCubit, int>( - builder: (BuildContext context, int pageIndex) { - return Padding( + return BlocBuilder<NavCubitScreen, int>( + builder: (BuildContext context, int screenIndex) { + return Scaffold( + appBar: const GlobalAppBar(), + extendBodyBehindAppBar: false, + body: Material( + color: Theme.of(context).colorScheme.surface, + child: Padding( padding: const EdgeInsets.only( top: 8, left: 2, right: 2, ), - child: Menu.getPageWidget(pageIndex), - ); - }, - ), - ), - backgroundColor: Theme.of(context).colorScheme.surface, + child: Screen.getWidget(screenIndex), + ), + ), + backgroundColor: Theme.of(context).colorScheme.surface, + bottomNavigationBar: ActivityPage.displayBottomNavBar ? const BottomNavBar() : null, + ); + }, ); } } diff --git a/lib/ui/widgets/actions/button_delete_saved_game.dart b/lib/ui/widgets/actions/button_delete_saved_game.dart index 28c215635e52e06cf72bacb6b041eadeab0b9056..be61ef548553091497b2ed8d283751c195c1ec83 100644 --- a/lib/ui/widgets/actions/button_delete_saved_game.dart +++ b/lib/ui/widgets/actions/button_delete_saved_game.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/cubit/game_cubit.dart'; +import 'package:puzzlegame/cubit/activity/activity_cubit.dart'; class DeleteSavedGameButton extends StatelessWidget { const DeleteSavedGameButton({super.key}); @@ -11,7 +11,7 @@ class DeleteSavedGameButton extends StatelessWidget { return StyledButton( color: Colors.grey, onPressed: () { - BlocProvider.of<GameCubit>(context).deleteSavedGame(); + BlocProvider.of<ActivityCubit>(context).deleteSavedActivity(); }, child: const Image( image: AssetImage('assets/ui/button_delete_saved_game.png'), diff --git a/lib/ui/widgets/actions/button_game_quit.dart b/lib/ui/widgets/actions/button_game_quit.dart index 9ef197ae16883cf6f3f1e95f78d77d97b75d4203..9f6c6f3a95a3d40b1a42599f4732dc2b85305056 100644 --- a/lib/ui/widgets/actions/button_game_quit.dart +++ b/lib/ui/widgets/actions/button_game_quit.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/cubit/game_cubit.dart'; +import 'package:puzzlegame/common/cubit/nav/nav_cubit_pages.dart'; + +import 'package:puzzlegame/cubit/activity/activity_cubit.dart'; class QuitGameButton extends StatelessWidget { const QuitGameButton({super.key}); @@ -11,7 +13,8 @@ class QuitGameButton extends StatelessWidget { return StyledButton( color: Colors.red, onPressed: () { - BlocProvider.of<GameCubit>(context).quitGame(); + BlocProvider.of<ActivityCubit>(context).quitActivity(); + BlocProvider.of<NavCubitPage>(context).goToPageHome(); }, child: const Image( image: AssetImage('assets/ui/button_back.png'), diff --git a/lib/ui/widgets/actions/button_game_start_new.dart b/lib/ui/widgets/actions/button_game_start_new.dart index cc904c5c10b5340fc496f1c21e134a039a227316..77d2396dc70d11e07e339c56db8d649e8ca1c502 100644 --- a/lib/ui/widgets/actions/button_game_start_new.dart +++ b/lib/ui/widgets/actions/button_game_start_new.dart @@ -1,26 +1,29 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/cubit/game_cubit.dart'; -import 'package:puzzlegame/cubit/settings_game_cubit.dart'; -import 'package:puzzlegame/cubit/settings_global_cubit.dart'; +import 'package:puzzlegame/common/cubit/nav/nav_cubit_pages.dart'; + +import 'package:puzzlegame/cubit/activity/activity_cubit.dart'; +import 'package:puzzlegame/cubit/settings/settings_activity_cubit.dart'; +import 'package:puzzlegame/cubit/settings/settings_global_cubit.dart'; class StartNewGameButton extends StatelessWidget { const StartNewGameButton({super.key}); @override Widget build(BuildContext context) { - return BlocBuilder<GameSettingsCubit, GameSettingsState>( - builder: (BuildContext context, GameSettingsState gameSettingsState) { + return BlocBuilder<ActivitySettingsCubit, ActivitySettingsState>( + builder: (BuildContext context, ActivitySettingsState activitySettingsState) { return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>( builder: (BuildContext context, GlobalSettingsState globalSettingsState) { return StyledButton( color: Colors.blue, onPressed: () { - BlocProvider.of<GameCubit>(context).startNewGame( - gameSettings: gameSettingsState.settings, + BlocProvider.of<ActivityCubit>(context).startNewActivity( + activitySettings: activitySettingsState.settings, globalSettings: globalSettingsState.settings, ); + BlocProvider.of<NavCubitPage>(context).goToPageGame(); }, child: const Image( image: AssetImage('assets/ui/button_start.png'), diff --git a/lib/ui/widgets/actions/button_resume_saved_game.dart b/lib/ui/widgets/actions/button_resume_saved_game.dart index e3490118c253812f4b85092134a2656187ee59b8..414cfa77ec77e5be405e3e4089254342149743b5 100644 --- a/lib/ui/widgets/actions/button_resume_saved_game.dart +++ b/lib/ui/widgets/actions/button_resume_saved_game.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/cubit/game_cubit.dart'; +import 'package:puzzlegame/common/cubit/nav/nav_cubit_pages.dart'; + +import 'package:puzzlegame/cubit/activity/activity_cubit.dart'; class ResumeSavedGameButton extends StatelessWidget { const ResumeSavedGameButton({super.key}); @@ -11,7 +13,8 @@ class ResumeSavedGameButton extends StatelessWidget { return StyledButton( color: Colors.blue, onPressed: () { - BlocProvider.of<GameCubit>(context).resumeSavedGame(); + BlocProvider.of<ActivityCubit>(context).resumeSavedActivity(); + BlocProvider.of<NavCubitPage>(context).goToPageGame(); }, child: const Image( image: AssetImage('assets/ui/button_resume_game.png'), diff --git a/lib/ui/widgets/game/game_board.dart b/lib/ui/widgets/game/game_board.dart index 8150970952a85e81f03d0bb00f5b4fb72884904b..288ae07909df8f92e1638c79707220425ed52646 100644 --- a/lib/ui/widgets/game/game_board.dart +++ b/lib/ui/widgets/game/game_board.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/cubit/game_cubit.dart'; -import 'package:puzzlegame/models/game/game.dart'; +import 'package:puzzlegame/cubit/activity/activity_cubit.dart'; +import 'package:puzzlegame/models/activity/activity.dart'; import 'package:puzzlegame/ui/widgets/game/game_shuffling.dart'; import 'package:puzzlegame/ui/widgets/game/game_tileset.dart'; @@ -12,15 +12,15 @@ class GameBoardWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Center( - child: BlocBuilder<GameCubit, GameState>( - builder: (BuildContext context, GameState gameState) { - final Game currentGame = gameState.currentGame; + child: BlocBuilder<ActivityCubit, ActivityState>( + builder: (BuildContext context, ActivityState activityState) { + final Activity currentActivity = activityState.currentActivity; - BlocProvider.of<GameCubit>(context).updateTileSize( + BlocProvider.of<ActivityCubit>(context).updateTileSize( (MediaQuery.of(context).size.width - 60) / - currentGame.gameSettings.tilesetSizeValue); + currentActivity.activitySettings.tilesetSizeValue); - if (currentGame.shufflingInProgress) { + if (currentActivity.shufflingInProgress) { return const GameShufflingWidget(); } else { return const GameTilesetwidget(); diff --git a/lib/ui/widgets/game/game_tile.dart b/lib/ui/widgets/game/game_tile.dart index 92646344136dd1cd18046dca8dcf20e898668e53..01210659691fb64adf0d8f892b83e36a7ef94c8a 100644 --- a/lib/ui/widgets/game/game_tile.dart +++ b/lib/ui/widgets/game/game_tile.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/cubit/game_cubit.dart'; -import 'package:puzzlegame/models/game/moving_tile.dart'; +import 'package:puzzlegame/cubit/activity/activity_cubit.dart'; +import 'package:puzzlegame/models/activity/moving_tile.dart'; class GameTileWidget extends StatelessWidget { const GameTileWidget({super.key, required this.tile}); @@ -43,7 +43,7 @@ class GameTileWidget extends StatelessWidget { ); }, onAcceptWithDetails: (DragTargetDetails<List<int>> source) { - BlocProvider.of<GameCubit>(context) + BlocProvider.of<ActivityCubit>(context) .swapTiles([tile.currentCol, tile.currentRow], source.data); }, ); diff --git a/lib/ui/widgets/game/game_tileset.dart b/lib/ui/widgets/game/game_tileset.dart index b45b01adc9dc69e16a03a3e0bd24f41f0d1ae46a..408d7c7e141c636b62a601678fabeaecb5eb0f3f 100644 --- a/lib/ui/widgets/game/game_tileset.dart +++ b/lib/ui/widgets/game/game_tileset.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/cubit/game_cubit.dart'; -import 'package:puzzlegame/models/game/game.dart'; -import 'package:puzzlegame/models/game/moving_tile.dart'; +import 'package:puzzlegame/cubit/activity/activity_cubit.dart'; +import 'package:puzzlegame/models/activity/activity.dart'; +import 'package:puzzlegame/models/activity/moving_tile.dart'; import 'package:puzzlegame/ui/widgets/game/game_tile.dart'; class GameTilesetwidget extends StatelessWidget { @@ -11,14 +11,14 @@ class GameTilesetwidget extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder<GameCubit, GameState>( - builder: (BuildContext context, GameState gameState) { - final Game currentGame = gameState.currentGame; + return BlocBuilder<ActivityCubit, ActivityState>( + builder: (BuildContext context, ActivityState activityState) { + final Activity currentActivity = activityState.currentActivity; - final Color borderColor = currentGame.isFinished ? Colors.green : Colors.orange; - final int tilesCount = currentGame.gameSettings.tilesetSizeValue; + final Color borderColor = currentActivity.isFinished ? Colors.green : Colors.orange; + final int tilesCount = currentActivity.activitySettings.tilesetSizeValue; - final List<MovingTile> tiles = currentGame.tiles; + final List<MovingTile> tiles = currentActivity.tiles; int tileIndex = 0; return Container( diff --git a/lib/ui/widgets/game/game_tip.dart b/lib/ui/widgets/game/game_tip.dart index a27e14d0eff49b96e4ba08599718cf52ae651631..fa6ed9758112d123bcfdfeaa2080d972e233785f 100644 --- a/lib/ui/widgets/game/game_tip.dart +++ b/lib/ui/widgets/game/game_tip.dart @@ -1,19 +1,19 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/cubit/game_cubit.dart'; -import 'package:puzzlegame/models/game/game.dart'; +import 'package:puzzlegame/cubit/activity/activity_cubit.dart'; +import 'package:puzzlegame/models/activity/activity.dart'; class GameTipWidget extends StatelessWidget { const GameTipWidget({super.key}); @override Widget build(BuildContext context) { - return BlocBuilder<GameCubit, GameState>( - builder: (BuildContext context, GameState gameState) { - final Game currentGame = gameState.currentGame; + return BlocBuilder<ActivityCubit, ActivityState>( + builder: (BuildContext context, ActivityState activityState) { + final Activity currentActivity = activityState.currentActivity; - final String asset = 'assets/images/${currentGame.image}.png'; + final String asset = 'assets/images/${currentActivity.image}.png'; precacheImage(AssetImage(asset), context); return TextButton( @@ -27,15 +27,15 @@ class GameTipWidget extends StatelessWidget { ), ), child: Image( - height: currentGame.tileSize, + height: currentActivity.tileSize, image: AssetImage( - currentGame.displayTip ? asset : 'assets/ui/tip_hidden.png', + currentActivity.displayTip ? asset : 'assets/ui/tip_hidden.png', ), fit: BoxFit.fill, ), ), onPressed: () { - BlocProvider.of<GameCubit>(context).toggleDisplayTipImage(); + BlocProvider.of<ActivityCubit>(context).toggleDisplayTipImage(); }, ); }, diff --git a/lib/ui/widgets/indicators/indicator_score.dart b/lib/ui/widgets/indicators/indicator_score.dart index b2692d4baa739c222e4a859698513c7c17361caf..13b43d0d8328aa469a87c59e1ee1a249cd675284 100644 --- a/lib/ui/widgets/indicators/indicator_score.dart +++ b/lib/ui/widgets/indicators/indicator_score.dart @@ -1,20 +1,20 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:puzzlegame/cubit/game_cubit.dart'; +import 'package:puzzlegame/cubit/activity/activity_cubit.dart'; class ScoreIndicator extends StatelessWidget { const ScoreIndicator({super.key}); @override Widget build(BuildContext context) { - return BlocBuilder<GameCubit, GameState>( - builder: (BuildContext context, GameState gameState) { + return BlocBuilder<ActivityCubit, ActivityState>( + builder: (BuildContext context, ActivityState activityState) { const Color baseColor = Color.fromARGB(255, 218, 218, 218); final Color outlineColor = baseColor.darken(); return OutlinedText( - text: gameState.currentGame.movesCount.toString(), + text: activityState.currentActivity.movesCount.toString(), fontSize: 70, textColor: baseColor, outlineColor: outlineColor, diff --git a/pubspec.lock b/pubspec.lock index 45f98bd9476c3a5a3858b2a933f1089097692e0f..fcc449a0634c203971ff34227ac03b5ea48186d7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -261,10 +261,10 @@ packages: dependency: transitive description: name: path_provider - sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" path_provider_android: dependency: transitive description: @@ -466,10 +466,10 @@ packages: dependency: transitive description: name: win32 - sha256: "2735daae5150e8b1dfeb3eb0544b4d3af0061e9e82cef063adcd583bdae4306a" + sha256: "10169d3934549017f0ae278ccb07f828f9d6ea21573bab0fb77b0e1ef0fce454" url: "https://pub.dev" source: hosted - version: "5.7.0" + version: "5.7.2" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 46e87c4230f3c5e847b3e6ad09d57984809e56ae..65d3507fcaaeb97c6b58a5f23c73da4bd042027d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: A puzzle game application. publish_to: "none" -version: 0.3.1+67 +version: 0.4.0+68 environment: sdk: "^3.0.0"