diff --git a/android/gradle.properties b/android/gradle.properties index 70de886456c88a8badd92e2ef144bac0b234c731..547a7f87984e869bc68c9f0a1c8e94cde2dcad43 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.56 -app.versionCode=56 +app.versionName=0.0.57 +app.versionCode=57 diff --git a/assets/translations/en.json b/assets/translations/en.json index 8ebe491d4fcc44fa1cb1ad5f4fb3af0ee7d49817..51578cf240c0ab7a404cca695f530d3cdc9e5937 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -28,6 +28,7 @@ "top_artists_title": "Top artists ({daysCount} days)", "settings_title": "Settings", + "settings_label_theme": "Theme mode", "settings_title_global": "Global:", "settings_label_username": "Username: ", "settings_label_security_token": "Security token: ", diff --git a/assets/translations/fr.json b/assets/translations/fr.json index 39625aecb52e201581a06a0727eff33b689daf17..86c15ba62ae62218c73dea90988fb32c4e70cdab 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -28,6 +28,7 @@ "top_artists_title": "Top artistes ({daysCount} jours)", "settings_title": "Paramètres", + "settings_label_theme": "Thème de couleurs", "settings_title_global": "Généraux :", "settings_label_username": "Utilisateur : ", "settings_label_security_token": "Jeton de sécurité : ", diff --git a/fastlane/metadata/android/en-US/changelogs/57.txt b/fastlane/metadata/android/en-US/changelogs/57.txt new file mode 100644 index 0000000000000000000000000000000000000000..e466561363a0fabd2043c6d7903c4280d6b7b3a8 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/57.txt @@ -0,0 +1 @@ +Add light/dark theme setting. diff --git a/fastlane/metadata/android/fr-FR/changelogs/57.txt b/fastlane/metadata/android/fr-FR/changelogs/57.txt new file mode 100644 index 0000000000000000000000000000000000000000..4a93af73582a415da90a3a845509c88ff0682057 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/57.txt @@ -0,0 +1 @@ +Ajout d'un réglage de thème clair/sombre. diff --git a/lib/cubit/theme_cubit.dart b/lib/cubit/theme_cubit.dart new file mode 100644 index 0000000000000000000000000000000000000000..b793e895dbb0c672d451cd403e0036c3d9ac9b42 --- /dev/null +++ b/lib/cubit/theme_cubit.dart @@ -0,0 +1,31 @@ +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()}; + } +} diff --git a/lib/cubit/theme_state.dart b/lib/cubit/theme_state.dart new file mode 100644 index 0000000000000000000000000000000000000000..e479a50f12fe72a35a1fd1722ff72afbb692a136 --- /dev/null +++ b/lib/cubit/theme_state.dart @@ -0,0 +1,15 @@ +part of 'theme_cubit.dart'; + +@immutable +class ThemeModeState extends Equatable { + const ThemeModeState({ + this.themeMode, + }); + + final ThemeMode? themeMode; + + @override + List<Object?> get props => <Object?>[ + themeMode, + ]; +} diff --git a/lib/main.dart b/lib/main.dart index 6d990ff5759cfe2e6942e40f2ea6045ef2d49e0f..b9d50e8af9617478cc7565a586559022565d21a4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,6 +20,7 @@ import 'package:scrobbles/cubit/data_statistics_recent_cubit.dart'; import 'package:scrobbles/cubit/data_timeline_cubit.dart'; import 'package:scrobbles/cubit/data_top_artists_cubit.dart'; import 'package:scrobbles/cubit/settings_cubit.dart'; +import 'package:scrobbles/cubit/theme_cubit.dart'; import 'package:scrobbles/ui/skeleton.dart'; void main() async { @@ -67,18 +68,26 @@ class MyApp extends StatelessWidget { create: (context) => DataStatisticsRecentCubit()), BlocProvider<DataTimelineCubit>(create: (context) => DataTimelineCubit()), BlocProvider<DataTopArtistsCubit>(create: (context) => DataTopArtistsCubit()), + BlocProvider<ThemeCubit>(create: (context) => ThemeCubit()), ], - child: MaterialApp( - title: 'Scrobbles', - theme: appTheme, - home: const SkeletonScreen(), + child: BlocBuilder<ThemeCubit, ThemeModeState>( + builder: (BuildContext context, ThemeModeState state) { + return MaterialApp( + title: 'Scrobbles', + home: const SkeletonScreen(), - // Localization stuff - localizationsDelegates: context.localizationDelegates, - supportedLocales: context.supportedLocales, - locale: context.locale, - debugShowCheckedModeBanner: false, - ), + // Theme stuff + theme: lightTheme, + darkTheme: darkTheme, + themeMode: state.themeMode, + + // Localization stuff + localizationsDelegates: context.localizationDelegates, + supportedLocales: context.supportedLocales, + locale: context.locale, + debugShowCheckedModeBanner: false, + ); + }), ); } } diff --git a/lib/ui/widgets/abstracts/custom_chart.dart b/lib/ui/widgets/abstracts/custom_chart.dart index e0e69fbce22c64e144de072118c07d51e9d1f158..6d220180bbf652b233bdc2d3c7873b97cebc2929 100644 --- a/lib/ui/widgets/abstracts/custom_chart.dart +++ b/lib/ui/widgets/abstracts/custom_chart.dart @@ -162,7 +162,7 @@ class CustomChart extends StatelessWidget { child: Text( text, style: TextStyle( - color: AppColors.mainTextColor1, + // color: getTextColor(), fontSize: titleFontSize, ), ), @@ -180,7 +180,6 @@ class CustomChart extends StatelessWidget { child: Text( tr(dayShortName), style: const TextStyle( - color: AppColors.mainTextColor1, fontSize: 11, ), ), @@ -200,7 +199,6 @@ class CustomChart extends StatelessWidget { child: Text( text, style: TextStyle( - color: AppColors.mainTextColor1, fontSize: titleFontSize, ), ), @@ -224,7 +222,6 @@ class CustomChart extends StatelessWidget { child: Text( value.toInt().toString() + suffix, style: TextStyle( - color: AppColors.mainTextColor1, fontSize: titleFontSize, ), ), diff --git a/lib/ui/widgets/charts/counts_by_day.dart b/lib/ui/widgets/charts/counts_by_day.dart index e571aa9ddbd41ba20df4de82049b4f3369a3d318..c43fc0d71fe4a0f26cd9337445e7d24cba3fa172 100644 --- a/lib/ui/widgets/charts/counts_by_day.dart +++ b/lib/ui/widgets/charts/counts_by_day.dart @@ -5,10 +5,10 @@ import 'package:scrobbles/models/counts_by_day.dart'; import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart'; class ChartCountsByDay extends CustomBarChart { - final CountsByDayData chartData; - const ChartCountsByDay({super.key, required this.chartData}); + final CountsByDayData chartData; + @override final double verticalTicksInterval = 5; @override @@ -31,7 +31,7 @@ class ChartCountsByDay extends CustomBarChart { return getBarChart( barWidth: getBarWidth(maxWidth, barsCount), - backgroundColor: Theme.of(context).colorScheme.onSurface, + backgroundColor: Theme.of(context).colorScheme.background, ); }, ), diff --git a/lib/ui/widgets/charts/counts_by_hour.dart b/lib/ui/widgets/charts/counts_by_hour.dart index 4bc2bf8ead411dbe3f350e15590d198f23340b7a..1a1c1539289a06e2f98f355924a2ffc80adcbc7f 100644 --- a/lib/ui/widgets/charts/counts_by_hour.dart +++ b/lib/ui/widgets/charts/counts_by_hour.dart @@ -7,10 +7,10 @@ import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart'; import 'package:scrobbles/utils/color_extensions.dart'; class ChartCountsByHour extends CustomBarChart { - final CountsByHourData chartData; - const ChartCountsByHour({super.key, required this.chartData}); + final CountsByHourData chartData; + @override final double verticalTicksInterval = 5; @override @@ -33,7 +33,7 @@ class ChartCountsByHour extends CustomBarChart { return getBarChart( barWidth: getBarWidth(maxWidth, barsCount), - backgroundColor: Theme.of(context).colorScheme.onSurface, + backgroundColor: Theme.of(context).colorScheme.background, ); }, ), diff --git a/lib/ui/widgets/charts/discoveries_artists.dart b/lib/ui/widgets/charts/discoveries_artists.dart index d5c9bd315b6674d7be6e4ddb804b0e61256d412f..98196656e55717b1bda9e8733aa5bce7a2398c39 100644 --- a/lib/ui/widgets/charts/discoveries_artists.dart +++ b/lib/ui/widgets/charts/discoveries_artists.dart @@ -7,10 +7,10 @@ import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart'; import 'package:scrobbles/utils/color_extensions.dart'; class ChartDiscoveriesArtists extends CustomBarChart { - final DiscoveriesData chartData; - const ChartDiscoveriesArtists({super.key, required this.chartData}); + final DiscoveriesData chartData; + @override Widget build(BuildContext context) { if (chartData.data.keys.isEmpty) { @@ -28,7 +28,7 @@ class ChartDiscoveriesArtists extends CustomBarChart { return getBarChart( barWidth: getBarWidth(maxWidth, barsCount), - backgroundColor: Theme.of(context).colorScheme.onSurface, + backgroundColor: Theme.of(context).colorScheme.background, ); }, ), diff --git a/lib/ui/widgets/charts/discoveries_tracks.dart b/lib/ui/widgets/charts/discoveries_tracks.dart index 9272e825baf60ad36daf2b77ad69bbb2a6f1c7e8..2f4626dcdad2ad8c0bfcf8d7c0364595a5b38c56 100644 --- a/lib/ui/widgets/charts/discoveries_tracks.dart +++ b/lib/ui/widgets/charts/discoveries_tracks.dart @@ -7,10 +7,10 @@ import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart'; import 'package:scrobbles/utils/color_extensions.dart'; class ChartDiscoveriesTracks extends CustomBarChart { - final DiscoveriesData chartData; - const ChartDiscoveriesTracks({super.key, required this.chartData}); + final DiscoveriesData chartData; + @override Widget build(BuildContext context) { if (chartData.data.keys.isEmpty) { @@ -28,7 +28,7 @@ class ChartDiscoveriesTracks extends CustomBarChart { return getBarChart( barWidth: getBarWidth(maxWidth, barsCount), - backgroundColor: Theme.of(context).colorScheme.onSurface, + backgroundColor: Theme.of(context).colorScheme.background, ); }, ), diff --git a/lib/ui/widgets/charts/heatmap.dart b/lib/ui/widgets/charts/heatmap.dart index 3867053c6d08049d9601da9983e1529e690f1461..0625dbfb332f795d310b16bf10c4e50f7bf2f11a 100644 --- a/lib/ui/widgets/charts/heatmap.dart +++ b/lib/ui/widgets/charts/heatmap.dart @@ -7,10 +7,10 @@ import 'package:scrobbles/models/heatmap.dart'; import 'package:scrobbles/ui/widgets/abstracts/custom_chart.dart'; class ChartHeatmap extends CustomChart { - final HeatmapData chartData; - const ChartHeatmap({super.key, required this.chartData}); + final HeatmapData chartData; + @override final double chartHeight = 150.0; @override @@ -133,7 +133,6 @@ class ChartHeatmap extends CustomChart { child: Text( tr(dayShortName), style: TextStyle( - color: AppColors.mainTextColor1, fontSize: titleFontSize, ), ), diff --git a/lib/ui/widgets/charts/timeline_counts.dart b/lib/ui/widgets/charts/timeline_counts.dart index 39fa5ac72681889951d0f2f8d1ee1d15532aff7c..4357774255a3d9f29c2242b5717e92de4cdddc14 100644 --- a/lib/ui/widgets/charts/timeline_counts.dart +++ b/lib/ui/widgets/charts/timeline_counts.dart @@ -6,10 +6,10 @@ import 'package:scrobbles/models/timeline.dart'; import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart'; class ChartTimelineCounts extends CustomBarChart { - final TimelineData chartData; - const ChartTimelineCounts({super.key, required this.chartData}); + final TimelineData chartData; + @override final double verticalTicksInterval = 50; @@ -30,7 +30,7 @@ class ChartTimelineCounts extends CustomBarChart { return getBarChart( barWidth: getBarWidth(maxWidth, barsCount), - backgroundColor: Theme.of(context).colorScheme.onSurface, + backgroundColor: Theme.of(context).colorScheme.background, ); }, ), diff --git a/lib/ui/widgets/charts/timeline_eclecticism.dart b/lib/ui/widgets/charts/timeline_eclecticism.dart index d3c8f201a269669327dc801388bb73b84bf5f1f9..3eeffe7b460368c5ada894a44d7e6c3ee397a4bd 100644 --- a/lib/ui/widgets/charts/timeline_eclecticism.dart +++ b/lib/ui/widgets/charts/timeline_eclecticism.dart @@ -7,10 +7,10 @@ import 'package:scrobbles/ui/widgets/abstracts/custom_line_chart.dart'; import 'package:scrobbles/utils/color_extensions.dart'; class ChartTimelineEclecticism extends CustomLineChart { - final TimelineData chartData; - const ChartTimelineEclecticism({super.key, required this.chartData}); + final TimelineData chartData; + @override final String verticalAxisTitleSuffix = '%'; diff --git a/lib/ui/widgets/charts/top_artists.dart b/lib/ui/widgets/charts/top_artists.dart index 369be995eee4b41f7a80c0ad4b6351ed88e41f8f..79b39d317d86516367915ba2e5ba6b270403a337 100644 --- a/lib/ui/widgets/charts/top_artists.dart +++ b/lib/ui/widgets/charts/top_artists.dart @@ -1,16 +1,15 @@ import 'package:flutter/material.dart'; import 'package:fl_chart/fl_chart.dart'; -import 'package:scrobbles/config/app_colors.dart'; import 'package:scrobbles/models/topartists.dart'; import 'package:scrobbles/ui/widgets/abstracts/custom_chart.dart'; import 'package:scrobbles/utils/color_extensions.dart'; class ChartTopArtists extends CustomChart { - final TopArtistsData chartData; - const ChartTopArtists({super.key, required this.chartData}); + final TopArtistsData chartData; + @override Widget build(BuildContext context) { return AspectRatio( @@ -68,7 +67,6 @@ class ChartTopArtists extends CustomChart { radius: radius, titleStyle: const TextStyle( fontSize: fontSize, - color: AppColors.mainTextColor1, shadows: shadows, ), ); diff --git a/lib/ui/widgets/charts/top_artists_stream.dart b/lib/ui/widgets/charts/top_artists_stream.dart index 738cc6d9b089a1fdcf73dc224ada86a3126d9e36..523c24dd3e2d33514d5dedfdd291f458f8220392 100644 --- a/lib/ui/widgets/charts/top_artists_stream.dart +++ b/lib/ui/widgets/charts/top_artists_stream.dart @@ -6,10 +6,10 @@ import 'package:scrobbles/ui/widgets/abstracts/custom_line_chart.dart'; import 'package:scrobbles/utils/color_extensions.dart'; class ChartTopArtistsStream extends CustomLineChart { - final TopArtistsData chartData; - const ChartTopArtistsStream({super.key, required this.chartData}); + final TopArtistsData chartData; + @override final double verticalTicksInterval = 10; @override @@ -68,8 +68,7 @@ class ChartTopArtistsStream extends CustomLineChart { List<LineChartBarData> getDataStreamLine() { final int artistsCount = - chartData.topArtistsStream[chartData.topArtistsStream.keys.first]?.length ?? - 0; + chartData.topArtistsStream[chartData.topArtistsStream.keys.first]?.length ?? 0; List<LineChartBarData> lines = []; @@ -133,8 +132,7 @@ class ChartTopArtistsStream extends CustomLineChart { List<BetweenBarsData> getBetweenBarsData() { final int artistsCount = - chartData.topArtistsStream[chartData.topArtistsStream.keys.first]?.length ?? - 0; + chartData.topArtistsStream[chartData.topArtistsStream.keys.first]?.length ?? 0; return Iterable<int>.generate(artistsCount) .toList() diff --git a/lib/ui/widgets/content/statistics_global.dart b/lib/ui/widgets/content/statistics_global.dart index 838e3b24b77473c48de2e74adfe9506709bb82b0..96a678aa156b34839be6229fb79cffd458655ab4 100644 --- a/lib/ui/widgets/content/statistics_global.dart +++ b/lib/ui/widgets/content/statistics_global.dart @@ -4,10 +4,10 @@ import 'package:flutter/material.dart'; import 'package:scrobbles/models/statistics_global.dart'; class ContentStatisticsGlobal extends StatelessWidget { - final StatisticsGlobalData statistics; - const ContentStatisticsGlobal({super.key, required this.statistics}); + final StatisticsGlobalData statistics; + @override Widget build(BuildContext context) { final TextTheme textTheme = Theme.of(context).primaryTextTheme; @@ -22,9 +22,8 @@ class ContentStatisticsGlobal extends StatelessWidget { style: textTheme.bodyMedium, ).tr( namedArgs: { - 'count': statistics.totalCount != null - ? statistics.totalCount.toString() - : placeholder, + 'count': + statistics.totalCount != null ? statistics.totalCount.toString() : placeholder, }, ), Text( diff --git a/lib/ui/widgets/content/statistics_recent.dart b/lib/ui/widgets/content/statistics_recent.dart index e5a748fe8c2597acea395a3f0e7a2fe833f984d9..678623a0f9ca98ee8eb2eb3d1c7eb6a8b06fb052 100644 --- a/lib/ui/widgets/content/statistics_recent.dart +++ b/lib/ui/widgets/content/statistics_recent.dart @@ -4,10 +4,10 @@ import 'package:flutter/material.dart'; import 'package:scrobbles/models/statistics_recent.dart'; class ContentStatisticsRecent extends StatelessWidget { - final StatisticsRecentData statistics; - const ContentStatisticsRecent({super.key, required this.statistics}); + final StatisticsRecentData statistics; + @override Widget build(BuildContext context) { final TextTheme textTheme = Theme.of(context).primaryTextTheme; diff --git a/lib/ui/widgets/settings_form.dart b/lib/ui/widgets/settings_form.dart index 0afaabee22c35d3a29eb7b18cab300d1889a0017..6e5401de8f55105e2e99dd2eb03fb4dc4de4f376 100644 --- a/lib/ui/widgets/settings_form.dart +++ b/lib/ui/widgets/settings_form.dart @@ -1,6 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:scrobbles/ui/widgets/theme_card.dart'; import 'package:unicons/unicons.dart'; import 'package:scrobbles/config/default_settings.dart'; @@ -99,6 +100,36 @@ class _SettingsFormState extends State<SettingsForm> { mainAxisSize: MainAxisSize.max, children: <Widget>[ const SizedBox(height: 8), + + // Light/dark theme + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: <Widget>[ + const Text('settings_label_theme').tr(), + const Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ThemeCard( + mode: ThemeMode.system, + icon: UniconsLine.cog, + ), + ThemeCard( + mode: ThemeMode.light, + icon: UniconsLine.sun, + ), + ThemeCard( + mode: ThemeMode.dark, + icon: UniconsLine.moon, + ) + ], + ), + ], + ), + + const SizedBox(height: 16), + AppTitle2(text: tr('settings_title_global')), // Username diff --git a/lib/ui/widgets/theme_card.dart b/lib/ui/widgets/theme_card.dart new file mode 100644 index 0000000000000000000000000000000000000000..738541d400cf312b7a1b657ff68bf22b1895f3ac --- /dev/null +++ b/lib/ui/widgets/theme_card.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:scrobbles/cubit/theme_cubit.dart'; + +class ThemeCard extends StatelessWidget { + const ThemeCard({ + super.key, + required this.mode, + required this.icon, + }); + + final IconData icon; + final ThemeMode mode; + + @override + Widget build(BuildContext context) { + return BlocBuilder<ThemeCubit, ThemeModeState>( + builder: (BuildContext context, ThemeModeState state) { + return Card( + elevation: 2, + shadowColor: Theme.of(context).colorScheme.shadow, + color: state.themeMode == mode + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.surface, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + margin: const EdgeInsets.all(5), + child: InkWell( + onTap: () => BlocProvider.of<ThemeCubit>(context).getTheme( + ThemeModeState(themeMode: mode), + ), + borderRadius: const BorderRadius.all(Radius.circular(12)), + child: Icon( + icon, + size: 32, + color: + state.themeMode != mode ? Theme.of(context).colorScheme.primary : Colors.white, + ), + ), + ); + }); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index fe4cb2f80516714b4d052788cc6e19b82de97cbe..8f5f50bd7f3e274c2c874c6042f2a707591e701a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: Display scrobbles data and charts publish_to: 'none' -version: 0.0.56+56 +version: 0.0.57+57 environment: sdk: '^3.0.0'