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

Use navigation tools from flutter_custom_toolbox

parent 5243f6fe
No related branches found
No related tags found
1 merge request!39Resolve "Use navigation widgets from flutter_custom_toolbox"
Pipeline #7159 passed
This commit is part of merge request !39. Comments created here will be created in the context of that merge request.
Showing
with 121 additions and 439 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:sortgame/cubit/activity/activity_cubit.dart';
import 'package:sortgame/config/application_config.dart';
import 'package:sortgame/models/activity/activity.dart';
import 'package:sortgame/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 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:sortgame/common/ui/screens/about.dart';
import 'package:sortgame/common/ui/screens/activity.dart';
import 'package:sortgame/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:sortgame/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:sortgame/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:sortgame/common/config/activity_page.dart';
import 'package:sortgame/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:sortgame/common/config/screen.dart';
import 'package:sortgame/common/cubit/nav/nav_cubit_pages.dart';
import 'package:sortgame/common/cubit/nav/nav_cubit_screens.dart';
import 'package:sortgame/cubit/activity/activity_cubit.dart';
import 'package:sortgame/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:sortgame/common/config/activity_page.dart';
import 'package:sortgame/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:sortgame/common/cubit/nav/nav_cubit_pages.dart';
import 'package:sortgame/cubit/activity/activity_cubit.dart'; import 'package:sortgame/cubit/activity/activity_cubit.dart';
import 'package:sortgame/ui/pages/game.dart';
import 'package:sortgame/data/fetch_data_helper.dart'; import 'package:sortgame/data/fetch_data_helper.dart';
class ApplicationConfig { class ApplicationConfig {
// activity parameter: items count
static const String parameterCodeItemsCount = 'itemsCount'; static const String parameterCodeItemsCount = 'itemsCount';
static const String parameterCodeThemeCode = 'themeCode';
static const String itemsCountValueLow = '5'; static const String itemsCountValueLow = '5';
static const String itemsCountValueMedium = '10'; static const String itemsCountValueMedium = '10';
static const String itemsCountValueHigh = '15'; static const String itemsCountValueHigh = '15';
static const String itemsCountValueVeryHigh = '20'; static const String itemsCountValueVeryHigh = '20';
// activity parameter: theme code
static const String parameterCodeThemeCode = 'themeCode';
// activity pages
static const int activityPageIndexHome = 0;
static const int activityPageIndexGame = 1;
static List<ApplicationSettingsParameterItemValue> getThemeCodes() { static List<ApplicationSettingsParameterItemValue> getThemeCodes() {
List<ApplicationSettingsParameterItemValue> list = []; List<ApplicationSettingsParameterItemValue> list = [];
...@@ -64,14 +71,66 @@ class ApplicationConfig { ...@@ -64,14 +71,66 @@ 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();
},
);
},
),
},
),
); );
} }
...@@ -4,9 +4,6 @@ import 'package:flutter/material.dart'; ...@@ -4,9 +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:sortgame/common/cubit/nav/nav_cubit_pages.dart';
import 'package:sortgame/common/cubit/nav/nav_cubit_screens.dart';
import 'package:sortgame/config/application_config.dart'; import 'package:sortgame/config/application_config.dart';
import 'package:sortgame/cubit/activity/activity_cubit.dart'; import 'package:sortgame/cubit/activity/activity_cubit.dart';
import 'package:sortgame/ui/skeleton.dart'; import 'package:sortgame/ui/skeleton.dart';
...@@ -48,7 +45,7 @@ class MyApp extends StatelessWidget { ...@@ -48,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:sortgame/common/cubit/nav/nav_cubit_pages.dart'; import 'package:sortgame/config/application_config.dart';
import 'package:sortgame/cubit/activity/activity_cubit.dart'; import 'package:sortgame/cubit/activity/activity_cubit.dart';
import 'package:sortgame/models/activity/activity.dart'; import 'package:sortgame/models/activity/activity.dart';
...@@ -46,7 +46,8 @@ class GameEndWidget extends StatelessWidget { ...@@ -46,7 +46,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:sortgame/common/config/activity_page.dart'; import 'package:sortgame/config/application_config.dart';
import 'package:sortgame/common/config/screen.dart'; import 'package:sortgame/cubit/activity/activity_cubit.dart';
import 'package:sortgame/common/cubit/nav/nav_cubit_screens.dart'; import 'package:sortgame/models/activity/activity.dart';
import 'package:sortgame/common/ui/nav/global_app_bar.dart';
import 'package:sortgame/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 sorting game application. ...@@ -3,7 +3,7 @@ description: A sorting game application.
publish_to: "none" publish_to: "none"
version: 0.5.0+37 version: 0.6.0+38
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 to comment