diff --git a/android/gradle.properties b/android/gradle.properties
index 007b41f87df662638a73e32c7ad937e5b08460a6..9e30671427dd5cd513d8a1f0583cfaf05e2e0a85 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.49
-app.versionCode=49
+app.versionName=0.0.50
+app.versionCode=50
diff --git a/fastlane/metadata/android/en-US/changelogs/50.txt b/fastlane/metadata/android/en-US/changelogs/50.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1b48c1cb52825415da5a4471018b2d13868580a6
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/50.txt
@@ -0,0 +1 @@
+Improve / clean some code.
diff --git a/fastlane/metadata/android/fr-FR/changelogs/50.txt b/fastlane/metadata/android/fr-FR/changelogs/50.txt
new file mode 100644
index 0000000000000000000000000000000000000000..67008a53a6a216ca7a82cf1bc0cdc8f95b9a2247
--- /dev/null
+++ b/fastlane/metadata/android/fr-FR/changelogs/50.txt
@@ -0,0 +1 @@
+Amélioration / nettoyage de code.
diff --git a/lib/ui/widgets/abstracts/custom_bar_chart.dart b/lib/ui/widgets/abstracts/custom_bar_chart.dart
index d80acd1d653949f76eb09321fdea4dc1bef457b3..07a0342e546c812386708f84fdf38cc8f479bf11 100644
--- a/lib/ui/widgets/abstracts/custom_bar_chart.dart
+++ b/lib/ui/widgets/abstracts/custom_bar_chart.dart
@@ -1,24 +1,16 @@
-import 'package:easy_localization/easy_localization.dart';
 import 'package:fl_chart/fl_chart.dart';
 import 'package:flutter/material.dart';
 
-import 'package:scrobbles/config/app_colors.dart';
+import 'package:scrobbles/ui/widgets/abstracts/custom_chart.dart';
 import 'package:scrobbles/utils/color_extensions.dart';
 
-class CustomBarChart extends StatelessWidget {
+class CustomBarChart extends CustomChart {
   const CustomBarChart({super.key});
 
-  final double chartHeight = 150.0;
-  final double verticalTicksInterval = 10;
-  final String verticalAxisTitleSuffix = '';
-  final double titleFontSize = 9;
-
   @override
   Widget build(BuildContext context) {
-    return Container(
-      child: SizedBox(
-        height: this.chartHeight,
-      ),
+    return SizedBox(
+      height: this.chartHeight,
     );
   }
 
@@ -27,8 +19,8 @@ class CustomBarChart extends StatelessWidget {
       BarChartData(
         barGroups: getDataCounts(barWidth),
         backgroundColor: backgroundColor,
-        borderData: getBorderData(),
-        gridData: getGridData(),
+        borderData: simpleBorderData,
+        gridData: horizontalGridData,
         titlesData: getTitlesData(),
         barTouchData: BarTouchData(enabled: false),
         maxY: getNextRoundNumber(getMaxCountsValue(), this.verticalTicksInterval),
@@ -36,6 +28,10 @@ class CustomBarChart extends StatelessWidget {
     );
   }
 
+  double getMaxCountsValue() {
+    return 0.0;
+  }
+
   double getBarWidth(double containerWidth, int barsCount) {
     return 0.65 * (containerWidth / barsCount);
   }
@@ -44,12 +40,19 @@ class CustomBarChart extends StatelessWidget {
     return [];
   }
 
-  double getMaxCountsValue() {
-    return 0.0;
-  }
+  LinearGradient getGradient(Color baseColor, double value, double maxValue) {
+    double alignmentTopValue = value != 0.0 ? -2 * maxValue / value + 1 : 0;
 
-  double getNextRoundNumber(double number, double scale) {
-    return scale * ((number ~/ scale).toInt() + 1);
+    return LinearGradient(
+      begin: Alignment(-1, alignmentTopValue),
+      end: Alignment(1, 1),
+      colors: <Color>[
+        baseColor.lighten(30),
+        baseColor,
+        baseColor.darken(30),
+      ],
+      tileMode: TileMode.mirror,
+    );
   }
 
   BarChartGroupData getBarItem(
@@ -86,39 +89,6 @@ class CustomBarChart extends StatelessWidget {
     );
   }
 
-  LinearGradient getGradient(Color baseColor, double value, double maxValue) {
-    double alignmentTopValue = value != 0.0 ? -2 * maxValue / value + 1 : 0;
-
-    return LinearGradient(
-      begin: Alignment(-1, alignmentTopValue),
-      end: Alignment(1, 1),
-      colors: <Color>[
-        baseColor.lighten(30),
-        baseColor,
-        baseColor.darken(30),
-      ],
-      tileMode: TileMode.mirror,
-    );
-  }
-
-  FlBorderData getBorderData() {
-    return FlBorderData(
-      show: true,
-      border: Border.all(
-        color: AppColors.borderColor,
-        width: 2,
-      ),
-    );
-  }
-
-  FlGridData getGridData() {
-    return const FlGridData(
-      show: true,
-      drawHorizontalLine: true,
-      drawVerticalLine: false,
-    );
-  }
-
   FlTitlesData getTitlesData() {
     const AxisTitles none = const AxisTitles(
       sideTitles: SideTitles(showTitles: false),
@@ -159,53 +129,7 @@ class CustomBarChart extends StatelessWidget {
     );
   }
 
-  Widget getVerticalTitlesWidget(double value, TitleMeta meta) {
-    String suffix =
-        this.verticalAxisTitleSuffix != '' ? ' ' + this.verticalAxisTitleSuffix : '';
-
-    return SideTitleWidget(
-      axisSide: meta.axisSide,
-      space: 4,
-      child: Text(
-        value.toInt().toString() + suffix,
-        style: TextStyle(
-          color: AppColors.mainTextColor1,
-          fontSize: this.titleFontSize,
-        ),
-      ),
-    );
-  }
-
-  Widget getVerticalTitlesSpacerWidget(double value, TitleMeta meta) {
-    return SideTitleWidget(
-      axisSide: meta.axisSide,
-      space: 4,
-      child: Text(''),
-    );
-  }
-
   Widget getHorizontalTitlesWidget(double value, TitleMeta meta) {
-    final DateFormat formatter = DateFormat('dd/MM');
-
-    final DateTime date = DateTime.fromMillisecondsSinceEpoch(value.toInt());
-    final String text = formatter.format(date);
-
-    return SideTitleWidget(
-      axisSide: meta.axisSide,
-      space: 4,
-      child: Padding(
-        padding: EdgeInsets.only(right: 10),
-        child: RotationTransition(
-          turns: new AlwaysStoppedAnimation(-30 / 360),
-          child: Text(
-            text,
-            style: TextStyle(
-              color: AppColors.mainTextColor1,
-              fontSize: this.titleFontSize,
-            ),
-          ),
-        ),
-      ),
-    );
+    return getHorizontalTitlesWidgetWithDate(value, meta);
   }
 }
diff --git a/lib/ui/widgets/abstracts/custom_chart.dart b/lib/ui/widgets/abstracts/custom_chart.dart
new file mode 100644
index 0000000000000000000000000000000000000000..ad555bcf073e17fa6a5ba5028bb4be0a7df2f3da
--- /dev/null
+++ b/lib/ui/widgets/abstracts/custom_chart.dart
@@ -0,0 +1,192 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:fl_chart/fl_chart.dart';
+import 'package:flutter/material.dart';
+
+import 'package:scrobbles/config/app_colors.dart';
+
+class CustomChart extends StatelessWidget {
+  const CustomChart({super.key});
+
+  final double chartHeight = 150.0;
+  final double verticalTicksInterval = 10;
+  final String verticalAxisTitleSuffix = '';
+  final double titleFontSize = 9;
+
+  @override
+  Widget build(BuildContext context) {
+    return SizedBox(
+      height: this.chartHeight,
+    );
+  }
+
+  double getNextRoundNumber(double number, double scale) {
+    return scale * ((number ~/ scale).toInt() + 1);
+  }
+
+  Color getColorFromIndex(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);
+  }
+
+  String getDayShortName(int dayIndex) {
+    switch (dayIndex) {
+      case 1:
+        return 'MON';
+      case 2:
+        return 'TUE';
+      case 3:
+        return 'WED';
+      case 4:
+        return 'THU';
+      case 5:
+        return 'FRI';
+      case 6:
+        return 'SAT';
+      case 7:
+        return 'SUN';
+      default:
+    }
+
+    return '';
+  }
+
+  FlBorderData get noBorderData {
+    return FlBorderData(
+      show: false,
+    );
+  }
+
+  FlBorderData get simpleBorderData {
+    return FlBorderData(
+      show: true,
+      border: Border.all(
+        color: AppColors.borderColor,
+        width: 2,
+      ),
+    );
+  }
+
+  FlGridData get noGridData {
+    return const FlGridData(
+      show: false,
+    );
+  }
+
+  FlGridData get horizontalGridData {
+    return const FlGridData(
+      show: true,
+      drawHorizontalLine: true,
+      drawVerticalLine: false,
+    );
+  }
+
+  FlTitlesData getTitlesData() {
+    return FlTitlesData(
+      show: false,
+    );
+  }
+
+  Widget getHorizontalTitlesWidget(double value, TitleMeta meta) {
+    return Text('');
+  }
+
+  Widget getHorizontalTitlesWidgetWithDate(double value, TitleMeta meta) {
+    final DateFormat formatter = DateFormat('dd/MM');
+
+    final DateTime date = DateTime.fromMillisecondsSinceEpoch(value.toInt());
+    final String text = formatter.format(date);
+
+    return SideTitleWidget(
+      axisSide: meta.axisSide,
+      space: 4,
+      child: Padding(
+        padding: EdgeInsets.only(right: 10),
+        child: RotationTransition(
+          turns: new AlwaysStoppedAnimation(-30 / 360),
+          child: Text(
+            text,
+            style: TextStyle(
+              color: AppColors.mainTextColor1,
+              fontSize: this.titleFontSize,
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+
+  Widget getHorizontalTitlesWidgetWithDay(double value, TitleMeta meta) {
+    final String dayShortName = getDayShortName(value.toInt());
+
+    return SideTitleWidget(
+      axisSide: meta.axisSide,
+      space: 2,
+      child: Text(
+        tr(dayShortName),
+        style: const TextStyle(
+          color: AppColors.mainTextColor1,
+          fontSize: 11,
+        ),
+      ),
+    );
+  }
+
+  Widget getHorizontalTitlesWidgetWithHour(double value, TitleMeta meta) {
+    String text = '';
+
+    if (value % 4 == 0 || value == 23) {
+      text = value.toInt().toString().padLeft(2, '0');
+    }
+
+    return SideTitleWidget(
+      axisSide: meta.axisSide,
+      space: 2,
+      child: Text(
+        text,
+        style: TextStyle(
+          color: AppColors.mainTextColor1,
+          fontSize: this.titleFontSize,
+        ),
+      ),
+    );
+  }
+
+  Widget getVerticalTitlesSpacerWidget(double value, TitleMeta meta) {
+    return SideTitleWidget(
+      axisSide: meta.axisSide,
+      space: 4,
+      child: Text(''),
+    );
+  }
+
+  Widget getVerticalTitlesWidget(double value, TitleMeta meta) {
+    String suffix =
+        this.verticalAxisTitleSuffix != '' ? ' ' + this.verticalAxisTitleSuffix : '';
+
+    return SideTitleWidget(
+      axisSide: meta.axisSide,
+      space: 4,
+      child: Text(
+        value.toInt().toString() + suffix,
+        style: TextStyle(
+          color: AppColors.mainTextColor1,
+          fontSize: this.titleFontSize,
+        ),
+      ),
+    );
+  }
+}
diff --git a/lib/ui/widgets/abstracts/custom_line_chart.dart b/lib/ui/widgets/abstracts/custom_line_chart.dart
index e0362d84aacc86f3c373525eeffb670acbf725f7..6efc4e7f8fa152abd2a3a5c942ebc3f2d2f46ba3 100644
--- a/lib/ui/widgets/abstracts/custom_line_chart.dart
+++ b/lib/ui/widgets/abstracts/custom_line_chart.dart
@@ -1,33 +1,37 @@
 import 'package:fl_chart/fl_chart.dart';
 import 'package:flutter/material.dart';
 
-import 'package:scrobbles/config/app_colors.dart';
+import 'package:scrobbles/ui/widgets/abstracts/custom_chart.dart';
 
-class CustomLineChart extends StatelessWidget {
+class CustomLineChart extends CustomChart {
   const CustomLineChart({super.key});
 
-  final double chartHeight = 150.0;
-  final double titleFontSize = 9;
-
   @override
   Widget build(BuildContext context) {
-    return Container(
-      child: SizedBox(
-        height: this.chartHeight,
-      ),
+    return SizedBox(
+      height: this.chartHeight,
     );
   }
 
-  FlBorderData getBorderData() {
-    return FlBorderData(
-      show: false,
-    );
-  }
+  Map<String, double> getHorizontalScaleFromDates(Iterable datesAsString) {
+    double minDateAsDouble = double.maxFinite;
+    double maxDateAsDouble = -double.maxFinite;
 
-  FlGridData getGridData() {
-    return const FlGridData(
-      show: false,
-    );
+    datesAsString.forEach((element) {
+      final double date = DateTime.parse(element).millisecondsSinceEpoch.toDouble();
+
+      if (date < minDateAsDouble) {
+        minDateAsDouble = date;
+      }
+      if (date > maxDateAsDouble) {
+        maxDateAsDouble = date;
+      }
+    });
+
+    return {
+      'min': minDateAsDouble,
+      'max': maxDateAsDouble,
+    };
   }
 
   FlTitlesData getTitlesData() {
@@ -40,7 +44,7 @@ class CustomLineChart extends StatelessWidget {
         showTitles: true,
         reservedSize: 35,
         getTitlesWidget: getVerticalTitlesWidget,
-        interval: 25,
+        interval: this.verticalTicksInterval,
       ),
     );
 
@@ -68,30 +72,4 @@ class CustomLineChart extends StatelessWidget {
       rightTitles: verticalTitles,
     );
   }
-
-  Widget getVerticalTitlesWidget(double value, TitleMeta meta) {
-    return SideTitleWidget(
-      axisSide: meta.axisSide,
-      space: 4,
-      child: Text(
-        value.toInt().toString() + ' %',
-        style: TextStyle(
-          color: AppColors.mainTextColor1,
-          fontSize: this.titleFontSize,
-        ),
-      ),
-    );
-  }
-
-  Widget getVerticalTitlesSpacerWidget(double value, TitleMeta meta) {
-    return SideTitleWidget(
-      axisSide: meta.axisSide,
-      space: 4,
-      child: Text(''),
-    );
-  }
-
-  Widget getHorizontalTitlesWidget(double value, TitleMeta meta) {
-    return Text('');
-  }
 }
diff --git a/lib/ui/widgets/cards/counts_by_day.dart b/lib/ui/widgets/cards/counts_by_day.dart
index f31a516d5fa4f978f7cb6fedd832149f4d5b63f0..ad1c3d143e0ed89a0ca42c123002af9b1e7c2219 100644
--- a/lib/ui/widgets/cards/counts_by_day.dart
+++ b/lib/ui/widgets/cards/counts_by_day.dart
@@ -20,7 +20,9 @@ class CardCountsByDay extends StatelessWidget {
     final int daysCount = settings.getDistributionDaysCount();
 
     return BlocBuilder<DataCountsByDayCubit, DataCountsByDayState>(
-      builder: (BuildContext context, DataCountsByDayState state) {
+      builder: (BuildContext context, DataCountsByDayState data) {
+        final CountsByDayData counts = data.countsByDay ?? CountsByDayData.fromJson({});
+
         return CardContent(
           color: Theme.of(context).colorScheme.surface,
           title: 'counts_by_day'.tr(
@@ -28,25 +30,23 @@ class CardCountsByDay extends StatelessWidget {
               'daysCount': daysCount.toString(),
             },
           ),
-          loader: updateCountsByDay(daysCount),
-          content: ChartCountsByDay(
-            chartData: CountsByDayData.fromJson(state.countsByDay?.toJson()),
-          ),
+          loader: update(daysCount),
+          content: ChartCountsByDay(chartData: counts),
         );
       },
     );
   }
 
-  Widget updateCountsByDay(int daysCount) {
+  Widget update(int daysCount) {
     final Widget loading = const Text('⏳');
     final Widget done = const Text('');
 
-    late Future<CountsByDayData> futureCountsByDay = ScrobblesApi.fetchCountsByDay(daysCount);
+    late Future<CountsByDayData> future = ScrobblesApi.fetchCountsByDay(daysCount);
 
     return BlocBuilder<DataCountsByDayCubit, DataCountsByDayState>(
-      builder: (BuildContext context, DataCountsByDayState state) {
+      builder: (BuildContext context, DataCountsByDayState data) {
         return FutureBuilder<CountsByDayData>(
-          future: futureCountsByDay,
+          future: future,
           builder: (context, snapshot) {
             if (snapshot.hasError) {
               return ShowErrorWidget(message: '${snapshot.error}');
diff --git a/lib/ui/widgets/cards/counts_by_hour.dart b/lib/ui/widgets/cards/counts_by_hour.dart
index 7618d3d394896f8b61f12f4c6630a54525b7b67f..3bfc614305a2a9cf174bb5cc9e9b79ad98bd8170 100644
--- a/lib/ui/widgets/cards/counts_by_hour.dart
+++ b/lib/ui/widgets/cards/counts_by_hour.dart
@@ -20,7 +20,9 @@ class CardCountsByHour extends StatelessWidget {
     final int daysCount = settings.getDistributionDaysCount();
 
     return BlocBuilder<DataCountsByHourCubit, DataCountsByHourState>(
-      builder: (BuildContext context, DataCountsByHourState state) {
+      builder: (BuildContext context, DataCountsByHourState data) {
+        final CountsByHourData counts = data.countsByHour ?? CountsByHourData.fromJson({});
+
         return CardContent(
           color: Theme.of(context).colorScheme.surface,
           title: 'counts_by_hour'.tr(
@@ -28,26 +30,23 @@ class CardCountsByHour extends StatelessWidget {
               'daysCount': daysCount.toString(),
             },
           ),
-          loader: updateCountsByHour(daysCount),
-          content: ChartCountsByHour(
-            chartData: CountsByHourData.fromJson(state.countsByHour?.toJson()),
-          ),
+          loader: update(daysCount),
+          content: ChartCountsByHour(chartData: counts),
         );
       },
     );
   }
 
-  Widget updateCountsByHour(int daysCount) {
+  Widget update(int daysCount) {
     final Widget loading = const Text('⏳');
     final Widget done = const Text('');
 
-    late Future<CountsByHourData> futureCountsByHour =
-        ScrobblesApi.fetchCountsByHour(daysCount);
+    late Future<CountsByHourData> future = ScrobblesApi.fetchCountsByHour(daysCount);
 
     return BlocBuilder<DataCountsByHourCubit, DataCountsByHourState>(
-      builder: (BuildContext context, DataCountsByHourState state) {
+      builder: (BuildContext context, DataCountsByHourState data) {
         return FutureBuilder<CountsByHourData>(
-          future: futureCountsByHour,
+          future: future,
           builder: (context, snapshot) {
             if (snapshot.hasError) {
               return ShowErrorWidget(message: '${snapshot.error}');
diff --git a/lib/ui/widgets/cards/discoveries.dart b/lib/ui/widgets/cards/discoveries.dart
index 5256ccaf483ed82c30e1057d622450597e7d484c..e136131536f67545c537c4c1e5464b043284913d 100644
--- a/lib/ui/widgets/cards/discoveries.dart
+++ b/lib/ui/widgets/cards/discoveries.dart
@@ -21,9 +21,11 @@ class CardDiscoveries extends StatelessWidget {
     final int daysCount = settings.getDiscoveriesDaysCount();
 
     return BlocBuilder<DataDiscoveriesCubit, DataDiscoveriesState>(
-      builder: (BuildContext context, DataDiscoveriesState state) {
+      builder: (BuildContext context, DataDiscoveriesState data) {
         final TextTheme textTheme = Theme.of(context).primaryTextTheme;
 
+        final DiscoveriesData discoveries = data.discoveries ?? DiscoveriesData.fromJson({});
+
         return CardContent(
           color: Theme.of(context).colorScheme.surface,
           title: 'discoveries_title'.tr(
@@ -31,7 +33,7 @@ class CardDiscoveries extends StatelessWidget {
               'daysCount': daysCount.toString(),
             },
           ),
-          loader: updateDiscoveries(daysCount),
+          loader: update(daysCount),
           content: Column(
             mainAxisSize: MainAxisSize.min,
             crossAxisAlignment: CrossAxisAlignment.start,
@@ -41,18 +43,14 @@ class CardDiscoveries extends StatelessWidget {
                 style: textTheme.titleMedium!.apply(fontWeightDelta: 2),
               ).tr(),
               const SizedBox(height: 8),
-              ChartDiscoveriesArtists(
-                chartData: DiscoveriesData.fromJson(state.discoveries?.toJson()),
-              ),
+              ChartDiscoveriesArtists(chartData: discoveries),
               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()),
-              ),
+              ChartDiscoveriesTracks(chartData: discoveries),
             ],
           ),
         );
@@ -60,16 +58,16 @@ class CardDiscoveries extends StatelessWidget {
     );
   }
 
-  Widget updateDiscoveries(int daysCount) {
+  Widget update(int daysCount) {
     final Widget loading = const Text('⏳');
     final Widget done = const Text('');
 
-    late Future<DiscoveriesData> futureDiscoveries = ScrobblesApi.fetchDiscoveries(daysCount);
+    late Future<DiscoveriesData> future = ScrobblesApi.fetchDiscoveries(daysCount);
 
     return BlocBuilder<DataDiscoveriesCubit, DataDiscoveriesState>(
-      builder: (BuildContext context, DataDiscoveriesState state) {
+      builder: (BuildContext context, DataDiscoveriesState data) {
         return FutureBuilder<DiscoveriesData>(
-          future: futureDiscoveries,
+          future: future,
           builder: (context, snapshot) {
             if (snapshot.hasError) {
               return ShowErrorWidget(message: '${snapshot.error}');
diff --git a/lib/ui/widgets/cards/heatmap.dart b/lib/ui/widgets/cards/heatmap.dart
index bbfdf4f80fdbcb0d47ddd942e2d6b81492289e72..485058dee9684e02dc225ba0bf0fec7b7262d251 100644
--- a/lib/ui/widgets/cards/heatmap.dart
+++ b/lib/ui/widgets/cards/heatmap.dart
@@ -20,8 +20,8 @@ class CardHeatmap extends StatelessWidget {
     final int daysCount = settings.getDistributionDaysCount();
 
     return BlocBuilder<DataHeatmapCubit, DataHeatmapState>(
-      builder: (BuildContext context, DataHeatmapState state) {
-        HeatmapData heatmap = state.heatmap ?? HeatmapData.fromJson({});
+      builder: (BuildContext context, DataHeatmapState data) {
+        final HeatmapData heatmap = data.heatmap ?? HeatmapData.fromJson({});
 
         return CardContent(
           color: Theme.of(context).colorScheme.surface,
@@ -30,25 +30,23 @@ class CardHeatmap extends StatelessWidget {
               'daysCount': daysCount.toString(),
             },
           ),
-          loader: updateHeatmapData(daysCount),
-          content: ChartHeatmap(
-            chartData: heatmap,
-          ),
+          loader: update(daysCount),
+          content: ChartHeatmap(chartData: heatmap),
         );
       },
     );
   }
 
-  Widget updateHeatmapData(int daysCount) {
+  Widget update(int daysCount) {
     final Widget loading = const Text('⏳');
     final Widget done = const Text('');
 
-    late Future<HeatmapData> futureHeatmap = ScrobblesApi.fetchHeatmap(daysCount);
+    late Future<HeatmapData> future = ScrobblesApi.fetchHeatmap(daysCount);
 
     return BlocBuilder<DataHeatmapCubit, DataHeatmapState>(
-      builder: (BuildContext context, DataHeatmapState state) {
+      builder: (BuildContext context, DataHeatmapState data) {
         return FutureBuilder<HeatmapData>(
-          future: futureHeatmap,
+          future: future,
           builder: (context, snapshot) {
             if (snapshot.hasError) {
               return ShowErrorWidget(message: '${snapshot.error}');
diff --git a/lib/ui/widgets/cards/new_artists.dart b/lib/ui/widgets/cards/new_artists.dart
index d9e898b94a66112dbc51842f983f636f32304940..432aa83c4ccdde0d3725615477750bbe54c625fc 100644
--- a/lib/ui/widgets/cards/new_artists.dart
+++ b/lib/ui/widgets/cards/new_artists.dart
@@ -19,7 +19,7 @@ class CardNewArtists extends StatelessWidget {
     final int count = settings.getNewArtistsCount();
 
     return BlocBuilder<DataNewArtistsCubit, DataNewArtistsState>(
-      builder: (BuildContext context, DataNewArtistsState state) {
+      builder: (BuildContext context, DataNewArtistsState data) {
         return CardContent(
           color: Theme.of(context).colorScheme.surface,
           title: 'new_artists_title'.tr(),
@@ -27,7 +27,7 @@ class CardNewArtists extends StatelessWidget {
           content: Column(
             mainAxisAlignment: MainAxisAlignment.start,
             crossAxisAlignment: CrossAxisAlignment.start,
-            children: state.newArtists?.data
+            children: data.newArtists?.data
                     .map((newArtist) => Text(newArtist.artist?.name ?? ''))
                     .toList() ??
                 [],
@@ -44,7 +44,7 @@ class CardNewArtists extends StatelessWidget {
     late Future<NewArtistsData> future = ScrobblesApi.fetchNewArtists(count);
 
     return BlocBuilder<DataNewArtistsCubit, DataNewArtistsState>(
-      builder: (BuildContext context, DataNewArtistsState state) {
+      builder: (BuildContext context, DataNewArtistsState data) {
         return FutureBuilder<NewArtistsData>(
           future: future,
           builder: (context, snapshot) {
diff --git a/lib/ui/widgets/cards/new_tracks.dart b/lib/ui/widgets/cards/new_tracks.dart
index d2bcc0baa35f28f3e908276621d61b9234a521eb..fd1dff5719f4bd5b18d289868daee0cae157bb0c 100644
--- a/lib/ui/widgets/cards/new_tracks.dart
+++ b/lib/ui/widgets/cards/new_tracks.dart
@@ -19,7 +19,7 @@ class CardNewTracks extends StatelessWidget {
     final int count = settings.getNewTracksCount();
 
     return BlocBuilder<DataNewTracksCubit, DataNewTracksState>(
-      builder: (BuildContext context, DataNewTracksState state) {
+      builder: (BuildContext context, DataNewTracksState data) {
         return CardContent(
           color: Theme.of(context).colorScheme.surface,
           title: 'new_tracks_title'.tr(),
@@ -27,7 +27,7 @@ class CardNewTracks extends StatelessWidget {
           content: Column(
             mainAxisAlignment: MainAxisAlignment.start,
             crossAxisAlignment: CrossAxisAlignment.start,
-            children: state.newTracks?.data
+            children: data.newTracks?.data
                     .map((newTrack) => Text((newTrack.track?.artist.name ?? '') +
                         ' - ' +
                         (newTrack.track?.name ?? '')))
@@ -46,7 +46,7 @@ class CardNewTracks extends StatelessWidget {
     late Future<NewTracksData> future = ScrobblesApi.fetchNewTracks(count);
 
     return BlocBuilder<DataNewTracksCubit, DataNewTracksState>(
-      builder: (BuildContext context, DataNewTracksState state) {
+      builder: (BuildContext context, DataNewTracksState data) {
         return FutureBuilder<NewTracksData>(
           future: future,
           builder: (context, snapshot) {
diff --git a/lib/ui/widgets/cards/statistics_global.dart b/lib/ui/widgets/cards/statistics_global.dart
index 467173d05837528a51c8fbfdd08bc42175a3a343..2d7351d1be95c0d916f57dc93f7c51b0b7000a95 100644
--- a/lib/ui/widgets/cards/statistics_global.dart
+++ b/lib/ui/widgets/cards/statistics_global.dart
@@ -15,30 +15,30 @@ class CardStatisticsGlobal extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     return BlocBuilder<DataStatisticsGlobalCubit, DataStatisticsGlobalState>(
-      builder: (BuildContext context, DataStatisticsGlobalState state) {
+      builder: (BuildContext context, DataStatisticsGlobalState data) {
+        final StatisticsGlobalData statistics =
+            data.statisticsGlobal ?? StatisticsGlobalData.fromJson({});
+
         return CardContent(
           color: Theme.of(context).colorScheme.primary,
           title: 'global_statistics'.tr(),
-          loader: updateStatisticsGlobal(),
-          content: ContentStatisticsGlobal(
-            statistics: StatisticsGlobalData.fromJson(state.statisticsGlobal?.toJson()),
-          ),
+          loader: update(),
+          content: ContentStatisticsGlobal(statistics: statistics),
         );
       },
     );
   }
 
-  Widget updateStatisticsGlobal() {
+  Widget update() {
     final Widget loading = const Text('⏳');
     final Widget done = const Text('');
 
-    late Future<StatisticsGlobalData> futureStatisticsGlobal =
-        ScrobblesApi.fetchGlobalStatistics();
+    late Future<StatisticsGlobalData> future = ScrobblesApi.fetchGlobalStatistics();
 
     return BlocBuilder<DataStatisticsGlobalCubit, DataStatisticsGlobalState>(
-      builder: (BuildContext context, DataStatisticsGlobalState dataState) {
+      builder: (BuildContext context, DataStatisticsGlobalState data) {
         return FutureBuilder<StatisticsGlobalData>(
-          future: futureStatisticsGlobal,
+          future: future,
           builder: (context, snapshot) {
             if (snapshot.hasError) {
               return ShowErrorWidget(message: '${snapshot.error}');
diff --git a/lib/ui/widgets/cards/statistics_recent.dart b/lib/ui/widgets/cards/statistics_recent.dart
index f5d4b3f3eb086cd2212fb6848bc81746f131e1ca..044ce9191acefb6f54fd6993c86d6cabd1bdda64 100644
--- a/lib/ui/widgets/cards/statistics_recent.dart
+++ b/lib/ui/widgets/cards/statistics_recent.dart
@@ -1,5 +1,3 @@
-import 'dart:convert';
-
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
@@ -22,7 +20,10 @@ class CardStatisticsRecent extends StatelessWidget {
     final int daysCount = settings.getStatisticsRecentDaysCount();
 
     return BlocBuilder<DataStatisticsRecentCubit, DataStatisticsRecentState>(
-      builder: (BuildContext context, DataStatisticsRecentState dataState) {
+      builder: (BuildContext context, DataStatisticsRecentState data) {
+        final StatisticsRecentData statistics =
+            data.statisticsRecent ?? StatisticsRecentData.fromJson({});
+
         return CardContent(
           color: Theme.of(context).colorScheme.primary,
           title: 'recent_statistics'.tr(
@@ -30,27 +31,23 @@ class CardStatisticsRecent extends StatelessWidget {
               'daysCount': daysCount.toString(),
             },
           ),
-          loader: updateStatisticsRecent(daysCount),
-          content: ContentStatisticsRecent(
-            statistics: StatisticsRecentData.fromJson(
-                jsonDecode(dataState.statisticsRecent.toString())),
-          ),
+          loader: update(daysCount),
+          content: ContentStatisticsRecent(statistics: statistics),
         );
       },
     );
   }
 
-  Widget updateStatisticsRecent(int daysCount) {
+  Widget update(int daysCount) {
     final Widget loading = const Text('⏳');
     final Widget done = const Text('');
 
-    late Future<StatisticsRecentData> futureStatisticsRecent =
-        ScrobblesApi.fetchRecentStatistics(daysCount);
+    late Future<StatisticsRecentData> future = ScrobblesApi.fetchRecentStatistics(daysCount);
 
     return BlocBuilder<DataStatisticsRecentCubit, DataStatisticsRecentState>(
-      builder: (BuildContext context, DataStatisticsRecentState state) {
+      builder: (BuildContext context, DataStatisticsRecentState data) {
         return FutureBuilder<StatisticsRecentData>(
-          future: futureStatisticsRecent,
+          future: future,
           builder: (context, snapshot) {
             if (snapshot.hasError) {
               return ShowErrorWidget(message: '${snapshot.error}');
diff --git a/lib/ui/widgets/cards/timeline.dart b/lib/ui/widgets/cards/timeline.dart
index b1a69b34ff89924b61b201e43401d96672f07f43..f343b316f09591da2cfd27990c6670abd82825fa 100644
--- a/lib/ui/widgets/cards/timeline.dart
+++ b/lib/ui/widgets/cards/timeline.dart
@@ -21,7 +21,9 @@ class CardTimeline extends StatelessWidget {
     final int daysCount = settings.getTimelineDaysCount();
 
     return BlocBuilder<DataTimelineCubit, DataTimelineState>(
-      builder: (BuildContext context, DataTimelineState state) {
+      builder: (BuildContext context, DataTimelineState data) {
+        final TimelineData timeline = data.timeline ?? TimelineData.fromJson({});
+
         return CardContent(
           color: Theme.of(context).colorScheme.surface,
           title: 'timeline_title'.tr(
@@ -29,15 +31,11 @@ class CardTimeline extends StatelessWidget {
               'daysCount': daysCount.toString(),
             },
           ),
-          loader: updateTimeline(daysCount),
+          loader: update(daysCount),
           content: Stack(
             children: [
-              ChartTimelineCounts(
-                chartData: TimelineData.fromJson(state.timeline?.toJson()),
-              ),
-              ChartTimelineEclecticism(
-                chartData: TimelineData.fromJson(state.timeline?.toJson()),
-              ),
+              ChartTimelineCounts(chartData: timeline),
+              ChartTimelineEclecticism(chartData: timeline),
             ],
           ),
         );
@@ -45,16 +43,16 @@ class CardTimeline extends StatelessWidget {
     );
   }
 
-  Widget updateTimeline(int daysCount) {
+  Widget update(int daysCount) {
     final Widget loading = const Text('⏳');
     final Widget done = const Text('');
 
-    late Future<TimelineData> futureTimeline = ScrobblesApi.fetchTimeline(daysCount);
+    late Future<TimelineData> future = ScrobblesApi.fetchTimeline(daysCount);
 
     return BlocBuilder<DataTimelineCubit, DataTimelineState>(
-      builder: (BuildContext context, DataTimelineState dataTimelineState) {
+      builder: (BuildContext context, DataTimelineState data) {
         return FutureBuilder<TimelineData>(
-          future: futureTimeline,
+          future: future,
           builder: (context, snapshot) {
             if (snapshot.hasError) {
               return ShowErrorWidget(message: '${snapshot.error}');
diff --git a/lib/ui/widgets/cards/top_artists.dart b/lib/ui/widgets/cards/top_artists.dart
index c528e431b73a8588bc6df7e442e76c41d66f5538..ff6a5035a212f61384c940a60e95bf027a6ff537 100644
--- a/lib/ui/widgets/cards/top_artists.dart
+++ b/lib/ui/widgets/cards/top_artists.dart
@@ -21,8 +21,8 @@ class CardTopArtists extends StatelessWidget {
     final int daysCount = settings.getTopArtistsDaysCount();
 
     return BlocBuilder<DataTopArtistsCubit, DataTopArtistsState>(
-      builder: (BuildContext context, DataTopArtistsState state) {
-        TopArtistsData artistsData = state.topArtists ?? TopArtistsData.fromJson({});
+      builder: (BuildContext context, DataTopArtistsState data) {
+        final TopArtistsData artistsData = data.topArtists ?? TopArtistsData.fromJson({});
 
         return CardContent(
           color: Theme.of(context).colorScheme.surface,
@@ -31,18 +31,14 @@ class CardTopArtists extends StatelessWidget {
               'daysCount': daysCount.toString(),
             },
           ),
-          loader: updateTopArtists(daysCount),
+          loader: update(daysCount),
           content: Column(
             mainAxisSize: MainAxisSize.min,
             crossAxisAlignment: CrossAxisAlignment.start,
             children: [
-              ChartTopArtists(
-                chartData: artistsData,
-              ),
+              ChartTopArtists(chartData: artistsData),
               const SizedBox(height: 8),
-              ChartTopArtistsStream(
-                chartData: artistsData,
-              ),
+              ChartTopArtistsStream(chartData: artistsData),
             ],
           ),
         );
@@ -50,16 +46,16 @@ class CardTopArtists extends StatelessWidget {
     );
   }
 
-  Widget updateTopArtists(int daysCount) {
+  Widget update(int daysCount) {
     final Widget loading = const Text('⏳');
     final Widget done = const Text('');
 
-    late Future<TopArtistsData> futureTopArtists = ScrobblesApi.fetchTopArtists(daysCount);
+    late Future<TopArtistsData> future = ScrobblesApi.fetchTopArtists(daysCount);
 
     return BlocBuilder<DataTopArtistsCubit, DataTopArtistsState>(
-      builder: (BuildContext context, DataTopArtistsState state) {
+      builder: (BuildContext context, DataTopArtistsState update) {
         return FutureBuilder<TopArtistsData>(
-          future: futureTopArtists,
+          future: future,
           builder: (context, snapshot) {
             if (snapshot.hasError) {
               return ShowErrorWidget(message: '${snapshot.error}');
diff --git a/lib/ui/widgets/charts/counts_by_day.dart b/lib/ui/widgets/charts/counts_by_day.dart
index ba2398f249b2dfb3502b694473e13e31084be702..b54be919b53324313c7cefb15d2370df5399fb12 100644
--- a/lib/ui/widgets/charts/counts_by_day.dart
+++ b/lib/ui/widgets/charts/counts_by_day.dart
@@ -1,8 +1,6 @@
-import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:fl_chart/fl_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';
 
@@ -53,9 +51,9 @@ class ChartCountsByDay extends CustomBarChart {
     return maxValue;
   }
 
-  Color getColorFromIndex(int index) {
-    Color barColor = AppColors.contentColorBlack;
-    switch (index) {
+  Color getColorFromDayIndex(int dayIndex) {
+    Color barColor = Colors.black;
+    switch (dayIndex) {
       case 1:
         barColor = Color.fromARGB(255, 255, 99, 132);
         break;
@@ -89,7 +87,7 @@ class ChartCountsByDay extends CustomBarChart {
       final double? counts = this.chartData.data[day];
 
       if (counts != null) {
-        final Color barColor = this.getColorFromIndex(day);
+        final Color barColor = this.getColorFromDayIndex(day);
 
         data.add(this.getBarItem(
           x: day,
@@ -104,43 +102,6 @@ class ChartCountsByDay extends CustomBarChart {
   }
 
   Widget getHorizontalTitlesWidget(double value, TitleMeta meta) {
-    final int day = value.toInt();
-    String dayShortName = '';
-    switch (day) {
-      case 1:
-        dayShortName = 'MON';
-        break;
-      case 2:
-        dayShortName = 'TUE';
-        break;
-      case 3:
-        dayShortName = 'WED';
-        break;
-      case 4:
-        dayShortName = 'THU';
-        break;
-      case 5:
-        dayShortName = 'FRI';
-        break;
-      case 6:
-        dayShortName = 'SAT';
-        break;
-      case 7:
-        dayShortName = 'SUN';
-        break;
-      default:
-    }
-
-    return SideTitleWidget(
-      axisSide: meta.axisSide,
-      space: 2,
-      child: Text(
-        tr(dayShortName),
-        style: const TextStyle(
-          color: AppColors.mainTextColor1,
-          fontSize: 11,
-        ),
-      ),
-    );
+    return getHorizontalTitlesWidgetWithDay(value, meta);
   }
 }
diff --git a/lib/ui/widgets/charts/counts_by_hour.dart b/lib/ui/widgets/charts/counts_by_hour.dart
index 179e5b5251ff4b2e98c18b04e740e94cdfc0ca47..73feb7ec6f2291f4b1c81e68fa44c2411be274ce 100644
--- a/lib/ui/widgets/charts/counts_by_hour.dart
+++ b/lib/ui/widgets/charts/counts_by_hour.dart
@@ -75,22 +75,6 @@ class ChartCountsByHour extends CustomBarChart {
   }
 
   Widget getHorizontalTitlesWidget(double value, TitleMeta meta) {
-    String text = '';
-
-    if (value % 4 == 0 || value == 23) {
-      text = value.toInt().toString().padLeft(2, '0');
-    }
-
-    return SideTitleWidget(
-      axisSide: meta.axisSide,
-      space: 2,
-      child: Text(
-        text,
-        style: const TextStyle(
-          color: AppColors.mainTextColor1,
-          fontSize: 11,
-        ),
-      ),
-    );
+    return getHorizontalTitlesWidgetWithHour(value, meta);
   }
 }
diff --git a/lib/ui/widgets/charts/heatmap.dart b/lib/ui/widgets/charts/heatmap.dart
index 3484da2b1cd2aeec8f4c8801b2a32e2c96211164..b4b1af1eadfcf6d96e8cd13982cadf3b48a80244 100644
--- a/lib/ui/widgets/charts/heatmap.dart
+++ b/lib/ui/widgets/charts/heatmap.dart
@@ -1,12 +1,13 @@
 import 'package:easy_localization/easy_localization.dart';
 import 'package:fl_chart/fl_chart.dart';
 import 'package:flutter/material.dart';
-import 'package:scrobbles/config/app_colors.dart';
 
+import 'package:scrobbles/config/app_colors.dart';
 import 'package:scrobbles/models/heatmap.dart';
+import 'package:scrobbles/ui/widgets/abstracts/custom_chart.dart';
 import 'package:scrobbles/utils/color_extensions.dart';
 
-class ChartHeatmap extends StatelessWidget {
+class ChartHeatmap extends CustomChart {
   final HeatmapData chartData;
 
   const ChartHeatmap({super.key, required this.chartData});
@@ -100,7 +101,7 @@ class ChartHeatmap extends StatelessWidget {
       sideTitles: SideTitles(
         showTitles: true,
         reservedSize: 20,
-        getTitlesWidget: getHorizontalTitlesWidget,
+        getTitlesWidget: getHorizontalTitlesWidgetWithHour,
         interval: 4,
       ),
     );
@@ -115,32 +116,7 @@ class ChartHeatmap extends StatelessWidget {
   }
 
   Widget getVerticalTitlesWidget(double value, TitleMeta meta) {
-    final int day = 8 - value.toInt();
-    String dayShortName = '';
-    switch (day) {
-      case 1:
-        dayShortName = 'MON';
-        break;
-      case 2:
-        dayShortName = 'TUE';
-        break;
-      case 3:
-        dayShortName = 'WED';
-        break;
-      case 4:
-        dayShortName = 'THU';
-        break;
-      case 5:
-        dayShortName = 'FRI';
-        break;
-      case 6:
-        dayShortName = 'SAT';
-        break;
-      case 7:
-        dayShortName = 'SUN';
-        break;
-      default:
-    }
+    final String dayShortName = getDayShortName(8 - value.toInt());
 
     return SideTitleWidget(
       axisSide: meta.axisSide,
@@ -154,24 +130,4 @@ class ChartHeatmap extends StatelessWidget {
       ),
     );
   }
-
-  Widget getHorizontalTitlesWidget(double value, TitleMeta meta) {
-    String text = '';
-
-    if (value % 4 == 0 || value == 23) {
-      text = value.toInt().toString().padLeft(2, '0');
-    }
-
-    return SideTitleWidget(
-      axisSide: meta.axisSide,
-      space: 2,
-      child: Text(
-        text,
-        style: TextStyle(
-          color: AppColors.mainTextColor1,
-          fontSize: this.titleFontSize,
-        ),
-      ),
-    );
-  }
 }
diff --git a/lib/ui/widgets/charts/timeline_eclecticism.dart b/lib/ui/widgets/charts/timeline_eclecticism.dart
index 273aad8067ad961cbd34351aa5b5e76e5b78ca5c..565cdad86809433d58440b96b24db8453bb495e3 100644
--- a/lib/ui/widgets/charts/timeline_eclecticism.dart
+++ b/lib/ui/widgets/charts/timeline_eclecticism.dart
@@ -11,27 +11,32 @@ class ChartTimelineEclecticism extends CustomLineChart {
 
   const ChartTimelineEclecticism({super.key, required this.chartData});
 
+  final String verticalAxisTitleSuffix = '%';
+
   @override
   Widget build(BuildContext context) {
-    final horizontalScale = getHorizontalScale();
-
     if (this.chartData.data.keys.length == 0) {
       return SizedBox(
         height: this.chartHeight,
       );
     }
 
+    final horizontalScale = getHorizontalScaleFromDates(this.chartData.data.keys);
+
+    // Left/right margins: 12h, in milliseconds
+    const double margin = 12 * 60 * 60 * 1000;
+
     return Container(
       height: this.chartHeight,
       child: LineChart(
         LineChartData(
           lineBarsData: getDataEclecticism(),
-          borderData: getBorderData(),
-          gridData: getGridData(),
+          borderData: noBorderData,
+          gridData: noGridData,
           titlesData: getTitlesData(),
           lineTouchData: const LineTouchData(enabled: false),
-          minX: horizontalScale['min'],
-          maxX: horizontalScale['max'],
+          minX: (horizontalScale['min'] ?? 0) - margin,
+          maxX: (horizontalScale['max'] ?? 0) + margin,
           maxY: 100,
           minY: 0,
         ),
@@ -40,33 +45,6 @@ class ChartTimelineEclecticism extends CustomLineChart {
     );
   }
 
-  Map<String, double> getHorizontalScale() {
-    // Left/right margins: 12h, in milliseconds
-    int margin = 12 * 60 * 60 * 1000;
-
-    double minDateAsDouble = double.maxFinite;
-    double maxDateAsDouble = -double.maxFinite;
-
-    this.chartData.data.keys.forEach((element) {
-      TimelineDataValue? value = this.chartData.data[element];
-      if (value != null) {
-        final double date = DateTime.parse(element).millisecondsSinceEpoch.toDouble();
-
-        if (date < minDateAsDouble) {
-          minDateAsDouble = date;
-        }
-        if (date > maxDateAsDouble) {
-          maxDateAsDouble = date;
-        }
-      }
-    });
-
-    return {
-      'min': minDateAsDouble - margin,
-      'max': maxDateAsDouble + margin,
-    };
-  }
-
   List<LineChartBarData> getDataEclecticism() {
     List<FlSpot> spots = [];
 
diff --git a/lib/ui/widgets/charts/top_artists.dart b/lib/ui/widgets/charts/top_artists.dart
index f6a995754d154f668bcbc4d3a565504d203db47b..bf380ee804916e37b145f7698859121b30b9b77e 100644
--- a/lib/ui/widgets/charts/top_artists.dart
+++ b/lib/ui/widgets/charts/top_artists.dart
@@ -3,9 +3,10 @@ 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 StatelessWidget {
+class ChartTopArtists extends CustomChart {
   final TopArtistsData chartData;
 
   const ChartTopArtists({super.key, required this.chartData});
@@ -15,6 +16,8 @@ class ChartTopArtists extends StatelessWidget {
     return AspectRatio(
       aspectRatio: 2.1,
       child: Row(
+        mainAxisAlignment: MainAxisAlignment.center,
+        crossAxisAlignment: CrossAxisAlignment.center,
         children: <Widget>[
           Expanded(
             child: AspectRatio(
@@ -37,36 +40,22 @@ class ChartTopArtists extends StatelessWidget {
     );
   }
 
-  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)];
+    const radius = 40.0;
+    const fontSize = 11.0;
+    const shadows = [
+      Shadow(
+        color: Colors.black,
+        blurRadius: 2,
+      )
+    ];
 
     int index = 0;
 
     this.chartData.topArtists.forEach((element) {
-      Color baseColor = getColorIndex(index++);
+      final Color baseColor = getColorFromIndex(index++);
 
       final PieChartSectionData item = PieChartSectionData(
         value: element.count.toDouble(),
@@ -77,7 +66,7 @@ class ChartTopArtists extends StatelessWidget {
           width: 1,
         ),
         radius: radius,
-        titleStyle: TextStyle(
+        titleStyle: const TextStyle(
           fontSize: fontSize,
           color: AppColors.mainTextColor1,
           shadows: shadows,
@@ -97,7 +86,7 @@ class ChartTopArtists extends StatelessWidget {
     int index = 0;
 
     this.chartData.topArtists.forEach((element) {
-      Color baseColor = getColorIndex(index++);
+      final Color baseColor = getColorFromIndex(index++);
 
       final Widget item = Row(
         children: <Widget>[
@@ -113,9 +102,7 @@ class ChartTopArtists extends StatelessWidget {
               ),
             ),
           ),
-          const SizedBox(
-            width: 4,
-          ),
+          const SizedBox(width: 4),
           Text(
             element.artistName + ' (' + element.count.toString() + ')',
             style: TextStyle(
diff --git a/lib/ui/widgets/charts/top_artists_stream.dart b/lib/ui/widgets/charts/top_artists_stream.dart
index 4a5fdf645f2a9d0aec01116981e7fab6d21617ca..2e43253efacc19730924da2df8823740484522d9 100644
--- a/lib/ui/widgets/charts/top_artists_stream.dart
+++ b/lib/ui/widgets/charts/top_artists_stream.dart
@@ -1,8 +1,6 @@
-import 'package:easy_localization/easy_localization.dart';
 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_line_chart.dart';
 import 'package:scrobbles/utils/color_extensions.dart';
@@ -16,22 +14,22 @@ class ChartTopArtistsStream extends CustomLineChart {
 
   @override
   Widget build(BuildContext context) {
-    final horizontalScale = getHorizontalScale();
-
     if (this.chartData.topArtistsStream.keys.isEmpty) {
       return SizedBox(
         height: this.chartHeight,
       );
     }
 
+    final horizontalScale = getHorizontalScaleFromDates(this.chartData.topArtistsStream.keys);
+
     return Container(
       height: this.chartHeight,
       child: LineChart(
         LineChartData(
           lineBarsData: getDataStreamLine(),
           betweenBarsData: getBetweenBarsData(),
-          borderData: getBorderData(),
-          gridData: getGridData(),
+          borderData: noBorderData,
+          gridData: horizontalGridData,
           titlesData: getTitlesData(),
           lineTouchData: const LineTouchData(enabled: false),
           minX: horizontalScale['min'],
@@ -44,24 +42,12 @@ class ChartTopArtistsStream extends CustomLineChart {
     );
   }
 
-  double getNextRoundNumber(double number, double scale) {
-    return scale * ((number ~/ scale).toInt() + 1);
-  }
-
-  FlGridData getGridData() {
-    return const FlGridData(
-      show: true,
-      drawHorizontalLine: true,
-      drawVerticalLine: false,
-    );
-  }
-
   double getMaxVerticalValue() {
     double maxValue = 0;
 
     this.chartData.topArtistsStream.keys.forEach((dateAsString) {
       double totalValue = 0.0;
-      List<TopArtistsStreamDataValue> artists =
+      final List<TopArtistsStreamDataValue> artists =
           this.chartData.topArtistsStream[dateAsString] ?? [];
 
       artists.forEach((artist) {
@@ -77,55 +63,15 @@ class ChartTopArtistsStream extends CustomLineChart {
     return maxValue;
   }
 
-  Map<String, double> getHorizontalScale() {
-    double minDateAsDouble = double.maxFinite;
-    double maxDateAsDouble = -double.maxFinite;
-
-    this.chartData.topArtistsStream.keys.forEach((dateAsString) {
-      final double date = DateTime.parse(dateAsString).millisecondsSinceEpoch.toDouble();
-
-      if (date < minDateAsDouble) {
-        minDateAsDouble = date;
-      }
-      if (date > maxDateAsDouble) {
-        maxDateAsDouble = date;
-      }
-    });
-
-    return {
-      'min': minDateAsDouble,
-      'max': maxDateAsDouble,
-    };
-  }
-
-  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<LineChartBarData> getDataStreamLine() {
-    int artistsCount =
+    final int artistsCount =
         this.chartData.topArtistsStream[this.chartData.topArtistsStream.keys.first]?.length ??
             0;
 
     List<LineChartBarData> lines = [];
 
     LineChartBarData getZeroHorizontalLine() {
-      final baseColor = getColorIndex(0);
+      final baseColor = getColorFromIndex(0);
       final borderColor = baseColor.darken(20);
 
       List<FlSpot> spots = [];
@@ -146,7 +92,7 @@ class ChartTopArtistsStream extends CustomLineChart {
     lines.add(getZeroHorizontalLine());
 
     LineChartBarData getLinesFromIndex(int index) {
-      final baseColor = getColorIndex(index);
+      final baseColor = getColorFromIndex(index);
       final borderColor = baseColor.darken(20);
 
       List<FlSpot> spots = [];
@@ -183,7 +129,7 @@ class ChartTopArtistsStream extends CustomLineChart {
   }
 
   List<BetweenBarsData> getBetweenBarsData() {
-    int artistsCount =
+    final int artistsCount =
         this.chartData.topArtistsStream[this.chartData.topArtistsStream.keys.first]?.length ??
             0;
 
@@ -192,33 +138,12 @@ class ChartTopArtistsStream extends CustomLineChart {
         .map((index) => BetweenBarsData(
               fromIndex: index,
               toIndex: index + 1,
-              color: getColorIndex(index),
+              color: getColorFromIndex(index),
             ))
         .toList();
   }
 
   Widget getHorizontalTitlesWidget(double value, TitleMeta meta) {
-    final DateFormat formatter = DateFormat('dd/MM');
-
-    final DateTime date = DateTime.fromMillisecondsSinceEpoch(value.toInt());
-    final String text = formatter.format(date);
-
-    return SideTitleWidget(
-      axisSide: meta.axisSide,
-      space: 4,
-      child: Padding(
-        padding: EdgeInsets.only(right: 10),
-        child: RotationTransition(
-          turns: new AlwaysStoppedAnimation(-30 / 360),
-          child: Text(
-            text,
-            style: TextStyle(
-              color: AppColors.mainTextColor1,
-              fontSize: this.titleFontSize,
-            ),
-          ),
-        ),
-      ),
-    );
+    return getHorizontalTitlesWidgetWithDate(value, meta);
   }
 }
diff --git a/pubspec.lock b/pubspec.lock
index e194593eb71c678f2003542d4360a296dc8ff551..059e47c85830a0ce74ba93abafc64db3e9860b5a 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -148,10 +148,10 @@ packages:
     dependency: "direct main"
     description:
       name: http
-      sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
+      sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139
       url: "https://pub.dev"
     source: hosted
-    version: "1.1.0"
+    version: "1.1.2"
   http_parser:
     dependency: transitive
     description:
@@ -417,10 +417,10 @@ packages:
     dependency: transitive
     description:
       name: win32
-      sha256: "7c99c0e1e2fa190b48d25c81ca5e42036d5cac81430ef249027d97b0935c553f"
+      sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574
       url: "https://pub.dev"
     source: hosted
-    version: "5.1.0"
+    version: "5.1.1"
   xdg_directories:
     dependency: transitive
     description:
diff --git a/pubspec.yaml b/pubspec.yaml
index bb7e9233bab3fc8a013aed2ed4fa7332bd6651ac..28be42a78e10cbf602b6cf3de2e599d00b040e8b 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,7 +3,7 @@ description: Display scrobbles data and charts
 
 publish_to: 'none'
 
-version: 0.0.49+49
+version: 0.0.50+50
 
 environment:
   sdk: '^3.0.0'