From ed5598fc6613119f90bf7e5ad8cbab8f7e3f1ebe Mon Sep 17 00:00:00 2001 From: PartyDonut Date: Sun, 26 Oct 2025 19:18:26 +0100 Subject: [PATCH] chore: Clean-up for status cards --- lib/models/book_model.dart | 9 +++- lib/models/item_base_model.dart | 2 + lib/models/items/season_model.dart | 3 ++ lib/models/items/series_model.dart | 3 ++ lib/screens/shared/media/chapter_row.dart | 5 +- .../shared/media/components/poster_image.dart | 50 +++++-------------- .../media/components/poster_placeholder.dart | 2 +- lib/screens/shared/media/episode_posters.dart | 5 +- lib/screens/shared/media/people_row.dart | 5 +- lib/screens/shared/media/season_row.dart | 17 ++++--- lib/theme.dart | 5 ++ lib/widgets/shared/status_card.dart | 44 ++++++++-------- 12 files changed, 69 insertions(+), 81 deletions(-) diff --git a/lib/models/book_model.dart b/lib/models/book_model.dart index 8f418a1..edb088e 100644 --- a/lib/models/book_model.dart +++ b/lib/models/book_model.dart @@ -1,11 +1,13 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_riverpod/flutter_riverpod.dart'; + import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart'; import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/models/items/images_models.dart'; import 'package:fladder/models/items/item_shared_models.dart'; import 'package:fladder/models/items/overview_model.dart'; import 'package:fladder/util/localization_helper.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; class BookModel extends ItemBaseModel { final String? parentName; @@ -46,6 +48,9 @@ class BookModel extends ItemBaseModel { @override double get progress => userData.progress != 0 ? 100 : 0; + @override + String? unplayedLabel(BuildContext context) => userData.progress != 0 ? context.localized.page(currentPage) : null; + @override String playButtonLabel(BuildContext context) => progress != 0 ? context.localized.continuePage(currentPage) diff --git a/lib/models/item_base_model.dart b/lib/models/item_base_model.dart index 0bd4670..962c00e 100644 --- a/lib/models/item_base_model.dart +++ b/lib/models/item_base_model.dart @@ -113,6 +113,8 @@ class ItemBaseModel with ItemBaseModelMappable { bool get watched => userData.played; + String? unplayedLabel(BuildContext context) => null; + String? detailedName(BuildContext context) => "$name${overview.yearAired != null ? " (${overview.yearAired})" : ""}"; String? get subText => null; diff --git a/lib/models/items/season_model.dart b/lib/models/items/season_model.dart index 9e43fbe..b6f119c 100644 --- a/lib/models/items/season_model.dart +++ b/lib/models/items/season_model.dart @@ -82,6 +82,9 @@ class SeasonModel extends ItemBaseModel with SeasonModelMappable { String localizedName(BuildContext context) => name.replaceFirst("Season", context.localized.season(1)); + @override + String? unplayedLabel(BuildContext context) => userData.played ? null : userData.unPlayedItemCount?.toString(); + @override SeriesModel get parentBaseModel => SeriesModel( originalTitle: '', diff --git a/lib/models/items/series_model.dart b/lib/models/items/series_model.dart index 5ab8dde..0b9b10a 100644 --- a/lib/models/items/series_model.dart +++ b/lib/models/items/series_model.dart @@ -74,6 +74,9 @@ class SeriesModel extends ItemBaseModel with SeriesModelMappable { return availableEpisodes?.map((e) => e).toList() ?? []; } + @override + String? unplayedLabel(BuildContext context) => userData.played ? null : userData.unPlayedItemCount?.toString(); + @override bool get syncAble => true; diff --git a/lib/screens/shared/media/chapter_row.dart b/lib/screens/shared/media/chapter_row.dart index 923268a..dabeec8 100644 --- a/lib/screens/shared/media/chapter_row.dart +++ b/lib/screens/shared/media/chapter_row.dart @@ -65,10 +65,7 @@ class ChapterRow extends ConsumerWidget { borderRadius: FladderTheme.smallShape.borderRadius, color: Theme.of(context).colorScheme.surfaceContainer, ), - foregroundDecoration: BoxDecoration( - borderRadius: FladderTheme.smallShape.borderRadius, - border: Border.all(width: 2, color: Colors.white.withAlpha(25)), - ), + foregroundDecoration: FladderTheme.defaultPosterDecoration, child: AspectRatio( aspectRatio: 1.75, child: CachedNetworkImage( diff --git a/lib/screens/shared/media/components/poster_image.dart b/lib/screens/shared/media/components/poster_image.dart index 0df6b9a..288fe12 100644 --- a/lib/screens/shared/media/components/poster_image.dart +++ b/lib/screens/shared/media/components/poster_image.dart @@ -4,11 +4,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:iconsax_plus/iconsax_plus.dart'; -import 'package:fladder/models/book_model.dart'; import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/models/items/item_shared_models.dart'; import 'package:fladder/models/items/photos_model.dart'; -import 'package:fladder/models/items/series_model.dart'; import 'package:fladder/screens/shared/media/components/poster_placeholder.dart'; import 'package:fladder/theme.dart'; import 'package:fladder/util/adaptive_layout/adaptive_layout.dart'; @@ -84,7 +82,7 @@ class PosterImage extends ConsumerWidget { ), foregroundDecoration: BoxDecoration( borderRadius: radius, - border: Border.all(width: 2, color: Colors.white.withAlpha(25)), + border: Border.all(width: 1, color: Colors.white.withAlpha(45)), ), child: FladderImage( image: primaryPosters @@ -94,26 +92,6 @@ class PosterImage extends ConsumerWidget { ), ), overlays: [ - if (poster.userData.progress > 0 && poster.type == FladderItemType.book) - Align( - alignment: Alignment.topLeft, - child: Padding( - padding: padding, - child: Card( - child: Padding( - padding: const EdgeInsets.all(5.5), - child: Text( - context.localized.page((poster as BookModel).currentPage), - style: Theme.of(context).textTheme.labelLarge?.copyWith( - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.primary, - fontSize: 12, - ), - ), - ), - ), - ), - ), if (selected == true) Container( decoration: BoxDecoration( @@ -196,27 +174,23 @@ class PosterImage extends ConsumerWidget { ), ), ), - if ((poster.unPlayedItemCount != null && poster is SeriesModel) || poster.watched) + if (poster.unplayedLabel(context) != null || poster.watched) IgnorePointer( child: Align( alignment: Alignment.topRight, child: StatusCard( - color: Theme.of(context).colorScheme.primary, - useFittedBox: poster.unPlayedItemCount != 0, + color: Theme.of(context).colorScheme.primaryContainer, child: Padding( padding: const EdgeInsets.all(6), - child: poster.unPlayedItemCount != 0 - ? Container( - constraints: const BoxConstraints(minWidth: 16), - child: Text( - poster.userData.unPlayedItemCount.toString(), - textAlign: TextAlign.center, - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - fontWeight: FontWeight.bold, - overflow: TextOverflow.visible, - fontSize: 14, - ), + child: poster.unplayedLabel(context) != null + ? Text( + poster.unplayedLabel(context) ?? "", + textAlign: TextAlign.center, + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.bold, + overflow: TextOverflow.visible, + fontSize: 14, ), ) : Icon( diff --git a/lib/screens/shared/media/components/poster_placeholder.dart b/lib/screens/shared/media/components/poster_placeholder.dart index b4f288d..bd4b49e 100644 --- a/lib/screens/shared/media/components/poster_placeholder.dart +++ b/lib/screens/shared/media/components/poster_placeholder.dart @@ -13,7 +13,7 @@ class PosterPlaceholder extends StatelessWidget { alignment: Alignment.center, children: [ Align( - alignment: Alignment.topRight, + alignment: Alignment.topLeft, child: Padding( padding: const EdgeInsets.all(12.0), child: Icon( diff --git a/lib/screens/shared/media/episode_posters.dart b/lib/screens/shared/media/episode_posters.dart index 31156a7..284502d 100644 --- a/lib/screens/shared/media/episode_posters.dart +++ b/lib/screens/shared/media/episode_posters.dart @@ -183,10 +183,7 @@ class EpisodePoster extends ConsumerWidget { borderRadius: FladderTheme.smallShape.borderRadius, color: Theme.of(context).colorScheme.surfaceContainer, ), - foregroundDecoration: BoxDecoration( - borderRadius: FladderTheme.smallShape.borderRadius, - border: Border.all(width: 2, color: Colors.white.withAlpha(25)), - ), + foregroundDecoration: FladderTheme.defaultPosterDecoration, child: FladderImage( image: !episodeAvailable ? episode.parentImages?.primary : episode.images?.primary, placeHolder: placeHolder, diff --git a/lib/screens/shared/media/people_row.dart b/lib/screens/shared/media/people_row.dart index 37459c0..309ee24 100644 --- a/lib/screens/shared/media/people_row.dart +++ b/lib/screens/shared/media/people_row.dart @@ -61,10 +61,7 @@ class PeopleRow extends ConsumerWidget { borderRadius: FladderTheme.smallShape.borderRadius, color: Theme.of(context).colorScheme.surfaceContainer, ), - foregroundDecoration: BoxDecoration( - borderRadius: FladderTheme.smallShape.borderRadius, - border: Border.all(width: 2, color: Colors.white.withAlpha(25)), - ), + foregroundDecoration: FladderTheme.defaultPosterDecoration, child: FocusButton( onTap: () => Navigator.of(context).push( MaterialPageRoute( diff --git a/lib/screens/shared/media/season_row.dart b/lib/screens/shared/media/season_row.dart index 64b1199..9df8b9a 100644 --- a/lib/screens/shared/media/season_row.dart +++ b/lib/screens/shared/media/season_row.dart @@ -89,10 +89,7 @@ class SeasonPoster extends ConsumerWidget { borderRadius: FladderTheme.smallShape.borderRadius, color: Theme.of(context).colorScheme.surfaceContainer, ), - foregroundDecoration: BoxDecoration( - borderRadius: FladderTheme.smallShape.borderRadius, - border: Border.all(width: 2, color: Colors.white.withAlpha(25)), - ), + foregroundDecoration: FladderTheme.defaultPosterDecoration, child: FladderImage( image: season.getPosters?.primary ?? season.parentImages?.backDrop?.firstOrNull ?? @@ -153,11 +150,17 @@ class SeasonPoster extends ConsumerWidget { if (season.userData.unPlayedItemCount != 0) StatusCard( color: Theme.of(context).colorScheme.primary, - useFittedBox: true, - child: Center( + child: Padding( + padding: const EdgeInsets.all(6), child: Text( season.userData.unPlayedItemCount.toString(), - style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 14), + textAlign: TextAlign.center, + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.bold, + overflow: TextOverflow.visible, + fontSize: 14, + ), ), ), ) diff --git a/lib/theme.dart b/lib/theme.dart index 451c7da..7439e71 100644 --- a/lib/theme.dart +++ b/lib/theme.dart @@ -34,6 +34,11 @@ class FladderTheme { static RoundedRectangleBorder get defaultShape => RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)); static RoundedRectangleBorder get largeShape => RoundedRectangleBorder(borderRadius: BorderRadius.circular(32)); + static BoxDecoration get defaultPosterDecoration => BoxDecoration( + borderRadius: FladderTheme.smallShape.borderRadius, + border: Border.all(width: 1, color: Colors.white.withAlpha(45)), + ); + static ThemeData theme(ColorScheme? colorScheme, DynamicSchemeVariant dynamicSchemeVariant) { final ColorScheme? scheme = generateDynamicColourSchemes(colorScheme, dynamicSchemeVariant); diff --git a/lib/widgets/shared/status_card.dart b/lib/widgets/shared/status_card.dart index 524a1d7..f702386 100644 --- a/lib/widgets/shared/status_card.dart +++ b/lib/widgets/shared/status_card.dart @@ -2,38 +2,40 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:fladder/theme.dart'; + class StatusCard extends ConsumerWidget { final Color? color; - final bool useFittedBox; final Widget child; - const StatusCard({this.color, this.useFittedBox = false, required this.child, super.key}); + const StatusCard({this.color, required this.child, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { return Padding( - padding: const EdgeInsets.all(2), - child: SizedBox.square( - dimension: 33, - child: Card( - elevation: 10, - surfaceTintColor: color, - shadowColor: color != null ? Colors.transparent : null, - child: IconTheme( - data: IconThemeData( - color: color, - ), - child: Center( - child: useFittedBox - ? FittedBox( - fit: BoxFit.scaleDown, - child: child, - ) - : child, - ), + padding: const EdgeInsets.all(2.5), + child: Container( + constraints: const BoxConstraints(minWidth: 32, minHeight: 32), + decoration: BoxDecoration( + color: blendColors( + Theme.of(context).colorScheme.surfaceContainer, + blend: color, ), + borderRadius: FladderTheme.smallShape.borderRadius, + ), + child: IconTheme( + data: IconThemeData( + color: color, + ), + child: child, ), ), ); } } + +Color blendColors(Color base, {Color? blend, double amount = 0.25}) { + if (blend == null) return base; + + return Color.lerp(base, blend, amount) ?? base; +}