diff --git a/android/gradle.properties b/android/gradle.properties index eeed3ef5a3d04530f5624cce71b2a57976938aed..ed86f0f26922ea79f4d0c8e0e84f6d74259952bb 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -app.versionName=0.0.21 -app.versionCode=21 +app.versionName=0.0.22 +app.versionCode=22 diff --git a/assets/translations/en.json b/assets/translations/en.json index d944c41fd8cf41de748c2678c2c1a459ed5fcc2f..9c047c5b642fcd110fd65c20b5aedb4dfc0f6fb5 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -1,6 +1,10 @@ { "app_name": "Scrobbles", + "bottom_nav_home": "Home", + "bottom_nav_discoveries": "Discoveries", + "bottom_nav_repartition": "Statistics", + "global_statistics": "Global statistics", "statistics_total_scrobbles_count": "Total scrobbles count: {count}", "statistics_last_scrobble": "Last scrobble: {datetime}", diff --git a/assets/translations/fr.json b/assets/translations/fr.json index a0a50111bcc45c4bd9b7237699b2a98076e82670..6ec386fb9c26e3b37e9502c724e2da361e319317 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -1,6 +1,10 @@ { "app_name": "Scrobbles", + "bottom_nav_home": "Accueil", + "bottom_nav_discoveries": "Découvertes", + "bottom_nav_repartition": "Statistiques", + "global_statistics": "Statistiques globales d'écoutes", "statistics_total_scrobbles_count": "Nombre total d'écoutes : {count}", "statistics_last_scrobble": "Dernière écoute : {datetime}", diff --git a/fastlane/metadata/android/en-US/changelogs/22.txt b/fastlane/metadata/android/en-US/changelogs/22.txt new file mode 100644 index 0000000000000000000000000000000000000000..7807734977384b676e78d54731d6054763f6ec81 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/22.txt @@ -0,0 +1 @@ +Add navigation bar, split charts in pages. diff --git a/fastlane/metadata/android/fr-FR/changelogs/22.txt b/fastlane/metadata/android/fr-FR/changelogs/22.txt new file mode 100644 index 0000000000000000000000000000000000000000..2bbf823d0a459a4f92fe3d97cda381f12ffc34ab --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/22.txt @@ -0,0 +1 @@ +Ajout d'une barre de navigation, séparation des graphiques en plusieurs pages. diff --git a/lib/cubit/bottom_nav_cubit.dart b/lib/cubit/bottom_nav_cubit.dart new file mode 100644 index 0000000000000000000000000000000000000000..57acf9a988ba60cea389d2087e0b927eac654a8f --- /dev/null +++ b/lib/cubit/bottom_nav_cubit.dart @@ -0,0 +1,21 @@ +import 'package:hydrated_bloc/hydrated_bloc.dart'; + +class BottomNavCubit extends HydratedCubit<int> { + BottomNavCubit() : super(0); + + void updateIndex(int index) => emit(index); + + void getHomePage() => emit(0); + void getDiscoveriesPage() => emit(1); + void getStatisticsPage() => emit(2); + + @override + int? fromJson(Map<String, dynamic> json) { + return json['pageIndex'] as int?; + } + + @override + Map<String, dynamic>? toJson(int state) { + return <String, int>{'pageIndex': state}; + } +} diff --git a/lib/main.dart b/lib/main.dart index 66de9e0cc06cc5000882742b4d6885123580ed44..c6ec2e707dc448f423707a8dcd1d8127de97db4b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,12 +1,24 @@ +import 'dart:io'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:hive/hive.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; +import 'package:path_provider/path_provider.dart'; import 'config/theme.dart'; + import 'ui/screens/skeleton_screen.dart'; void main() async { + /// Initialize packages WidgetsFlutterBinding.ensureInitialized(); await EasyLocalization.ensureInitialized(); + final Directory tmpDir = await getTemporaryDirectory(); + Hive.init(tmpDir.toString()); + HydratedBloc.storage = await HydratedStorage.build( + storageDirectory: tmpDir, + ); runApp( EasyLocalization( diff --git a/lib/ui/screens/discoveries_screen.dart b/lib/ui/screens/discoveries_screen.dart new file mode 100644 index 0000000000000000000000000000000000000000..ca590c79bcc0cc864a305b2f56b86a6ba367b10b --- /dev/null +++ b/lib/ui/screens/discoveries_screen.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; + +import '../widgets/main_screen/discoveries_card.dart'; + +class DiscoveriesScreen extends StatefulWidget { + const DiscoveriesScreen({super.key}); + + @override + State<DiscoveriesScreen> createState() => _DiscoveriesScreenState(); +} + +class _DiscoveriesScreenState extends State<DiscoveriesScreen> { + @override + Widget build(BuildContext context) { + return Material( + color: Theme.of(context).colorScheme.background, + child: ListView( + padding: const EdgeInsets.symmetric(horizontal: 4), + physics: const BouncingScrollPhysics(), + children: <Widget>[ + const SizedBox(height: 8), + const ChartDiscoveriesCard(), + const SizedBox(height: 36), + ], + ), + ); + } +} diff --git a/lib/ui/screens/home_screen.dart b/lib/ui/screens/home_screen.dart new file mode 100644 index 0000000000000000000000000000000000000000..d6407545c1a07e0742d32d6fb093e8ae5c255c2f --- /dev/null +++ b/lib/ui/screens/home_screen.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +import '../widgets/main_screen/statistics_card.dart'; +import '../widgets/main_screen/timeline_card.dart'; + +class HomeScreen extends StatefulWidget { + const HomeScreen({super.key}); + + @override + State<HomeScreen> createState() => _HomeScreenState(); +} + +class _HomeScreenState extends State<HomeScreen> { + @override + Widget build(BuildContext context) { + return Material( + color: Theme.of(context).colorScheme.background, + child: ListView( + padding: const EdgeInsets.symmetric(horizontal: 4), + physics: const BouncingScrollPhysics(), + children: <Widget>[ + const SizedBox(height: 8), + const StatisticsCard(), + const SizedBox(height: 6), + const ChartTimelineCard(), + const SizedBox(height: 36), + ], + ), + ); + } +} diff --git a/lib/ui/screens/skeleton_screen.dart b/lib/ui/screens/skeleton_screen.dart index 161794eb70900fde16134ff46b23c5953b7e9187..edef18deb6a78751e64073f458f8596ec0f5a8a4 100644 --- a/lib/ui/screens/skeleton_screen.dart +++ b/lib/ui/screens/skeleton_screen.dart @@ -1,7 +1,13 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; -import 'main_screen.dart'; +import '../../cubit/bottom_nav_cubit.dart'; import '../widgets/app_bar.dart'; +import '../widgets/bottom_nav_bar.dart'; + +import 'discoveries_screen.dart'; +import 'home_screen.dart'; +import 'statistics_screen.dart'; class SkeletonScreen extends StatefulWidget { const SkeletonScreen({super.key}); @@ -13,11 +19,27 @@ class SkeletonScreen extends StatefulWidget { class _SkeletonScreenState extends State<SkeletonScreen> { @override Widget build(BuildContext context) { - return Scaffold( - appBar: StandardAppBar(notifyParent: refresh), - extendBodyBehindAppBar: true, - body: const MainScreen(), - backgroundColor: Theme.of(context).colorScheme.background, + const List<Widget> pageNavigation = <Widget>[ + const HomeScreen(), + const DiscoveriesScreen(), + const StatisticsScreen(), + ]; + + return BlocProvider<BottomNavCubit>( + create: (BuildContext context) => BottomNavCubit(), + child: Scaffold( + appBar: StandardAppBar(notifyParent: refresh), + extendBodyBehindAppBar: false, + body: BlocBuilder<BottomNavCubit, int>( + builder: (BuildContext context, int state) { + return AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: pageNavigation.elementAt(state)); + }, + ), + bottomNavigationBar: const BottomNavBar(), + backgroundColor: Theme.of(context).colorScheme.background, + ), ); } diff --git a/lib/ui/screens/main_screen.dart b/lib/ui/screens/statistics_screen.dart similarity index 51% rename from lib/ui/screens/main_screen.dart rename to lib/ui/screens/statistics_screen.dart index 780c64447c9f01a86db1594f6f0d97e1d201d850..c3f9e460c514f460c36b112a067e728b196b69fc 100644 --- a/lib/ui/screens/main_screen.dart +++ b/lib/ui/screens/statistics_screen.dart @@ -2,18 +2,15 @@ import 'package:flutter/material.dart'; import '../widgets/main_screen/counts_by_day_card.dart'; import '../widgets/main_screen/counts_by_hour_card.dart'; -import '../widgets/main_screen/discoveries_card.dart'; -import '../widgets/main_screen/statistics_card.dart'; -import '../widgets/main_screen/timeline_card.dart'; -class MainScreen extends StatefulWidget { - const MainScreen({super.key}); +class StatisticsScreen extends StatefulWidget { + const StatisticsScreen({super.key}); @override - State<MainScreen> createState() => _MainScreenState(); + State<StatisticsScreen> createState() => _StatisticsScreenState(); } -class _MainScreenState extends State<MainScreen> { +class _StatisticsScreenState extends State<StatisticsScreen> { @override Widget build(BuildContext context) { return Material( @@ -22,13 +19,7 @@ class _MainScreenState extends State<MainScreen> { padding: const EdgeInsets.symmetric(horizontal: 4), physics: const BouncingScrollPhysics(), children: <Widget>[ - const SizedBox(height: 90), - const StatisticsCard(), - const SizedBox(height: 6), - const ChartTimelineCard(), - const SizedBox(height: 6), - const ChartDiscoveriesCard(), - const SizedBox(height: 6), + const SizedBox(height: 8), const ChartCountsByDayCard(), const SizedBox(height: 6), const ChartCountsByHourCard(), diff --git a/lib/ui/widgets/bottom_nav_bar.dart b/lib/ui/widgets/bottom_nav_bar.dart new file mode 100644 index 0000000000000000000000000000000000000000..c06cecbf6e665b2981d5c5e80a91c16d544bc123 --- /dev/null +++ b/lib/ui/widgets/bottom_nav_bar.dart @@ -0,0 +1,51 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:ionicons/ionicons.dart'; + +import '../../cubit/bottom_nav_cubit.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.surfaceVariant, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), + ), + ), + child: BlocBuilder<BottomNavCubit, int>(builder: (BuildContext context, int state) { + return BottomNavigationBar( + currentIndex: state, + onTap: (int index) => context.read<BottomNavCubit>().updateIndex(index), + type: BottomNavigationBarType.fixed, + elevation: 0, + backgroundColor: Colors.transparent, + selectedItemColor: Theme.of(context).colorScheme.primary, + unselectedItemColor: Theme.of(context).textTheme.bodySmall!.color, + items: <BottomNavigationBarItem>[ + BottomNavigationBarItem( + icon: const Icon(Ionicons.home_outline), + label: tr('bottom_nav_home'), + ), + BottomNavigationBarItem( + icon: const Icon(Ionicons.star_outline), + label: tr('bottom_nav_discoveries'), + ), + BottomNavigationBarItem( + icon: const Icon(Ionicons.bar_chart_outline), + label: tr('bottom_nav_repartition'), + ), + ], + ); + }), + ); + } +} diff --git a/lib/ui/widgets/charts/custom_bar_chart.dart b/lib/ui/widgets/charts/custom_bar_chart.dart index 162d53b69516dbd81b56f860a37e5dfe23bc2282..8cdd32dc37b03b528ee28f90868880fb98547666 100644 --- a/lib/ui/widgets/charts/custom_bar_chart.dart +++ b/lib/ui/widgets/charts/custom_bar_chart.dart @@ -8,7 +8,7 @@ import '../../../utils/color_extensions.dart'; class CustomBarChart extends StatelessWidget { CustomBarChart({super.key}); - final double chartHeight = 120.0; + final double chartHeight = 150.0; final double verticalTicksInterval = 10; final String verticalAxisTitleSuffix = ''; final double titleFontSize = 10; diff --git a/lib/ui/widgets/charts/custom_line_chart.dart b/lib/ui/widgets/charts/custom_line_chart.dart index 01a48a9c7151a5702a56b578c7681e4a9e69cfe1..fadf283d5001e3a1e03f79698871b3590022c387 100644 --- a/lib/ui/widgets/charts/custom_line_chart.dart +++ b/lib/ui/widgets/charts/custom_line_chart.dart @@ -6,7 +6,7 @@ import '../../../config/app_colors.dart'; class CustomLineChart extends StatelessWidget { CustomLineChart({super.key}); - final double chartHeight = 120.0; + final double chartHeight = 150.0; final double titleFontSize = 10; @override diff --git a/pubspec.lock b/pubspec.lock index 42ca9d5d8fcdcdad10527cea41aeae9344429d37..7dd2956be827f4d3099d3d1ac46471799444c5f5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + bloc: + dependency: transitive + description: + name: bloc + sha256: "3820f15f502372d979121de1f6b97bfcf1630ebff8fe1d52fb2b0bfa49be5b49" + url: "https://pub.dev" + source: hosted + version: "8.1.2" characters: dependency: transitive description: @@ -41,6 +49,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.2" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" easy_localization: dependency: "direct main" description: @@ -58,7 +74,7 @@ packages: source: hosted version: "0.0.2" equatable: - dependency: transitive + dependency: "direct main" description: name: equatable sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 @@ -94,6 +110,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + sha256: e74efb89ee6945bcbce74a5b3a5a3376b088e5f21f55c263fc38cbdc6237faae + url: "https://pub.dev" + source: hosted + version: "8.1.3" flutter_localizations: dependency: transitive description: flutter @@ -104,6 +128,14 @@ packages: description: flutter source: sdk version: "0.0.0" + hive: + dependency: transitive + description: + name: hive + sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + url: "https://pub.dev" + source: hosted + version: "2.2.3" http: dependency: "direct main" description: @@ -120,6 +152,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + hydrated_bloc: + dependency: "direct main" + description: + name: hydrated_bloc + sha256: "24994e61f64904d911683cce1a31dc4ef611619da5253f1de2b7b8fc6f79a118" + url: "https://pub.dev" + source: hosted + version: "9.1.2" intl: dependency: transitive description: @@ -128,6 +168,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.18.1" + ionicons: + dependency: "direct main" + description: + name: ionicons + sha256: "5496bc65a16115ecf05b15b78f494ee4a8869504357668f0a11d689e970523cf" + url: "https://pub.dev" + source: hosted + version: "0.2.2" material_color_utilities: dependency: transitive description: @@ -144,6 +192,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -152,6 +208,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.3" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + url: "https://pub.dev" + source: hosted + version: "2.1.1" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + url: "https://pub.dev" + source: hosted + version: "2.3.1" path_provider_linux: dependency: transitive description: @@ -192,6 +272,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.6" + provider: + dependency: transitive + description: + name: provider + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" + source: hosted + version: "6.0.5" shared_preferences: dependency: transitive description: @@ -269,6 +357,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + url: "https://pub.dev" + source: hosted + version: "3.1.0" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 0e1c58c950a09ce8a50711add52f4d8726c0b30d..415477b88b70f539b6e924691837cdf88f1714de 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: Display scrobbles data and charts publish_to: 'none' -version: 0.0.21+21 +version: 0.0.22+22 environment: sdk: '^3.0.0' @@ -13,8 +13,13 @@ dependencies: sdk: flutter easy_localization: ^3.0.1 - http: ^1.1.0 + equatable: ^2.0.5 fl_chart: ^0.64.0 + flutter_bloc: ^8.1.1 + http: ^1.1.0 + path_provider: ^2.0.11 + hydrated_bloc: ^9.0.0 + ionicons: ^0.2.2 unicons: ^2.1.1 flutter: