Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • android/org.benoitharrault.scrobbles
1 result
Show changes
Showing
with 751 additions and 136 deletions
import 'package:flutter/material.dart';
import '../widgets/main_screen/statistics_global_card.dart';
import '../widgets/main_screen/statistics_recent_card.dart';
import '../widgets/main_screen/timeline_card.dart';
import 'package:scrobbles/ui/widgets/cards/statistics_global.dart';
import 'package:scrobbles/ui/widgets/cards/statistics_recent.dart';
import 'package:scrobbles/ui/widgets/cards/timeline.dart';
import 'package:scrobbles/ui/widgets/cards/top_artists.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
class ScreenHome extends StatelessWidget {
const ScreenHome({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return Material(
......@@ -21,11 +17,13 @@ class _HomeScreenState extends State<HomeScreen> {
physics: const BouncingScrollPhysics(),
children: <Widget>[
const SizedBox(height: 8),
const StatisticsGlobalCard(),
const CardStatisticsGlobal(),
const SizedBox(height: 6),
const CardStatisticsRecent(),
const SizedBox(height: 6),
const StatisticsRecentCard(),
const CardTimeline(),
const SizedBox(height: 6),
const ChartTimelineCard(),
const CardTopArtists(),
const SizedBox(height: 36),
],
),
......
import 'package:flutter/material.dart';
import 'package:scrobbles/ui/widgets/header_app.dart';
import 'package:scrobbles/ui/widgets/settings_form.dart';
class ScreenSettings extends StatelessWidget {
const ScreenSettings({super.key});
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
SizedBox(height: 8),
AppHeader(text: 'settings_title'),
SizedBox(height: 8),
SettingsForm(),
],
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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});
@override
State<SkeletonScreen> createState() => _SkeletonScreenState();
}
class _SkeletonScreenState extends State<SkeletonScreen> {
@override
Widget build(BuildContext context) {
const List<Widget> pageNavigation = <Widget>[
const HomeScreen(),
const DiscoveriesScreen(),
const StatisticsScreen(),
];
return BlocProvider<BottomNavCubit>(
create: (BuildContext context) => BottomNavCubit(),
child: BlocBuilder<BottomNavCubit, int>(
builder: (BuildContext context, int state) {
return GestureDetector(
onHorizontalDragEnd: (dragDetail) {
if (dragDetail.velocity.pixelsPerSecond.dx < 1) {
context.read<BottomNavCubit>().moveNext();
} else {
context.read<BottomNavCubit>().movePrevious();
}
},
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,
),
);
},
),
);
}
refresh() {
void rebuild(Element el) {
el.markNeedsBuild();
el.visitChildren(rebuild);
}
(context as Element).visitChildren(rebuild);
}
}
import 'package:flutter/material.dart';
import '../widgets/main_screen/discoveries_card.dart';
import 'package:scrobbles/ui/widgets/cards/counts_by_day.dart';
import 'package:scrobbles/ui/widgets/cards/counts_by_hour.dart';
class DiscoveriesScreen extends StatefulWidget {
const DiscoveriesScreen({super.key});
class ScreenStatistics extends StatelessWidget {
const ScreenStatistics({super.key});
@override
State<DiscoveriesScreen> createState() => _DiscoveriesScreenState();
}
class _DiscoveriesScreenState extends State<DiscoveriesScreen> {
@override
Widget build(BuildContext context) {
return Material(
......@@ -19,7 +15,9 @@ class _DiscoveriesScreenState extends State<DiscoveriesScreen> {
physics: const BouncingScrollPhysics(),
children: <Widget>[
const SizedBox(height: 8),
const ChartDiscoveriesCard(),
const CardCountsByDay(),
const SizedBox(height: 6),
const CardCountsByHour(),
const SizedBox(height: 36),
],
),
......
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_swipe/flutter_swipe.dart';
import 'package:scrobbles/cubit/bottom_nav_cubit.dart';
import 'package:scrobbles/ui/screens/discoveries.dart';
import 'package:scrobbles/ui/screens/home.dart';
import 'package:scrobbles/ui/screens/settings.dart';
import 'package:scrobbles/ui/screens/statistics.dart';
import 'package:scrobbles/ui/widgets/app_bar.dart';
import 'package:scrobbles/ui/widgets/bottom_nav_bar.dart';
class SkeletonScreen extends StatefulWidget {
const SkeletonScreen({super.key});
@override
State<SkeletonScreen> createState() => _SkeletonScreenState();
}
class _SkeletonScreenState extends State<SkeletonScreen> {
@override
Widget build(BuildContext context) {
const List<Widget> pageNavigation = <Widget>[
const ScreenHome(),
const ScreenDiscoveries(),
const ScreenStatistics(),
const ScreenSettings(),
];
return Scaffold(
appBar: StandardAppBar(notifyParent: refresh),
extendBodyBehindAppBar: false,
body: Swiper(
itemCount: BlocProvider.of<BottomNavCubit>(context).pagesCount,
itemBuilder: (BuildContext context, int index) {
return pageNavigation.elementAt(index);
},
pagination: SwiperPagination(
builder: SwiperCustomPagination(
builder: (BuildContext context, SwiperPluginConfig config) {
return BottomNavBar(swipeController: config.controller);
},
),
),
onIndexChanged: (newPageIndex) {
BlocProvider.of<BottomNavCubit>(context).updateIndex(newPageIndex);
},
outer: true,
loop: false,
),
backgroundColor: Theme.of(context).colorScheme.background,
);
}
refresh() {
setState(() {});
}
}
......@@ -2,16 +2,16 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import '../../../config/app_colors.dart';
import '../../../utils/color_extensions.dart';
import 'package:scrobbles/config/app_colors.dart';
import 'package:scrobbles/utils/color_extensions.dart';
class CustomBarChart extends StatelessWidget {
CustomBarChart({super.key});
const CustomBarChart({super.key});
final double chartHeight = 150.0;
final double verticalTicksInterval = 10;
final String verticalAxisTitleSuffix = '';
final double titleFontSize = 10;
final double titleFontSize = 9;
@override
Widget build(BuildContext context) {
......
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import '../../../config/app_colors.dart';
import 'package:scrobbles/config/app_colors.dart';
class CustomLineChart extends StatelessWidget {
CustomLineChart({super.key});
const CustomLineChart({super.key});
final double chartHeight = 150.0;
final double titleFontSize = 10;
final double titleFontSize = 9;
@override
Widget build(BuildContext context) {
......
import 'package:flutter/material.dart';
import 'package:unicons/unicons.dart';
import '../widgets/header.dart';
import 'package:scrobbles/ui/widgets/header_app.dart';
class StandardAppBar extends StatelessWidget implements PreferredSizeWidget {
final Function() notifyParent;
......@@ -11,7 +11,7 @@ class StandardAppBar extends StatelessWidget implements PreferredSizeWidget {
@override
Widget build(BuildContext context) {
return AppBar(
title: const Header(text: 'app_name'),
title: const AppHeader(text: 'app_name'),
actions: [
IconButton(
onPressed: () {
......
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_swipe/flutter_swipe.dart';
import 'package:ionicons/ionicons.dart';
import '../../cubit/bottom_nav_cubit.dart';
import 'package:scrobbles/cubit/bottom_nav_cubit.dart';
class BottomNavBar extends StatelessWidget {
const BottomNavBar({super.key});
const BottomNavBar({super.key, required this.swipeController});
final SwiperController swipeController;
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.only(top: 1, right: 4, left: 4),
margin: const EdgeInsets.only(
top: 1,
right: 0,
left: 0,
),
elevation: 4,
shadowColor: Theme.of(context).colorScheme.shadow,
color: Theme.of(context).colorScheme.surfaceVariant,
......@@ -25,7 +32,10 @@ class BottomNavBar extends StatelessWidget {
builder: (BuildContext context, int state) {
return BottomNavigationBar(
currentIndex: state,
onTap: (int index) => context.read<BottomNavCubit>().updateIndex(index),
onTap: (int index) {
context.read<BottomNavCubit>().updateIndex(index);
swipeController.move(index);
},
type: BottomNavigationBarType.fixed,
elevation: 0,
backgroundColor: Colors.transparent,
......@@ -44,6 +54,10 @@ class BottomNavBar extends StatelessWidget {
icon: const Icon(Ionicons.bar_chart_outline),
label: tr('bottom_nav_repartition'),
),
BottomNavigationBarItem(
icon: const Icon(Ionicons.settings_outline),
label: tr('bottom_nav_settings'),
),
],
);
},
......
import 'package:flutter/material.dart';
class CardContent extends StatelessWidget {
const CardContent({
super.key,
required this.title,
required this.color,
required this.loader,
required this.content,
});
final String title;
final Color color;
final Widget loader;
final Widget content;
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
shadowColor: Theme.of(context).colorScheme.shadow,
color: this.color,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(8),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
this.title,
style:
Theme.of(context).primaryTextTheme.titleLarge!.apply(fontWeightDelta: 2),
),
this.loader,
],
),
const SizedBox(height: 8),
this.content,
],
),
),
);
}
}
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:scrobbles/cubit/data_counts_by_day_cubit.dart';
import 'package:scrobbles/cubit/settings_cubit.dart';
import 'package:scrobbles/models/counts_by_day.dart';
import 'package:scrobbles/network/scrobbles.dart';
import 'package:scrobbles/ui/widgets/card_content.dart';
import 'package:scrobbles/ui/widgets/charts/counts_by_day.dart';
import 'package:scrobbles/ui/widgets/error.dart';
class CardCountsByDay extends StatelessWidget {
const CardCountsByDay({super.key});
@override
Widget build(BuildContext context) {
SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
final int daysCount = settings.getCountsByDayDaysCount();
return BlocBuilder<DataCountsByDayCubit, DataCountsByDayState>(
builder: (BuildContext context, DataCountsByDayState state) {
return CardContent(
color: Theme.of(context).colorScheme.surface,
title: 'counts_by_day'.tr(
namedArgs: {
'daysCount': daysCount.toString(),
},
),
loader: updateCountsByDay(daysCount),
content: ChartCountsByDay(
chartData: CountsByDayData.fromJson(state.countsByDay?.toJson()),
),
);
},
);
}
Widget updateCountsByDay(int daysCount) {
final Widget loading = const Text('⏳');
final Widget done = const Text('');
late Future<CountsByDayData> futureCountsByDay = ScrobblesApi.fetchCountsByDay(daysCount);
return BlocBuilder<DataCountsByDayCubit, DataCountsByDayState>(
builder: (BuildContext context, DataCountsByDayState state) {
return FutureBuilder<CountsByDayData>(
future: futureCountsByDay,
builder: (context, snapshot) {
if (snapshot.hasError) {
return ShowErrorWidget(message: '${snapshot.error}');
}
BlocProvider.of<DataCountsByDayCubit>(context).update(snapshot.data);
return !snapshot.hasData ? loading : done;
},
);
},
);
}
}
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:scrobbles/cubit/data_counts_by_hour_cubit.dart';
import 'package:scrobbles/cubit/settings_cubit.dart';
import 'package:scrobbles/models/counts_by_hour.dart';
import 'package:scrobbles/network/scrobbles.dart';
import 'package:scrobbles/ui/widgets/card_content.dart';
import 'package:scrobbles/ui/widgets/charts/counts_by_hour.dart';
import 'package:scrobbles/ui/widgets/error.dart';
class CardCountsByHour extends StatelessWidget {
const CardCountsByHour({super.key});
@override
Widget build(BuildContext context) {
SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
final int daysCount = settings.getCountsByHourDaysCount();
return BlocBuilder<DataCountsByHourCubit, DataCountsByHourState>(
builder: (BuildContext context, DataCountsByHourState state) {
return CardContent(
color: Theme.of(context).colorScheme.surface,
title: 'counts_by_hour'.tr(
namedArgs: {
'daysCount': daysCount.toString(),
},
),
loader: updateCountsByHour(daysCount),
content: ChartCountsByHour(
chartData: CountsByHourData.fromJson(state.countsByHour?.toJson()),
),
);
},
);
}
Widget updateCountsByHour(int daysCount) {
final Widget loading = const Text('⏳');
final Widget done = const Text('');
late Future<CountsByHourData> futureCountsByHour =
ScrobblesApi.fetchCountsByHour(daysCount);
return BlocBuilder<DataCountsByHourCubit, DataCountsByHourState>(
builder: (BuildContext context, DataCountsByHourState state) {
return FutureBuilder<CountsByHourData>(
future: futureCountsByHour,
builder: (context, snapshot) {
if (snapshot.hasError) {
return ShowErrorWidget(message: '${snapshot.error}');
}
BlocProvider.of<DataCountsByHourCubit>(context).update(snapshot.data);
return !snapshot.hasData ? loading : done;
},
);
},
);
}
}
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:scrobbles/cubit/data_discoveries_cubit.dart';
import 'package:scrobbles/cubit/settings_cubit.dart';
import 'package:scrobbles/models/discoveries.dart';
import 'package:scrobbles/network/scrobbles.dart';
import 'package:scrobbles/ui/widgets/card_content.dart';
import 'package:scrobbles/ui/widgets/charts/discoveries_artists.dart';
import 'package:scrobbles/ui/widgets/charts/discoveries_tracks.dart';
import 'package:scrobbles/ui/widgets/error.dart';
class CardDiscoveries extends StatelessWidget {
const CardDiscoveries({super.key});
@override
Widget build(BuildContext context) {
SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
final int daysCount = settings.getDiscoveriesDaysCount();
return BlocBuilder<DataDiscoveriesCubit, DataDiscoveriesState>(
builder: (BuildContext context, DataDiscoveriesState state) {
final TextTheme textTheme = Theme.of(context).primaryTextTheme;
return CardContent(
color: Theme.of(context).colorScheme.surface,
title: 'discoveries_title'.tr(
namedArgs: {
'daysCount': daysCount.toString(),
},
),
loader: updateDiscoveries(daysCount),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'discoveries_artists_title',
style: textTheme.titleMedium!.apply(fontWeightDelta: 2),
).tr(),
const SizedBox(height: 8),
ChartDiscoveriesArtists(
chartData: DiscoveriesData.fromJson(state.discoveries?.toJson()),
),
const SizedBox(height: 8),
Text(
'discoveries_tracks_title',
style: textTheme.titleMedium!.apply(fontWeightDelta: 2),
).tr(),
const SizedBox(height: 8),
ChartDiscoveriesTracks(
chartData: DiscoveriesData.fromJson(state.discoveries?.toJson()),
),
],
),
);
},
);
}
Widget updateDiscoveries(int daysCount) {
final Widget loading = const Text('⏳');
final Widget done = const Text('');
late Future<DiscoveriesData> futureDiscoveries = ScrobblesApi.fetchDiscoveries(daysCount);
return BlocBuilder<DataDiscoveriesCubit, DataDiscoveriesState>(
builder: (BuildContext context, DataDiscoveriesState state) {
return FutureBuilder<DiscoveriesData>(
future: futureDiscoveries,
builder: (context, snapshot) {
if (snapshot.hasError) {
return ShowErrorWidget(message: '${snapshot.error}');
}
BlocProvider.of<DataDiscoveriesCubit>(context).update(snapshot.data);
return !snapshot.hasData ? loading : done;
},
);
},
);
}
}
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:scrobbles/cubit/data_statistics_global_cubit.dart';
import 'package:scrobbles/models/statistics_global.dart';
import 'package:scrobbles/network/scrobbles.dart';
import 'package:scrobbles/ui/widgets/card_content.dart';
import 'package:scrobbles/ui/widgets/content/statistics_global.dart';
import 'package:scrobbles/ui/widgets/error.dart';
class CardStatisticsGlobal extends StatelessWidget {
const CardStatisticsGlobal({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<DataStatisticsGlobalCubit, DataStatisticsGlobalState>(
builder: (BuildContext context, DataStatisticsGlobalState state) {
return CardContent(
color: Theme.of(context).colorScheme.primary,
title: 'global_statistics'.tr(),
loader: updateStatisticsGlobal(),
content: ContentStatisticsGlobal(
statistics: StatisticsGlobalData.fromJson(state.statisticsGlobal?.toJson()),
),
);
},
);
}
Widget updateStatisticsGlobal() {
final Widget loading = const Text('⏳');
final Widget done = const Text('');
late Future<StatisticsGlobalData> futureStatisticsGlobal =
ScrobblesApi.fetchGlobalStatistics();
return BlocBuilder<DataStatisticsGlobalCubit, DataStatisticsGlobalState>(
builder: (BuildContext context, DataStatisticsGlobalState dataState) {
return FutureBuilder<StatisticsGlobalData>(
future: futureStatisticsGlobal,
builder: (context, snapshot) {
if (snapshot.hasError) {
return ShowErrorWidget(message: '${snapshot.error}');
}
BlocProvider.of<DataStatisticsGlobalCubit>(context).update(snapshot.data);
return !snapshot.hasData ? loading : done;
},
);
},
);
}
}
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:scrobbles/cubit/data_statistics_recent_cubit.dart';
import 'package:scrobbles/cubit/settings_cubit.dart';
import 'package:scrobbles/models/statistics_recent.dart';
import 'package:scrobbles/network/scrobbles.dart';
import 'package:scrobbles/ui/widgets/card_content.dart';
import 'package:scrobbles/ui/widgets/content/statistics_recent.dart';
import 'package:scrobbles/ui/widgets/error.dart';
class CardStatisticsRecent extends StatelessWidget {
const CardStatisticsRecent({super.key});
@override
Widget build(BuildContext context) {
SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
final int daysCount = settings.getStatisticsRecentDaysCount();
return BlocBuilder<DataStatisticsRecentCubit, DataStatisticsRecentState>(
builder: (BuildContext context, DataStatisticsRecentState dataState) {
return CardContent(
color: Theme.of(context).colorScheme.primary,
title: 'recent_statistics'.tr(
namedArgs: {
'daysCount': daysCount.toString(),
},
),
loader: updateStatisticsRecent(daysCount),
content: ContentStatisticsRecent(
statistics: StatisticsRecentData.fromJson(
jsonDecode(dataState.statisticsRecent.toString())),
),
);
},
);
}
Widget updateStatisticsRecent(int daysCount) {
final Widget loading = const Text('⏳');
final Widget done = const Text('');
late Future<StatisticsRecentData> futureStatisticsRecent =
ScrobblesApi.fetchRecentStatistics(daysCount);
return BlocBuilder<DataStatisticsRecentCubit, DataStatisticsRecentState>(
builder: (BuildContext context, DataStatisticsRecentState state) {
return FutureBuilder<StatisticsRecentData>(
future: futureStatisticsRecent,
builder: (context, snapshot) {
if (snapshot.hasError) {
return ShowErrorWidget(message: '${snapshot.error}');
}
BlocProvider.of<DataStatisticsRecentCubit>(context).update(snapshot.data);
return !snapshot.hasData ? loading : done;
},
);
},
);
}
}
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:scrobbles/cubit/data_timeline_cubit.dart';
import 'package:scrobbles/cubit/settings_cubit.dart';
import 'package:scrobbles/models/timeline.dart';
import 'package:scrobbles/network/scrobbles.dart';
import 'package:scrobbles/ui/widgets/card_content.dart';
import 'package:scrobbles/ui/widgets/charts/timeline_counts.dart';
import 'package:scrobbles/ui/widgets/charts/timeline_eclecticism.dart';
import 'package:scrobbles/ui/widgets/error.dart';
class CardTimeline extends StatelessWidget {
const CardTimeline({super.key});
@override
Widget build(BuildContext context) {
SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
final int daysCount = settings.getTimelineDaysCount();
return BlocBuilder<DataTimelineCubit, DataTimelineState>(
builder: (BuildContext context, DataTimelineState state) {
return CardContent(
color: Theme.of(context).colorScheme.surface,
title: 'timeline_title'.tr(
namedArgs: {
'daysCount': daysCount.toString(),
},
),
loader: updateTimeline(daysCount),
content: Stack(
children: [
ChartTimelineCounts(
chartData: TimelineData.fromJson(state.timeline?.toJson()),
),
ChartTimelineEclecticism(
chartData: TimelineData.fromJson(state.timeline?.toJson()),
),
],
),
);
},
);
}
Widget updateTimeline(int daysCount) {
final Widget loading = const Text('⏳');
final Widget done = const Text('');
late Future<TimelineData> futureTimeline = ScrobblesApi.fetchTimeline(daysCount);
return BlocBuilder<DataTimelineCubit, DataTimelineState>(
builder: (BuildContext context, DataTimelineState dataTimelineState) {
return FutureBuilder<TimelineData>(
future: futureTimeline,
builder: (context, snapshot) {
if (snapshot.hasError) {
return ShowErrorWidget(message: '${snapshot.error}');
}
BlocProvider.of<DataTimelineCubit>(context).update(snapshot.data);
return !snapshot.hasData ? loading : done;
},
);
},
);
}
}
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:scrobbles/cubit/data_top_artists_cubit.dart';
import 'package:scrobbles/cubit/settings_cubit.dart';
import 'package:scrobbles/models/topartists.dart';
import 'package:scrobbles/network/scrobbles.dart';
import 'package:scrobbles/ui/widgets/card_content.dart';
import 'package:scrobbles/ui/widgets/charts/top_artists.dart';
import 'package:scrobbles/ui/widgets/charts/top_artists_stream.dart';
import 'package:scrobbles/ui/widgets/error.dart';
class CardTopArtists extends StatelessWidget {
const CardTopArtists({super.key});
@override
Widget build(BuildContext context) {
SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
final int daysCount = settings.getTopArtistsDaysCount();
return BlocBuilder<DataTopArtistsCubit, DataTopArtistsState>(
builder: (BuildContext context, DataTopArtistsState state) {
TopArtistsData artistsData = state.topArtists ?? TopArtistsData.fromJson({});
return CardContent(
color: Theme.of(context).colorScheme.surface,
title: 'top_artists_title'.tr(
namedArgs: {
'daysCount': daysCount.toString(),
},
),
loader: updateTopArtists(daysCount),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ChartTopArtists(
chartData: artistsData,
),
const SizedBox(height: 8),
ChartTopArtistsStream(
chartData: artistsData,
),
],
),
);
},
);
}
Widget updateTopArtists(int daysCount) {
final Widget loading = const Text('⏳');
final Widget done = const Text('');
late Future<TopArtistsData> futureTopArtists = ScrobblesApi.fetchTopArtists(daysCount);
return BlocBuilder<DataTopArtistsCubit, DataTopArtistsState>(
builder: (BuildContext context, DataTopArtistsState state) {
return FutureBuilder<TopArtistsData>(
future: futureTopArtists,
builder: (context, snapshot) {
if (snapshot.hasError) {
return ShowErrorWidget(message: '${snapshot.error}');
}
BlocProvider.of<DataTopArtistsCubit>(context).update(snapshot.data);
return !snapshot.hasData ? loading : done;
},
);
},
);
}
}
......@@ -2,28 +2,39 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
import '../../../config/app_colors.dart';
import '../../../models/counts_by_day.dart';
import '../../../ui/widgets/charts/custom_bar_chart.dart';
import 'package:scrobbles/config/app_colors.dart';
import 'package:scrobbles/models/counts_by_day.dart';
import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart';
class CountsByDayCardContentChart extends CustomBarChart {
class ChartCountsByDay extends CustomBarChart {
final CountsByDayData chartData;
CountsByDayCardContentChart({super.key, required this.chartData});
const ChartCountsByDay({super.key, required this.chartData});
final double verticalTicksInterval = 5;
final String verticalAxisTitleSuffix = '%';
@override
Widget build(BuildContext context) {
if (this.chartData.data.keys.length == 0) {
return SizedBox(
height: this.chartHeight,
);
}
return Container(
height: this.chartHeight,
child: LayoutBuilder(builder: (context, constraints) {
return getBarChart(
barWidth: this.getBarWidth(constraints.maxWidth, this.chartData.data.keys.length),
backgroundColor: Theme.of(context).colorScheme.onSurface,
);
}),
child: LayoutBuilder(
builder: (context, constraints) {
final double maxWidth = constraints.maxWidth;
final int barsCount = this.chartData.data.keys.length;
return getBarChart(
barWidth: this.getBarWidth(maxWidth, barsCount),
backgroundColor: Theme.of(context).colorScheme.onSurface,
);
},
),
);
}
......
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
import '../../../config/app_colors.dart';
import '../../../models/counts_by_hour.dart';
import '../../../ui/widgets/charts/custom_bar_chart.dart';
import '../../../utils/color_extensions.dart';
import 'package:scrobbles/config/app_colors.dart';
import 'package:scrobbles/models/counts_by_hour.dart';
import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart';
import 'package:scrobbles/utils/color_extensions.dart';
class CountsByHourCardContentChart extends CustomBarChart {
class ChartCountsByHour extends CustomBarChart {
final CountsByHourData chartData;
CountsByHourCardContentChart({super.key, required this.chartData});
const ChartCountsByHour({super.key, required this.chartData});
final double verticalTicksInterval = 5;
final String verticalAxisTitleSuffix = '%';
@override
Widget build(BuildContext context) {
if (this.chartData.data.keys.length == 0) {
return SizedBox(
height: this.chartHeight,
);
}
return Container(
height: this.chartHeight,
child: LayoutBuilder(builder: (context, constraints) {
return getBarChart(
barWidth: this.getBarWidth(constraints.maxWidth, this.chartData.data.keys.length),
backgroundColor: Theme.of(context).colorScheme.onSurface,
);
}),
child: LayoutBuilder(
builder: (context, constraints) {
final double maxWidth = constraints.maxWidth;
final int barsCount = this.chartData.data.keys.length;
return getBarChart(
barWidth: this.getBarWidth(maxWidth, barsCount),
backgroundColor: Theme.of(context).colorScheme.onSurface,
);
},
),
);
}
......
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
import '../../../config/app_colors.dart';
import '../../../models/discoveries.dart';
import '../../../utils/color_extensions.dart';
import '../../../ui/widgets/charts/custom_bar_chart.dart';
import 'package:scrobbles/config/app_colors.dart';
import 'package:scrobbles/models/discoveries.dart';
import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart';
import 'package:scrobbles/utils/color_extensions.dart';
class ChartDiscoveries extends CustomBarChart {
class ChartDiscoveriesArtists extends CustomBarChart {
final DiscoveriesData chartData;
ChartDiscoveries({super.key, required this.chartData});
final double chartHeight = 120.0;
final double verticalTicksInterval = 10;
const ChartDiscoveriesArtists({super.key, required this.chartData});
@override
Widget build(BuildContext context) {
if (this.chartData.data.keys.length == 0) {
return SizedBox(
height: this.chartHeight,
);
}
return Container(
height: this.chartHeight,
child: LayoutBuilder(builder: (context, constraints) {
return getBarChart(
barWidth: this.getBarWidth(constraints.maxWidth, this.chartData.data.keys.length),
backgroundColor: Theme.of(context).colorScheme.onSurface,
);
}),
child: LayoutBuilder(
builder: (context, constraints) {
final double maxWidth = constraints.maxWidth;
final int barsCount = this.chartData.data.keys.length;
return getBarChart(
barWidth: this.getBarWidth(maxWidth, barsCount),
backgroundColor: Theme.of(context).colorScheme.onSurface,
);
},
),
);
}
......@@ -34,14 +42,10 @@ class ChartDiscoveries extends CustomBarChart {
DiscoveriesDataValue? value = this.chartData.data[key];
if (value != null) {
double newArtistsCount = value.newArtistsCount.toDouble();
double newTracksCount = value.newTracksCount.toDouble();
if (newArtistsCount > maxValue) {
maxValue = newArtistsCount;
}
if (newTracksCount > maxValue) {
maxValue = newTracksCount;
}
}
});
......@@ -52,7 +56,6 @@ class ChartDiscoveries extends CustomBarChart {
List<BarChartGroupData> data = [];
final newArtistsBarColor = AppColors.contentColorGreen.darken(20);
final newTracksBarColor = AppColors.contentColorBlue.darken(20);
this.chartData.data.keys.forEach((key) {
DiscoveriesDataValue? value = this.chartData.data[key];
......@@ -63,13 +66,11 @@ class ChartDiscoveries extends CustomBarChart {
x: date,
values: [
value.newArtistsCount.toDouble(),
value.newTracksCount.toDouble(),
],
barColors: [
newArtistsBarColor,
newTracksBarColor,
],
barWidth: barWidth / 2.5,
barWidth: barWidth,
));
}
});
......