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

Normalize app architecture

parent d8867283
Branches
Tags
1 merge request!7Resolve "Normalize game architecture"
Pipeline #5731 passed
Showing
with 516 additions and 61 deletions
...@@ -37,7 +37,7 @@ if (keystorePropertiesFile.exists()) { ...@@ -37,7 +37,7 @@ if (keystorePropertiesFile.exists()) {
} }
android { android {
compileSdkVersion 33 compileSdkVersion 34
namespace "org.benoitharrault.spotifyplaylistgenerator" namespace "org.benoitharrault.spotifyplaylistgenerator"
defaultConfig { defaultConfig {
......
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
app.versionName=0.0.5 app.versionName=0.0.6
app.versionCode=5 app.versionCode=6
{ {
"app_name": "Spotify playlist generator" "app_name": "Spotify playlist generator",
"settings_title": "Settings",
"settings_label_theme": "Theme mode",
"about_title": "Informations",
"about_content": "Spotify playlist generator",
"about_version": "Version: {version}",
"page_home": "Home",
"page_playlist": "Playlists",
"": ""
} }
{ {
"app_name": "Générateur de playlist Spotify" "app_name": "Générateur de playlist Spotify",
"settings_title": "Réglages",
"settings_label_theme": "Thème de couleurs",
"about_title": "Informations",
"about_content": "Générateur de playlist Spotify.",
"about_version": "Version : {version}",
"page_home": "Accueil",
"page_playlist": "Playlists",
"": ""
} }
Improve/normalize app architecture.
Amélioration/normalisation de l'architecture de l'application.
import 'package:flutter/material.dart';
import 'package:unicons/unicons.dart';
import 'package:spotifyplaylistgenerator/ui/pages/playlist.dart';
import 'package:spotifyplaylistgenerator/ui/pages/home.dart';
class ActivityPageItem {
final Icon icon;
final Widget page;
final String code;
const ActivityPageItem({
required this.icon,
required this.page,
required this.code,
});
}
class ActivityPage {
static const indexHome = 0;
static const pageHome = ActivityPageItem(
icon: Icon(UniconsLine.home),
page: PageHome(),
code: 'page_home',
);
static const indexPlaylist = 1;
static const pagePlaylist = ActivityPageItem(
icon: Icon(UniconsLine.list_ul),
page: PagePlaylist(),
code: 'page_playlist',
);
static Map<int, ActivityPageItem> items = {
indexHome: pageHome,
indexPlaylist: pagePlaylist,
};
static bool isIndexAllowed(int pageIndex) {
return items.keys.contains(pageIndex);
}
static ActivityPageItem getPageItem(int pageIndex) {
return items[pageIndex] ?? pageHome;
}
static Widget getPageWidget(int pageIndex) {
return items[pageIndex]?.page ?? pageHome.page;
}
static int itemsCount = ActivityPage.items.length;
}
import 'package:flutter/material.dart';
class AppColors {
static const Color primary = contentColorCyan;
static const Color menuBackground = Color(0xFF090912);
static const Color itemsBackground = Color(0xFF1B2339);
static const Color pageBackground = Color(0xFF282E45);
static const Color mainTextColor1 = Colors.white;
static const Color mainTextColor2 = Colors.white70;
static const Color mainTextColor3 = Colors.white38;
static const Color mainGridLineColor = Colors.white10;
static const Color borderColor = Colors.white54;
static const Color gridLinesColor = Color(0x11FFFFFF);
static const Color contentColorBlack = Colors.black;
static const Color contentColorWhite = Colors.white;
static const Color contentColorBlue = Color(0xFF2196F3);
static const Color contentColorYellow = Color(0xFFFFC300);
static const Color contentColorOrange = Color(0xFFFF683B);
static const Color contentColorGreen = Color(0xFF3BFF49);
static const Color contentColorPurple = Color(0xFF6E1BFF);
static const Color contentColorPink = Color(0xFFFF3AF2);
static const Color contentColorRed = Color(0xFFE80054);
static const Color contentColorCyan = Color(0xFF50E4FF);
}
import 'package:spotifyplaylistgenerator/utils/tools.dart';
class DefaultGlobalSettings {
// available global parameters codes
static const String parameterCodeSkin = 'skin';
static const List<String> availableParameters = [
parameterCodeSkin,
];
// skin: available values
static const String skinValueDefault = 'default';
static const List<String> allowedSkinValues = [
skinValueDefault,
];
// skin: default value
static const String defaultSkinValue = skinValueDefault;
// available values from parameter code
static List<String> getAvailableValues(String parameterCode) {
switch (parameterCode) {
case parameterCodeSkin:
return DefaultGlobalSettings.allowedSkinValues;
}
printlog('Did not find any available value for global parameter "$parameterCode".');
return [];
}
// parameters displayed with assets (instead of painter)
static List<String> displayedWithAssets = [
//
];
}
import 'package:flutter/material.dart';
import 'package:unicons/unicons.dart';
import 'package:spotifyplaylistgenerator/ui/screens/about.dart';
import 'package:spotifyplaylistgenerator/ui/screens/activity.dart';
import 'package:spotifyplaylistgenerator/ui/screens/settings.dart';
class ScreenItem {
final Icon icon;
final Widget screen;
final bool displayBottomNavBar;
const ScreenItem({
required this.icon,
required this.screen,
required this.displayBottomNavBar,
});
}
class Screen {
static const indexActivity = 0;
static const screenActivity = ScreenItem(
icon: Icon(UniconsLine.home),
screen: ScreenActivity(),
displayBottomNavBar: true,
);
static const indexSettings = 1;
static const screenSettings = ScreenItem(
icon: Icon(UniconsLine.setting),
screen: ScreenSettings(),
displayBottomNavBar: false,
);
static const indexAbout = 2;
static const screenAbout = ScreenItem(
icon: Icon(UniconsLine.info_circle),
screen: ScreenAbout(),
displayBottomNavBar: false,
);
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;
}
static bool displayBottomNavBar(int screenIndex) {
return items[screenIndex]?.displayBottomNavBar ?? screenActivity.displayBottomNavBar;
}
static int itemsCount = Screen.items.length;
}
...@@ -39,11 +39,9 @@ final ColorScheme lightColorScheme = ColorScheme.light( ...@@ -39,11 +39,9 @@ final ColorScheme lightColorScheme = ColorScheme.light(
secondary: primarySwatch.shade500, secondary: primarySwatch.shade500,
onSecondary: Colors.white, onSecondary: Colors.white,
error: errorColor, error: errorColor,
background: textSwatch.shade200,
onBackground: textSwatch.shade500,
onSurface: textSwatch.shade500, onSurface: textSwatch.shade500,
surface: textSwatch.shade50, surface: textSwatch.shade50,
surfaceVariant: Colors.white, surfaceContainerHighest: Colors.white,
shadow: textSwatch.shade900.withOpacity(.1), shadow: textSwatch.shade900.withOpacity(.1),
); );
...@@ -52,11 +50,9 @@ final ColorScheme darkColorScheme = ColorScheme.dark( ...@@ -52,11 +50,9 @@ final ColorScheme darkColorScheme = ColorScheme.dark(
secondary: primarySwatch.shade500, secondary: primarySwatch.shade500,
onSecondary: Colors.white, onSecondary: Colors.white,
error: errorColor, error: errorColor,
background: const Color(0xFF171724),
onBackground: textSwatch.shade400,
onSurface: textSwatch.shade300, onSurface: textSwatch.shade300,
surface: const Color(0xFF262630), surface: const Color(0xFF262630),
surfaceVariant: const Color(0xFF282832), surfaceContainerHighest: const Color(0xFF282832),
shadow: textSwatch.shade900.withOpacity(.2), shadow: textSwatch.shade900.withOpacity(.2),
); );
...@@ -192,5 +188,3 @@ final ThemeData darkTheme = lightTheme.copyWith( ...@@ -192,5 +188,3 @@ final ThemeData darkTheme = lightTheme.copyWith(
), ),
), ),
); );
final ThemeData appTheme = darkTheme;
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:spotifyplaylistgenerator/models/activity/activity.dart';
import 'package:spotifyplaylistgenerator/models/settings/settings_global.dart';
part 'activity_state.dart';
class ActivityCubit extends HydratedCubit<ActivityState> {
ActivityCubit()
: super(ActivityState(
currentActivity: Activity.createNull(),
));
void updateState(Activity activity) {
emit(ActivityState(
currentActivity: activity,
));
}
void refresh() {
final Activity activity = Activity(
// Settings
globalSettings: state.currentActivity.globalSettings,
// State
isConnected: state.currentActivity.isConnected,
// Base data
userId: state.currentActivity.userId,
// Activity data
token: state.currentActivity.token,
);
// activity.dump();
updateState(activity);
}
void startNewActivity({
required GlobalSettings globalSettings,
}) {
final Activity newActivity = Activity.createNew(
// Settings
globalSettings: globalSettings,
);
newActivity.dump();
updateState(newActivity);
refresh();
}
void quitActivity() {
state.currentActivity.isConnected = false;
refresh();
}
void resumeSavedActivity() {
state.currentActivity.isConnected = true;
refresh();
}
void deleteSavedActivity() {
state.currentActivity.isConnected = 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(),
};
}
}
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,
];
}
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:spotifyplaylistgenerator/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);
}
}
@override
int fromJson(Map<String, dynamic> json) {
return ActivityPage.indexHome;
}
@override
Map<String, dynamic>? toJson(int state) {
return <String, int>{'pageIndex': state};
}
}
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:spotifyplaylistgenerator/config/screen.dart';
class NavCubitScreen extends HydratedCubit<int> {
NavCubitScreen() : super(0);
void updateIndex(int index) {
if (Screen.isIndexAllowed(index)) {
emit(index);
} else {
goToActivityPage();
}
}
void goToActivityPage() {
emit(Screen.indexActivity);
}
void goToSettingsPage() {
emit(Screen.indexSettings);
}
void goToAboutPage() {
emit(Screen.indexAbout);
}
@override
int fromJson(Map<String, dynamic> json) {
return Screen.indexActivity;
}
@override
Map<String, dynamic>? toJson(int state) {
return <String, int>{'screenIndex': state};
}
}
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:spotifyplaylistgenerator/config/default_global_settings.dart';
import 'package:spotifyplaylistgenerator/models/settings/settings_global.dart';
part 'settings_global_state.dart';
class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> {
GlobalSettingsCubit() : super(GlobalSettingsState(settings: GlobalSettings.createDefault()));
void setValues({
String? skin,
}) {
emit(
GlobalSettingsState(
settings: GlobalSettings(
skin: skin ?? state.settings.skin,
),
),
);
}
String getParameterValue(String code) {
switch (code) {
case DefaultGlobalSettings.parameterCodeSkin:
return GlobalSettings.getSkinValueFromUnsafe(state.settings.skin);
}
return '';
}
void setParameterValue(String code, String value) {
final String skin = (code == DefaultGlobalSettings.parameterCodeSkin)
? value
: getParameterValue(DefaultGlobalSettings.parameterCodeSkin);
setValues(
skin: skin,
);
}
@override
GlobalSettingsState? fromJson(Map<String, dynamic> json) {
final String skin = json[DefaultGlobalSettings.parameterCodeSkin] as String;
return GlobalSettingsState(
settings: GlobalSettings(
skin: skin,
),
);
}
@override
Map<String, dynamic>? toJson(GlobalSettingsState state) {
return <String, dynamic>{
DefaultGlobalSettings.parameterCodeSkin: state.settings.skin,
};
}
}
part of 'settings_global_cubit.dart';
@immutable
class GlobalSettingsState extends Equatable {
const GlobalSettingsState({
required this.settings,
});
final GlobalSettings settings;
@override
List<dynamic> get props => <dynamic>[
settings,
];
}
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
part 'theme_state.dart';
class ThemeCubit extends HydratedCubit<ThemeModeState> {
ThemeCubit() : super(const ThemeModeState());
void getTheme(ThemeModeState state) {
emit(state);
}
@override
ThemeModeState? fromJson(Map<String, dynamic> json) {
switch (json['themeMode']) {
case 'ThemeMode.dark':
return const ThemeModeState(themeMode: ThemeMode.dark);
case 'ThemeMode.light':
return const ThemeModeState(themeMode: ThemeMode.light);
case 'ThemeMode.system':
default:
return const ThemeModeState(themeMode: ThemeMode.system);
}
}
@override
Map<String, String>? toJson(ThemeModeState state) {
return <String, String>{'themeMode': state.themeMode.toString()};
}
}
part of 'theme_cubit.dart';
@immutable
class ThemeModeState extends Equatable {
const ThemeModeState({
this.themeMode,
});
final ThemeMode? themeMode;
@override
List<Object?> get props => <Object?>[
themeMode,
];
}
import 'dart:io';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive/hive.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:path_provider/path_provider.dart';
import 'package:spotifyplaylistgenerator/config/theme.dart'; import 'package:spotifyplaylistgenerator/config/theme.dart';
import 'package:spotifyplaylistgenerator/ui/screens/skeleton_screen.dart'; import 'package:spotifyplaylistgenerator/cubit/activity_cubit.dart';
import 'package:spotifyplaylistgenerator/cubit/nav_cubit_pages.dart';
import 'package:spotifyplaylistgenerator/cubit/nav_cubit_screens.dart';
import 'package:spotifyplaylistgenerator/cubit/settings_global_cubit.dart';
import 'package:spotifyplaylistgenerator/cubit/theme_cubit.dart';
import 'package:spotifyplaylistgenerator/ui/skeleton.dart';
void main() async { void main() async {
// Initialize packages
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized(); await EasyLocalization.ensureInitialized();
final Directory tmpDir = await getTemporaryDirectory();
Hive.init(tmpDir.toString());
HydratedBloc.storage = await HydratedStorage.build(
storageDirectory: tmpDir,
);
runApp( SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
EasyLocalization( .then((value) => runApp(EasyLocalization(
path: 'assets/translations', path: 'assets/translations',
supportedLocales: const <Locale>[ supportedLocales: const <Locale>[
Locale('en'), Locale('en'),
...@@ -18,8 +36,7 @@ void main() async { ...@@ -18,8 +36,7 @@ void main() async {
fallbackLocale: const Locale('en'), fallbackLocale: const Locale('en'),
useFallbackTranslations: true, useFallbackTranslations: true,
child: const MyApp(), child: const MyApp(),
), )));
);
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
...@@ -27,16 +44,33 @@ class MyApp extends StatelessWidget { ...@@ -27,16 +44,33 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<NavCubitPage>(create: (context) => NavCubitPage()),
BlocProvider<NavCubitScreen>(create: (context) => NavCubitScreen()),
BlocProvider<ThemeCubit>(create: (context) => ThemeCubit()),
BlocProvider<ActivityCubit>(create: (context) => ActivityCubit()),
BlocProvider<GlobalSettingsCubit>(create: (context) => GlobalSettingsCubit()),
],
child: BlocBuilder<ThemeCubit, ThemeModeState>(
builder: (BuildContext context, ThemeModeState state) {
return MaterialApp( return MaterialApp(
title: 'Spotify playlist generator', title: 'Spotify playlist generator',
theme: appTheme,
home: const SkeletonScreen(), home: const SkeletonScreen(),
// Theme stuff
theme: lightTheme,
darkTheme: darkTheme,
themeMode: state.themeMode,
// Localization stuff // Localization stuff
localizationsDelegates: context.localizationDelegates, localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales, supportedLocales: context.supportedLocales,
locale: context.locale, locale: context.locale,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
); );
},
),
);
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment