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

Use navigation tools from flutter_custom_toolbox

parent e1fe0018
No related branches found
No related tags found
1 merge request!76Resolve "Use navigation widgets from flutter_custom_toolbox"
Pipeline #7155 passed
This commit is part of merge request !76. Comments created here will be created in the context of that merge request.
Showing
with 121 additions and 441 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:puzzlegame/cubit/activity/activity_cubit.dart';
import 'package:puzzlegame/config/application_config.dart';
import 'package:puzzlegame/models/activity/activity.dart';
import 'package:puzzlegame/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:puzzlegame/common/ui/screens/about.dart';
import 'package:puzzlegame/common/ui/screens/activity.dart';
import 'package:puzzlegame/common/ui/screens/settings.dart';
class ScreenItem {
final String code;
final Icon icon;
final Widget screen;
const ScreenItem({
required this.code,
required this.icon,
required this.screen,
});
}
class Screen {
static const indexActivity = 0;
static const screenActivity = ScreenItem(
code: 'screen_activity',
icon: Icon(UniconsLine.home),
screen: ScreenActivity(),
);
static const indexSettings = 1;
static const screenSettings = ScreenItem(
code: 'screen_settings',
icon: Icon(UniconsLine.setting),
screen: ScreenSettings(),
);
static const indexAbout = 2;
static const screenAbout = ScreenItem(
code: 'screen_about',
icon: Icon(UniconsLine.info_circle),
screen: ScreenAbout(),
);
static Map<int, ScreenItem> items = {
indexActivity: screenActivity,
indexSettings: screenSettings,
indexAbout: screenAbout,
};
static bool isIndexAllowed(int screenIndex) {
return items.keys.contains(screenIndex);
}
static Widget getWidget(int screenIndex) {
return items[screenIndex]?.screen ?? screenActivity.screen;
}
}
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:puzzlegame/common/config/activity_page.dart';
class NavCubitPage extends HydratedCubit<int> {
NavCubitPage() : super(0);
void updateIndex(int index) {
if (ActivityPage.isIndexAllowed(index)) {
emit(index);
} else {
emit(ActivityPage.indexHome);
}
}
void goToPageHome() {
updateIndex(ActivityPage.indexHome);
}
void goToPageGame() {
updateIndex(ActivityPage.indexGame);
}
@override
int fromJson(Map<String, dynamic> json) {
return ActivityPage.indexHome;
}
@override
Map<String, dynamic>? toJson(int state) {
return <String, int>{'index': state};
}
}
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:puzzlegame/common/config/screen.dart';
class NavCubitScreen extends HydratedCubit<int> {
NavCubitScreen() : super(0);
void updateIndex(int index) {
if (Screen.isIndexAllowed(index)) {
emit(index);
} else {
goToScreenActivity();
}
}
void goToScreenActivity() {
emit(Screen.indexActivity);
}
void goToScreenSettings() {
emit(Screen.indexSettings);
}
void goToScreenAbout() {
emit(Screen.indexAbout);
}
@override
int fromJson(Map<String, dynamic> json) {
return Screen.indexActivity;
}
@override
Map<String, dynamic>? toJson(int state) {
return <String, int>{'index': state};
}
}
import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:puzzlegame/common/config/activity_page.dart';
import 'package:puzzlegame/common/cubit/nav/nav_cubit_pages.dart';
class BottomNavBar extends StatelessWidget {
const BottomNavBar({super.key});
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.only(top: 1, right: 4, left: 4),
elevation: 4,
shadowColor: Theme.of(context).colorScheme.shadow,
color: Theme.of(context).colorScheme.surfaceContainerHighest,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
),
child: BlocBuilder<NavCubitPage, int>(builder: (BuildContext context, int state) {
final List<BottomNavigationBarItem> items = [];
ActivityPage.items.forEach((int pageIndex, ActivityPageItem item) {
items.add(BottomNavigationBarItem(
icon: item.icon,
label: tr(item.code),
));
});
return BottomNavigationBar(
currentIndex: state,
onTap: (int index) => BlocProvider.of<NavCubitPage>(context).updateIndex(index),
type: BottomNavigationBarType.fixed,
elevation: 0,
backgroundColor: Colors.transparent,
selectedItemColor: Theme.of(context).colorScheme.primary,
unselectedItemColor: Theme.of(context).textTheme.bodySmall!.color,
items: items,
);
}),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:puzzlegame/common/config/screen.dart';
import 'package:puzzlegame/common/cubit/nav/nav_cubit_pages.dart';
import 'package:puzzlegame/common/cubit/nav/nav_cubit_screens.dart';
import 'package:puzzlegame/cubit/activity/activity_cubit.dart';
import 'package:puzzlegame/models/activity/activity.dart';
class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget {
const GlobalAppBar({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<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:puzzlegame/common/config/activity_page.dart';
import 'package:puzzlegame/common/cubit/nav/nav_cubit_pages.dart';
class ScreenActivity extends StatelessWidget {
const ScreenActivity({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<NavCubitPage, int>(
builder: (BuildContext context, int pageIndex) {
return ActivityPage.getWidget(pageIndex);
},
);
}
}
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_custom_toolbox/flutter_toolbox.dart';
import 'package:puzzlegame/common/cubit/nav/nav_cubit_pages.dart';
import 'package:puzzlegame/cubit/activity/activity_cubit.dart';
import 'package:puzzlegame/ui/pages/game.dart';
import 'package:puzzlegame/ui/parameters/parameter_painter_tileset_size.dart';
class ApplicationConfig {
// activity parameter: tileset size
static const String parameterCodeTilesetSize = 'activity.tilesetSize';
static const String parameterCodeImageName = 'activity.imageName';
static const String tilesetSizeValueSmall = '3x3';
static const String tilesetSizeValueMedium = '4x4';
static const String tilesetSizeValueLarge = '5x5';
// activity parameter: image picking type
static const String parameterCodeImageName = 'activity.imageName';
static const String imageNameRandomlyPicked = 'random';
// activity pages
static const int activityPageIndexHome = 0;
static const int activityPageIndexGame = 1;
static final ApplicationConfigDefinition config = ApplicationConfigDefinition(
appTitle: 'Puzzle',
activitySettings: [
......@@ -41,7 +47,7 @@ class ApplicationConfig {
context: context,
value: value,
),
intValueGetter: (value) => int.parse(value.split('x')[0]),
intValueGetter: (String value) => int.parse(value.split('x')[0]),
),
// image name
......@@ -57,14 +63,66 @@ class ApplicationConfig {
],
startNewActivity: (BuildContext 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) {
BlocProvider.of<ActivityCubit>(context).deleteSavedActivity();
},
resumeActivity: (BuildContext context) {
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();
},
);
},
),
},
),
);
}
......@@ -4,9 +4,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:puzzlegame/common/cubit/nav/nav_cubit_pages.dart';
import 'package:puzzlegame/common/cubit/nav/nav_cubit_screens.dart';
import 'package:puzzlegame/config/application_config.dart';
import 'package:puzzlegame/cubit/activity/activity_cubit.dart';
import 'package:puzzlegame/ui/skeleton.dart';
......@@ -48,7 +45,7 @@ class MyApp extends StatelessWidget {
providers: [
// default providers
BlocProvider<NavCubitPage>(
create: (context) => NavCubitPage(),
create: (context) => NavCubitPage(appConfig: ApplicationConfig.config),
),
BlocProvider<NavCubitScreen>(
create: (context) => NavCubitScreen(),
......
import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:puzzlegame/common/cubit/nav/nav_cubit_pages.dart';
import 'package:puzzlegame/config/application_config.dart';
import 'package:puzzlegame/cubit/activity/activity_cubit.dart';
import 'package:puzzlegame/models/activity/activity.dart';
......@@ -45,7 +44,8 @@ class GameEndWidget extends StatelessWidget {
: ActivityButtonQuit(
onPressed: () {
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_custom_toolbox/flutter_toolbox.dart';
import 'package:puzzlegame/common/config/activity_page.dart';
import 'package:puzzlegame/common/config/screen.dart';
import 'package:puzzlegame/common/cubit/nav/nav_cubit_screens.dart';
import 'package:puzzlegame/common/ui/nav/global_app_bar.dart';
import 'package:puzzlegame/common/ui/nav/bottom_nav_bar.dart';
import 'package:puzzlegame/config/application_config.dart';
import 'package:puzzlegame/cubit/activity/activity_cubit.dart';
import 'package:puzzlegame/models/activity/activity.dart';
class SkeletonScreen extends StatelessWidget {
const SkeletonScreen({super.key});
......@@ -14,22 +12,48 @@ class SkeletonScreen extends StatelessWidget {
Widget build(BuildContext context) {
return BlocBuilder<NavCubitScreen, int>(
builder: (BuildContext context, int screenIndex) {
return Scaffold(
appBar: const GlobalAppBar(),
extendBodyBehindAppBar: false,
body: Material(
color: Theme.of(context).colorScheme.surface,
child: Padding(
padding: const EdgeInsets.only(
top: 8,
left: 2,
right: 2,
),
child: Screen.getWidget(screenIndex),
),
),
backgroundColor: Theme.of(context).colorScheme.surface,
bottomNavigationBar: ActivityPage.displayBottomNavBar ? const BottomNavBar() : null,
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(
appBar: GlobalAppBar(
appConfig: ApplicationConfig.config,
pageIndex: pageIndex,
isActivityRunning:
currentActivity.isRunning && !currentActivity.isFinished,
),
extendBodyBehindAppBar: false,
body: Material(
color: Theme.of(context).colorScheme.surface,
child: Padding(
padding: const EdgeInsets.only(
top: 8,
left: 2,
right: 2,
),
child: ApplicationConfig.config.navigation.getScreenWidget(
appConfig: ApplicationConfig.config,
screenIndex: screenIndex,
),
),
),
backgroundColor: Theme.of(context).colorScheme.surface,
bottomNavigationBar: ApplicationConfig.config.navigation.displayBottomNavBar
? BottomNavBar(appConfig: ApplicationConfig.config)
: null,
);
},
);
},
);
},
);
......
......@@ -130,11 +130,11 @@ packages:
dependency: "direct main"
description:
path: "."
ref: "0.5.0"
resolved-ref: b8164a50489ba981ea57d9f02e2334f09cb8c6a7
ref: "0.6.0"
resolved-ref: dfe0f6b7b49f9aa32f085e4a5d3ea320b3611eed
url: "https://git.harrault.fr/android/flutter-toolbox.git"
source: git
version: "0.5.0"
version: "0.6.0"
flutter_lints:
dependency: "direct dev"
description:
......
......@@ -3,7 +3,7 @@ description: A puzzle game application.
publish_to: "none"
version: 0.5.0+71
version: 0.6.0+72
environment:
sdk: "^3.0.0"
......@@ -16,7 +16,7 @@ dependencies:
flutter_custom_toolbox:
git:
url: https://git.harrault.fr/android/flutter-toolbox.git
ref: 0.5.0
ref: 0.6.0
# specific
image: ^4.1.3
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment