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 383 additions and 524 deletions
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/config/settings.dart';
import 'package:scrobbles/cubit/data_top_artists_cubit.dart';
import 'package:scrobbles/models/topartists.dart';
import 'package:scrobbles/ui/widgets/card_content.dart';
import 'package:scrobbles/ui/widgets/charts/top_artists.dart';
class CardTopArtists extends StatelessWidget {
const CardTopArtists({super.key});
@override
Widget build(BuildContext context) {
final int daysCount = Settings.topArtistsDaysCount;
return BlocProvider<DataTopArtistsCubit>(
create: (BuildContext context) => DataTopArtistsCubit(),
child: BlocBuilder<DataTopArtistsCubit, DataTopArtistsState>(
builder: (BuildContext context, DataTopArtistsState state) {
return CardContent(
color: Theme.of(context).colorScheme.surface,
title: 'top_artists_title'.tr(
namedArgs: {
'daysCount': daysCount.toString(),
},
),
content: ChartTopArtists(
chartData: TopArtistsData.fromJson(jsonDecode(state.topArtists.toString())),
isLoading: false,
),
);
},
),
);
}
}
......@@ -2,14 +2,15 @@ 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;
final bool isLoading;
CountsByDayCardContentChart({super.key, required this.chartData});
ChartCountsByDay({super.key, required this.chartData, required this.isLoading});
final double verticalTicksInterval = 5;
final String verticalAxisTitleSuffix = '%';
......@@ -18,12 +19,19 @@ class CountsByDayCardContentChart extends CustomBarChart {
Widget build(BuildContext context) {
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: this.isLoading
? this.placeholder
: 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;
final bool isLoading;
CountsByHourCardContentChart({super.key, required this.chartData});
ChartCountsByHour({super.key, required this.chartData, required this.isLoading});
final double verticalTicksInterval = 5;
final String verticalAxisTitleSuffix = '%';
......@@ -18,12 +19,19 @@ class CountsByHourCardContentChart extends CustomBarChart {
Widget build(BuildContext context) {
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: this.isLoading
? this.placeholder
: 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;
final bool isLoading;
ChartDiscoveries({super.key, required this.chartData});
final double chartHeight = 120.0;
final double verticalTicksInterval = 10;
ChartDiscoveriesArtists({super.key, required this.chartData, required this.isLoading});
@override
Widget build(BuildContext context) {
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: this.isLoading
? this.placeholder
: 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 +39,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 +53,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 +63,11 @@ class ChartDiscoveries extends CustomBarChart {
x: date,
values: [
value.newArtistsCount.toDouble(),
value.newTracksCount.toDouble(),
],
barColors: [
newArtistsBarColor,
newTracksBarColor,
],
barWidth: barWidth / 2.5,
barWidth: barWidth,
));
}
});
......
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_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 ChartDiscoveriesTracks extends CustomBarChart {
final DiscoveriesData chartData;
final bool isLoading;
ChartDiscoveriesTracks({super.key, required this.chartData, required this.isLoading});
@override
Widget build(BuildContext context) {
return Container(
height: this.chartHeight,
child: this.isLoading
? this.placeholder
: 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,
);
},
),
);
}
double getMaxCountsValue() {
double maxValue = 0;
this.chartData.data.keys.forEach((key) {
DiscoveriesDataValue? value = this.chartData.data[key];
if (value != null) {
double newTracksCount = value.newTracksCount.toDouble();
if (newTracksCount > maxValue) {
maxValue = newTracksCount;
}
}
});
return maxValue;
}
List<BarChartGroupData> getDataCounts(double barWidth) {
List<BarChartGroupData> data = [];
final newTracksBarColor = AppColors.contentColorBlue.darken(20);
this.chartData.data.keys.forEach((key) {
DiscoveriesDataValue? value = this.chartData.data[key];
if (value != null) {
final int date = DateTime.parse(key).millisecondsSinceEpoch;
data.add(this.getBarItem(
x: date,
values: [
value.newTracksCount.toDouble(),
],
barColors: [
newTracksBarColor,
],
barWidth: barWidth,
));
}
});
return data;
}
}
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
import '../../../config/app_colors.dart';
import '../../../models/timeline.dart';
import '../../../ui/widgets/charts/custom_bar_chart.dart';
import 'package:scrobbles/config/app_colors.dart';
import 'package:scrobbles/models/timeline.dart';
import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart';
class ChartTimelineCounts extends CustomBarChart {
final TimelineData chartData;
final bool isLoading;
ChartTimelineCounts({super.key, required this.chartData});
ChartTimelineCounts({super.key, required this.chartData, required this.isLoading});
final double chartHeight = 120.0;
final double verticalTicksInterval = 50;
@override
Widget build(BuildContext context) {
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: this.isLoading
? this.placeholder
: 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/timeline.dart';
import '../../../ui/widgets/charts/custom_line_chart.dart';
import '../../../utils/color_extensions.dart';
import 'package:scrobbles/config/app_colors.dart';
import 'package:scrobbles/models/timeline.dart';
import 'package:scrobbles/ui/widgets/abstracts/custom_line_chart.dart';
import 'package:scrobbles/utils/color_extensions.dart';
class ChartTimelineEclecticism extends CustomLineChart {
final TimelineData chartData;
final bool isLoading;
ChartTimelineEclecticism({super.key, required this.chartData});
ChartTimelineEclecticism({super.key, required this.chartData, required this.isLoading});
@override
Widget build(BuildContext context) {
......@@ -17,20 +18,22 @@ class ChartTimelineEclecticism extends CustomLineChart {
return Container(
height: this.chartHeight,
child: LineChart(
LineChartData(
lineBarsData: getDataEclecticism(),
borderData: getBorderData(),
gridData: getGridData(),
titlesData: getTitlesData(),
lineTouchData: const LineTouchData(enabled: false),
minX: horizontalScale['min'],
maxX: horizontalScale['max'],
maxY: 100,
minY: 0,
),
duration: const Duration(milliseconds: 250),
),
child: this.isLoading
? this.placeholder
: LineChart(
LineChartData(
lineBarsData: getDataEclecticism(),
borderData: getBorderData(),
gridData: getGridData(),
titlesData: getTitlesData(),
lineTouchData: const LineTouchData(enabled: false),
minX: horizontalScale['min'],
maxX: horizontalScale['max'],
maxY: 100,
minY: 0,
),
duration: const Duration(milliseconds: 250),
),
);
}
......
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/utils/color_extensions.dart';
class ChartTopArtists extends StatelessWidget {
final TopArtistsData chartData;
final bool isLoading;
ChartTopArtists({super.key, required this.chartData, required this.isLoading});
final Widget placeholder = Text('⏳');
@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 2.1,
child: this.isLoading
? this.placeholder
: Row(
children: <Widget>[
Expanded(
child: AspectRatio(
aspectRatio: 1,
child: PieChart(
PieChartData(
sections: getPieChartData(),
sectionsSpace: 2,
centerSpaceRadius: 40,
startDegreeOffset: -45,
pieTouchData: PieTouchData(enabled: false),
borderData: FlBorderData(show: false),
),
),
),
),
buildLegendWidget(),
],
),
);
}
Color getColorIndex(int index) {
const List<int> hexValues = [
0x8dd3c7,
0xffffb3,
0xbebada,
0xfb8072,
0x80b1d3,
0xfdb462,
0xb3de69,
0xfccde5,
0xd9d9d9,
0xbc80bd,
0xccebc5,
0xffed6f,
];
return Color(hexValues[index % hexValues.length] + 0xff000000);
}
List<PieChartSectionData> getPieChartData() {
List<PieChartSectionData> items = [];
final radius = 40.0;
final fontSize = 11.0;
const shadows = [Shadow(color: Colors.black, blurRadius: 2)];
int index = 0;
this.chartData.topArtists.forEach((element) {
Color baseColor = getColorIndex(index++);
final PieChartSectionData item = PieChartSectionData(
value: element.count.toDouble(),
title: element.artistName,
color: baseColor.darken(20),
borderSide: BorderSide(
color: baseColor.darken(40),
width: 1,
),
radius: radius,
titleStyle: TextStyle(
fontSize: fontSize,
color: AppColors.mainTextColor1,
shadows: shadows,
),
);
items.add(item);
});
return items;
}
Widget buildLegendWidget() {
const double itemSize = 12;
List<Widget> items = [];
int index = 0;
this.chartData.topArtists.forEach((element) {
Color baseColor = getColorIndex(index++);
final Widget item = Row(
children: <Widget>[
Container(
width: itemSize,
height: itemSize,
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: baseColor.darken(20),
border: Border.all(
color: baseColor.darken(40),
width: 1,
),
),
),
const SizedBox(
width: 4,
),
Text(
element.artistName + ' (' + element.count.toString() + ')',
style: TextStyle(
fontSize: itemSize - 2,
fontWeight: FontWeight.bold,
),
)
],
);
items.add(item);
});
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: items,
);
}
}
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import '../../../models/statistics_global.dart';
import 'package:scrobbles/models/statistics_global.dart';
class StatisticsGlobalContent extends StatelessWidget {
class ContentStatisticsGlobal extends StatelessWidget {
final StatisticsGlobalData statistics;
final bool isLoading;
const StatisticsGlobalContent(
const ContentStatisticsGlobal(
{super.key, required this.statistics, required this.isLoading});
@override
Widget build(BuildContext context) {
final TextTheme textTheme = Theme.of(context).primaryTextTheme;
final String placeholder = '⏳';
const String placeholder = '⏳';
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'global_statistics',
style: textTheme.titleLarge!.apply(fontWeightDelta: 2),
).tr(),
Text(
'statistics_total_scrobbles_count',
style: textTheme.bodyMedium,
).tr(
namedArgs: {
'count': this.isLoading ? placeholder : this.statistics.totalCount.toString(),
'count': this.isLoading
? placeholder
: (this.statistics.totalCount != null
? this.statistics.totalCount.toString()
: ''),
},
),
Text(
......@@ -39,7 +38,9 @@ class StatisticsGlobalContent extends StatelessWidget {
namedArgs: {
'datetime': this.isLoading
? placeholder
: DateFormat().format(this.statistics.lastScrobble),
: (this.statistics.lastScrobble != null
? DateFormat().format(this.statistics.lastScrobble ?? DateTime.now())
: ''),
},
),
],
......
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import '../../../models/statistics_recent.dart';
import 'package:scrobbles/models/statistics_recent.dart';
class StatisticsRecentContent extends StatelessWidget {
class ContentStatisticsRecent extends StatelessWidget {
final StatisticsRecentData statistics;
final bool isLoading;
const StatisticsRecentContent(
const ContentStatisticsRecent(
{super.key, required this.statistics, required this.isLoading});
@override
Widget build(BuildContext context) {
final TextTheme textTheme = Theme.of(context).primaryTextTheme;
final String placeholder = '⏳';
const String placeholder = '⏳';
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'recent_statistics',
style: textTheme.titleLarge!.apply(fontWeightDelta: 2),
).tr(),
Text(
'statistics_selected_period',
style: textTheme.bodyMedium!.apply(fontWeightDelta: 2),
).tr(
namedArgs: {
'daysCount':
this.isLoading ? placeholder : this.statistics.selectedPeriod.toString(),
},
),
Text(
'statistics_recent_scrobbles_count_and_discoveries',
(this.statistics.recentCount != null &&
this.statistics.firstPlayedArtistsCount != null &&
this.statistics.firstPlayedTracksCount != null)
? 'statistics_recent_scrobbles_count_and_discoveries'
: '',
style: textTheme.bodyMedium,
).tr(
namedArgs: {
......
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
class Header extends StatelessWidget {
const Header({super.key, required this.text});
class AppHeader extends StatelessWidget {
const AppHeader({super.key, required this.text});
final String text;
......
import 'dart:convert';
import 'package:flutter/material.dart';
import '../../../models/counts_by_day.dart';
import '../../../network/scrobbles_api.dart';
import '../../../ui/widgets/error.dart';
import '../../../ui/widgets/main_screen/counts_by_day_content.dart';
class ChartCountsByDayCard extends StatelessWidget {
const ChartCountsByDayCard({super.key});
@override
Widget build(BuildContext context) {
final int daysCount = 21;
late Future<CountsByDayData> futureCountsByDay = ScrobblesApi.fetchCountsByDay(daysCount);
return FutureBuilder<CountsByDayData>(
future: futureCountsByDay,
builder: (context, snapshot) {
if (snapshot.hasError) {
return ShowErrorWidget(message: '${snapshot.error}');
}
return Card(
elevation: 2,
shadowColor: Theme.of(context).colorScheme.shadow,
color: Theme.of(context).colorScheme.surface,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(8),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ChartCountsByDayCardContent(
daysCount: daysCount,
chartData: snapshot.hasData
? CountsByDayData.fromJson(jsonDecode(snapshot.data.toString()))
: CountsByDayData.createEmpty(),
isLoading: !snapshot.hasData,
),
),
);
},
);
}
}
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import '../../../models/counts_by_day.dart';
import '../../../ui/widgets/main_screen/counts_by_day_chart.dart';
class ChartCountsByDayCardContent extends StatelessWidget {
final int daysCount;
final CountsByDayData chartData;
final bool isLoading;
const ChartCountsByDayCardContent(
{super.key, required this.daysCount, required this.chartData, required this.isLoading});
@override
Widget build(BuildContext context) {
final TextTheme textTheme = Theme.of(context).primaryTextTheme;
final String placeholder = '⏳';
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'counts_by_day',
style: textTheme.titleLarge!.apply(fontWeightDelta: 2),
).tr(
namedArgs: {
'daysCount': this.daysCount.toString(),
},
),
const SizedBox(height: 8),
this.isLoading
? Text(placeholder)
: CountsByDayCardContentChart(
chartData: CountsByDayData.fromJson(jsonDecode(this.chartData.toString())),
),
],
);
}
}
import 'dart:convert';
import 'package:flutter/material.dart';
import '../../../models/counts_by_hour.dart';
import '../../../network/scrobbles_api.dart';
import '../../../ui/widgets/error.dart';
import '../../../ui/widgets/main_screen/counts_by_hour_content.dart';
class ChartCountsByHourCard extends StatelessWidget {
const ChartCountsByHourCard({super.key});
@override
Widget build(BuildContext context) {
final int daysCount = 21;
late Future<CountsByHourData> futureCountsByHour =
ScrobblesApi.fetchCountsByHour(daysCount);
return FutureBuilder<CountsByHourData>(
future: futureCountsByHour,
builder: (context, snapshot) {
if (snapshot.hasError) {
return ShowErrorWidget(message: '${snapshot.error}');
}
return Card(
elevation: 2,
shadowColor: Theme.of(context).colorScheme.shadow,
color: Theme.of(context).colorScheme.surface,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(8),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ChartCountsByHourCardContent(
daysCount: daysCount,
chartData: snapshot.hasData
? CountsByHourData.fromJson(jsonDecode(snapshot.data.toString()))
: CountsByHourData.createEmpty(),
isLoading: !snapshot.hasData,
),
),
);
},
);
}
}
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import '../../../models/counts_by_hour.dart';
import '../../../ui/widgets/main_screen/counts_by_hour_chart.dart';
class ChartCountsByHourCardContent extends StatelessWidget {
final int daysCount;
final CountsByHourData chartData;
final bool isLoading;
const ChartCountsByHourCardContent(
{super.key, required this.daysCount, required this.chartData, required this.isLoading});
@override
Widget build(BuildContext context) {
final TextTheme textTheme = Theme.of(context).primaryTextTheme;
final String placeholder = '⏳';
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'counts_by_hour',
style: textTheme.titleLarge!.apply(fontWeightDelta: 2),
).tr(
namedArgs: {
'daysCount': this.daysCount.toString(),
},
),
const SizedBox(height: 8),
this.isLoading
? Text(placeholder)
: CountsByHourCardContentChart(
chartData: CountsByHourData.fromJson(jsonDecode(this.chartData.toString())),
),
],
);
}
}
import 'dart:convert';
import 'package:flutter/material.dart';
import '../../../models/discoveries.dart';
import '../../../network/scrobbles_api.dart';
import '../../../ui/widgets/error.dart';
import '../../../ui/widgets/main_screen/discoveries_content.dart';
class ChartDiscoveriesCard extends StatelessWidget {
const ChartDiscoveriesCard({super.key});
@override
Widget build(BuildContext context) {
final int daysCount = 14;
late Future<DiscoveriesData> futureTimeline = ScrobblesApi.fetchDiscoveries(daysCount);
return FutureBuilder<DiscoveriesData>(
future: futureTimeline,
builder: (context, snapshot) {
if (snapshot.hasError) {
return ShowErrorWidget(message: '${snapshot.error}');
}
return Card(
elevation: 2,
shadowColor: Theme.of(context).colorScheme.shadow,
color: Theme.of(context).colorScheme.surface,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(8),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ChartDiscoveriesCardContent(
daysCount: daysCount,
chartData: snapshot.hasData
? DiscoveriesData.fromJson(jsonDecode(snapshot.data.toString()))
: DiscoveriesData.createEmpty(),
isLoading: !snapshot.hasData,
),
),
);
},
);
}
}
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import '../../../models/discoveries.dart';
import '../../../ui/widgets/main_screen/discoveries_chart.dart';
class ChartDiscoveriesCardContent extends StatelessWidget {
final int daysCount;
final DiscoveriesData chartData;
final bool isLoading;
const ChartDiscoveriesCardContent(
{super.key, required this.daysCount, required this.chartData, required this.isLoading});
@override
Widget build(BuildContext context) {
final TextTheme textTheme = Theme.of(context).primaryTextTheme;
final String placeholder = '⏳';
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'discoveries_title',
style: textTheme.titleLarge!.apply(fontWeightDelta: 2),
).tr(
namedArgs: {
'daysCount': this.daysCount.toString(),
},
),
const SizedBox(height: 8),
this.isLoading
? Text(placeholder)
: ChartDiscoveries(
chartData: DiscoveriesData.fromJson(jsonDecode(this.chartData.toString())),
),
],
);
}
}
import 'dart:convert';
import 'package:flutter/material.dart';
import '../../../models/statistics_global.dart';
import '../../../network/scrobbles_api.dart';
import '../../../ui/widgets/error.dart';
import 'statistics_global_content.dart';
class StatisticsGlobalCard extends StatelessWidget {
const StatisticsGlobalCard({super.key});
@override
Widget build(BuildContext context) {
late Future<StatisticsGlobalData> future = ScrobblesApi.fetchGlobalStatistics();
return FutureBuilder<StatisticsGlobalData>(
future: future,
builder: (context, snapshot) {
if (snapshot.hasError) {
return ShowErrorWidget(message: '${snapshot.error}');
}
return Card(
elevation: 2,
shadowColor: Theme.of(context).colorScheme.shadow,
color: Theme.of(context).colorScheme.primary,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(8),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: StatisticsGlobalContent(
statistics: snapshot.hasData
? StatisticsGlobalData.fromJson(jsonDecode(snapshot.data.toString()))
: StatisticsGlobalData.createEmpty(),
isLoading: !snapshot.hasData,
),
),
);
},
);
}
}
import 'dart:convert';
import 'package:flutter/material.dart';
import '../../../models/statistics_recent.dart';
import '../../../network/scrobbles_api.dart';
import '../../../ui/widgets/error.dart';
import 'statistics_recent_content.dart';
class StatisticsRecentCard extends StatelessWidget {
const StatisticsRecentCard({super.key});
@override
Widget build(BuildContext context) {
final int daysCount = 21;
late Future<StatisticsRecentData> future = ScrobblesApi.fetchRecentStatistics(daysCount);
return FutureBuilder<StatisticsRecentData>(
future: future,
builder: (context, snapshot) {
if (snapshot.hasError) {
return ShowErrorWidget(message: '${snapshot.error}');
}
return Card(
elevation: 2,
shadowColor: Theme.of(context).colorScheme.shadow,
color: Theme.of(context).colorScheme.primary,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(8),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: StatisticsRecentContent(
statistics: snapshot.hasData
? StatisticsRecentData.fromJson(jsonDecode(snapshot.data.toString()))
: StatisticsRecentData.createEmpty(),
isLoading: !snapshot.hasData,
),
),
);
},
);
}
}
import 'dart:convert';
import 'package:flutter/material.dart';
import '../../../models/timeline.dart';
import '../../../network/scrobbles_api.dart';
import '../../../ui/widgets/error.dart';
import '../../../ui/widgets/main_screen/timeline_content.dart';
class ChartTimelineCard extends StatelessWidget {
const ChartTimelineCard({super.key});
@override
Widget build(BuildContext context) {
final int daysCount = 14;
late Future<TimelineData> futureTimeline = ScrobblesApi.fetchTimeline(daysCount);
return FutureBuilder<TimelineData>(
future: futureTimeline,
builder: (context, snapshot) {
if (snapshot.hasError) {
return ShowErrorWidget(message: '${snapshot.error}');
}
return Card(
elevation: 2,
shadowColor: Theme.of(context).colorScheme.shadow,
color: Theme.of(context).colorScheme.surface,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(8),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ChartTimelineCardContent(
daysCount: daysCount,
chartData: snapshot.hasData
? TimelineData.fromJson(jsonDecode(snapshot.data.toString()))
: TimelineData.createEmpty(),
isLoading: !snapshot.hasData,
),
),
);
},
);
}
}