diff --git a/assets/translations/en.json b/assets/translations/en.json index 6924de1c80dc4230424a788d29994fb7c2639864..65e22e652e5b3ff6348923eb5634bb0427edbee1 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -1,6 +1,9 @@ { "app_name": "Jeweled", + "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 23adc4c3871a3b8ceb09eab619465d95e7d23aff..4d13af3800ff0d66c9bfe69bcbb8a905f9a64162 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -1,6 +1,9 @@ { "app_name": "Jeweled", + "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/38.txt b/fastlane/metadata/android/en-US/changelogs/38.txt new file mode 100644 index 0000000000000000000000000000000000000000..ac2c90e221caf3cf01ebba168fb16a8f31c0253a --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/38.txt @@ -0,0 +1 @@ +Normalize Activity application architecture. diff --git a/fastlane/metadata/android/fr-FR/changelogs/38.txt b/fastlane/metadata/android/fr-FR/changelogs/38.txt new file mode 100644 index 0000000000000000000000000000000000000000..1d6843d89ba84ea4147528bc3d62d9ecf1a4a762 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/38.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..ecf719af3e6dbe4829590904836df90e0a195729 --- /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:jeweled/common/ui/pages/game.dart'; +import 'package:jeweled/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..d18634c369e142500052b6e1e3a2e3910da950b9 --- /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:jeweled/common/ui/screens/about.dart'; +import 'package:jeweled/common/ui/screens/activity.dart'; +import 'package:jeweled/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..6e8bfc7e44ef7fb9182d864cf2c929bea357cbda --- /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:jeweled/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..f0d82d80b434ad3e02f705c92384a951c10ae701 --- /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:jeweled/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..0ed2afba63051e4b9e331a94a3a822aed89835fa --- /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:jeweled/common/config/activity_page.dart'; +import 'package:jeweled/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 58% rename from lib/ui/widgets/global_app_bar.dart rename to lib/common/ui/nav/global_app_bar.dart index 6ac5318fd59039b3993c723b2f66b9ee03b37812..77b379e54eae4e02d780f66cdd4f8b21ca079d5f 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:jeweled/config/menu.dart'; -import 'package:jeweled/cubit/game_cubit.dart'; -import 'package:jeweled/cubit/nav_cubit.dart'; -import 'package:jeweled/models/game/game.dart'; +import 'package:jeweled/common/config/screen.dart'; +import 'package:jeweled/common/cubit/nav/nav_cubit_pages.dart'; +import 'package:jeweled/common/cubit/nav/nav_cubit_screens.dart'; + +import 'package:jeweled/cubit/activity/activity_cubit.dart'; +import 'package:jeweled/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,44 +35,44 @@ 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, )); } } return AppBar( - title: const AppTitle(text: 'app_name'), + title: const AppHeader(text: 'app_name'), actions: menuActions, ); }, diff --git a/lib/ui/layouts/game_layout.dart b/lib/common/ui/pages/game.dart similarity index 64% rename from lib/ui/layouts/game_layout.dart rename to lib/common/ui/pages/game.dart index 089495539f5da281e318d698fc8c4611e3a40715..b605c3cf162917c9d10b048d06df12b667bbb35b 100644 --- a/lib/ui/layouts/game_layout.dart +++ b/lib/common/ui/pages/game.dart @@ -1,20 +1,20 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:jeweled/cubit/game_cubit.dart'; -import 'package:jeweled/models/game/game.dart'; +import 'package:jeweled/cubit/activity/activity_cubit.dart'; +import 'package:jeweled/models/activity/activity.dart'; import 'package:jeweled/ui/game/game_end.dart'; import 'package:jeweled/ui/game/game_top.dart'; import 'package:jeweled/ui/widgets/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, @@ -28,7 +28,7 @@ class GameLayout extends StatelessWidget { const BoardWidget(), const SizedBox(height: 8), 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..b4fa14822c2a8561e0e3fda84ec1ab9d80d801f5 --- /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:jeweled/common/ui/parameters/parameter_widget.dart'; + +import 'package:jeweled/config/default_activity_settings.dart'; +import 'package:jeweled/config/default_global_settings.dart'; +import 'package:jeweled/cubit/activity/activity_cubit.dart'; +import 'package:jeweled/cubit/settings/settings_activity_cubit.dart'; +import 'package:jeweled/cubit/settings/settings_global_cubit.dart'; +import 'package:jeweled/models/activity/activity.dart'; +import 'package:jeweled/ui/widgets/actions/button_delete_saved_game.dart'; +import 'package:jeweled/ui/widgets/actions/button_game_start_new.dart'; +import 'package:jeweled/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 94% rename from lib/ui/parameters/parameter_painter.dart rename to lib/common/ui/parameters/parameter_painter.dart index e63459a685399c4fa0737cb646eb0de49483e0be..bd83f249b22c314df589041effc0b17665084e0d 100644 --- a/lib/ui/parameters/parameter_painter.dart +++ b/lib/common/ui/parameters/parameter_painter.dart @@ -5,22 +5,22 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; import 'package:jeweled/config/color_theme.dart'; -import 'package:jeweled/config/default_game_settings.dart'; +import 'package:jeweled/config/default_activity_settings.dart'; import 'package:jeweled/config/default_global_settings.dart'; -import 'package:jeweled/models/settings/settings_game.dart'; +import 'package:jeweled/models/settings/settings_activity.dart'; import 'package:jeweled/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 @@ -30,10 +30,10 @@ class ParameterPainter extends CustomPainter { // content switch (code) { - case DefaultGameSettings.parameterCodeColorsCount: + case DefaultActivitySettings.parameterCodeColorsCount: paintColorsCountParameterItem(canvas, canvasSize); break; - case DefaultGameSettings.parameterCodeBoardSize: + case DefaultActivitySettings.parameterCodeBoardSize: paintBoardSizeParameterItem(canvas, canvasSize); break; case DefaultGlobalSettings.parameterCodeColorsTheme: @@ -92,16 +92,16 @@ class ParameterPainter extends CustomPainter { int gridWidth = 1; switch (value) { - case DefaultGameSettings.boardSizeValueSmall: + case DefaultActivitySettings.boardSizeValueSmall: gridWidth = 2; break; - case DefaultGameSettings.boardSizeValueMedium: + case DefaultActivitySettings.boardSizeValueMedium: gridWidth = 3; break; - case DefaultGameSettings.boardSizeValueLarge: + case DefaultActivitySettings.boardSizeValueLarge: gridWidth = 4; break; - case DefaultGameSettings.boardSizeValueExtraLarge: + case DefaultActivitySettings.boardSizeValueExtraLarge: gridWidth = 5; break; default: diff --git a/lib/ui/parameters/parameter_widget.dart b/lib/common/ui/parameters/parameter_widget.dart similarity index 81% rename from lib/ui/parameters/parameter_widget.dart rename to lib/common/ui/parameters/parameter_widget.dart index 915121d37ee180ebdbc7d11ffab0c28840eb93f8..ed4865252df923f835db4eb5d4f75fbbac09003c 100644 --- a/lib/ui/parameters/parameter_widget.dart +++ b/lib/common/ui/parameters/parameter_widget.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; - import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:jeweled/config/default_game_settings.dart'; +import 'package:jeweled/common/ui/parameters/parameter_painter.dart'; + +import 'package:jeweled/config/default_activity_settings.dart'; import 'package:jeweled/config/default_global_settings.dart'; -import 'package:jeweled/models/settings/settings_game.dart'; +import 'package:jeweled/models/settings/settings_activity.dart'; import 'package:jeweled/models/settings/settings_global.dart'; -import 'package:jeweled/ui/parameters/parameter_painter.dart'; class ParameterWidget extends StatelessWidget { const ParameterWidget({ @@ -15,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, }); @@ -24,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; @@ -38,10 +38,10 @@ class ParameterWidget extends StatelessWidget { Widget content = const SizedBox.shrink(); switch (code) { - case DefaultGameSettings.parameterCodeColorsCount: + case DefaultActivitySettings.parameterCodeColorsCount: content = getColorsCountParameterItem(); break; - case DefaultGameSettings.parameterCodeBoardSize: + case DefaultActivitySettings.parameterCodeBoardSize: content = getBoardSizeParameterItem(); break; case DefaultGlobalSettings.parameterCodeColorsTheme: @@ -83,16 +83,16 @@ class ParameterWidget extends StatelessWidget { Color backgroundColor = Colors.grey; switch (value) { - case DefaultGameSettings.colorsCountValueLow: + case DefaultActivitySettings.colorsCountValueLow: backgroundColor = Colors.green; break; - case DefaultGameSettings.colorsCountValueMedium: + case DefaultActivitySettings.colorsCountValueMedium: backgroundColor = Colors.orange; break; - case DefaultGameSettings.colorsCountValueHigh: + case DefaultActivitySettings.colorsCountValueHigh: backgroundColor = Colors.red; break; - case DefaultGameSettings.colorsCountValueVeryHigh: + case DefaultActivitySettings.colorsCountValueVeryHigh: backgroundColor = Colors.purple; break; default: @@ -108,7 +108,7 @@ class ParameterWidget extends StatelessWidget { painter: ParameterPainter( code: code, value: value, - gameSettings: gameSettings, + activitySettings: activitySettings, globalSettings: globalSettings, ), isComplex: true, @@ -120,16 +120,16 @@ class ParameterWidget extends StatelessWidget { Color backgroundColor = Colors.grey; switch (value) { - case DefaultGameSettings.boardSizeValueSmall: + case DefaultActivitySettings.boardSizeValueSmall: backgroundColor = Colors.green; break; - case DefaultGameSettings.boardSizeValueMedium: + case DefaultActivitySettings.boardSizeValueMedium: backgroundColor = Colors.orange; break; - case DefaultGameSettings.boardSizeValueLarge: + case DefaultActivitySettings.boardSizeValueLarge: backgroundColor = Colors.red; break; - case DefaultGameSettings.boardSizeValueExtraLarge: + case DefaultActivitySettings.boardSizeValueExtraLarge: backgroundColor = Colors.purple; break; default: @@ -145,7 +145,7 @@ class ParameterWidget extends StatelessWidget { painter: ParameterPainter( code: code, value: value, - gameSettings: gameSettings, + activitySettings: activitySettings, globalSettings: globalSettings, ), isComplex: true, @@ -165,7 +165,7 @@ class ParameterWidget extends StatelessWidget { painter: ParameterPainter( code: code, value: value, - gameSettings: gameSettings, + activitySettings: activitySettings, globalSettings: globalSettings, ), isComplex: true, @@ -185,7 +185,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..5eca4ca9e13cd2ec540a838a4559a0b8e9274738 --- /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:jeweled/common/config/activity_page.dart'; +import 'package:jeweled/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..8c9cd93a5b2d5e12df69c6bd4fa0d0fc7e4f5be2 --- /dev/null +++ b/lib/config/application_config.dart @@ -0,0 +1,3 @@ +class ApplicationConfig { + static const String appTitle = 'Jeweled'; +} diff --git a/lib/config/default_game_settings.dart b/lib/config/default_activity_settings.dart similarity index 91% rename from lib/config/default_game_settings.dart rename to lib/config/default_activity_settings.dart index ae24687f10b5f56c8c1cf2dbdec7c3365e1e1ba9..5439a6cabe00ee4198467b13b66a65a07bb784d9 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 parameterCodeBoardSize = 'boardSize'; static const String parameterCodeColorsCount = 'colorsCount'; @@ -41,9 +41,9 @@ class DefaultGameSettings { static List<String> getAvailableValues(String parameterCode) { switch (parameterCode) { case parameterCodeBoardSize: - return DefaultGameSettings.allowedBoardSizeValues; + return DefaultActivitySettings.allowedBoardSizeValues; case parameterCodeColorsCount: - return DefaultGameSettings.allowedColorsCountValues; + return DefaultActivitySettings.allowedColorsCountValues; } 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 af477181778261bb6a08e5886855219e6df5c719..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:jeweled/ui/screens/page_game.dart'; -import 'package:jeweled/ui/screens/page_settings.dart'; -import 'package:jeweled/ui/screens/page_about.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..55b5ba485d032a7eb1fe64d8a8968a3fb9f26e18 --- /dev/null +++ b/lib/cubit/activity/activity_cubit.dart @@ -0,0 +1,188 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; + +import 'package:jeweled/config/default_activity_settings.dart'; +import 'package:jeweled/models/activity/cell_location.dart'; +import 'package:jeweled/models/activity/activity.dart'; +import 'package:jeweled/models/settings/settings_activity.dart'; +import 'package:jeweled/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, + // Base data + board: state.currentActivity.board, + // Game data + shuffledColors: state.currentActivity.shuffledColors, + availableBlocksCount: state.currentActivity.availableBlocksCount, + score: state.currentActivity.score, + movesCount: state.currentActivity.movesCount, + ); + // game.dump(); + + updateState(activity); + } + + void startNewActivity({ + required ActivitySettings activitySettings, + required GlobalSettings globalSettings, + }) { + final Activity newActivity = Activity.createNew( + activitySettings: activitySettings, + globalSettings: globalSettings, + ); + + newActivity.dump(); + + updateState(newActivity); + postAnimate(); + } + + 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 updateCellValue(CellLocation locationToUpdate, int? value) { + state.currentActivity.updateCellValue(locationToUpdate, value); + refresh(); + } + + void increaseMovesCount() { + state.currentActivity.isStarted = true; + state.currentActivity.increaseMovesCount(); + refresh(); + } + + void increaseScore(int increment) { + state.currentActivity.increaseScore(increment); + refresh(); + } + + void updateAvailableBlocksCount() { + state.currentActivity.updateAvailableBlocksCount(); + refresh(); + } + + void updateGameIsFinished(bool gameIsFinished) { + state.currentActivity.isFinished = gameIsFinished; + refresh(); + } + + void shuffleColors(final String colorsTheme) { + state.currentActivity.shuffleColorsAgain(colorsTheme); + } + + moveCellsDown() { + final Activity currentActivity = state.currentActivity; + + final int boardSizeHorizontal = currentActivity.activitySettings.boardSizeValue; + final int boardSizeVertical = currentActivity.activitySettings.boardSizeValue; + + for (int row = 0; row < boardSizeVertical; row++) { + for (int col = 0; col < boardSizeHorizontal; col++) { + // empty cell? + if (currentActivity.getCellValue(CellLocation.go(row, col)) == null) { + // move cells down + for (int r = row; r > 0; r--) { + updateCellValue(CellLocation.go(r, col), + currentActivity.getCellValue(CellLocation.go(r - 1, col))); + } + // fill top empty cell + updateCellValue(CellLocation.go(0, col), + currentActivity.getFillValue(CellLocation.go(row, col))); + } + } + } + } + + void deleteBlock(List<CellLocation> block) { + // Sort cells from top to bottom + block.sort((cell1, cell2) => cell1.row.compareTo(cell2.row)); + // Delete all cells + for (CellLocation blockItemToDelete in block) { + updateCellValue(blockItemToDelete, null); + } + } + + int getScoreFromBlock(int blockSize) { + return 3 * (blockSize - 2); + } + + List<CellLocation> tapOnCell(CellLocation tappedCellLocation) { + final Activity currentActivity = state.currentActivity; + + final int? cellValue = currentActivity.getCellValue(tappedCellLocation); + + if (cellValue != null) { + List<CellLocation> blockCells = currentActivity.getSiblingCells(tappedCellLocation, []); + if (blockCells.length >= DefaultActivitySettings.blockMinimumCellsCount) { + deleteBlock(blockCells); + increaseMovesCount(); + increaseScore(getScoreFromBlock(blockCells.length)); + return blockCells; + } + } + + return []; + } + + void postAnimate() { + moveCellsDown(); + updateAvailableBlocksCount(); + refresh(); + + if (!state.currentActivity.hasAtLeastOneAvailableBlock()) { + printlog('no more block found. finish game.'); + updateGameIsFinished(true); + } + } + + @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 3bddd1a6a48b47449cc665378cb09d0b1c2f4bc7..0000000000000000000000000000000000000000 --- a/lib/cubit/game_cubit.dart +++ /dev/null @@ -1,188 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; - -import 'package:jeweled/config/default_game_settings.dart'; -import 'package:jeweled/models/game/cell_location.dart'; -import 'package:jeweled/models/game/game.dart'; -import 'package:jeweled/models/settings/settings_game.dart'; -import 'package:jeweled/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, - // Base data - board: state.currentGame.board, - // Game data - shuffledColors: state.currentGame.shuffledColors, - availableBlocksCount: state.currentGame.availableBlocksCount, - score: state.currentGame.score, - movesCount: state.currentGame.movesCount, - ); - // game.dump(); - - updateState(game); - } - - void startNewGame({ - required GameSettings gameSettings, - required GlobalSettings globalSettings, - }) { - final Game newGame = Game.createNew( - gameSettings: gameSettings, - globalSettings: globalSettings, - ); - - newGame.dump(); - - updateState(newGame); - postAnimate(); - } - - 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 updateCellValue(CellLocation locationToUpdate, int? value) { - state.currentGame.updateCellValue(locationToUpdate, value); - refresh(); - } - - void increaseMovesCount() { - state.currentGame.isStarted = true; - state.currentGame.increaseMovesCount(); - refresh(); - } - - void increaseScore(int increment) { - state.currentGame.increaseScore(increment); - refresh(); - } - - void updateAvailableBlocksCount() { - state.currentGame.updateAvailableBlocksCount(); - refresh(); - } - - void updateGameIsFinished(bool gameIsFinished) { - state.currentGame.isFinished = gameIsFinished; - refresh(); - } - - void shuffleColors(final String colorsTheme) { - state.currentGame.shuffleColorsAgain(colorsTheme); - } - - moveCellsDown() { - final Game currentGame = state.currentGame; - - final int boardSizeHorizontal = currentGame.gameSettings.boardSizeValue; - final int boardSizeVertical = currentGame.gameSettings.boardSizeValue; - - for (int row = 0; row < boardSizeVertical; row++) { - for (int col = 0; col < boardSizeHorizontal; col++) { - // empty cell? - if (currentGame.getCellValue(CellLocation.go(row, col)) == null) { - // move cells down - for (int r = row; r > 0; r--) { - updateCellValue(CellLocation.go(r, col), - currentGame.getCellValue(CellLocation.go(r - 1, col))); - } - // fill top empty cell - updateCellValue( - CellLocation.go(0, col), currentGame.getFillValue(CellLocation.go(row, col))); - } - } - } - } - - void deleteBlock(List<CellLocation> block) { - // Sort cells from top to bottom - block.sort((cell1, cell2) => cell1.row.compareTo(cell2.row)); - // Delete all cells - for (CellLocation blockItemToDelete in block) { - updateCellValue(blockItemToDelete, null); - } - } - - int getScoreFromBlock(int blockSize) { - return 3 * (blockSize - 2); - } - - List<CellLocation> tapOnCell(CellLocation tappedCellLocation) { - final Game currentGame = state.currentGame; - - final int? cellValue = currentGame.getCellValue(tappedCellLocation); - - if (cellValue != null) { - List<CellLocation> blockCells = currentGame.getSiblingCells(tappedCellLocation, []); - if (blockCells.length >= DefaultGameSettings.blockMinimumCellsCount) { - deleteBlock(blockCells); - increaseMovesCount(); - increaseScore(getScoreFromBlock(blockCells.length)); - return blockCells; - } - } - - return []; - } - - void postAnimate() { - moveCellsDown(); - updateAvailableBlocksCount(); - refresh(); - - if (!state.currentGame.hasAtLeastOneAvailableBlock()) { - printlog('no more block found. finish game.'); - updateGameIsFinished(true); - } - } - - @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 cbccfa80ea6e54c065ae68e030969af8766c19dc..0000000000000000000000000000000000000000 --- a/lib/cubit/nav_cubit.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; - -import 'package:jeweled/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..338113b14938e0569ef48fd6387eb290aa01447f --- /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:jeweled/config/default_activity_settings.dart'; +import 'package:jeweled/models/settings/settings_activity.dart'; + +part 'settings_activity_state.dart'; + +class ActivitySettingsCubit extends HydratedCubit<ActivitySettingsState> { + ActivitySettingsCubit() + : super(ActivitySettingsState(settings: ActivitySettings.createDefault())); + + void setValues({ + String? boardSize, + String? colorsCount, + }) { + emit( + ActivitySettingsState( + settings: ActivitySettings( + boardSize: boardSize ?? state.settings.boardSize, + colorsCount: colorsCount ?? state.settings.colorsCount, + ), + ), + ); + } + + String getParameterValue(String code) { + switch (code) { + case DefaultActivitySettings.parameterCodeBoardSize: + return ActivitySettings.getBoardSizeValueFromUnsafe(state.settings.boardSize); + case DefaultActivitySettings.parameterCodeColorsCount: + return ActivitySettings.getColorsCountValueFromUnsafe(state.settings.colorsCount); + } + + return ''; + } + + void setParameterValue(String code, String value) { + final String boardSize = code == DefaultActivitySettings.parameterCodeBoardSize + ? value + : getParameterValue(DefaultActivitySettings.parameterCodeBoardSize); + final String colorsCount = code == DefaultActivitySettings.parameterCodeColorsCount + ? value + : getParameterValue(DefaultActivitySettings.parameterCodeColorsCount); + + setValues( + boardSize: boardSize, + colorsCount: colorsCount, + ); + } + + @override + ActivitySettingsState? fromJson(Map<String, dynamic> json) { + final String boardSize = json[DefaultActivitySettings.parameterCodeBoardSize] as String; + final String colorsCount = + json[DefaultActivitySettings.parameterCodeColorsCount] as String; + + return ActivitySettingsState( + settings: ActivitySettings( + boardSize: boardSize, + colorsCount: colorsCount, + ), + ); + } + + @override + Map<String, dynamic>? toJson(ActivitySettingsState state) { + return <String, dynamic>{ + DefaultActivitySettings.parameterCodeBoardSize: state.settings.boardSize, + DefaultActivitySettings.parameterCodeColorsCount: state.settings.colorsCount, + }; + } +} 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 2aee5b73bceae98dfab3c2ab57ca352defaedd0c..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:jeweled/config/default_game_settings.dart'; -import 'package:jeweled/models/settings/settings_game.dart'; - -part 'settings_game_state.dart'; - -class GameSettingsCubit extends HydratedCubit<GameSettingsState> { - GameSettingsCubit() : super(GameSettingsState(settings: GameSettings.createDefault())); - - void setValues({ - String? boardSize, - String? colorsCount, - }) { - emit( - GameSettingsState( - settings: GameSettings( - boardSize: boardSize ?? state.settings.boardSize, - colorsCount: colorsCount ?? state.settings.colorsCount, - ), - ), - ); - } - - String getParameterValue(String code) { - switch (code) { - case DefaultGameSettings.parameterCodeBoardSize: - return GameSettings.getBoardSizeValueFromUnsafe(state.settings.boardSize); - case DefaultGameSettings.parameterCodeColorsCount: - return GameSettings.getColorsCountValueFromUnsafe(state.settings.colorsCount); - } - - return ''; - } - - void setParameterValue(String code, String value) { - final String boardSize = code == DefaultGameSettings.parameterCodeBoardSize - ? value - : getParameterValue(DefaultGameSettings.parameterCodeBoardSize); - final String colorsCount = code == DefaultGameSettings.parameterCodeColorsCount - ? value - : getParameterValue(DefaultGameSettings.parameterCodeColorsCount); - - setValues( - boardSize: boardSize, - colorsCount: colorsCount, - ); - } - - @override - GameSettingsState? fromJson(Map<String, dynamic> json) { - final String boardSize = json[DefaultGameSettings.parameterCodeBoardSize] as String; - final String colorsCount = json[DefaultGameSettings.parameterCodeColorsCount] as String; - - return GameSettingsState( - settings: GameSettings( - boardSize: boardSize, - colorsCount: colorsCount, - ), - ); - } - - @override - Map<String, dynamic>? toJson(GameSettingsState state) { - return <String, dynamic>{ - DefaultGameSettings.parameterCodeBoardSize: state.settings.boardSize, - DefaultGameSettings.parameterCodeColorsCount: state.settings.colorsCount, - }; - } -} 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 9595cd38261efc7ae25643c5c4bdb879579aa013..76cf91c4643e574b2ea782263350e1e05333ef56 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:jeweled/cubit/game_cubit.dart'; -import 'package:jeweled/cubit/nav_cubit.dart'; -import 'package:jeweled/cubit/settings_game_cubit.dart'; -import 'package:jeweled/cubit/settings_global_cubit.dart'; +import 'package:jeweled/common/cubit/nav/nav_cubit_pages.dart'; +import 'package:jeweled/common/cubit/nav/nav_cubit_screens.dart'; + +import 'package:jeweled/config/application_config.dart'; +import 'package:jeweled/cubit/activity/activity_cubit.dart'; +import 'package:jeweled/cubit/settings/settings_activity_cubit.dart'; +import 'package:jeweled/cubit/settings/settings_global_cubit.dart'; import 'package:jeweled/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: 'Jeweled', + title: ApplicationConfig.appTitle, home: const SkeletonScreen(), // Theme stuff diff --git a/lib/models/game/game.dart b/lib/models/activity/activity.dart similarity index 81% rename from lib/models/game/game.dart rename to lib/models/activity/activity.dart index 26f71becea327b6c2881ec54613213604f86863c..c955f3afe5e6d9a3d7bd840778a4e983f154a806 100644 --- a/lib/models/game/game.dart +++ b/lib/models/activity/activity.dart @@ -4,16 +4,16 @@ import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; import 'package:jeweled/config/color_theme.dart'; import 'package:jeweled/config/default_global_settings.dart'; -import 'package:jeweled/models/game/board.dart'; -import 'package:jeweled/models/game/cell.dart'; -import 'package:jeweled/models/game/cell_location.dart'; -import 'package:jeweled/models/settings/settings_game.dart'; +import 'package:jeweled/models/activity/board.dart'; +import 'package:jeweled/models/activity/cell.dart'; +import 'package:jeweled/models/activity/cell_location.dart'; +import 'package:jeweled/models/settings/settings_activity.dart'; import 'package:jeweled/models/settings/settings_global.dart'; -class Game { - Game({ +class Activity { + Activity({ // Settings - required this.gameSettings, + required this.activitySettings, required this.globalSettings, // State @@ -33,7 +33,7 @@ class Game { }); // Settings - final GameSettings gameSettings; + final ActivitySettings activitySettings; final GlobalSettings globalSettings; // State @@ -51,10 +51,10 @@ class Game { int score; int movesCount; - factory Game.createEmpty() { - return Game( + factory Activity.createEmpty() { + return Activity( // Settings - gameSettings: GameSettings.createDefault(), + activitySettings: ActivitySettings.createDefault(), globalSettings: GlobalSettings.createDefault(), // Base data board: Board.createEmpty(), @@ -63,21 +63,22 @@ class Game { ); } - 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(); - return Game( + return Activity( // Settings - gameSettings: newGameSettings, + activitySettings: newActivitySettings, globalSettings: newGlobalSettings, // State isRunning: true, // Base data - board: Board.createRandom(newGameSettings), + board: Board.createRandom(newActivitySettings), // Game data shuffledColors: shuffleColors(newGlobalSettings.colorsTheme), ); @@ -132,8 +133,8 @@ class Game { final CellLocation referenceCellLocation, List<CellLocation> siblingCells, ) { - final int boardSizeHorizontal = gameSettings.boardSizeValue; - final int boardSizeVertical = gameSettings.boardSizeValue; + final int boardSizeHorizontal = activitySettings.boardSizeValue; + final int boardSizeVertical = activitySettings.boardSizeValue; final int? referenceValue = getCellValue(referenceCellLocation); @@ -168,16 +169,16 @@ class Game { return siblingCells; } - List<List<CellLocation>> getAvailableBlocks(final Game game) { - final int boardSizeHorizontal = game.gameSettings.boardSizeValue; - final int boardSizeVertical = game.gameSettings.boardSizeValue; + List<List<CellLocation>> getAvailableBlocks(final Activity activity) { + final int boardSizeHorizontal = activity.activitySettings.boardSizeValue; + final int boardSizeVertical = activity.activitySettings.boardSizeValue; final List<List<CellLocation>> blocks = []; for (int row = 0; row < boardSizeVertical; row++) { for (int col = 0; col < boardSizeHorizontal; col++) { final CellLocation cellLocation = CellLocation.go(row, col); - if (game.getCellValue(cellLocation) != null) { + if (activity.getCellValue(cellLocation) != null) { // if current cell not already in a found block bool alreadyFound = false; @@ -189,7 +190,7 @@ class Game { } } if (!alreadyFound) { - final List<CellLocation> block = game.getSiblingCells(cellLocation, []); + final List<CellLocation> block = activity.getSiblingCells(cellLocation, []); if (block.length >= 3) { blocks.add(block); } @@ -202,8 +203,8 @@ class Game { } bool hasAtLeastOneAvailableBlock() { - final int boardSizeHorizontal = gameSettings.boardSizeValue; - final int boardSizeVertical = gameSettings.boardSizeValue; + final int boardSizeHorizontal = activitySettings.boardSizeValue; + final int boardSizeVertical = activitySettings.boardSizeValue; for (int row = 0; row < boardSizeVertical; row++) { for (int col = 0; col < boardSizeHorizontal; col++) { @@ -223,7 +224,7 @@ class Game { } bool isInBoard(CellLocation cell) { - final int boardSize = gameSettings.boardSizeValue; + final int boardSize = activitySettings.boardSizeValue; if (cell.row > 0 && cell.row < boardSize && cell.col > 0 && cell.col < boardSize) { return true; @@ -239,14 +240,14 @@ class Game { final List<int> values = []; // All eligible values (twice) - final int maxValue = gameSettings.colorsCountValue; + final int maxValue = activitySettings.colorsCountValue; for (int i = 1; i <= maxValue; i++) { values.add(i); values.add(i); } // Add values of current col (twice) - for (int r = 0; r <= gameSettings.boardSizeValue; r++) { + for (int r = 0; r <= activitySettings.boardSizeValue; r++) { if (isInBoard(CellLocation.go(r, col))) { final int? value = getCellValue(CellLocation.go(r, col)); if (value != null) { @@ -259,12 +260,12 @@ class Game { // Add values of sibling cols (twice for top rows) for (int deltaCol = -1; deltaCol <= 1; deltaCol++) { final int c = col + deltaCol; - for (int r = 0; r < gameSettings.boardSizeValue; r++) { + for (int r = 0; r < activitySettings.boardSizeValue; r++) { if (isInBoard(CellLocation.go(r, c))) { final int? value = getCellValue(CellLocation.go(r, c)); if (value != null) { values.add(value); - if (row < gameSettings.boardSizeValue / 3) { + if (row < activitySettings.boardSizeValue / 3) { values.add(value); } } @@ -294,9 +295,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'); @@ -315,13 +316,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/board.dart b/lib/models/activity/board.dart similarity index 77% rename from lib/models/game/board.dart rename to lib/models/activity/board.dart index bc3dfc04658c05c10d437b8c9a132e4848461e21..af933d168b0c5e77acb84edc7200e8c994c5f330 100644 --- a/lib/models/game/board.dart +++ b/lib/models/activity/board.dart @@ -2,8 +2,8 @@ import 'dart:math'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:jeweled/models/game/cell.dart'; -import 'package:jeweled/models/settings/settings_game.dart'; +import 'package:jeweled/models/activity/cell.dart'; +import 'package:jeweled/models/settings/settings_activity.dart'; class Board { final List<List<Cell>> cells; @@ -16,10 +16,10 @@ class Board { return Board(cells: []); } - factory Board.createRandom(GameSettings gameSettings) { - final int boardSizeHorizontal = gameSettings.boardSizeValue; - final int boardSizeVertical = gameSettings.boardSizeValue; - final int maxValue = gameSettings.colorsCountValue; + factory Board.createRandom(ActivitySettings activitySettings) { + final int boardSizeHorizontal = activitySettings.boardSizeValue; + final int boardSizeVertical = activitySettings.boardSizeValue; + final int maxValue = activitySettings.colorsCountValue; final rand = Random(); diff --git a/lib/models/game/cell.dart b/lib/models/activity/cell.dart similarity index 100% rename from lib/models/game/cell.dart rename to lib/models/activity/cell.dart diff --git a/lib/models/game/cell_location.dart b/lib/models/activity/cell_location.dart similarity index 100% rename from lib/models/game/cell_location.dart rename to lib/models/activity/cell_location.dart diff --git a/lib/models/settings/settings_activity.dart b/lib/models/settings/settings_activity.dart new file mode 100644 index 0000000000000000000000000000000000000000..106c7a12d94820bb83fec32c206f449cc1cc5495 --- /dev/null +++ b/lib/models/settings/settings_activity.dart @@ -0,0 +1,59 @@ +import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; + +import 'package:jeweled/config/default_activity_settings.dart'; + +class ActivitySettings { + final String boardSize; + final String colorsCount; + + ActivitySettings({ + required this.boardSize, + required this.colorsCount, + }); + + // Getters to convert String to int + int get boardSizeValue => int.parse(boardSize); + int get colorsCountValue => int.parse(colorsCount); + + static String getBoardSizeValueFromUnsafe(String size) { + if (DefaultActivitySettings.allowedBoardSizeValues.contains(size)) { + return size; + } + + return DefaultActivitySettings.defaultBoardSizeValue; + } + + static String getColorsCountValueFromUnsafe(String colorsCount) { + if (DefaultActivitySettings.allowedColorsCountValues.contains(colorsCount)) { + return colorsCount; + } + + return DefaultActivitySettings.defaultColorsCountValue; + } + + factory ActivitySettings.createDefault() { + return ActivitySettings( + boardSize: DefaultActivitySettings.defaultBoardSizeValue, + colorsCount: DefaultActivitySettings.defaultColorsCountValue, + ); + } + + void dump() { + printlog('$ActivitySettings:'); + printlog(' ${DefaultActivitySettings.parameterCodeBoardSize}: $boardSize'); + printlog(' ${DefaultActivitySettings.parameterCodeColorsCount}: $colorsCount'); + printlog(''); + } + + @override + String toString() { + return '$ActivitySettings(${toJson()})'; + } + + Map<String, dynamic>? toJson() { + return <String, dynamic>{ + DefaultActivitySettings.parameterCodeBoardSize: boardSize, + DefaultActivitySettings.parameterCodeColorsCount: colorsCount, + }; + } +} diff --git a/lib/models/settings/settings_game.dart b/lib/models/settings/settings_game.dart deleted file mode 100644 index c733c1a34fafbf2e8f85b104e7549cd98472d076..0000000000000000000000000000000000000000 --- a/lib/models/settings/settings_game.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; - -import 'package:jeweled/config/default_game_settings.dart'; - -class GameSettings { - final String boardSize; - final String colorsCount; - - GameSettings({ - required this.boardSize, - required this.colorsCount, - }); - - // Getters to convert String to int - int get boardSizeValue => int.parse(boardSize); - int get colorsCountValue => int.parse(colorsCount); - - static String getBoardSizeValueFromUnsafe(String size) { - if (DefaultGameSettings.allowedBoardSizeValues.contains(size)) { - return size; - } - - return DefaultGameSettings.defaultBoardSizeValue; - } - - static String getColorsCountValueFromUnsafe(String colorsCount) { - if (DefaultGameSettings.allowedColorsCountValues.contains(colorsCount)) { - return colorsCount; - } - - return DefaultGameSettings.defaultColorsCountValue; - } - - factory GameSettings.createDefault() { - return GameSettings( - boardSize: DefaultGameSettings.defaultBoardSizeValue, - colorsCount: DefaultGameSettings.defaultColorsCountValue, - ); - } - - void dump() { - printlog('$GameSettings:'); - printlog(' ${DefaultGameSettings.parameterCodeBoardSize}: $boardSize'); - printlog(' ${DefaultGameSettings.parameterCodeColorsCount}: $colorsCount'); - printlog(''); - } - - @override - String toString() { - return '$GameSettings(${toJson()})'; - } - - Map<String, dynamic>? toJson() { - return <String, dynamic>{ - DefaultGameSettings.parameterCodeBoardSize: boardSize, - DefaultGameSettings.parameterCodeColorsCount: colorsCount, - }; - } -} diff --git a/lib/ui/game/game_top.dart b/lib/ui/game/game_top.dart index 920171a2ea06063b40224775558459da3218a484..6fc6299d347016ee95d78b21c2cda1dc737e0bf0 100644 --- a/lib/ui/game/game_top.dart +++ b/lib/ui/game/game_top.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:jeweled/cubit/game_cubit.dart'; -import 'package:jeweled/models/game/game.dart'; +import 'package:jeweled/cubit/activity/activity_cubit.dart'; +import 'package:jeweled/models/activity/activity.dart'; import 'package:jeweled/ui/widgets/indicators/indicator_available_blocks.dart'; import 'package:jeweled/ui/widgets/indicators/indicator_moves_count.dart'; import 'package:jeweled/ui/widgets/indicators/indicator_score.dart'; @@ -13,24 +13,24 @@ class GameTopWidget 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 Column( children: [ - ScoreIndicator(game: currentGame), + ScoreIndicator(activity: currentActivity), Table( defaultVerticalAlignment: TableCellVerticalAlignment.middle, children: [ TableRow( children: [ Center( - child: MovesCountsIndicator(game: currentGame), + child: MovesCountsIndicator(activity: currentActivity), ), - AvailableBlocksCountIndicator(game: currentGame), + AvailableBlocksCountIndicator(activity: currentActivity), Center( - child: ShuffleButton(game: currentGame), + child: ShuffleButton(activity: currentActivity), ), ], ) diff --git a/lib/ui/layouts/parameters_layout.dart b/lib/ui/layouts/parameters_layout.dart deleted file mode 100644 index f702b4fed1763ddd1b4c9e01132552340ba5fc4f..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:jeweled/config/default_game_settings.dart'; -import 'package:jeweled/config/default_global_settings.dart'; -import 'package:jeweled/cubit/settings_game_cubit.dart'; -import 'package:jeweled/cubit/settings_global_cubit.dart'; -import 'package:jeweled/ui/parameters/parameter_widget.dart'; -import 'package:jeweled/ui/widgets/actions/button_delete_saved_game.dart'; -import 'package:jeweled/ui/widgets/actions/button_game_start_new.dart'; -import 'package:jeweled/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/painters/game_board_painter.dart b/lib/ui/painters/game_board_painter.dart index f1523f0a72dec7303cc4fedc09ac0c34c61190fb..989a1141c122cf3ad80b4522414bc85697e2b4d9 100644 --- a/lib/ui/painters/game_board_painter.dart +++ b/lib/ui/painters/game_board_painter.dart @@ -6,21 +6,21 @@ import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; import 'package:jeweled/config/color_theme.dart'; import 'package:jeweled/config/default_global_settings.dart'; -import 'package:jeweled/models/game/cell_location.dart'; -import 'package:jeweled/models/game/game.dart'; +import 'package:jeweled/models/activity/cell_location.dart'; +import 'package:jeweled/models/activity/activity.dart'; class GameBoardPainter extends CustomPainter { const GameBoardPainter({ - required this.game, + required this.activity, required this.animations, }); - final Game game; + final Activity activity; final List<List<Animation<double>?>> animations; @override void paint(Canvas canvas, Size size) { - String graphicTheme = game.globalSettings.graphicTheme; + String graphicTheme = activity.globalSettings.graphicTheme; final double canvasSize = min(size.width, size.height); @@ -32,14 +32,14 @@ class GameBoardPainter extends CustomPainter { drawBoard( canvas: canvas, canvasSize: canvasSize, - game: game, + activity: activity, ); break; case DefaultGlobalSettings.graphicThemeGradientAndBorder: drawBoard( canvas: canvas, canvasSize: canvasSize, - game: game, + activity: activity, borderWidth: cellBorderWidth, gradientFrom: -10, gradientTo: 10, @@ -49,7 +49,7 @@ class GameBoardPainter extends CustomPainter { drawBoard( canvas: canvas, canvasSize: canvasSize, - game: game, + activity: activity, contentStrings: DefaultGlobalSettings.graphicThemeContentEmojiStrings, ); break; @@ -57,7 +57,7 @@ class GameBoardPainter extends CustomPainter { drawBoard( canvas: canvas, canvasSize: canvasSize, - game: game, + activity: activity, contentStrings: DefaultGlobalSettings.graphicThemeContentPatternStrings, ); break; @@ -90,16 +90,16 @@ class GameBoardPainter extends CustomPainter { void drawBoard({ required Canvas canvas, required double canvasSize, - required Game game, + required Activity activity, double overlapping = 1, int gradientFrom = 0, int gradientTo = 0, double borderWidth = 0, List<String>? contentStrings, }) { - final int cellsCountHorizontal = game.gameSettings.boardSizeValue; - final int cellsCountVertical = game.gameSettings.boardSizeValue; - final String colorsTheme = game.globalSettings.colorsTheme; + final int cellsCountHorizontal = activity.activitySettings.boardSizeValue; + final int cellsCountVertical = activity.activitySettings.boardSizeValue; + final String colorsTheme = activity.globalSettings.colorsTheme; final double size = canvasSize / max(cellsCountHorizontal, cellsCountVertical); @@ -109,7 +109,7 @@ class GameBoardPainter extends CustomPainter { final double x = size * col; final CellLocation cellLocation = CellLocation.go(row, col); - final int? cellValue = game.getCellValueShuffled(cellLocation); + final int? cellValue = activity.getCellValueShuffled(cellLocation); if (cellValue != null) { final Animation<double>? translation = animations[row][col]; final double y = yOrigin + (translation?.value ?? 0) * size; @@ -187,7 +187,7 @@ class GameBoardPainter extends CustomPainter { // draw content if (contentStrings != null) { final int emojiIndex = - cellValue - 1 + game.shuffledColors[0] + 2 * game.shuffledColors[1]; + cellValue - 1 + activity.shuffledColors[0] + 2 * activity.shuffledColors[1]; final String text = contentStrings[emojiIndex % contentStrings.length]; final textSpan = TextSpan( diff --git a/lib/ui/screens/page_game.dart b/lib/ui/screens/page_game.dart deleted file mode 100644 index 46ca3b5d6c3b834f58e3d80b7ccbe765aa9ac1d9..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:jeweled/cubit/game_cubit.dart'; -import 'package:jeweled/models/game/game.dart'; -import 'package:jeweled/ui/layouts/game_layout.dart'; -import 'package:jeweled/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 36e5e54add8564bd81b23895aebcb34e7510c682..d778b5152e3a30329dfa268a21178261e6c7d219 100644 --- a/lib/ui/skeleton.dart +++ b/lib/ui/skeleton.dart @@ -1,24 +1,37 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:jeweled/config/menu.dart'; -import 'package:jeweled/cubit/nav_cubit.dart'; -import 'package:jeweled/ui/widgets/global_app_bar.dart'; +import 'package:jeweled/common/config/activity_page.dart'; +import 'package:jeweled/common/config/screen.dart'; +import 'package:jeweled/common/cubit/nav/nav_cubit_screens.dart'; +import 'package:jeweled/common/ui/nav/global_app_bar.dart'; +import 'package:jeweled/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: BlocBuilder<NavCubit, int>( - builder: (BuildContext context, int pageIndex) { - return Menu.getPageWidget(pageIndex); - }, - ), - backgroundColor: Theme.of(context).colorScheme.surface, + 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: 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 64d9ff2c01c2e31152e07598acdfaad0dffeeccc..e339b459f24d674ef3d43c4de52713d6b65feb80 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:jeweled/cubit/game_cubit.dart'; +import 'package:jeweled/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 73697c1de4a417e7581e2824745ee8fc353debb4..09a230851fd15fd6b63a57afaf6a10bb768c2cf5 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:jeweled/cubit/game_cubit.dart'; +import 'package:jeweled/common/cubit/nav/nav_cubit_pages.dart'; + +import 'package:jeweled/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 443ae93b82669ea8fade8b040c734763b46d36ea..20737af4c03cebb9da16206a8e80668548b28167 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:jeweled/cubit/game_cubit.dart'; -import 'package:jeweled/cubit/settings_game_cubit.dart'; -import 'package:jeweled/cubit/settings_global_cubit.dart'; +import 'package:jeweled/common/cubit/nav/nav_cubit_pages.dart'; + +import 'package:jeweled/cubit/activity/activity_cubit.dart'; +import 'package:jeweled/cubit/settings/settings_activity_cubit.dart'; +import 'package:jeweled/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 30eb40738d2c3d86852a4b1803459125ec42bd30..10f9f285b8f57cbe094ec61ec1bb97b2b275d253 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:jeweled/cubit/game_cubit.dart'; +import 'package:jeweled/common/cubit/nav/nav_cubit_pages.dart'; + +import 'package:jeweled/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/board.dart b/lib/ui/widgets/game/board.dart index 08db3c8a185a440a2a0cc064a712cbd4703e0003..e1c4ffa250a71e75ad648033226bfce971cbf5de 100644 --- a/lib/ui/widgets/game/board.dart +++ b/lib/ui/widgets/game/board.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:jeweled/cubit/game_cubit.dart'; -import 'package:jeweled/models/game/cell_location.dart'; -import 'package:jeweled/models/game/game.dart'; +import 'package:jeweled/cubit/activity/activity_cubit.dart'; +import 'package:jeweled/models/activity/cell_location.dart'; +import 'package:jeweled/models/activity/activity.dart'; import 'package:jeweled/ui/painters/game_board_painter.dart'; class BoardWidget extends StatefulWidget { @@ -29,8 +29,8 @@ class _BoardWidget extends State<BoardWidget> with TickerProviderStateMixin { } void animateCells(List<CellLocation> cellsToRemove) { - final GameCubit gameCubit = BlocProvider.of<GameCubit>(context); - final Game currentGame = gameCubit.state.currentGame; + final ActivityCubit activityCubit = BlocProvider.of<ActivityCubit>(context); + final Activity currentActivity = activityCubit.state.currentActivity; // "move down" cells final controller = AnimationController( @@ -55,9 +55,9 @@ class _BoardWidget extends State<BoardWidget> with TickerProviderStateMixin { )) ..addStatusListener((status) { if (status == AnimationStatus.completed) { - gameCubit.postAnimate(); + activityCubit.postAnimate(); - resetAnimations(currentGame.gameSettings.boardSizeValue); + resetAnimations(currentActivity.activitySettings.boardSizeValue); setState(() {}); controller.dispose(); @@ -66,9 +66,9 @@ class _BoardWidget extends State<BoardWidget> with TickerProviderStateMixin { // Count translation length for each cell to move final List<List<int>> stepsDownCounts = List.generate( - currentGame.gameSettings.boardSizeValue, + currentActivity.activitySettings.boardSizeValue, (i) => List.generate( - currentGame.gameSettings.boardSizeValue, + currentActivity.activitySettings.boardSizeValue, (i) => 0, ), ); @@ -91,7 +91,7 @@ class _BoardWidget extends State<BoardWidget> with TickerProviderStateMixin { controller.forward().orCancel; if (!needAnimation) { - gameCubit.postAnimate(); + activityCubit.postAnimate(); } } @@ -100,12 +100,12 @@ class _BoardWidget extends State<BoardWidget> with TickerProviderStateMixin { final double displayWidth = MediaQuery.of(context).size.width; 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; if (animations.isEmpty) { - resetAnimations(currentGame.gameSettings.boardSizeValue); + resetAnimations(currentActivity.activitySettings.boardSizeValue); } return GestureDetector( @@ -123,19 +123,19 @@ class _BoardWidget extends State<BoardWidget> with TickerProviderStateMixin { final double xTap = details.localPosition.dx; final double yTap = details.localPosition.dy; final int col = - xTap ~/ (displayWidth / currentGame.gameSettings.boardSizeValue); + xTap ~/ (displayWidth / currentActivity.activitySettings.boardSizeValue); final int row = - yTap ~/ (displayWidth / currentGame.gameSettings.boardSizeValue); + yTap ~/ (displayWidth / currentActivity.activitySettings.boardSizeValue); - animateCells( - BlocProvider.of<GameCubit>(context).tapOnCell(CellLocation.go(row, col))); + animateCells(BlocProvider.of<ActivityCubit>(context) + .tapOnCell(CellLocation.go(row, col))); } }, child: CustomPaint( size: Size(displayWidth, displayWidth), willChange: false, painter: GameBoardPainter( - game: currentGame, + activity: currentActivity, animations: animations, ), isComplex: true, diff --git a/lib/ui/widgets/indicators/indicator_available_blocks.dart b/lib/ui/widgets/indicators/indicator_available_blocks.dart index a7742e7da5eb1919a3bc7bc0c2472f489438c2d6..3f414d301da53501fc47ecd5cdbefcb66452f098 100644 --- a/lib/ui/widgets/indicators/indicator_available_blocks.dart +++ b/lib/ui/widgets/indicators/indicator_available_blocks.dart @@ -3,12 +3,12 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:jeweled/models/game/game.dart'; +import 'package:jeweled/models/activity/activity.dart'; class AvailableBlocksCountIndicator extends StatelessWidget { - const AvailableBlocksCountIndicator({super.key, required this.game}); + const AvailableBlocksCountIndicator({super.key, required this.activity}); - final Game game; + final Activity activity; @override Widget build(BuildContext context) { @@ -16,7 +16,8 @@ class AvailableBlocksCountIndicator extends StatelessWidget { const minBlocksCount = 6; // Normalized [0..1] value - final double barValue = min(minBlocksCount, game.availableBlocksCount) / minBlocksCount; + final double barValue = + min(minBlocksCount, activity.availableBlocksCount) / minBlocksCount; // Bar color: red < orange < green final Color baseColor = (barValue < 0.5 @@ -39,7 +40,7 @@ class AvailableBlocksCountIndicator extends StatelessWidget { borderRadius: const BorderRadius.all(Radius.circular(15)), ), OutlinedText( - text: game.availableBlocksCount.toString(), + text: activity.availableBlocksCount.toString(), fontSize: barHeight, textColor: textColor, outlineColor: outlineColor, diff --git a/lib/ui/widgets/indicators/indicator_moves_count.dart b/lib/ui/widgets/indicators/indicator_moves_count.dart index 7af1fd2504ead2345665b034d1b7ce1ad5c94a9a..b4ed2460897ba72a3d0fd2251eb7755d7cdeccf1 100644 --- a/lib/ui/widgets/indicators/indicator_moves_count.dart +++ b/lib/ui/widgets/indicators/indicator_moves_count.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:jeweled/models/game/game.dart'; +import 'package:jeweled/models/activity/activity.dart'; class MovesCountsIndicator extends StatelessWidget { - const MovesCountsIndicator({super.key, required this.game}); + const MovesCountsIndicator({super.key, required this.activity}); - final Game game; + final Activity activity; @override Widget build(BuildContext context) { @@ -14,7 +14,7 @@ class MovesCountsIndicator extends StatelessWidget { final Color outlineColor = baseColor.darken(); return OutlinedText( - text: game.movesCount.toString(), + text: activity.movesCount.toString(), fontSize: 40, textColor: baseColor, outlineColor: outlineColor, diff --git a/lib/ui/widgets/indicators/indicator_score.dart b/lib/ui/widgets/indicators/indicator_score.dart index 7d25e7e05dd01922a00dd95ffcbfada1ebddd762..563b36b170f0d23b5fb1006dd2c9013f9f0806fb 100644 --- a/lib/ui/widgets/indicators/indicator_score.dart +++ b/lib/ui/widgets/indicators/indicator_score.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:jeweled/models/game/game.dart'; +import 'package:jeweled/models/activity/activity.dart'; class ScoreIndicator extends StatelessWidget { - const ScoreIndicator({super.key, required this.game}); + const ScoreIndicator({super.key, required this.activity}); - final Game game; + final Activity activity; @override Widget build(BuildContext context) { @@ -14,7 +14,7 @@ class ScoreIndicator extends StatelessWidget { final Color outlineColor = baseColor.darken(); return OutlinedText( - text: game.score.toString(), + text: activity.score.toString(), fontSize: 70, textColor: baseColor, outlineColor: outlineColor, diff --git a/lib/ui/widgets/indicators/indicator_shuffle_button.dart b/lib/ui/widgets/indicators/indicator_shuffle_button.dart index 56f815c5c15e95cd5315d320af4d5b71f20a0cfa..63f206365397b7a27c6751428651df255e6061fc 100644 --- a/lib/ui/widgets/indicators/indicator_shuffle_button.dart +++ b/lib/ui/widgets/indicators/indicator_shuffle_button.dart @@ -1,13 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:jeweled/cubit/game_cubit.dart'; -import 'package:jeweled/models/game/game.dart'; +import 'package:jeweled/cubit/activity/activity_cubit.dart'; +import 'package:jeweled/models/activity/activity.dart'; class ShuffleButton extends StatelessWidget { - const ShuffleButton({super.key, required this.game}); + const ShuffleButton({super.key, required this.activity}); - final Game game; + final Activity activity; @override Widget build(BuildContext context) { @@ -22,7 +22,8 @@ class ShuffleButton extends StatelessWidget { outlineColor: outlineColor, ), onPressed: () { - BlocProvider.of<GameCubit>(context).shuffleColors(game.globalSettings.colorsTheme); + BlocProvider.of<ActivityCubit>(context) + .shuffleColors(activity.globalSettings.colorsTheme); }, ); } diff --git a/pubspec.lock b/pubspec.lock index 011ee2e27741f0e29cbc4b4aac803190885ffbae..5f7d30f1d27f049d10d751142b1e10ef2a65d726 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -245,10 +245,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: @@ -442,10 +442,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 2f31c3e76b5012ded63087d26b087dd90178968c..f170d6cb2bfb31773a34dfd4de426e4766cd2b4c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: Jeweled Game publish_to: "none" -version: 0.3.1+37 +version: 0.4.0+38 environment: sdk: "^3.0.0"