Fladder/lib/screens/shared/media/components/next_up_episode.dart
2025-07-27 10:54:29 +02:00

106 lines
4.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:fladder/models/items/episode_model.dart';
import 'package:fladder/screens/details_screens/components/media_stream_information.dart';
import 'package:fladder/screens/shared/media/episode_posters.dart';
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
import 'package:fladder/util/localization_helper.dart';
import 'package:fladder/util/sticky_header_text.dart';
import 'package:fladder/util/string_extensions.dart';
class NextUpEpisode extends ConsumerWidget {
final EpisodeModel nextEpisode;
final Function(EpisodeModel episode)? onChanged;
const NextUpEpisode({required this.nextEpisode, this.onChanged, super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final alreadyPlayed = nextEpisode.userData.played;
final episodeSummary = nextEpisode.overview.summary.maxLength(limitTo: 250);
return Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StickyHeaderText(
label: alreadyPlayed ? context.localized.reWatch : context.localized.nextUp,
),
Opacity(
opacity: 0.75,
child: SelectableText(
nextEpisode.seasonEpisodeLabelFull(context),
style: Theme.of(context).textTheme.titleMedium,
),
),
SelectableText(
nextEpisode.name,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 16),
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 550) {
return Column(
children: [
EpisodePoster(
episode: nextEpisode,
showLabel: false,
onTap: () => nextEpisode.navigateTo(context),
actions: const [],
isCurrentEpisode: false,
),
const SizedBox(height: 16),
if (nextEpisode.overview.summary.isNotEmpty)
HtmlWidget(
episodeSummary,
textStyle: Theme.of(context).textTheme.titleMedium,
),
],
);
} else {
return Row(
children: [
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: AdaptiveLayout.poster(context).gridRatio,
maxWidth: MediaQuery.of(context).size.width / 2),
child: EpisodePoster(
episode: nextEpisode,
showLabel: false,
onTap: () => nextEpisode.navigateTo(context),
actions: const [],
isCurrentEpisode: false,
),
),
const SizedBox(width: 32),
Flexible(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MediaStreamInformation(
mediaStream: nextEpisode.mediaStreams,
onVersionIndexChanged: (index) => onChanged?.call(nextEpisode.copyWith(
mediaStreams: nextEpisode.mediaStreams.copyWith(versionStreamIndex: index),
)),
onAudioIndexChanged: (index) => onChanged?.call(nextEpisode.copyWith(
mediaStreams: nextEpisode.mediaStreams.copyWith(defaultAudioStreamIndex: index))),
onSubIndexChanged: (index) => onChanged?.call(nextEpisode.copyWith(
mediaStreams: nextEpisode.mediaStreams.copyWith(defaultSubStreamIndex: index))),
),
if (nextEpisode.overview.summary.isNotEmpty)
HtmlWidget(episodeSummary, textStyle: Theme.of(context).textTheme.titleMedium),
],
),
),
],
);
}
},
),
],
);
}
}