From 60a0755415cb2b74ceeaee843269503e908465cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Harrault?= <benoit@harrault.fr> Date: Tue, 22 Oct 2024 23:56:13 +0200 Subject: [PATCH] Normalize Activity application architecture --- assets/translations/en.json | 3 + assets/translations/fr.json | 3 + .../metadata/android/en-US/changelogs/78.txt | 1 + .../metadata/android/fr-FR/changelogs/78.txt | 1 + lib/common/config/activity_page.dart | 50 +++++ lib/common/config/screen.dart | 55 +++++ lib/common/cubit/nav/nav_cubit_pages.dart | 33 +++ lib/common/cubit/nav/nav_cubit_screens.dart | 37 ++++ lib/common/ui/nav/bottom_nav_bar.dart | 46 +++++ .../ui/nav}/global_app_bar.dart | 37 ++-- .../ui/pages/game.dart} | 16 +- lib/common/ui/pages/parameters.dart | 148 ++++++++++++++ .../ui/parameters/parameter_painter.dart | 28 +-- .../ui/parameters/parameter_widget.dart | 35 ++-- .../ui/screens/about.dart} | 4 +- lib/common/ui/screens/activity.dart | 18 ++ .../ui/screens/settings.dart} | 4 +- lib/config/application_config.dart | 3 + ...gs.dart => default_activity_settings.dart} | 6 +- lib/config/menu.dart | 52 ----- lib/cubit/activity/activity_cubit.dart | 188 ++++++++++++++++++ lib/cubit/activity/activity_state.dart | 15 ++ lib/cubit/game_cubit.dart | 188 ------------------ lib/cubit/game_state.dart | 15 -- lib/cubit/nav_cubit.dart | 37 ---- .../settings/settings_activity_cubit.dart | 72 +++++++ .../settings/settings_activity_state.dart | 15 ++ .../{ => settings}/settings_global_cubit.dart | 0 .../{ => settings}/settings_global_state.dart | 0 lib/cubit/settings_game_cubit.dart | 71 ------- lib/cubit/settings_game_state.dart | 15 -- lib/main.dart | 36 +++- .../game.dart => activity/activity.dart} | 79 ++++---- lib/models/{game => activity}/board.dart | 6 +- lib/models/{game => activity}/cell.dart | 2 +- .../{game => activity}/cell_location.dart | 0 lib/models/{game => activity}/types.dart | 2 +- lib/models/settings/settings_activity.dart | 55 +++++ lib/models/settings/settings_game.dart | 55 ----- lib/ui/game/game_bottom.dart | 14 +- lib/ui/game/game_end.dart | 14 +- lib/ui/game/game_top.dart | 12 +- lib/ui/layouts/parameters_layout.dart | 141 ------------- lib/ui/screens/page_game.dart | 24 --- lib/ui/skeleton.dart | 37 ++-- .../actions/button_delete_saved_game.dart | 4 +- lib/ui/widgets/actions/button_game_quit.dart | 7 +- .../actions/button_game_start_new.dart | 17 +- .../actions/button_resume_saved_game.dart | 7 +- .../widgets/game/bar_select_cell_value.dart | 18 +- .../widgets/game/button_show_conflicts.dart | 14 +- lib/ui/widgets/game/button_show_tip.dart | 26 +-- lib/ui/widgets/game/cell.dart | 64 +++--- lib/ui/widgets/game/cell_update.dart | 38 ++-- lib/ui/widgets/game/game_board.dart | 18 +- lib/utils/board_animate.dart | 46 ++--- pubspec.lock | 8 +- pubspec.yaml | 2 +- 58 files changed, 1063 insertions(+), 879 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/78.txt create mode 100644 fastlane/metadata/android/fr-FR/changelogs/78.txt create mode 100644 lib/common/config/activity_page.dart create mode 100644 lib/common/config/screen.dart create mode 100644 lib/common/cubit/nav/nav_cubit_pages.dart create mode 100644 lib/common/cubit/nav/nav_cubit_screens.dart create mode 100644 lib/common/ui/nav/bottom_nav_bar.dart rename lib/{ui/widgets => common/ui/nav}/global_app_bar.dart (61%) rename lib/{ui/layouts/game_layout.dart => common/ui/pages/game.dart} (66%) create mode 100644 lib/common/ui/pages/parameters.dart rename lib/{ => common}/ui/parameters/parameter_painter.dart (85%) rename lib/{ => common}/ui/parameters/parameter_widget.dart (80%) rename lib/{ui/screens/page_about.dart => common/ui/screens/about.dart} (93%) create mode 100644 lib/common/ui/screens/activity.dart rename lib/{ui/screens/page_settings.dart => common/ui/screens/settings.dart} (87%) create mode 100644 lib/config/application_config.dart rename lib/config/{default_game_settings.dart => default_activity_settings.dart} (91%) delete mode 100644 lib/config/menu.dart create mode 100644 lib/cubit/activity/activity_cubit.dart create mode 100644 lib/cubit/activity/activity_state.dart delete mode 100644 lib/cubit/game_cubit.dart delete mode 100644 lib/cubit/game_state.dart delete mode 100644 lib/cubit/nav_cubit.dart create mode 100644 lib/cubit/settings/settings_activity_cubit.dart create mode 100644 lib/cubit/settings/settings_activity_state.dart rename lib/cubit/{ => settings}/settings_global_cubit.dart (100%) rename lib/cubit/{ => settings}/settings_global_state.dart (100%) delete mode 100644 lib/cubit/settings_game_cubit.dart delete mode 100644 lib/cubit/settings_game_state.dart rename lib/models/{game/game.dart => activity/activity.dart} (86%) rename lib/models/{game => activity}/board.dart (98%) rename lib/models/{game => activity}/cell.dart (92%) rename lib/models/{game => activity}/cell_location.dart (100%) rename lib/models/{game => activity}/types.dart (77%) create mode 100644 lib/models/settings/settings_activity.dart delete mode 100644 lib/models/settings/settings_game.dart delete mode 100644 lib/ui/layouts/parameters_layout.dart delete mode 100644 lib/ui/screens/page_game.dart diff --git a/assets/translations/en.json b/assets/translations/en.json index 698056d..e680e83 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -1,6 +1,9 @@ { "app_name": "Sudoku", + "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 7d1f4e8..1cfd715 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -1,6 +1,9 @@ { "app_name": "Sudoku", + "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/78.txt b/fastlane/metadata/android/en-US/changelogs/78.txt new file mode 100644 index 0000000..ac2c90e --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/78.txt @@ -0,0 +1 @@ +Normalize Activity application architecture. diff --git a/fastlane/metadata/android/fr-FR/changelogs/78.txt b/fastlane/metadata/android/fr-FR/changelogs/78.txt new file mode 100644 index 0000000..1d6843d --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/78.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 0000000..d23e488 --- /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:sudoku/common/ui/pages/game.dart'; +import 'package:sudoku/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 0000000..fdbcaa3 --- /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:sudoku/common/ui/screens/about.dart'; +import 'package:sudoku/common/ui/screens/activity.dart'; +import 'package:sudoku/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 0000000..b185e89 --- /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:sudoku/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 0000000..f30c0f1 --- /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:sudoku/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 0000000..9429470 --- /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:sudoku/common/config/activity_page.dart'; +import 'package:sudoku/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 61% rename from lib/ui/widgets/global_app_bar.dart rename to lib/common/ui/nav/global_app_bar.dart index d7b499b..bfe92eb 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:sudoku/config/menu.dart'; -import 'package:sudoku/cubit/game_cubit.dart'; -import 'package:sudoku/cubit/nav_cubit.dart'; -import 'package:sudoku/models/game/game.dart'; +import 'package:sudoku/common/config/screen.dart'; +import 'package:sudoku/common/cubit/nav/nav_cubit_pages.dart'; +import 'package:sudoku/common/cubit/nav/nav_cubit_screens.dart'; + +import 'package:sudoku/cubit/activity/activity_cubit.dart'; +import 'package:sudoku/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'), @@ -33,38 +36,38 @@ class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget { )); menuActions.add(const Spacer(flex: 6)); } 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 0798d0b..e66453d 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:sudoku/cubit/game_cubit.dart'; -import 'package:sudoku/models/game/game.dart'; +import 'package:sudoku/cubit/activity/activity_cubit.dart'; +import 'package:sudoku/models/activity/activity.dart'; import 'package:sudoku/ui/game/game_bottom.dart'; import 'package:sudoku/ui/game/game_end.dart'; import 'package:sudoku/ui/game/game_top.dart'; import 'package:sudoku/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 0000000..d93a8ce --- /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:sudoku/common/ui/parameters/parameter_widget.dart'; + +import 'package:sudoku/config/default_activity_settings.dart'; +import 'package:sudoku/config/default_global_settings.dart'; +import 'package:sudoku/cubit/activity/activity_cubit.dart'; +import 'package:sudoku/cubit/settings/settings_activity_cubit.dart'; +import 'package:sudoku/cubit/settings/settings_global_cubit.dart'; +import 'package:sudoku/models/activity/activity.dart'; +import 'package:sudoku/ui/widgets/actions/button_delete_saved_game.dart'; +import 'package:sudoku/ui/widgets/actions/button_game_start_new.dart'; +import 'package:sudoku/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 85% rename from lib/ui/parameters/parameter_painter.dart rename to lib/common/ui/parameters/parameter_painter.dart index e83e6fb..182b1f3 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:sudoku/config/default_game_settings.dart'; -import 'package:sudoku/models/settings/settings_game.dart'; +import 'package:sudoku/config/default_activity_settings.dart'; +import 'package:sudoku/models/settings/settings_activity.dart'; import 'package:sudoku/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,10 +27,10 @@ class ParameterPainter extends CustomPainter { // content switch (code) { - case DefaultGameSettings.parameterCodeLevel: + case DefaultActivitySettings.parameterCodeLevel: paintLevelParameterItem(canvas, canvasSize); break; - case DefaultGameSettings.parameterCodeSize: + case DefaultActivitySettings.parameterCodeSize: paintSizeParameterItem(canvas, canvasSize); break; default: @@ -83,19 +83,19 @@ class ParameterPainter extends CustomPainter { final List<dynamic> stars = []; switch (value) { - case DefaultGameSettings.levelValueEasy: + case DefaultActivitySettings.levelValueEasy: stars.add([0.5, 0.5]); break; - case DefaultGameSettings.levelValueMedium: + case DefaultActivitySettings.levelValueMedium: stars.add([0.3, 0.5]); stars.add([0.7, 0.5]); break; - case DefaultGameSettings.levelValueHard: + case DefaultActivitySettings.levelValueHard: stars.add([0.3, 0.3]); stars.add([0.7, 0.3]); stars.add([0.5, 0.7]); break; - case DefaultGameSettings.levelValueNightmare: + case DefaultActivitySettings.levelValueNightmare: stars.add([0.3, 0.3]); stars.add([0.7, 0.3]); stars.add([0.3, 0.7]); @@ -144,19 +144,19 @@ class ParameterPainter extends CustomPainter { int gridHeight = 1; switch (value) { - case DefaultGameSettings.sizeValueTiny: + case DefaultActivitySettings.sizeValueTiny: gridWidth = 2; gridHeight = 2; break; - case DefaultGameSettings.sizeValueSmall: + case DefaultActivitySettings.sizeValueSmall: gridWidth = 3; gridHeight = 2; break; - case DefaultGameSettings.sizeValueStandard: + case DefaultActivitySettings.sizeValueStandard: gridWidth = 3; gridHeight = 3; break; - case DefaultGameSettings.sizeValueLarge: + case DefaultActivitySettings.sizeValueLarge: gridWidth = 4; gridHeight = 4; break; diff --git a/lib/ui/parameters/parameter_widget.dart b/lib/common/ui/parameters/parameter_widget.dart similarity index 80% rename from lib/ui/parameters/parameter_widget.dart rename to lib/common/ui/parameters/parameter_widget.dart index 09eb967..1eec514 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:sudoku/config/default_game_settings.dart'; +import 'package:sudoku/common/ui/parameters/parameter_painter.dart'; + +import 'package:sudoku/config/default_activity_settings.dart'; import 'package:sudoku/config/default_global_settings.dart'; -import 'package:sudoku/models/settings/settings_game.dart'; +import 'package:sudoku/models/settings/settings_activity.dart'; import 'package:sudoku/models/settings/settings_global.dart'; -import 'package:sudoku/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,10 +38,10 @@ class ParameterWidget extends StatelessWidget { Widget content = const SizedBox.shrink(); switch (code) { - case DefaultGameSettings.parameterCodeLevel: + case DefaultActivitySettings.parameterCodeLevel: content = getLevelParameterItem(); break; - case DefaultGameSettings.parameterCodeSize: + case DefaultActivitySettings.parameterCodeSize: content = getSizeParameterItem(); break; case DefaultGlobalSettings.parameterCodeSkin: @@ -79,16 +80,16 @@ class ParameterWidget extends StatelessWidget { Color backgroundColor = Colors.grey; switch (value) { - case DefaultGameSettings.levelValueEasy: + case DefaultActivitySettings.levelValueEasy: backgroundColor = Colors.green; break; - case DefaultGameSettings.levelValueMedium: + case DefaultActivitySettings.levelValueMedium: backgroundColor = Colors.orange; break; - case DefaultGameSettings.levelValueHard: + case DefaultActivitySettings.levelValueHard: backgroundColor = Colors.red; break; - case DefaultGameSettings.levelValueNightmare: + case DefaultActivitySettings.levelValueNightmare: backgroundColor = Colors.purple; break; default: @@ -104,7 +105,7 @@ class ParameterWidget extends StatelessWidget { painter: ParameterPainter( code: code, value: value, - gameSettings: gameSettings, + activitySettings: activitySettings, globalSettings: globalSettings, ), isComplex: true, @@ -116,16 +117,16 @@ class ParameterWidget extends StatelessWidget { Color backgroundColor = Colors.grey; switch (value) { - case DefaultGameSettings.sizeValueTiny: + case DefaultActivitySettings.sizeValueTiny: backgroundColor = Colors.green; break; - case DefaultGameSettings.sizeValueSmall: + case DefaultActivitySettings.sizeValueSmall: backgroundColor = Colors.orange; break; - case DefaultGameSettings.sizeValueStandard: + case DefaultActivitySettings.sizeValueStandard: backgroundColor = Colors.red; break; - case DefaultGameSettings.sizeValueLarge: + case DefaultActivitySettings.sizeValueLarge: backgroundColor = Colors.purple; break; default: @@ -141,7 +142,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 ab73e30..f7a14a9 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 0000000..9e71d61 --- /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:sudoku/common/config/activity_page.dart'; +import 'package:sudoku/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 50964ef..7981b1c 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 0000000..582766a --- /dev/null +++ b/lib/config/application_config.dart @@ -0,0 +1,3 @@ +class ApplicationConfig { + static const String appTitle = 'Sudoku'; +} 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 00d8e20..9f6ce9c 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 parameterCodeLevel = 'level'; static const String parameterCodeSize = 'size'; @@ -41,9 +41,9 @@ class DefaultGameSettings { static List<String> getAvailableValues(String parameterCode) { switch (parameterCode) { case parameterCodeLevel: - return DefaultGameSettings.allowedLevelValues; + return DefaultActivitySettings.allowedLevelValues; case parameterCodeSize: - return DefaultGameSettings.allowedSizeValues; + return DefaultActivitySettings.allowedSizeValues; } 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 d3c7f1e..0000000 --- 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:sudoku/ui/screens/page_about.dart'; -import 'package:sudoku/ui/screens/page_game.dart'; -import 'package:sudoku/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 0000000..18d8cde --- /dev/null +++ b/lib/cubit/activity/activity_cubit.dart @@ -0,0 +1,188 @@ +import 'dart:async'; +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; + +import 'package:sudoku/config/default_global_settings.dart'; +import 'package:sudoku/models/activity/cell.dart'; +import 'package:sudoku/models/activity/cell_location.dart'; +import 'package:sudoku/models/activity/activity.dart'; +import 'package:sudoku/models/settings/settings_activity.dart'; +import 'package:sudoku/models/settings/settings_global.dart'; +import 'package:sudoku/utils/board_animate.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, + boardAnimated: state.currentActivity.boardAnimated, + // Base data + board: state.currentActivity.board, + solvedBoard: state.currentActivity.solvedBoard, + blockSizeHorizontal: state.currentActivity.blockSizeHorizontal, + blockSizeVertical: state.currentActivity.blockSizeVertical, + boardSize: state.currentActivity.boardSize, + // Game data + shuffledCellValues: state.currentActivity.shuffledCellValues, + boardConflicts: state.currentActivity.boardConflicts, + selectedCell: state.currentActivity.selectedCell, + showConflicts: state.currentActivity.showConflicts, + givenTipsCount: state.currentActivity.givenTipsCount, + buttonTipsCountdown: state.currentActivity.buttonTipsCountdown, + ); + // 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(); + + BoardAnimate.startAnimation(this, 'start'); + } + + void selectCell(CellLocation location) { + state.currentActivity.selectedCell = state.currentActivity.board.get(location); + refresh(); + } + + void unselectCell() { + state.currentActivity.selectedCell = null; + refresh(); + } + + void updateCellValue(CellLocation location, int value) { + if (state.currentActivity.board.get(location).isFixed == false) { + state.currentActivity.board.set( + location, + Cell( + location: location, + value: value, + isFixed: false, + ), + ); + state.currentActivity.isStarted = true; + refresh(); + } + + if (state.currentActivity.checkBoardIsSolved()) { + BoardAnimate.startAnimation(this, 'win'); + state.currentActivity.isFinished = true; + refresh(); + } + } + + void toggleShowConflicts() { + state.currentActivity.showConflicts = !state.currentActivity.showConflicts; + refresh(); + } + + void increaseGivenTipsCount() { + state.currentActivity.givenTipsCount++; + state.currentActivity.buttonTipsCountdown = + DefaultGlobalSettings.defaultTipCountDownValueInSeconds; + refresh(); + + const Duration interval = Duration(milliseconds: 500); + Timer.periodic( + interval, + (Timer timer) { + if (state.currentActivity.buttonTipsCountdown == 0) { + timer.cancel(); + } else { + state.currentActivity.buttonTipsCountdown = + max(state.currentActivity.buttonTipsCountdown - 1, 0); + } + refresh(); + }, + ); + } + + 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 updateAnimationInProgress(bool animationInProgress) { + state.currentActivity.animationInProgress = animationInProgress; + refresh(); + } + + void setAnimatedBackground(List animatedCellsPattern) { + for (int row = 0; row < state.currentActivity.boardSize; row++) { + for (int col = 0; col < state.currentActivity.boardSize; col++) { + state.currentActivity.boardAnimated[row][col] = animatedCellsPattern[row][col]; + } + } + refresh(); + } + + void resetAnimatedBackground() { + for (int row = 0; row < state.currentActivity.boardSize; row++) { + for (int col = 0; col < state.currentActivity.boardSize; col++) { + state.currentActivity.boardAnimated[row][col] = false; + } + } + refresh(); + } + + @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 0000000..887b45e --- /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 0faf02a..0000000 --- a/lib/cubit/game_cubit.dart +++ /dev/null @@ -1,188 +0,0 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; - -import 'package:sudoku/config/default_global_settings.dart'; -import 'package:sudoku/models/game/cell.dart'; -import 'package:sudoku/models/game/cell_location.dart'; -import 'package:sudoku/models/game/game.dart'; -import 'package:sudoku/models/settings/settings_game.dart'; -import 'package:sudoku/models/settings/settings_global.dart'; -import 'package:sudoku/utils/board_animate.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, - boardAnimated: state.currentGame.boardAnimated, - // Base data - board: state.currentGame.board, - solvedBoard: state.currentGame.solvedBoard, - blockSizeHorizontal: state.currentGame.blockSizeHorizontal, - blockSizeVertical: state.currentGame.blockSizeVertical, - boardSize: state.currentGame.boardSize, - // Game data - shuffledCellValues: state.currentGame.shuffledCellValues, - boardConflicts: state.currentGame.boardConflicts, - selectedCell: state.currentGame.selectedCell, - showConflicts: state.currentGame.showConflicts, - givenTipsCount: state.currentGame.givenTipsCount, - buttonTipsCountdown: state.currentGame.buttonTipsCountdown, - ); - // 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(); - - BoardAnimate.startAnimation(this, 'start'); - } - - void selectCell(CellLocation location) { - state.currentGame.selectedCell = state.currentGame.board.get(location); - refresh(); - } - - void unselectCell() { - state.currentGame.selectedCell = null; - refresh(); - } - - void updateCellValue(CellLocation location, int value) { - if (state.currentGame.board.get(location).isFixed == false) { - state.currentGame.board.set( - location, - Cell( - location: location, - value: value, - isFixed: false, - ), - ); - state.currentGame.isStarted = true; - refresh(); - } - - if (state.currentGame.checkBoardIsSolved()) { - BoardAnimate.startAnimation(this, 'win'); - state.currentGame.isFinished = true; - refresh(); - } - } - - void toggleShowConflicts() { - state.currentGame.showConflicts = !state.currentGame.showConflicts; - refresh(); - } - - void increaseGivenTipsCount() { - state.currentGame.givenTipsCount++; - state.currentGame.buttonTipsCountdown = - DefaultGlobalSettings.defaultTipCountDownValueInSeconds; - refresh(); - - const Duration interval = Duration(milliseconds: 500); - Timer.periodic( - interval, - (Timer timer) { - if (state.currentGame.buttonTipsCountdown == 0) { - timer.cancel(); - } else { - state.currentGame.buttonTipsCountdown = - max(state.currentGame.buttonTipsCountdown - 1, 0); - } - refresh(); - }, - ); - } - - 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 updateAnimationInProgress(bool animationInProgress) { - state.currentGame.animationInProgress = animationInProgress; - refresh(); - } - - void setAnimatedBackground(List animatedCellsPattern) { - for (int row = 0; row < state.currentGame.boardSize; row++) { - for (int col = 0; col < state.currentGame.boardSize; col++) { - state.currentGame.boardAnimated[row][col] = animatedCellsPattern[row][col]; - } - } - refresh(); - } - - void resetAnimatedBackground() { - for (int row = 0; row < state.currentGame.boardSize; row++) { - for (int col = 0; col < state.currentGame.boardSize; col++) { - state.currentGame.boardAnimated[row][col] = false; - } - } - refresh(); - } - - @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 00e2116..0000000 --- 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 b224fae..0000000 --- a/lib/cubit/nav_cubit.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; - -import 'package:sudoku/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 0000000..1d82f83 --- /dev/null +++ b/lib/cubit/settings/settings_activity_cubit.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; + +import 'package:sudoku/config/default_activity_settings.dart'; +import 'package:sudoku/models/settings/settings_activity.dart'; + +part 'settings_activity_state.dart'; + +class ActivitySettingsCubit extends HydratedCubit<ActivitySettingsState> { + ActivitySettingsCubit() + : super(ActivitySettingsState(settings: ActivitySettings.createDefault())); + + void setValues({ + String? level, + String? size, + }) { + emit( + ActivitySettingsState( + settings: ActivitySettings( + level: level ?? state.settings.level, + size: size ?? state.settings.size, + ), + ), + ); + } + + String getParameterValue(String code) { + switch (code) { + case DefaultActivitySettings.parameterCodeLevel: + return ActivitySettings.getLevelValueFromUnsafe(state.settings.level); + case DefaultActivitySettings.parameterCodeSize: + return ActivitySettings.getSizeValueFromUnsafe(state.settings.size); + } + + return ''; + } + + void setParameterValue(String code, String value) { + final String level = (code == DefaultActivitySettings.parameterCodeLevel) + ? value + : getParameterValue(DefaultActivitySettings.parameterCodeLevel); + final String size = (code == DefaultActivitySettings.parameterCodeSize) + ? value + : getParameterValue(DefaultActivitySettings.parameterCodeSize); + + setValues( + level: level, + size: size, + ); + } + + @override + ActivitySettingsState? fromJson(Map<String, dynamic> json) { + final String level = json[DefaultActivitySettings.parameterCodeLevel] as String; + final String size = json[DefaultActivitySettings.parameterCodeSize] as String; + + return ActivitySettingsState( + settings: ActivitySettings( + level: level, + size: size, + ), + ); + } + + @override + Map<String, dynamic>? toJson(ActivitySettingsState state) { + return <String, dynamic>{ + DefaultActivitySettings.parameterCodeLevel: state.settings.level, + DefaultActivitySettings.parameterCodeSize: state.settings.size, + }; + } +} diff --git a/lib/cubit/settings/settings_activity_state.dart b/lib/cubit/settings/settings_activity_state.dart new file mode 100644 index 0000000..2b2de42 --- /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 65111b0..0000000 --- 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:sudoku/config/default_game_settings.dart'; -import 'package:sudoku/models/settings/settings_game.dart'; - -part 'settings_game_state.dart'; - -class GameSettingsCubit extends HydratedCubit<GameSettingsState> { - GameSettingsCubit() : super(GameSettingsState(settings: GameSettings.createDefault())); - - void setValues({ - String? level, - String? size, - }) { - emit( - GameSettingsState( - settings: GameSettings( - level: level ?? state.settings.level, - size: size ?? state.settings.size, - ), - ), - ); - } - - String getParameterValue(String code) { - switch (code) { - case DefaultGameSettings.parameterCodeLevel: - return GameSettings.getLevelValueFromUnsafe(state.settings.level); - case DefaultGameSettings.parameterCodeSize: - return GameSettings.getSizeValueFromUnsafe(state.settings.size); - } - - return ''; - } - - void setParameterValue(String code, String value) { - final String level = (code == DefaultGameSettings.parameterCodeLevel) - ? value - : getParameterValue(DefaultGameSettings.parameterCodeLevel); - final String size = (code == DefaultGameSettings.parameterCodeSize) - ? value - : getParameterValue(DefaultGameSettings.parameterCodeSize); - - setValues( - level: level, - size: size, - ); - } - - @override - GameSettingsState? fromJson(Map<String, dynamic> json) { - final String level = json[DefaultGameSettings.parameterCodeLevel] as String; - final String size = json[DefaultGameSettings.parameterCodeSize] as String; - - return GameSettingsState( - settings: GameSettings( - level: level, - size: size, - ), - ); - } - - @override - Map<String, dynamic>? toJson(GameSettingsState state) { - return <String, dynamic>{ - DefaultGameSettings.parameterCodeLevel: state.settings.level, - DefaultGameSettings.parameterCodeSize: state.settings.size, - }; - } -} diff --git a/lib/cubit/settings_game_state.dart b/lib/cubit/settings_game_state.dart deleted file mode 100644 index 5acd85b..0000000 --- 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 3656421..ead3d7f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,11 +4,14 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; +import 'package:sudoku/common/cubit/nav/nav_cubit_pages.dart'; +import 'package:sudoku/common/cubit/nav/nav_cubit_screens.dart'; + +import 'package:sudoku/config/application_config.dart'; import 'package:sudoku/config/default_global_settings.dart'; -import 'package:sudoku/cubit/game_cubit.dart'; -import 'package:sudoku/cubit/nav_cubit.dart'; -import 'package:sudoku/cubit/settings_game_cubit.dart'; -import 'package:sudoku/cubit/settings_global_cubit.dart'; +import 'package:sudoku/cubit/activity/activity_cubit.dart'; +import 'package:sudoku/cubit/settings/settings_activity_cubit.dart'; +import 'package:sudoku/cubit/settings/settings_global_cubit.dart'; import 'package:sudoku/ui/skeleton.dart'; void main() async { @@ -46,17 +49,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: 'Sudoku', + title: ApplicationConfig.appTitle, home: const SkeletonScreen(), // Theme stuff diff --git a/lib/models/game/game.dart b/lib/models/activity/activity.dart similarity index 86% rename from lib/models/game/game.dart rename to lib/models/activity/activity.dart index e8490ff..c7f312d 100644 --- a/lib/models/game/game.dart +++ b/lib/models/activity/activity.dart @@ -3,19 +3,19 @@ import 'dart:math'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; import 'package:sudoku/config/default_global_settings.dart'; -import 'package:sudoku/cubit/game_cubit.dart'; +import 'package:sudoku/cubit/activity/activity_cubit.dart'; import 'package:sudoku/data/game_data.dart'; -import 'package:sudoku/models/game/board.dart'; -import 'package:sudoku/models/game/cell.dart'; -import 'package:sudoku/models/game/cell_location.dart'; -import 'package:sudoku/models/game/types.dart'; -import 'package:sudoku/models/settings/settings_game.dart'; +import 'package:sudoku/models/activity/board.dart'; +import 'package:sudoku/models/activity/cell.dart'; +import 'package:sudoku/models/activity/cell_location.dart'; +import 'package:sudoku/models/activity/types.dart'; +import 'package:sudoku/models/settings/settings_activity.dart'; import 'package:sudoku/models/settings/settings_global.dart'; -class Game { - Game({ +class Activity { + Activity({ // Settings - required this.gameSettings, + required this.activitySettings, required this.globalSettings, // State @@ -42,7 +42,7 @@ class Game { }); // Settings - final GameSettings gameSettings; + final ActivitySettings activitySettings; final GlobalSettings globalSettings; // State @@ -67,10 +67,10 @@ class Game { int givenTipsCount; int buttonTipsCountdown; - 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(), @@ -84,15 +84,16 @@ 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(); - final int blockSizeHorizontal = int.parse(newGameSettings.size.split('x')[0]); - final int blockSizeVertical = int.parse(newGameSettings.size.split('x')[1]); + final int blockSizeHorizontal = int.parse(newActivitySettings.size.split('x')[0]); + final int blockSizeVertical = int.parse(newActivitySettings.size.split('x')[1]); final int boardSize = blockSizeHorizontal * blockSizeVertical; const int maxCellValue = 16; @@ -114,12 +115,12 @@ class Game { } final List<String> templates = - GameData.templates[newGameSettings.size]?[newGameSettings.level] ?? []; + GameData.templates[newActivitySettings.size]?[newActivitySettings.level] ?? []; final String template = templates.elementAt(Random().nextInt(templates.length)).toString(); if (template.length != pow(blockSizeHorizontal * blockSizeVertical, 2)) { printlog('Failed to get grid template...'); - return Game.createEmpty(); + return Activity.createEmpty(); } final Board board = Board.createFromTemplate( @@ -138,9 +139,9 @@ class Game { notAnimatedBoard.add(line); } - return Game( + return Activity( // Settings - gameSettings: newGameSettings, + activitySettings: newActivitySettings, globalSettings: newGlobalSettings, // State isRunning: true, @@ -271,36 +272,36 @@ class Game { return conflicts; } - void showTip(GameCubit gameCubit) { + void showTip(ActivityCubit activityCubit) { if (selectedCell == null) { // no selected cell -> pick one - helpSelectCell(gameCubit); + helpSelectCell(activityCubit); } else { // currently selected cell -> set value - helpFillCell(gameCubit); + helpFillCell(activityCubit); } - gameCubit.increaseGivenTipsCount(); + activityCubit.increaseGivenTipsCount(); } - void helpSelectCell(GameCubit gameCubit) { + void helpSelectCell(ActivityCubit activityCubit) { // pick one of wrong value cells, if found final List<List<int>> wrongValueCells = getCellsWithWrongValue(); if (wrongValueCells.isNotEmpty) { - pickRandomFromList(gameCubit, wrongValueCells); + pickRandomFromList(activityCubit, wrongValueCells); return; } // pick one of conflicting cells, if found final List<List<int>> conflictingCells = getCellsWithConflicts(); if (conflictingCells.isNotEmpty) { - pickRandomFromList(gameCubit, conflictingCells); + pickRandomFromList(activityCubit, conflictingCells); return; } // pick one form cells with unique non-conflicting candidate value final List<List<int>> candidateCells = board.getEmptyCellsWithUniqueAvailableValue(); if (candidateCells.isNotEmpty) { - pickRandomFromList(gameCubit, candidateCells); + pickRandomFromList(activityCubit, candidateCells); return; } } @@ -337,15 +338,15 @@ class Game { return cellsWithConflict; } - void pickRandomFromList(GameCubit gameCubit, List<List<int>> cellsCoordinates) { + void pickRandomFromList(ActivityCubit activityCubit, List<List<int>> cellsCoordinates) { if (cellsCoordinates.isNotEmpty) { cellsCoordinates.shuffle(); final List<int> cell = cellsCoordinates[0]; - gameCubit.selectCell(CellLocation.go(cell[0], cell[1])); + activityCubit.selectCell(CellLocation.go(cell[0], cell[1])); } } - void helpFillCell(GameCubit gameCubit) { + void helpFillCell(ActivityCubit activityCubit) { // Will clean cell if no eligible value found int eligibleValue = 0; @@ -358,18 +359,18 @@ class Game { } } - gameCubit.updateCellValue( + activityCubit.updateCellValue( selectedCell!.location, allowedValuesCount == 1 ? eligibleValue : 0); - gameCubit.unselectCell(); + activityCubit.unselectCell(); } void dump() { printlog(''); printlog('## Current game dump:'); printlog(''); - printlog('$Game:'); + printlog('$Activity:'); printlog(' Settings'); - gameSettings.dump(); + activitySettings.dump(); globalSettings.dump(); printlog(' State'); printlog(' isRunning: $isRunning'); @@ -414,13 +415,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 98% rename from lib/models/game/board.dart rename to lib/models/activity/board.dart index aa6d6df..9397f5c 100644 --- a/lib/models/game/board.dart +++ b/lib/models/activity/board.dart @@ -2,9 +2,9 @@ import 'dart:math'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:sudoku/models/game/cell.dart'; -import 'package:sudoku/models/game/cell_location.dart'; -import 'package:sudoku/models/game/types.dart'; +import 'package:sudoku/models/activity/cell.dart'; +import 'package:sudoku/models/activity/cell_location.dart'; +import 'package:sudoku/models/activity/types.dart'; class Board { Board({ diff --git a/lib/models/game/cell.dart b/lib/models/activity/cell.dart similarity index 92% rename from lib/models/game/cell.dart rename to lib/models/activity/cell.dart index ffd7e63..c7636d6 100644 --- a/lib/models/game/cell.dart +++ b/lib/models/activity/cell.dart @@ -1,6 +1,6 @@ import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:sudoku/models/game/cell_location.dart'; +import 'package:sudoku/models/activity/cell_location.dart'; class Cell { const Cell({ 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/game/types.dart b/lib/models/activity/types.dart similarity index 77% rename from lib/models/game/types.dart rename to lib/models/activity/types.dart index 6fbdd10..9a8b13a 100644 --- a/lib/models/game/types.dart +++ b/lib/models/activity/types.dart @@ -1,4 +1,4 @@ -import 'package:sudoku/models/game/cell.dart'; +import 'package:sudoku/models/activity/cell.dart'; typedef BoardCells = List<List<Cell>>; typedef ConflictsCount = List<List<int>>; diff --git a/lib/models/settings/settings_activity.dart b/lib/models/settings/settings_activity.dart new file mode 100644 index 0000000..8884739 --- /dev/null +++ b/lib/models/settings/settings_activity.dart @@ -0,0 +1,55 @@ +import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; + +import 'package:sudoku/config/default_activity_settings.dart'; + +class ActivitySettings { + final String level; + final String size; + + ActivitySettings({ + required this.level, + required this.size, + }); + + static String getLevelValueFromUnsafe(String level) { + if (DefaultActivitySettings.allowedLevelValues.contains(level)) { + return level; + } + + return DefaultActivitySettings.defaultLevelValue; + } + + static String getSizeValueFromUnsafe(String size) { + if (DefaultActivitySettings.allowedSizeValues.contains(size)) { + return size; + } + + return DefaultActivitySettings.defaultSizeValue; + } + + factory ActivitySettings.createDefault() { + return ActivitySettings( + level: DefaultActivitySettings.defaultLevelValue, + size: DefaultActivitySettings.defaultSizeValue, + ); + } + + void dump() { + printlog('$ActivitySettings:'); + printlog(' ${DefaultActivitySettings.parameterCodeLevel}: $level'); + printlog(' ${DefaultActivitySettings.parameterCodeSize}: $size'); + printlog(''); + } + + @override + String toString() { + return '$ActivitySettings(${toJson()})'; + } + + Map<String, dynamic>? toJson() { + return <String, dynamic>{ + DefaultActivitySettings.parameterCodeLevel: level, + DefaultActivitySettings.parameterCodeSize: size, + }; + } +} diff --git a/lib/models/settings/settings_game.dart b/lib/models/settings/settings_game.dart deleted file mode 100644 index 5b7c005..0000000 --- a/lib/models/settings/settings_game.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; - -import 'package:sudoku/config/default_game_settings.dart'; - -class GameSettings { - final String level; - final String size; - - GameSettings({ - required this.level, - required this.size, - }); - - static String getLevelValueFromUnsafe(String level) { - if (DefaultGameSettings.allowedLevelValues.contains(level)) { - return level; - } - - return DefaultGameSettings.defaultLevelValue; - } - - static String getSizeValueFromUnsafe(String size) { - if (DefaultGameSettings.allowedSizeValues.contains(size)) { - return size; - } - - return DefaultGameSettings.defaultSizeValue; - } - - factory GameSettings.createDefault() { - return GameSettings( - level: DefaultGameSettings.defaultLevelValue, - size: DefaultGameSettings.defaultSizeValue, - ); - } - - void dump() { - printlog('$GameSettings:'); - printlog(' ${DefaultGameSettings.parameterCodeLevel}: $level'); - printlog(' ${DefaultGameSettings.parameterCodeSize}: $size'); - printlog(''); - } - - @override - String toString() { - return '$GameSettings(${toJson()})'; - } - - Map<String, dynamic>? toJson() { - return <String, dynamic>{ - DefaultGameSettings.parameterCodeLevel: level, - DefaultGameSettings.parameterCodeSize: size, - }; - } -} diff --git a/lib/ui/game/game_bottom.dart b/lib/ui/game/game_bottom.dart index e6e358d..c807511 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:sudoku/cubit/game_cubit.dart'; -import 'package:sudoku/models/game/game.dart'; +import 'package:sudoku/cubit/activity/activity_cubit.dart'; +import 'package:sudoku/models/activity/activity.dart'; import 'package:sudoku/ui/widgets/game/bar_select_cell_value.dart'; class GameBottomWidget extends StatelessWidget { @@ -10,11 +10,13 @@ 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 ? const SizedBox.shrink() : const SelectCellValueBar(); + return currentActivity.isFinished + ? const SizedBox.shrink() + : const SelectCellValueBar(); }, ); } diff --git a/lib/ui/game/game_end.dart b/lib/ui/game/game_end.dart index 62f2bcd..2ee0282 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:sudoku/cubit/game_cubit.dart'; -import 'package:sudoku/models/game/game.dart'; +import 'package:sudoku/cubit/activity/activity_cubit.dart'; +import 'package:sudoku/models/activity/activity.dart'; import 'package:sudoku/ui/widgets/actions/button_game_quit.dart'; class GameEndWidget extends StatelessWidget { @@ -10,13 +10,13 @@ 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; final Image decorationImage = Image( image: AssetImage( - currentGame.gameWon ? 'assets/ui/game_win.png' : 'assets/ui/game_fail.png'), + currentActivity.gameWon ? 'assets/ui/game_win.png' : 'assets/ui/game_fail.png'), fit: BoxFit.fill, ); @@ -34,7 +34,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 32aace4..428b88c 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:sudoku/cubit/game_cubit.dart'; -import 'package:sudoku/models/game/game.dart'; +import 'package:sudoku/cubit/activity/activity_cubit.dart'; +import 'package:sudoku/models/activity/activity.dart'; import 'package:sudoku/ui/widgets/game/button_show_conflicts.dart'; import 'package:sudoku/ui/widgets/game/button_show_tip.dart'; @@ -11,13 +11,13 @@ 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 SizedBox( height: 60, - child: (currentGame.isRunning && !currentGame.isFinished) + child: (currentActivity.isRunning && !currentActivity.isFinished) ? const Row( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.center, diff --git a/lib/ui/layouts/parameters_layout.dart b/lib/ui/layouts/parameters_layout.dart deleted file mode 100644 index 5a16634..0000000 --- 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:sudoku/config/default_game_settings.dart'; -import 'package:sudoku/config/default_global_settings.dart'; -import 'package:sudoku/cubit/settings_game_cubit.dart'; -import 'package:sudoku/cubit/settings_global_cubit.dart'; -import 'package:sudoku/ui/parameters/parameter_widget.dart'; -import 'package:sudoku/ui/widgets/actions/button_delete_saved_game.dart'; -import 'package:sudoku/ui/widgets/actions/button_game_start_new.dart'; -import 'package:sudoku/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 cdd15d8..0000000 --- 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:sudoku/cubit/game_cubit.dart'; -import 'package:sudoku/models/game/game.dart'; -import 'package:sudoku/ui/layouts/game_layout.dart'; -import 'package:sudoku/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 f235f48..49ca2e1 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:sudoku/config/menu.dart'; -import 'package:sudoku/cubit/nav_cubit.dart'; -import 'package:sudoku/ui/widgets/global_app_bar.dart'; +import 'package:sudoku/common/config/activity_page.dart'; +import 'package:sudoku/common/config/screen.dart'; +import 'package:sudoku/common/cubit/nav/nav_cubit_screens.dart'; +import 'package:sudoku/common/ui/nav/global_app_bar.dart'; +import 'package:sudoku/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 dea6d4f..5c63256 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:sudoku/cubit/game_cubit.dart'; +import 'package:sudoku/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 56d80c9..b321688 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:sudoku/cubit/game_cubit.dart'; +import 'package:sudoku/common/cubit/nav/nav_cubit_pages.dart'; + +import 'package:sudoku/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 87ecf84..295498d 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:sudoku/cubit/game_cubit.dart'; -import 'package:sudoku/cubit/settings_game_cubit.dart'; -import 'package:sudoku/cubit/settings_global_cubit.dart'; +import 'package:sudoku/common/cubit/nav/nav_cubit_pages.dart'; + +import 'package:sudoku/cubit/activity/activity_cubit.dart'; +import 'package:sudoku/cubit/settings/settings_activity_cubit.dart'; +import 'package:sudoku/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 3b45393..301a1be 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:sudoku/cubit/game_cubit.dart'; +import 'package:sudoku/common/cubit/nav/nav_cubit_pages.dart'; + +import 'package:sudoku/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/bar_select_cell_value.dart b/lib/ui/widgets/game/bar_select_cell_value.dart index be62f99..3ef348d 100644 --- a/lib/ui/widgets/game/bar_select_cell_value.dart +++ b/lib/ui/widgets/game/bar_select_cell_value.dart @@ -3,10 +3,10 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:sudoku/cubit/game_cubit.dart'; -import 'package:sudoku/models/game/cell.dart'; -import 'package:sudoku/models/game/cell_location.dart'; -import 'package:sudoku/models/game/game.dart'; +import 'package:sudoku/cubit/activity/activity_cubit.dart'; +import 'package:sudoku/models/activity/cell.dart'; +import 'package:sudoku/models/activity/cell_location.dart'; +import 'package:sudoku/models/activity/activity.dart'; import 'package:sudoku/ui/widgets/game/cell_update.dart'; class SelectCellValueBar extends StatelessWidget { @@ -14,13 +14,13 @@ class SelectCellValueBar extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder<GameCubit, GameState>( - builder: (BuildContext context, GameState gameState) { - final Game game = gameState.currentGame; + return BlocBuilder<ActivityCubit, ActivityState>( + builder: (BuildContext context, ActivityState activityState) { + final Activity activity = activityState.currentActivity; final bool isUpdatableCellSelected = - (game.selectedCell != null) ? !game.selectedCell!.isFixed : false; - final int maxValue = game.boardSize; + (activity.selectedCell != null) ? !activity.selectedCell!.isFixed : false; + final int maxValue = activity.boardSize; const int maxItemsPerLine = 10; final int linesCount = (maxValue / maxItemsPerLine).floor() + 1; diff --git a/lib/ui/widgets/game/button_show_conflicts.dart b/lib/ui/widgets/game/button_show_conflicts.dart index 0ba35cf..58b0c24 100644 --- a/lib/ui/widgets/game/button_show_conflicts.dart +++ b/lib/ui/widgets/game/button_show_conflicts.dart @@ -1,26 +1,26 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:sudoku/cubit/game_cubit.dart'; -import 'package:sudoku/models/game/game.dart'; +import 'package:sudoku/cubit/activity/activity_cubit.dart'; +import 'package:sudoku/models/activity/activity.dart'; class ButtonShowConflicts extends StatelessWidget { const ButtonShowConflicts({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 StyledButton( - color: currentGame.showConflicts == true ? Colors.amber : Colors.grey, + color: currentActivity.showConflicts == true ? Colors.amber : Colors.grey, child: const Image( image: AssetImage('assets/ui/button_show_conflicts.png'), fit: BoxFit.fill, ), onPressed: () { - BlocProvider.of<GameCubit>(context).toggleShowConflicts(); + BlocProvider.of<ActivityCubit>(context).toggleShowConflicts(); }, ); }, diff --git a/lib/ui/widgets/game/button_show_tip.dart b/lib/ui/widgets/game/button_show_tip.dart index 55dc904..57fcb25 100644 --- a/lib/ui/widgets/game/button_show_tip.dart +++ b/lib/ui/widgets/game/button_show_tip.dart @@ -3,36 +3,38 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; import 'package:sudoku/config/default_global_settings.dart'; -import 'package:sudoku/cubit/game_cubit.dart'; -import 'package:sudoku/models/game/game.dart'; +import 'package:sudoku/cubit/activity/activity_cubit.dart'; +import 'package:sudoku/models/activity/activity.dart'; class ButtonShowTip extends StatelessWidget { const ButtonShowTip({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 StyledButton( color: Colors.purple, child: badges.Badge( - showBadge: currentGame.givenTipsCount == 0 ? false : true, + showBadge: currentActivity.givenTipsCount == 0 ? false : true, badgeStyle: badges.BadgeStyle( - badgeColor: currentGame.givenTipsCount < 10 + badgeColor: currentActivity.givenTipsCount < 10 ? Colors.green - : currentGame.givenTipsCount < 20 + : currentActivity.givenTipsCount < 20 ? Colors.orange : Colors.red, ), badgeContent: Text( - currentGame.givenTipsCount == 0 ? '' : currentGame.givenTipsCount.toString(), + currentActivity.givenTipsCount == 0 + ? '' + : currentActivity.givenTipsCount.toString(), style: const TextStyle(color: Colors.white), ), child: Container( padding: EdgeInsets.all(10 * - currentGame.buttonTipsCountdown / + currentActivity.buttonTipsCountdown / DefaultGlobalSettings.defaultTipCountDownValueInSeconds), child: const Image( image: AssetImage('assets/ui/button_help.png'), @@ -41,8 +43,8 @@ class ButtonShowTip extends StatelessWidget { ), ), onPressed: () { - currentGame.canGiveTip - ? currentGame.showTip(BlocProvider.of<GameCubit>(context)) + currentActivity.canGiveTip + ? currentActivity.showTip(BlocProvider.of<ActivityCubit>(context)) : null; }, ); diff --git a/lib/ui/widgets/game/cell.dart b/lib/ui/widgets/game/cell.dart index f85f9bb..3134f9f 100644 --- a/lib/ui/widgets/game/cell.dart +++ b/lib/ui/widgets/game/cell.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:sudoku/cubit/game_cubit.dart'; -import 'package:sudoku/models/game/cell.dart'; -import 'package:sudoku/models/game/game.dart'; +import 'package:sudoku/cubit/activity/activity_cubit.dart'; +import 'package:sudoku/models/activity/cell.dart'; +import 'package:sudoku/models/activity/activity.dart'; class CellWidget extends StatelessWidget { const CellWidget({super.key, required this.cell}); @@ -12,16 +12,16 @@ class CellWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder<GameCubit, GameState>( - builder: (BuildContext context, GameState gameState) { - final Game game = gameState.currentGame; + return BlocBuilder<ActivityCubit, ActivityState>( + builder: (BuildContext context, ActivityState activityState) { + final Activity activity = activityState.currentActivity; - final String imageAsset = getImageAssetName(game); + final String imageAsset = getImageAssetName(activity); return Container( decoration: BoxDecoration( - color: getBackgroundColor(game), - border: getCellBorders(game), + color: getBackgroundColor(activity), + border: getCellBorders(activity), ), child: GestureDetector( child: AnimatedSwitcher( @@ -36,13 +36,13 @@ class CellWidget extends StatelessWidget { ), ), onTap: () { - final GameCubit gameCubit = BlocProvider.of<GameCubit>(context); + final ActivityCubit activityCubit = BlocProvider.of<ActivityCubit>(context); - if (cell.location.col != game.selectedCell?.location.col || - cell.location.row != game.selectedCell?.location.row) { - gameCubit.selectCell(cell.location); + if (cell.location.col != activity.selectedCell?.location.col || + cell.location.row != activity.selectedCell?.location.row) { + activityCubit.selectCell(cell.location); } else { - gameCubit.unselectCell(); + activityCubit.unselectCell(); } }, ), @@ -54,17 +54,17 @@ class CellWidget extends StatelessWidget { /* * Compute image asset name, from skin and cell value/state */ - String getImageAssetName(Game game) { + String getImageAssetName(Activity activity) { if ((cell.value) > 0) { - final int cellValue = game.getTranslatedValueForDisplay(cell.value); - return 'assets/skins/${game.globalSettings.skin}_$cellValue.png'; + final int cellValue = activity.getTranslatedValueForDisplay(cell.value); + return 'assets/skins/${activity.globalSettings.skin}_$cellValue.png'; } return 'assets/ui/cell_empty.png'; } // Compute cell background color, from cell state - Color getBackgroundColor(Game game) { + Color getBackgroundColor(Activity activity) { final Color editableCellColor = Colors.grey.shade100; final Color editableCellColorConflict = Colors.pink.shade100; final Color fixedCellColor = Colors.grey.shade300; @@ -80,9 +80,9 @@ class CellWidget extends StatelessWidget { backgroundColor = fixedCellColor; } - final int conflictsCount = game.boardConflicts[cell.location.row][cell.location.col]; + final int conflictsCount = activity.boardConflicts[cell.location.row][cell.location.col]; - if (game.showConflicts == true) { + if (activity.showConflicts == true) { if (conflictsCount != 0) { if (cell.isFixed == true) { backgroundColor = fixedCellColorConflict; @@ -91,7 +91,7 @@ class CellWidget extends StatelessWidget { } } - if ((cell.value != 0) && (cell.value == game.selectedCell?.value)) { + if ((cell.value != 0) && (cell.value == activity.selectedCell?.value)) { if (cell.isFixed == true) { backgroundColor = fixedSelectedValueColor; } else { @@ -100,7 +100,7 @@ class CellWidget extends StatelessWidget { } } - final bool isAnimated = game.boardAnimated[cell.location.row][cell.location.col]; + final bool isAnimated = activity.boardAnimated[cell.location.row][cell.location.col]; if (isAnimated) { if (cell.isFixed == true) { @@ -114,7 +114,7 @@ class CellWidget extends StatelessWidget { } // Compute cell borders, from board size and cell state - Border getCellBorders(Game game) { + Border getCellBorders(Activity activity) { final Color cellBorderDarkColor = Colors.grey.shade800; final Color cellBorderLightColor = Colors.grey.shade600; const Color cellBorderSelectedColor = Colors.red; @@ -123,14 +123,14 @@ class CellWidget extends StatelessWidget { double cellBorderWidth = 4; // Reduce cell border width on big boards - if (game.boardSize > 8) { + if (activity.boardSize > 8) { cellBorderWidth = 2; - if (game.boardSize > 10) { + if (activity.boardSize > 10) { cellBorderWidth = 1; } } - if (!game.isRunning) { + if (!activity.isRunning) { cellBorderColor = Colors.green.shade700; } @@ -140,27 +140,27 @@ class CellWidget extends StatelessWidget { ); // Update cell borders if not currently selected cell - if (cell.location.col != game.selectedCell?.location.col || - cell.location.row != game.selectedCell?.location.row) { + if (cell.location.col != activity.selectedCell?.location.col || + cell.location.row != activity.selectedCell?.location.row) { borders = Border( top: BorderSide( width: cellBorderWidth, - color: (((cell.location.row) % game.blockSizeVertical) == 0) + color: (((cell.location.row) % activity.blockSizeVertical) == 0) ? cellBorderDarkColor : cellBorderLightColor), left: BorderSide( width: cellBorderWidth, - color: (((cell.location.col) % game.blockSizeHorizontal) == 0) + color: (((cell.location.col) % activity.blockSizeHorizontal) == 0) ? cellBorderDarkColor : cellBorderLightColor), right: BorderSide( width: cellBorderWidth, - color: ((((cell.location.col) + 1) % game.blockSizeHorizontal) == 0) + color: ((((cell.location.col) + 1) % activity.blockSizeHorizontal) == 0) ? cellBorderDarkColor : cellBorderLightColor), bottom: BorderSide( width: cellBorderWidth, - color: ((((cell.location.row) + 1) % game.blockSizeVertical) == 0) + color: ((((cell.location.row) + 1) % activity.blockSizeVertical) == 0) ? cellBorderDarkColor : cellBorderLightColor), ); diff --git a/lib/ui/widgets/game/cell_update.dart b/lib/ui/widgets/game/cell_update.dart index c4a09d4..e8182c9 100644 --- a/lib/ui/widgets/game/cell_update.dart +++ b/lib/ui/widgets/game/cell_update.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:sudoku/cubit/game_cubit.dart'; -import 'package:sudoku/models/game/cell.dart'; -import 'package:sudoku/models/game/game.dart'; +import 'package:sudoku/cubit/activity/activity_cubit.dart'; +import 'package:sudoku/models/activity/cell.dart'; +import 'package:sudoku/models/activity/activity.dart'; class CellWidgetUpdate extends StatelessWidget { const CellWidgetUpdate({super.key, this.cell}); @@ -12,22 +12,23 @@ class CellWidgetUpdate extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder<GameCubit, GameState>( - builder: (BuildContext context, GameState gameState) { - final Game game = gameState.currentGame; + return BlocBuilder<ActivityCubit, ActivityState>( + builder: (BuildContext context, ActivityState activityState) { + final Activity activity = activityState.currentActivity; if ((cell?.value ?? 0) < 0) { return Container(); } - final String imageAsset = getImageAssetName(game); + final String imageAsset = getImageAssetName(activity); Color backgroundColor = Colors.grey.shade200; - if (game.showConflicts == true && - game.selectedCell?.location.col != null && - game.selectedCell?.location.row != null) { - if (!game.board.isValueAllowed(game.selectedCell?.location, cell?.value ?? 0)) { + if (activity.showConflicts == true && + activity.selectedCell?.location.col != null && + activity.selectedCell?.location.row != null) { + if (!activity.board + .isValueAllowed(activity.selectedCell?.location, cell?.value ?? 0)) { backgroundColor = Colors.pink.shade100; } } @@ -43,13 +44,14 @@ class CellWidgetUpdate extends StatelessWidget { child: GestureDetector( child: Image(image: AssetImage(imageAsset), fit: BoxFit.fill), onTap: () { - final GameCubit gameCubit = BlocProvider.of<GameCubit>(context); + final ActivityCubit activityCubit = BlocProvider.of<ActivityCubit>(context); - if (game.selectedCell != null) { - gameCubit.updateCellValue(game.selectedCell!.location, cell?.value ?? 0); + if (activity.selectedCell != null) { + activityCubit.updateCellValue( + activity.selectedCell!.location, cell?.value ?? 0); } - gameCubit.unselectCell(); + activityCubit.unselectCell(); }, ), ); @@ -60,10 +62,10 @@ class CellWidgetUpdate extends StatelessWidget { /* * Compute image asset name, from skin and cell value/state */ - String getImageAssetName(Game game) { + String getImageAssetName(Activity activity) { if ((cell?.value ?? 0) > 0) { - final int cellValue = game.getTranslatedValueForDisplay(cell?.value ?? 0); - return 'assets/skins/${game.globalSettings.skin}_$cellValue.png'; + final int cellValue = activity.getTranslatedValueForDisplay(cell?.value ?? 0); + return 'assets/skins/${activity.globalSettings.skin}_$cellValue.png'; } return 'assets/ui/cell_empty.png'; diff --git a/lib/ui/widgets/game/game_board.dart b/lib/ui/widgets/game/game_board.dart index de98e55..898137e 100644 --- a/lib/ui/widgets/game/game_board.dart +++ b/lib/ui/widgets/game/game_board.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; -import 'package:sudoku/cubit/game_cubit.dart'; -import 'package:sudoku/models/game/cell_location.dart'; -import 'package:sudoku/models/game/game.dart'; +import 'package:sudoku/cubit/activity/activity_cubit.dart'; +import 'package:sudoku/models/activity/cell_location.dart'; +import 'package:sudoku/models/activity/activity.dart'; import 'package:sudoku/ui/widgets/game/cell.dart'; class GameBoardWidget extends StatelessWidget { @@ -11,9 +11,9 @@ class GameBoardWidget 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 = Theme.of(context).colorScheme.onSurface; @@ -33,14 +33,14 @@ class GameBoardWidget extends StatelessWidget { Table( defaultColumnWidth: const IntrinsicColumnWidth(), children: [ - for (int row = 0; row < currentGame.boardSize; row++) + for (int row = 0; row < currentActivity.boardSize; row++) TableRow( children: [ - for (int col = 0; col < currentGame.boardSize; col++) + for (int col = 0; col < currentActivity.boardSize; col++) Column( children: [ CellWidget( - cell: currentGame.board.get(CellLocation.go(row, col))) + cell: currentActivity.board.get(CellLocation.go(row, col))) ], ), ], diff --git a/lib/utils/board_animate.dart b/lib/utils/board_animate.dart index a1a5239..9a7d566 100644 --- a/lib/utils/board_animate.dart +++ b/lib/utils/board_animate.dart @@ -1,21 +1,21 @@ import 'dart:async'; -import 'package:sudoku/cubit/game_cubit.dart'; -import 'package:sudoku/models/game/game.dart'; -import 'package:sudoku/models/game/types.dart'; +import 'package:sudoku/cubit/activity/activity_cubit.dart'; +import 'package:sudoku/models/activity/activity.dart'; +import 'package:sudoku/models/activity/types.dart'; class BoardAnimate { // Start game animation: blinking tiles - static AnimatedBoardSequence createStartGameAnimationPatterns(Game game) { + static AnimatedBoardSequence createStartGameAnimationPatterns(Activity activity) { AnimatedBoardSequence patterns = []; int patternsCount = 3; for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) { AnimatedBoard pattern = []; - for (int row = 0; row < game.boardSize; row++) { + for (int row = 0; row < activity.boardSize; row++) { List<bool> patternRow = []; - for (int col = 0; col < game.boardSize; col++) { + for (int col = 0; col < activity.boardSize; col++) { patternRow.add(((patternIndex + row + col) % 2 == 0)); } pattern.add(patternRow); @@ -27,16 +27,16 @@ class BoardAnimate { } // Win game animation: fill board with colored rows, from bottom to top - static AnimatedBoardSequence createWinGameAnimationPatterns(Game game) { + static AnimatedBoardSequence createWinGameAnimationPatterns(Activity activity) { AnimatedBoardSequence patterns = []; - int patternsCount = game.boardSize + 6; + int patternsCount = activity.boardSize + 6; for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) { AnimatedBoard pattern = []; - for (int row = 0; row < game.boardSize; row++) { + for (int row = 0; row < activity.boardSize; row++) { List<bool> patternRow = []; - for (int col = 0; col < game.boardSize; col++) { + for (int col = 0; col < activity.boardSize; col++) { patternRow.add(row > (patternIndex - 4)); } pattern.add(patternRow); @@ -48,17 +48,17 @@ class BoardAnimate { } // Default multi-purpose animation: sliding stripes, from top left to right bottom - static AnimatedBoardSequence createDefaultAnimationPatterns(Game game) { + static AnimatedBoardSequence createDefaultAnimationPatterns(Activity activity) { AnimatedBoardSequence patterns = []; - int boardSideLength = game.boardSize; + int boardSideLength = activity.boardSize; int patternsCount = boardSideLength; for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) { AnimatedBoard pattern = []; - for (int row = 0; row < game.boardSize; row++) { + for (int row = 0; row < activity.boardSize; row++) { List<bool> patternRow = []; - for (int col = 0; col < game.boardSize; col++) { + for (int col = 0; col < activity.boardSize; col++) { patternRow.add(((patternIndex + row + col) % 4 == 0)); } pattern.add(patternRow); @@ -69,25 +69,25 @@ class BoardAnimate { return patterns; } - static void startAnimation(GameCubit gameCubit, String animationType) { - final Game game = gameCubit.state.currentGame; + static void startAnimation(ActivityCubit activityCubit, String animationType) { + final Activity activity = activityCubit.state.currentActivity; AnimatedBoardSequence patterns = []; switch (animationType) { case 'start': - patterns = createStartGameAnimationPatterns(game); + patterns = createStartGameAnimationPatterns(activity); break; case 'win': - patterns = createWinGameAnimationPatterns(game); + patterns = createWinGameAnimationPatterns(activity); break; default: - patterns = createDefaultAnimationPatterns(game); + patterns = createDefaultAnimationPatterns(activity); } int patternIndex = patterns.length; - gameCubit.updateAnimationInProgress(true); + activityCubit.updateAnimationInProgress(true); const interval = Duration(milliseconds: 200); Timer.periodic( @@ -95,11 +95,11 @@ class BoardAnimate { (Timer timer) { if (patternIndex == 0) { timer.cancel(); - gameCubit.resetAnimatedBackground(); - gameCubit.updateAnimationInProgress(false); + activityCubit.resetAnimatedBackground(); + activityCubit.updateAnimationInProgress(false); } else { patternIndex--; - gameCubit.setAnimatedBackground(patterns[patternIndex]); + activityCubit.setAnimatedBackground(patterns[patternIndex]); } }, ); diff --git a/pubspec.lock b/pubspec.lock index cd1c7bb..0604c29 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -253,10 +253,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: @@ -450,10 +450,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 fa69170..dd80ac4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: A sudoku game application. publish_to: "none" -version: 0.4.1+77 +version: 0.5.0+78 environment: sdk: "^3.0.0" -- GitLab