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

Use navigation tools from flutter_custom_toolbox

parent 4bc79cc1
No related branches found
No related tags found
1 merge request!54Resolve "Use navigation widgets from flutter_custom_toolbox"
Pipeline #7151 passed
This commit is part of merge request !54. Comments created here will be created in the context of that merge request.
Showing
with 122 additions and 442 deletions
Use navigation tools from flutter_custom_toolbox.
Utilisation des outils de navigation entre pages depuis flutter_custom_toolbox.
import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:minehunter/cubit/activity/activity_cubit.dart';
import 'package:minehunter/config/application_config.dart';
import 'package:minehunter/models/activity/activity.dart';
import 'package:minehunter/ui/pages/game.dart';
class ActivityPageItem {
final String code;
final Icon icon;
final Widget Function({required Activity currentActivity})? builder;
const ActivityPageItem({
required this.code,
required this.icon,
required this.builder,
});
}
class ActivityPage {
static const bool displayBottomNavBar = false;
static const indexHome = 0;
static final ActivityPageItem pageHome = ActivityPageItem(
code: 'page_home',
icon: Icon(UniconsLine.home),
builder: ({required Activity currentActivity}) {
return PageParameters(
config: ApplicationConfig.config,
canBeResumed: currentActivity.canBeResumed,
);
},
);
static const indexGame = 1;
static final ActivityPageItem pageGame = ActivityPageItem(
code: 'page_game',
icon: Icon(UniconsLine.star),
builder: ({required Activity currentActivity}) {
return PageGame();
},
);
static final 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 BlocBuilder<ActivityCubit, ActivityState>(
builder: (BuildContext context, ActivityState activityState) {
final Activity currentActivity = activityState.currentActivity;
if (items.keys.contains(pageIndex)) {
return items[pageIndex]?.builder!(currentActivity: currentActivity) ?? Text('oups');
} else {
return getWidget(defaultPageIndex);
}
},
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:minehunter/common/ui/screens/about.dart';
import 'package:minehunter/common/ui/screens/activity.dart';
import 'package:minehunter/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;
}
}
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:minehunter/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};
}
}
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:minehunter/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};
}
}
import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:minehunter/common/config/activity_page.dart';
import 'package:minehunter/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,
);
}),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:minehunter/common/config/screen.dart';
import 'package:minehunter/common/cubit/nav/nav_cubit_pages.dart';
import 'package:minehunter/common/cubit/nav/nav_cubit_screens.dart';
import 'package:minehunter/cubit/activity/activity_cubit.dart';
import 'package:minehunter/models/activity/activity.dart';
class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget {
const GlobalAppBar({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<ActivityCubit, ActivityState>(
builder: (BuildContext context, ActivityState activityState) {
return BlocBuilder<NavCubitScreen, int>(
builder: (BuildContext context, int pageIndex) {
final Activity currentActivity = activityState.currentActivity;
final List<Widget> menuActions = [];
if (currentActivity.isRunning && !currentActivity.isFinished) {
menuActions.add(ActivityButtonQuit(
onPressed: () {},
onLongPress: () {
BlocProvider.of<ActivityCubit>(context).quitActivity();
BlocProvider.of<NavCubitPage>(context).goToPageHome();
},
));
} else {
if (pageIndex == Screen.indexActivity) {
// go to Settings page
menuActions.add(ElevatedButton(
onPressed: () {
BlocProvider.of<NavCubitScreen>(context).goToScreenSettings();
},
style: ElevatedButton.styleFrom(
shape: const CircleBorder(),
),
child: Screen.screenSettings.icon,
));
// go to About page
menuActions.add(ElevatedButton(
onPressed: () {
BlocProvider.of<NavCubitScreen>(context).goToScreenAbout();
},
style: ElevatedButton.styleFrom(
shape: const CircleBorder(),
),
child: Screen.screenAbout.icon,
));
} else {
// back to Home page
menuActions.add(ElevatedButton(
onPressed: () {
BlocProvider.of<NavCubitScreen>(context).goToScreenActivity();
},
style: ElevatedButton.styleFrom(
shape: const CircleBorder(),
),
child: Screen.screenActivity.icon,
));
}
}
return AppBar(
title: const AppHeader(text: 'app_name'),
actions: menuActions,
);
},
);
},
);
}
@override
Size get preferredSize => const Size.fromHeight(50);
}
import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
class ScreenAbout extends StatelessWidget {
const ScreenAbout({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
const SizedBox(height: 8),
const AppTitle(text: 'about_title'),
const Text('about_content').tr(),
FutureBuilder<PackageInfo>(
future: PackageInfo.fromPlatform(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
return const Text('about_version').tr(
namedArgs: {
'version': snapshot.data!.version,
},
);
default:
return const SizedBox();
}
},
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:minehunter/common/config/activity_page.dart';
import 'package:minehunter/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);
},
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
class ScreenSettings extends StatelessWidget {
const ScreenSettings({super.key});
@override
Widget build(BuildContext context) {
return const Padding(
padding: EdgeInsets.symmetric(horizontal: 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
SizedBox(height: 8),
AppTitle(text: 'settings_title'),
SizedBox(height: 8),
ApplicationSettingsForm(),
],
),
);
}
}
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:minehunter/common/cubit/nav/nav_cubit_pages.dart';
import 'package:minehunter/cubit/activity/activity_cubit.dart'; import 'package:minehunter/cubit/activity/activity_cubit.dart';
import 'package:minehunter/ui/pages/game.dart';
import 'package:minehunter/ui/parameters/parameter_painter_board_size.dart'; import 'package:minehunter/ui/parameters/parameter_painter_board_size.dart';
import 'package:minehunter/ui/parameters/parameter_painter_difficulty_level.dart'; import 'package:minehunter/ui/parameters/parameter_painter_difficulty_level.dart';
class ApplicationConfig { class ApplicationConfig {
// activity parameter: skin
static const String parameterCodeSkin = 'global.skin'; static const String parameterCodeSkin = 'global.skin';
static const String parameterCodeDifficultyLevel = 'level';
static const String parameterCodeBoardSize = 'size';
static const String skinValueDefault = 'default'; static const String skinValueDefault = 'default';
// activity parameter: difficulty level
static const String parameterCodeDifficultyLevel = 'level';
static const String difficultyLevelValueEasy = 'easy'; static const String difficultyLevelValueEasy = 'easy';
static const String difficultyLevelValueMedium = 'medium'; static const String difficultyLevelValueMedium = 'medium';
static const String difficultyLevelValueHard = 'hard'; static const String difficultyLevelValueHard = 'hard';
static const String difficultyLevelValueNightmare = 'nightmare'; static const String difficultyLevelValueNightmare = 'nightmare';
// activity parameter: board size
static const String parameterCodeBoardSize = 'size';
static const String boardSizeValueSmall = '10x10'; static const String boardSizeValueSmall = '10x10';
static const String boardSizeValueMedium = '15x15'; static const String boardSizeValueMedium = '15x15';
static const String boardSizeValueLarge = '20x20'; static const String boardSizeValueLarge = '20x20';
static const String boardSizeValueExtraLarge = '25x25'; static const String boardSizeValueExtraLarge = '25x25';
// activity pages
static const int activityPageIndexHome = 0;
static const int activityPageIndexGame = 1;
static final ApplicationConfigDefinition config = ApplicationConfigDefinition( static final ApplicationConfigDefinition config = ApplicationConfigDefinition(
appTitle: 'Minehunter', appTitle: 'Minehunter',
activitySettings: [ activitySettings: [
...@@ -96,15 +103,67 @@ class ApplicationConfig { ...@@ -96,15 +103,67 @@ class ApplicationConfig {
], ],
startNewActivity: (BuildContext context) { startNewActivity: (BuildContext context) {
BlocProvider.of<ActivityCubit>(context).startNewActivity(context); BlocProvider.of<ActivityCubit>(context).startNewActivity(context);
BlocProvider.of<NavCubitPage>(context).goToPageGame(); BlocProvider.of<NavCubitPage>(context)
.updateIndex(ApplicationConfig.activityPageIndexGame);
},
quitCurrentActivity: (BuildContext context) {
BlocProvider.of<ActivityCubit>(context).quitActivity();
BlocProvider.of<NavCubitPage>(context)
.updateIndex(ApplicationConfig.activityPageIndexHome);
}, },
deleteCurrentActivity: (BuildContext context) { deleteCurrentActivity: (BuildContext context) {
BlocProvider.of<ActivityCubit>(context).deleteSavedActivity(); BlocProvider.of<ActivityCubit>(context).deleteSavedActivity();
}, },
resumeActivity: (BuildContext context) { resumeActivity: (BuildContext context) {
BlocProvider.of<ActivityCubit>(context).resumeSavedActivity(); BlocProvider.of<ActivityCubit>(context).resumeSavedActivity();
BlocProvider.of<NavCubitPage>(context).goToPageGame(); BlocProvider.of<NavCubitPage>(context)
.updateIndex(ApplicationConfig.activityPageIndexGame);
},
navigation: ApplicationNavigation(
screenActivity: ScreenItem(
code: 'screen_activity',
icon: Icon(UniconsLine.home),
screen: ({required ApplicationConfigDefinition appConfig}) =>
ScreenActivity(appConfig: appConfig),
),
screenSettings: ScreenItem(
code: 'screen_settings',
icon: Icon(UniconsLine.setting),
screen: ({required ApplicationConfigDefinition appConfig}) => ScreenSettings(),
),
screenAbout: ScreenItem(
code: 'screen_about',
icon: Icon(UniconsLine.info_circle),
screen: ({required ApplicationConfigDefinition appConfig}) => ScreenAbout(),
),
activityPages: {
activityPageIndexHome: ActivityPageItem(
code: 'page_home',
icon: Icon(UniconsLine.home),
builder: ({required ApplicationConfigDefinition appConfig}) {
return BlocBuilder<ActivityCubit, ActivityState>(
builder: (BuildContext context, ActivityState activityState) {
return PageParameters(
appConfig: appConfig,
canBeResumed: activityState.currentActivity.canBeResumed,
);
},
);
},
),
activityPageIndexGame: ActivityPageItem(
code: 'page_game',
icon: Icon(UniconsLine.star),
builder: ({required ApplicationConfigDefinition appConfig}) {
return BlocBuilder<ActivityCubit, ActivityState>(
builder: (BuildContext context, ActivityState activityState) {
return PageGame();
},
);
}, },
),
},
),
); );
// how many mines? (per 100 tiles) // how many mines? (per 100 tiles)
......
...@@ -4,10 +4,6 @@ import 'package:flutter/material.dart'; ...@@ -4,10 +4,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:minehunter/common/cubit/nav/nav_cubit_pages.dart';
import 'package:minehunter/common/cubit/nav/nav_cubit_screens.dart';
import 'package:minehunter/config/application_config.dart'; import 'package:minehunter/config/application_config.dart';
import 'package:minehunter/cubit/activity/activity_cubit.dart'; import 'package:minehunter/cubit/activity/activity_cubit.dart';
import 'package:minehunter/ui/skeleton.dart'; import 'package:minehunter/ui/skeleton.dart';
...@@ -49,7 +45,7 @@ class MyApp extends StatelessWidget { ...@@ -49,7 +45,7 @@ class MyApp extends StatelessWidget {
providers: [ providers: [
// default providers // default providers
BlocProvider<NavCubitPage>( BlocProvider<NavCubitPage>(
create: (context) => NavCubitPage(), create: (context) => NavCubitPage(appConfig: ApplicationConfig.config),
), ),
BlocProvider<NavCubitScreen>( BlocProvider<NavCubitScreen>(
create: (context) => NavCubitScreen(), create: (context) => NavCubitScreen(),
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:minehunter/common/cubit/nav/nav_cubit_pages.dart'; import 'package:minehunter/config/application_config.dart';
import 'package:minehunter/cubit/activity/activity_cubit.dart'; import 'package:minehunter/cubit/activity/activity_cubit.dart';
import 'package:minehunter/models/activity/activity.dart'; import 'package:minehunter/models/activity/activity.dart';
...@@ -49,7 +49,8 @@ class GameEndWidget extends StatelessWidget { ...@@ -49,7 +49,8 @@ class GameEndWidget extends StatelessWidget {
: ActivityButtonQuit( : ActivityButtonQuit(
onPressed: () { onPressed: () {
BlocProvider.of<ActivityCubit>(context).quitActivity(); BlocProvider.of<ActivityCubit>(context).quitActivity();
BlocProvider.of<NavCubitPage>(context).goToPageHome(); BlocProvider.of<NavCubitPage>(context)
.updateIndex(ApplicationConfig.activityPageIndexHome);
}, },
), ),
], ],
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:minehunter/common/config/activity_page.dart'; import 'package:minehunter/config/application_config.dart';
import 'package:minehunter/common/config/screen.dart'; import 'package:minehunter/cubit/activity/activity_cubit.dart';
import 'package:minehunter/common/cubit/nav/nav_cubit_screens.dart'; import 'package:minehunter/models/activity/activity.dart';
import 'package:minehunter/common/ui/nav/global_app_bar.dart';
import 'package:minehunter/common/ui/nav/bottom_nav_bar.dart';
class SkeletonScreen extends StatelessWidget { class SkeletonScreen extends StatelessWidget {
const SkeletonScreen({super.key}); const SkeletonScreen({super.key});
...@@ -14,8 +12,25 @@ class SkeletonScreen extends StatelessWidget { ...@@ -14,8 +12,25 @@ class SkeletonScreen extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<NavCubitScreen, int>( return BlocBuilder<NavCubitScreen, int>(
builder: (BuildContext context, int screenIndex) { builder: (BuildContext context, int screenIndex) {
return BlocBuilder<ActivityCubit, ActivityState>(
builder: (BuildContext context, ActivityState activityState) {
return BlocBuilder<NavCubitPage, int>(
builder: (BuildContext context, int pageIndex) {
final Activity currentActivity = activityState.currentActivity;
// autostart activity
if (ApplicationConfig.config.autoStartActivity &&
(!currentActivity.isRunning)) {
ApplicationConfig.config.startNewActivity(context);
}
return Scaffold( return Scaffold(
appBar: const GlobalAppBar(), appBar: GlobalAppBar(
appConfig: ApplicationConfig.config,
pageIndex: pageIndex,
isActivityRunning:
currentActivity.isRunning && !currentActivity.isFinished,
),
extendBodyBehindAppBar: false, extendBodyBehindAppBar: false,
body: Material( body: Material(
color: Theme.of(context).colorScheme.surface, color: Theme.of(context).colorScheme.surface,
...@@ -25,11 +40,20 @@ class SkeletonScreen extends StatelessWidget { ...@@ -25,11 +40,20 @@ class SkeletonScreen extends StatelessWidget {
left: 2, left: 2,
right: 2, right: 2,
), ),
child: Screen.getWidget(screenIndex), child: ApplicationConfig.config.navigation.getScreenWidget(
appConfig: ApplicationConfig.config,
screenIndex: screenIndex,
),
), ),
), ),
backgroundColor: Theme.of(context).colorScheme.surface, backgroundColor: Theme.of(context).colorScheme.surface,
bottomNavigationBar: ActivityPage.displayBottomNavBar ? const BottomNavBar() : null, bottomNavigationBar: ApplicationConfig.config.navigation.displayBottomNavBar
? BottomNavBar(appConfig: ApplicationConfig.config)
: null,
);
},
);
},
); );
}, },
); );
......
...@@ -122,11 +122,11 @@ packages: ...@@ -122,11 +122,11 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: "0.5.0" ref: "0.6.0"
resolved-ref: b8164a50489ba981ea57d9f02e2334f09cb8c6a7 resolved-ref: dfe0f6b7b49f9aa32f085e4a5d3ea320b3611eed
url: "https://git.harrault.fr/android/flutter-toolbox.git" url: "https://git.harrault.fr/android/flutter-toolbox.git"
source: git source: git
version: "0.5.0" version: "0.6.0"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
......
...@@ -3,7 +3,7 @@ description: A minehunter game application. ...@@ -3,7 +3,7 @@ description: A minehunter game application.
publish_to: "none" publish_to: "none"
version: 0.6.0+51 version: 0.7.0+52
environment: environment:
sdk: "^3.0.0" sdk: "^3.0.0"
...@@ -16,7 +16,7 @@ dependencies: ...@@ -16,7 +16,7 @@ dependencies:
flutter_custom_toolbox: flutter_custom_toolbox:
git: git:
url: https://git.harrault.fr/android/flutter-toolbox.git url: https://git.harrault.fr/android/flutter-toolbox.git
ref: 0.5.0 ref: 0.6.0
# specific # specific
# (none) # (none)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment