Files
sm-emby-share/lib/features/detail/widgets/additional_parts_section.dart
2026-07-14 11:11:36 +08:00

142 lines
4.4 KiB
Dart

import 'dart:io' show Platform;
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../../core/contracts/library.dart';
import '../../../providers/di_providers.dart';
import '../../../providers/emby_headers_provider.dart';
import '../../../providers/session_provider.dart';
import '../../../shared/mappers/media_image_url.dart';
import '../../../shared/theme/app_theme.dart';
import '../../../shared/widgets/hover_scrollable_row.dart';
import '../../../shared/widgets/media_poster_card.dart';
import 'section_container.dart';
typedef AdditionalPartTap = void Function(EmbyRawItem item);
class AdditionalPartsSection extends ConsumerWidget {
final String itemId;
final bool embedded;
final String? serverId;
final AdditionalPartTap? onItemTap;
final Widget? leading;
const AdditionalPartsSection({
super.key,
required this.itemId,
this.embedded = false,
this.serverId,
this.onItemTap,
this.leading,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final scopedServerId = serverId;
final AsyncValue<List<EmbyRawItem>> asyncItems;
final String? baseUrl;
final String? token;
final Map<String, String>? imageHeaders;
if (scopedServerId != null) {
asyncItems = ref.watch(
tmdbServerScopedAdditionalPartsProvider((
serverId: scopedServerId,
itemId: itemId,
)),
);
final auth = ref
.watch(tmdbServerScopedAuthProvider(scopedServerId))
.value;
baseUrl = auth?.baseUrl;
token = auth?.token;
imageHeaders = auth?.imageHeaders;
} else {
final session = ref.watch(activeSessionProvider);
if (session == null) return const SizedBox.shrink();
asyncItems = ref.watch(additionalPartsDataProvider(itemId));
baseUrl = session.serverUrl;
token = session.token;
imageHeaders = ref.watch(embyImageHeadersProvider).value;
}
return asyncItems.when(
loading: () => const SizedBox.shrink(),
error: (error, stackTrace) => const SizedBox.shrink(),
data: (items) {
if (items.isEmpty || baseUrl == null || token == null) {
return const SizedBox.shrink();
}
final hPad = SectionContainer.hPadOf(context);
final resolvedBaseUrl = baseUrl;
final resolvedToken = token;
final isAndroid = Platform.isAndroid;
return SectionContainer(
title: '附加片段',
embedded: embedded,
leadingDivider: embedded,
leading: leading,
child: SizedBox(
height: isAndroid ? 210 : 300,
child: Theme(
data: AppTheme.dark(),
child: HoverScrollableRow(
builder: (_, controller) => ListView.builder(
controller: controller,
scrollDirection: Axis.horizontal,
padding: EdgeInsets.symmetric(horizontal: hPad),
itemCount: items.length,
itemBuilder: (_, i) {
final item = items[i];
final imageUrl = EmbyImageUrl.primary(
baseUrl: resolvedBaseUrl,
token: resolvedToken,
item: item,
maxHeight: 480,
);
return Padding(
padding: EdgeInsets.only(
right: i < items.length - 1 ? 12 : 0,
),
child: MediaPosterCard(
item: item,
imageUrl: imageUrl,
width: isAndroid ? 105 : 160,
showHoverPlayIcon: false,
enableContextMenu: false,
imageHeaders: imageHeaders,
onTap: () {
final tap = onItemTap;
if (tap != null) {
tap(item);
} else {
context.pushReplacement(
'/media/${item.Id}',
extra: item,
);
}
},
),
);
},
),
),
),
),
);
},
);
}
}