mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-08 23:18:16 -07:00
feat: Android TV support (#503)
Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
parent
7ab8c015b9
commit
c299492d6d
168 changed files with 12019 additions and 3073 deletions
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
||||
|
|
@ -23,6 +24,7 @@ import 'package:fladder/screens/shared/media/poster_row.dart';
|
|||
import 'package:fladder/screens/shared/nested_scaffold.dart';
|
||||
import 'package:fladder/screens/shared/nested_sliver_appbar.dart';
|
||||
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
|
||||
import 'package:fladder/util/focus_provider.dart';
|
||||
import 'package:fladder/util/list_padding.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/util/sliver_list_padding.dart';
|
||||
|
|
@ -45,6 +47,10 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
|||
late final Timer _timer;
|
||||
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey = GlobalKey<RefreshIndicatorState>();
|
||||
|
||||
final textController = TextEditingController();
|
||||
|
||||
ItemBaseModel? selectedPoster;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
|
@ -70,6 +76,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final padding = AdaptiveLayout.adaptivePadding(context);
|
||||
final bannerType = ref.watch(homeSettingsProvider.select((value) => value.homeBanner));
|
||||
|
||||
final dashboardData = ref.watch(dashboardProvider);
|
||||
final views = ref.watch(viewsProvider);
|
||||
|
|
@ -87,10 +94,15 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
|||
HomeCarouselSettings.cont => allResume,
|
||||
};
|
||||
|
||||
final viewSize = AdaptiveLayout.viewSizeOf(context);
|
||||
|
||||
return MediaQuery.removeViewInsets(
|
||||
context: context,
|
||||
child: NestedScaffold(
|
||||
background: BackgroundImage(items: [...homeCarouselItems, ...dashboardData.nextUp, ...allResume]),
|
||||
background: BackgroundImage(
|
||||
items: selectedPoster != null
|
||||
? [selectedPoster!]
|
||||
: [...homeCarouselItems, ...dashboardData.nextUp, ...allResume]),
|
||||
body: PullToRefresh(
|
||||
refreshKey: _refreshIndicatorKey,
|
||||
displacement: 80 + MediaQuery.of(context).viewPadding.top,
|
||||
|
|
@ -101,8 +113,8 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
|||
controller: AdaptiveLayout.scrollOf(context, HomeTabs.dashboard),
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
slivers: [
|
||||
const DefaultSliverTopBadding(),
|
||||
if (AdaptiveLayout.viewSizeOf(context) == ViewSize.phone)
|
||||
if (bannerType != HomeBanner.detailedBanner) const DefaultSliverTopBadding(),
|
||||
if (viewSize == ViewSize.phone)
|
||||
NestedSliverAppBar(
|
||||
route: LibrarySearchRoute(),
|
||||
parent: context,
|
||||
|
|
@ -114,7 +126,16 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
|||
context,
|
||||
horizontalPadding: 0,
|
||||
),
|
||||
child: HomeBannerWidget(posters: homeCarouselItems),
|
||||
child: HomeBannerWidget(
|
||||
posters: homeCarouselItems,
|
||||
onSelect: (selected) {
|
||||
// if (selectedPoster != selected) {
|
||||
// setState(() {
|
||||
// selectedPoster = selected;
|
||||
// });
|
||||
// }
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
|
|
@ -130,80 +151,84 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
|||
...[
|
||||
if (resumeVideo.isNotEmpty &&
|
||||
(homeSettings.nextUp == HomeNextUp.cont || homeSettings.nextUp == HomeNextUp.separate))
|
||||
SliverToBoxAdapter(
|
||||
child: PosterRow(
|
||||
contentPadding: padding,
|
||||
label: context.localized.dashboardContinueWatching,
|
||||
posters: resumeVideo,
|
||||
),
|
||||
PosterRow(
|
||||
contentPadding: padding,
|
||||
label: context.localized.dashboardContinueWatching,
|
||||
posters: resumeVideo,
|
||||
),
|
||||
if (resumeAudio.isNotEmpty &&
|
||||
(homeSettings.nextUp == HomeNextUp.cont || homeSettings.nextUp == HomeNextUp.separate))
|
||||
SliverToBoxAdapter(
|
||||
child: PosterRow(
|
||||
contentPadding: padding,
|
||||
label: context.localized.dashboardContinueListening,
|
||||
posters: resumeAudio,
|
||||
),
|
||||
PosterRow(
|
||||
contentPadding: padding,
|
||||
label: context.localized.dashboardContinueListening,
|
||||
posters: resumeAudio,
|
||||
),
|
||||
if (resumeBooks.isNotEmpty &&
|
||||
(homeSettings.nextUp == HomeNextUp.cont || homeSettings.nextUp == HomeNextUp.separate))
|
||||
SliverToBoxAdapter(
|
||||
child: PosterRow(
|
||||
contentPadding: padding,
|
||||
label: context.localized.dashboardContinueReading,
|
||||
posters: resumeBooks,
|
||||
),
|
||||
PosterRow(
|
||||
contentPadding: padding,
|
||||
label: context.localized.dashboardContinueReading,
|
||||
posters: resumeBooks,
|
||||
),
|
||||
if (dashboardData.nextUp.isNotEmpty &&
|
||||
(homeSettings.nextUp == HomeNextUp.nextUp || homeSettings.nextUp == HomeNextUp.separate))
|
||||
SliverToBoxAdapter(
|
||||
child: PosterRow(
|
||||
contentPadding: padding,
|
||||
label: context.localized.nextUp,
|
||||
posters: dashboardData.nextUp,
|
||||
),
|
||||
PosterRow(
|
||||
contentPadding: padding,
|
||||
label: context.localized.nextUp,
|
||||
posters: dashboardData.nextUp,
|
||||
),
|
||||
if ([...allResume, ...dashboardData.nextUp].isNotEmpty && homeSettings.nextUp == HomeNextUp.combined)
|
||||
SliverToBoxAdapter(
|
||||
child: PosterRow(
|
||||
contentPadding: padding,
|
||||
label: context.localized.dashboardContinue,
|
||||
posters: [...allResume, ...dashboardData.nextUp],
|
||||
PosterRow(
|
||||
contentPadding: padding,
|
||||
label: context.localized.dashboardContinue,
|
||||
posters: [...allResume, ...dashboardData.nextUp],
|
||||
),
|
||||
...views.dashboardViews.where((element) => element.recentlyAdded.isNotEmpty).map(
|
||||
(view) => PosterRow(
|
||||
contentPadding: padding,
|
||||
label: context.localized.dashboardRecentlyAdded(view.name),
|
||||
collectionAspectRatio: view.collectionType.aspectRatio,
|
||||
onLabelClick: () => context.router.push(
|
||||
LibrarySearchRoute(
|
||||
viewModelId: view.id,
|
||||
types: switch (view.collectionType) {
|
||||
CollectionType.tvshows => {
|
||||
FladderItemType.episode: true,
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
sortingOptions: switch (view.collectionType) {
|
||||
CollectionType.books ||
|
||||
CollectionType.boxsets ||
|
||||
CollectionType.folders ||
|
||||
CollectionType.music =>
|
||||
SortingOptions.dateLastContentAdded,
|
||||
_ => SortingOptions.dateAdded,
|
||||
},
|
||||
sortOrder: SortingOrder.descending,
|
||||
recursive: true,
|
||||
),
|
||||
),
|
||||
posters: view.recentlyAdded,
|
||||
),
|
||||
),
|
||||
]
|
||||
.nonNulls
|
||||
.toList()
|
||||
.mapIndexed(
|
||||
(index, child) => SliverToBoxAdapter(
|
||||
child: FocusProvider(
|
||||
autoFocus: bannerType != HomeBanner.detailedBanner ? index == 0 : false,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList()
|
||||
.addInBetween(
|
||||
const SliverToBoxAdapter(
|
||||
child: SizedBox(height: 16),
|
||||
),
|
||||
),
|
||||
...views.dashboardViews
|
||||
.where((element) => element.recentlyAdded.isNotEmpty)
|
||||
.map((view) => SliverToBoxAdapter(
|
||||
child: PosterRow(
|
||||
contentPadding: padding,
|
||||
label: context.localized.dashboardRecentlyAdded(view.name),
|
||||
collectionAspectRatio: view.collectionType.aspectRatio,
|
||||
onLabelClick: () => context.router.push(
|
||||
LibrarySearchRoute(
|
||||
viewModelId: view.id,
|
||||
types: switch (view.collectionType) {
|
||||
CollectionType.tvshows => {
|
||||
FladderItemType.episode: true,
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
sortingOptions: switch (view.collectionType) {
|
||||
CollectionType.books ||
|
||||
CollectionType.boxsets ||
|
||||
CollectionType.folders ||
|
||||
CollectionType.music =>
|
||||
SortingOptions.dateLastContentAdded,
|
||||
_ => SortingOptions.dateAdded,
|
||||
},
|
||||
sortOrder: SortingOrder.descending,
|
||||
recursive: true,
|
||||
),
|
||||
),
|
||||
posters: view.recentlyAdded,
|
||||
),
|
||||
)),
|
||||
].nonNulls.toList().addInBetween(const SliverToBoxAdapter(child: SizedBox(height: 16))),
|
||||
const DefautlSliverBottomPadding(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -5,17 +5,26 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/settings/home_settings_model.dart';
|
||||
import 'package:fladder/providers/settings/home_settings_provider.dart';
|
||||
import 'package:fladder/screens/shared/animated_fade_size.dart';
|
||||
import 'package:fladder/screens/shared/media/carousel_banner.dart';
|
||||
import 'package:fladder/screens/shared/media/detailed_banner.dart';
|
||||
import 'package:fladder/screens/shared/media/media_banner.dart';
|
||||
|
||||
class HomeBannerWidget extends ConsumerWidget {
|
||||
final List<ItemBaseModel> posters;
|
||||
const HomeBannerWidget({required this.posters, super.key});
|
||||
final Function(ItemBaseModel selected) onSelect;
|
||||
|
||||
const HomeBannerWidget({
|
||||
required this.posters,
|
||||
required this.onSelect,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final bannerType = ref.watch(homeSettingsProvider.select((value) => value.homeBanner));
|
||||
final maxHeight = (MediaQuery.sizeOf(context).shortestSide * 0.6).clamp(125.0, 375.0);
|
||||
|
||||
return switch (bannerType) {
|
||||
HomeBanner.carousel => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
|
@ -34,6 +43,12 @@ class HomeBannerWidget extends ConsumerWidget {
|
|||
maxHeight: maxHeight,
|
||||
),
|
||||
),
|
||||
HomeBanner.detailedBanner => AnimatedFadeSize(
|
||||
child: DetailedBanner(
|
||||
posters: posters,
|
||||
onSelect: onSelect,
|
||||
),
|
||||
),
|
||||
_ => const SizedBox.shrink(),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue