chore: Clean-up for status cards

This commit is contained in:
PartyDonut 2025-10-26 19:18:26 +01:00
parent c9ce5b9b90
commit ed5598fc66
12 changed files with 69 additions and 81 deletions

View file

@ -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/jellyfin/jellyfin_open_api.swagger.dart';
import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/images_models.dart'; import 'package:fladder/models/items/images_models.dart';
import 'package:fladder/models/items/item_shared_models.dart'; import 'package:fladder/models/items/item_shared_models.dart';
import 'package:fladder/models/items/overview_model.dart'; import 'package:fladder/models/items/overview_model.dart';
import 'package:fladder/util/localization_helper.dart'; import 'package:fladder/util/localization_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class BookModel extends ItemBaseModel { class BookModel extends ItemBaseModel {
final String? parentName; final String? parentName;
@ -46,6 +48,9 @@ class BookModel extends ItemBaseModel {
@override @override
double get progress => userData.progress != 0 ? 100 : 0; double get progress => userData.progress != 0 ? 100 : 0;
@override
String? unplayedLabel(BuildContext context) => userData.progress != 0 ? context.localized.page(currentPage) : null;
@override @override
String playButtonLabel(BuildContext context) => progress != 0 String playButtonLabel(BuildContext context) => progress != 0
? context.localized.continuePage(currentPage) ? context.localized.continuePage(currentPage)

View file

@ -113,6 +113,8 @@ class ItemBaseModel with ItemBaseModelMappable {
bool get watched => userData.played; bool get watched => userData.played;
String? unplayedLabel(BuildContext context) => null;
String? detailedName(BuildContext context) => "$name${overview.yearAired != null ? " (${overview.yearAired})" : ""}"; String? detailedName(BuildContext context) => "$name${overview.yearAired != null ? " (${overview.yearAired})" : ""}";
String? get subText => null; String? get subText => null;

View file

@ -82,6 +82,9 @@ class SeasonModel extends ItemBaseModel with SeasonModelMappable {
String localizedName(BuildContext context) => name.replaceFirst("Season", context.localized.season(1)); String localizedName(BuildContext context) => name.replaceFirst("Season", context.localized.season(1));
@override
String? unplayedLabel(BuildContext context) => userData.played ? null : userData.unPlayedItemCount?.toString();
@override @override
SeriesModel get parentBaseModel => SeriesModel( SeriesModel get parentBaseModel => SeriesModel(
originalTitle: '', originalTitle: '',

View file

@ -74,6 +74,9 @@ class SeriesModel extends ItemBaseModel with SeriesModelMappable {
return availableEpisodes?.map((e) => e).toList() ?? []; return availableEpisodes?.map((e) => e).toList() ?? [];
} }
@override
String? unplayedLabel(BuildContext context) => userData.played ? null : userData.unPlayedItemCount?.toString();
@override @override
bool get syncAble => true; bool get syncAble => true;

View file

@ -65,10 +65,7 @@ class ChapterRow extends ConsumerWidget {
borderRadius: FladderTheme.smallShape.borderRadius, borderRadius: FladderTheme.smallShape.borderRadius,
color: Theme.of(context).colorScheme.surfaceContainer, color: Theme.of(context).colorScheme.surfaceContainer,
), ),
foregroundDecoration: BoxDecoration( foregroundDecoration: FladderTheme.defaultPosterDecoration,
borderRadius: FladderTheme.smallShape.borderRadius,
border: Border.all(width: 2, color: Colors.white.withAlpha(25)),
),
child: AspectRatio( child: AspectRatio(
aspectRatio: 1.75, aspectRatio: 1.75,
child: CachedNetworkImage( child: CachedNetworkImage(

View file

@ -4,11 +4,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:iconsax_plus/iconsax_plus.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/item_base_model.dart';
import 'package:fladder/models/items/item_shared_models.dart'; import 'package:fladder/models/items/item_shared_models.dart';
import 'package:fladder/models/items/photos_model.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/screens/shared/media/components/poster_placeholder.dart';
import 'package:fladder/theme.dart'; import 'package:fladder/theme.dart';
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart'; import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
@ -84,7 +82,7 @@ class PosterImage extends ConsumerWidget {
), ),
foregroundDecoration: BoxDecoration( foregroundDecoration: BoxDecoration(
borderRadius: radius, borderRadius: radius,
border: Border.all(width: 2, color: Colors.white.withAlpha(25)), border: Border.all(width: 1, color: Colors.white.withAlpha(45)),
), ),
child: FladderImage( child: FladderImage(
image: primaryPosters image: primaryPosters
@ -94,26 +92,6 @@ class PosterImage extends ConsumerWidget {
), ),
), ),
overlays: [ 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) if (selected == true)
Container( Container(
decoration: BoxDecoration( 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( IgnorePointer(
child: Align( child: Align(
alignment: Alignment.topRight, alignment: Alignment.topRight,
child: StatusCard( child: StatusCard(
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primaryContainer,
useFittedBox: poster.unPlayedItemCount != 0,
child: Padding( child: Padding(
padding: const EdgeInsets.all(6), padding: const EdgeInsets.all(6),
child: poster.unPlayedItemCount != 0 child: poster.unplayedLabel(context) != null
? Container( ? Text(
constraints: const BoxConstraints(minWidth: 16), poster.unplayedLabel(context) ?? "",
child: Text( textAlign: TextAlign.center,
poster.userData.unPlayedItemCount.toString(), style: TextStyle(
textAlign: TextAlign.center, color: Theme.of(context).colorScheme.primary,
style: TextStyle( fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary, overflow: TextOverflow.visible,
fontWeight: FontWeight.bold, fontSize: 14,
overflow: TextOverflow.visible,
fontSize: 14,
),
), ),
) )
: Icon( : Icon(

View file

@ -13,7 +13,7 @@ class PosterPlaceholder extends StatelessWidget {
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
Align( Align(
alignment: Alignment.topRight, alignment: Alignment.topLeft,
child: Padding( child: Padding(
padding: const EdgeInsets.all(12.0), padding: const EdgeInsets.all(12.0),
child: Icon( child: Icon(

View file

@ -183,10 +183,7 @@ class EpisodePoster extends ConsumerWidget {
borderRadius: FladderTheme.smallShape.borderRadius, borderRadius: FladderTheme.smallShape.borderRadius,
color: Theme.of(context).colorScheme.surfaceContainer, color: Theme.of(context).colorScheme.surfaceContainer,
), ),
foregroundDecoration: BoxDecoration( foregroundDecoration: FladderTheme.defaultPosterDecoration,
borderRadius: FladderTheme.smallShape.borderRadius,
border: Border.all(width: 2, color: Colors.white.withAlpha(25)),
),
child: FladderImage( child: FladderImage(
image: !episodeAvailable ? episode.parentImages?.primary : episode.images?.primary, image: !episodeAvailable ? episode.parentImages?.primary : episode.images?.primary,
placeHolder: placeHolder, placeHolder: placeHolder,

View file

@ -61,10 +61,7 @@ class PeopleRow extends ConsumerWidget {
borderRadius: FladderTheme.smallShape.borderRadius, borderRadius: FladderTheme.smallShape.borderRadius,
color: Theme.of(context).colorScheme.surfaceContainer, color: Theme.of(context).colorScheme.surfaceContainer,
), ),
foregroundDecoration: BoxDecoration( foregroundDecoration: FladderTheme.defaultPosterDecoration,
borderRadius: FladderTheme.smallShape.borderRadius,
border: Border.all(width: 2, color: Colors.white.withAlpha(25)),
),
child: FocusButton( child: FocusButton(
onTap: () => Navigator.of(context).push( onTap: () => Navigator.of(context).push(
MaterialPageRoute( MaterialPageRoute(

View file

@ -89,10 +89,7 @@ class SeasonPoster extends ConsumerWidget {
borderRadius: FladderTheme.smallShape.borderRadius, borderRadius: FladderTheme.smallShape.borderRadius,
color: Theme.of(context).colorScheme.surfaceContainer, color: Theme.of(context).colorScheme.surfaceContainer,
), ),
foregroundDecoration: BoxDecoration( foregroundDecoration: FladderTheme.defaultPosterDecoration,
borderRadius: FladderTheme.smallShape.borderRadius,
border: Border.all(width: 2, color: Colors.white.withAlpha(25)),
),
child: FladderImage( child: FladderImage(
image: season.getPosters?.primary ?? image: season.getPosters?.primary ??
season.parentImages?.backDrop?.firstOrNull ?? season.parentImages?.backDrop?.firstOrNull ??
@ -153,11 +150,17 @@ class SeasonPoster extends ConsumerWidget {
if (season.userData.unPlayedItemCount != 0) if (season.userData.unPlayedItemCount != 0)
StatusCard( StatusCard(
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
useFittedBox: true, child: Padding(
child: Center( padding: const EdgeInsets.all(6),
child: Text( child: Text(
season.userData.unPlayedItemCount.toString(), 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,
),
), ),
), ),
) )

View file

@ -34,6 +34,11 @@ class FladderTheme {
static RoundedRectangleBorder get defaultShape => RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)); static RoundedRectangleBorder get defaultShape => RoundedRectangleBorder(borderRadius: BorderRadius.circular(16));
static RoundedRectangleBorder get largeShape => RoundedRectangleBorder(borderRadius: BorderRadius.circular(32)); 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) { static ThemeData theme(ColorScheme? colorScheme, DynamicSchemeVariant dynamicSchemeVariant) {
final ColorScheme? scheme = generateDynamicColourSchemes(colorScheme, dynamicSchemeVariant); final ColorScheme? scheme = generateDynamicColourSchemes(colorScheme, dynamicSchemeVariant);

View file

@ -2,38 +2,40 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fladder/theme.dart';
class StatusCard extends ConsumerWidget { class StatusCard extends ConsumerWidget {
final Color? color; final Color? color;
final bool useFittedBox;
final Widget child; 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 @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
return Padding( return Padding(
padding: const EdgeInsets.all(2), padding: const EdgeInsets.all(2.5),
child: SizedBox.square( child: Container(
dimension: 33, constraints: const BoxConstraints(minWidth: 32, minHeight: 32),
child: Card( decoration: BoxDecoration(
elevation: 10, color: blendColors(
surfaceTintColor: color, Theme.of(context).colorScheme.surfaceContainer,
shadowColor: color != null ? Colors.transparent : null, blend: color,
child: IconTheme(
data: IconThemeData(
color: color,
),
child: Center(
child: useFittedBox
? FittedBox(
fit: BoxFit.scaleDown,
child: child,
)
: child,
),
), ),
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;
}