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/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)

View file

@ -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;

View file

@ -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: '',

View file

@ -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;

View file

@ -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(

View file

@ -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(

View file

@ -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(

View file

@ -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,

View file

@ -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(

View file

@ -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,
),
),
),
)

View file

@ -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);

View file

@ -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;
}