mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-07 21:48:14 -08:00
[Fixed] Use browser navigation
This commit is contained in:
parent
69b3a77d17
commit
d19fa0ccd0
22 changed files with 210 additions and 156 deletions
|
|
@ -1,31 +1,32 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:chopper/chopper.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:page_transition/page_transition.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||
import 'package:fladder/models/collection_types.dart';
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/folder_model.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
import 'package:fladder/models/items/photos_model.dart';
|
||||
import 'package:fladder/models/library_search/library_search_model.dart';
|
||||
import 'package:fladder/models/library_search/library_search_options.dart';
|
||||
import 'package:fladder/models/playlist_model.dart';
|
||||
import 'package:fladder/models/view_model.dart';
|
||||
import 'package:fladder/providers/api_provider.dart';
|
||||
import 'package:fladder/providers/service_provider.dart';
|
||||
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/screens/photo_viewer/photo_viewer_screen.dart';
|
||||
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
||||
import 'package:fladder/util/item_base_model/play_item_helpers.dart';
|
||||
import 'package:fladder/util/list_extensions.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
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/library_search/library_search_model.dart';
|
||||
import 'package:fladder/models/view_model.dart';
|
||||
import 'package:fladder/providers/api_provider.dart';
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/util/map_bool_helper.dart';
|
||||
import 'package:page_transition/page_transition.dart';
|
||||
|
||||
final librarySearchProvider =
|
||||
StateNotifierProvider.family.autoDispose<LibrarySearchNotifier, LibrarySearchModel, Key>((ref, id) {
|
||||
|
|
@ -652,6 +653,10 @@ class LibrarySearchNotifier extends StateNotifier<LibrarySearchModel> {
|
|||
items.firstOrNull?.navigateTo(context);
|
||||
}
|
||||
}
|
||||
|
||||
void updateEverything() {
|
||||
state = state.copyWith();
|
||||
}
|
||||
}
|
||||
|
||||
extension SimpleSorter on List<ItemBaseModel> {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ import 'package:fladder/util/adaptive_layout.dart';
|
|||
|
||||
@AutoRouterConfig(replaceInRouteName: 'Screen|Page,Route')
|
||||
class AutoRouter extends RootStackRouter {
|
||||
AutoRouter({required this.layout, required this.ref});
|
||||
AutoRouter({
|
||||
required this.layout,
|
||||
required this.ref,
|
||||
});
|
||||
|
||||
final WidgetRef ref;
|
||||
final ScreenLayout layout;
|
||||
|
|
@ -35,8 +38,8 @@ class AutoRouter extends RootStackRouter {
|
|||
_syncedRoute,
|
||||
],
|
||||
),
|
||||
AutoRoute(page: DetailsRoute.page, path: '/details'),
|
||||
AutoRoute(page: LibrarySearchRoute.page, path: '/library'),
|
||||
AutoRoute(page: DetailsRoute.page, path: '/details', usesPathAsKey: true),
|
||||
AutoRoute(page: LibrarySearchRoute.page, path: '/library', usesPathAsKey: true),
|
||||
AutoRoute(page: SettingsRoute.page, path: '/settings'),
|
||||
..._settingsChildren.map(
|
||||
(e) => e.copyWith(path: "/$e", initial: false),
|
||||
|
|
@ -49,8 +52,8 @@ class AutoRouter extends RootStackRouter {
|
|||
_dashboardRoute,
|
||||
_favouritesRoute,
|
||||
_syncedRoute,
|
||||
AutoRoute(page: DetailsRoute.page, path: 'details'),
|
||||
AutoRoute(page: LibrarySearchRoute.page, path: 'library'),
|
||||
AutoRoute(page: DetailsRoute.page, path: 'details', usesPathAsKey: true),
|
||||
AutoRoute(page: LibrarySearchRoute.page, path: 'library', usesPathAsKey: true),
|
||||
AutoRoute(
|
||||
page: SettingsRoute.page,
|
||||
path: 'settings',
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/providers/items/item_details_provider.dart';
|
||||
import 'package:fladder/routes/auto_router.gr.dart';
|
||||
import 'package:fladder/util/fladder_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
@RoutePage()
|
||||
class DetailsScreen extends ConsumerStatefulWidget {
|
||||
|
|
@ -22,9 +25,21 @@ class _DetailsScreenState extends ConsumerState<DetailsScreen> {
|
|||
child: CircularProgressIndicator.adaptive(strokeCap: StrokeCap.round),
|
||||
);
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant DetailsScreen oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (kIsWeb) {
|
||||
updateWidget();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
updateWidget();
|
||||
}
|
||||
|
||||
Future<void> updateWidget() async {
|
||||
Future.microtask(() async {
|
||||
if (widget.item != null) {
|
||||
setState(() {
|
||||
|
|
@ -48,6 +63,7 @@ class _DetailsScreenState extends ConsumerState<DetailsScreen> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
key: Key(widget.id),
|
||||
children: [
|
||||
Hero(
|
||||
tag: widget.id,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:ficonsax/ficonsax.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/models/book_model.dart';
|
||||
import 'package:fladder/providers/items/book_details_provider.dart';
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
|
|
@ -13,12 +17,11 @@ import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
|
|||
import 'package:fladder/util/item_base_model/play_item_helpers.dart';
|
||||
import 'package:fladder/util/list_padding.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/util/router_extension.dart';
|
||||
import 'package:fladder/util/widget_extensions.dart';
|
||||
import 'package:fladder/widgets/shared/item_actions.dart';
|
||||
import 'package:fladder/widgets/shared/modal_bottom_sheet.dart';
|
||||
import 'package:fladder/widgets/shared/selectable_icon_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class BookDetailScreen extends ConsumerStatefulWidget {
|
||||
final BookModel item;
|
||||
|
|
@ -29,7 +32,8 @@ class BookDetailScreen extends ConsumerStatefulWidget {
|
|||
}
|
||||
|
||||
class _BookDetailScreenState extends ConsumerState<BookDetailScreen> {
|
||||
late final provider = bookDetailsProvider(widget.item.id);
|
||||
AutoDisposeStateNotifierProvider<BookDetailsProviderNotifier, BookProviderModel> get provider =>
|
||||
bookDetailsProvider(widget.item.id);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -47,7 +51,7 @@ class _BookDetailScreenState extends ConsumerState<BookDetailScreen> {
|
|||
},
|
||||
onDeleteSuccesFully: (item) {
|
||||
if (context.mounted) {
|
||||
context.router.maybePop();
|
||||
context.router.popBack();
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,25 +1,26 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:ficonsax/ficonsax.dart';
|
||||
import 'package:fladder/screens/details_screens/components/overview_header.dart';
|
||||
import 'package:fladder/screens/shared/media/components/media_play_button.dart';
|
||||
import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
|
||||
import 'package:fladder/util/item_base_model/play_item_helpers.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:ficonsax/ficonsax.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/providers/items/episode_details_provider.dart';
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/screens/details_screens/components/media_stream_information.dart';
|
||||
import 'package:fladder/screens/details_screens/components/overview_header.dart';
|
||||
import 'package:fladder/screens/shared/detail_scaffold.dart';
|
||||
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
||||
import 'package:fladder/screens/shared/media/chapter_row.dart';
|
||||
import 'package:fladder/screens/shared/media/components/media_header.dart';
|
||||
import 'package:fladder/screens/shared/media/components/media_play_button.dart';
|
||||
import 'package:fladder/screens/shared/media/episode_posters.dart';
|
||||
import 'package:fladder/screens/shared/media/expanding_overview.dart';
|
||||
import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
|
||||
import 'package:fladder/util/item_base_model/play_item_helpers.dart';
|
||||
import 'package:fladder/util/list_padding.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/util/router_extension.dart';
|
||||
import 'package:fladder/util/widget_extensions.dart';
|
||||
import 'package:fladder/widgets/shared/selectable_icon_button.dart';
|
||||
|
||||
|
|
@ -32,7 +33,8 @@ class EpisodeDetailScreen extends ConsumerStatefulWidget {
|
|||
}
|
||||
|
||||
class _ItemDetailScreenState extends ConsumerState<EpisodeDetailScreen> {
|
||||
late final providerInstance = episodeDetailsProvider(widget.item.id);
|
||||
AutoDisposeStateNotifierProvider<EpisodeDetailsProvider, EpisodeDetailModel> get providerInstance =>
|
||||
episodeDetailsProvider(widget.item.id);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -52,7 +54,7 @@ class _ItemDetailScreenState extends ConsumerState<EpisodeDetailScreen> {
|
|||
},
|
||||
onDeleteSuccesFully: (item) {
|
||||
if (context.mounted) {
|
||||
context.router.maybePop();
|
||||
context.router.popBack();
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,25 +1,27 @@
|
|||
import 'package:fladder/util/router_extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:ficonsax/ficonsax.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/providers/items/movies_details_provider.dart';
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/screens/details_screens/components/overview_header.dart';
|
||||
import 'package:fladder/screens/details_screens/components/media_stream_information.dart';
|
||||
import 'package:fladder/screens/shared/media/components/media_header.dart';
|
||||
import 'package:fladder/screens/details_screens/components/overview_header.dart';
|
||||
import 'package:fladder/screens/shared/detail_scaffold.dart';
|
||||
import 'package:fladder/screens/shared/media/chapter_row.dart';
|
||||
import 'package:fladder/screens/shared/media/components/media_header.dart';
|
||||
import 'package:fladder/screens/shared/media/components/media_play_button.dart';
|
||||
import 'package:fladder/screens/shared/media/expanding_overview.dart';
|
||||
import 'package:fladder/screens/shared/media/people_row.dart';
|
||||
import 'package:fladder/screens/shared/media/poster_row.dart';
|
||||
import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
|
||||
import 'package:fladder/util/item_base_model/play_item_helpers.dart';
|
||||
|
||||
import 'package:fladder/util/list_padding.dart';
|
||||
import 'package:fladder/util/widget_extensions.dart';
|
||||
import 'package:fladder/widgets/shared/selectable_icon_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class MovieDetailScreen extends ConsumerStatefulWidget {
|
||||
final ItemBaseModel item;
|
||||
|
|
@ -30,7 +32,7 @@ class MovieDetailScreen extends ConsumerStatefulWidget {
|
|||
}
|
||||
|
||||
class _ItemDetailScreenState extends ConsumerState<MovieDetailScreen> {
|
||||
late final providerInstance = movieDetailsProvider(widget.item.id);
|
||||
MovieDetailsProvider get providerInstance => movieDetailsProvider(widget.item.id);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -49,7 +51,7 @@ class _ItemDetailScreenState extends ConsumerState<MovieDetailScreen> {
|
|||
},
|
||||
onDeleteSuccesFully: (item) {
|
||||
if (context.mounted) {
|
||||
context.router.maybePop();
|
||||
context.router.popBack();
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:ficonsax/ficonsax.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/season_model.dart';
|
||||
import 'package:fladder/providers/items/season_details_provider.dart';
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/screens/details_screens/components/overview_header.dart';
|
||||
|
|
@ -16,8 +21,6 @@ import 'package:fladder/util/localization_helper.dart';
|
|||
import 'package:fladder/util/string_extensions.dart';
|
||||
import 'package:fladder/util/widget_extensions.dart';
|
||||
import 'package:fladder/widgets/shared/selectable_icon_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class SeasonDetailScreen extends ConsumerStatefulWidget {
|
||||
final ItemBaseModel item;
|
||||
|
|
@ -29,7 +32,8 @@ class SeasonDetailScreen extends ConsumerStatefulWidget {
|
|||
|
||||
class _SeasonDetailScreenState extends ConsumerState<SeasonDetailScreen> {
|
||||
Set<EpisodeDetailsViewType> viewOptions = {EpisodeDetailsViewType.grid};
|
||||
late final providerId = seasonDetailsProvider(widget.item.id);
|
||||
AutoDisposeStateNotifierProvider<SeasonDetailsNotifier, SeasonModel?> get providerId =>
|
||||
seasonDetailsProvider(widget.item.id);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
|
|||
|
|
@ -1,26 +1,28 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:ficonsax/ficonsax.dart';
|
||||
import 'package:fladder/screens/details_screens/components/overview_header.dart';
|
||||
import 'package:fladder/screens/shared/media/components/media_play_button.dart';
|
||||
import 'package:fladder/screens/shared/media/components/next_up_episode.dart';
|
||||
import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
|
||||
import 'package:fladder/util/item_base_model/play_item_helpers.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:ficonsax/ficonsax.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/series_model.dart';
|
||||
import 'package:fladder/providers/items/series_details_provider.dart';
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/screens/details_screens/components/overview_header.dart';
|
||||
import 'package:fladder/screens/shared/detail_scaffold.dart';
|
||||
import 'package:fladder/screens/shared/media/components/media_header.dart';
|
||||
import 'package:fladder/screens/shared/media/components/media_play_button.dart';
|
||||
import 'package:fladder/screens/shared/media/components/next_up_episode.dart';
|
||||
import 'package:fladder/screens/shared/media/episode_posters.dart';
|
||||
import 'package:fladder/screens/shared/media/expanding_overview.dart';
|
||||
import 'package:fladder/screens/shared/media/people_row.dart';
|
||||
import 'package:fladder/screens/shared/media/poster_row.dart';
|
||||
import 'package:fladder/screens/shared/media/season_row.dart';
|
||||
import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
|
||||
import 'package:fladder/util/item_base_model/play_item_helpers.dart';
|
||||
import 'package:fladder/util/list_padding.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/util/router_extension.dart';
|
||||
import 'package:fladder/util/widget_extensions.dart';
|
||||
import 'package:fladder/widgets/shared/selectable_icon_button.dart';
|
||||
|
||||
|
|
@ -33,7 +35,8 @@ class SeriesDetailScreen extends ConsumerStatefulWidget {
|
|||
}
|
||||
|
||||
class _SeriesDetailScreenState extends ConsumerState<SeriesDetailScreen> {
|
||||
late final providerId = seriesDetailsProvider(widget.item.id);
|
||||
AutoDisposeStateNotifierProvider<SeriesDetailViewNotifier, SeriesModel?> get providerId =>
|
||||
seriesDetailsProvider(widget.item.id);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -51,7 +54,7 @@ class _SeriesDetailScreenState extends ConsumerState<SeriesDetailScreen> {
|
|||
},
|
||||
onDeleteSuccesFully: (item) {
|
||||
if (context.mounted) {
|
||||
context.router.maybePop();
|
||||
context.router.popBack();
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:ficonsax/ficonsax.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
|
@ -28,6 +32,7 @@ import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
|
|||
import 'package:fladder/util/list_padding.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/util/refresh_state.dart';
|
||||
import 'package:fladder/util/router_extension.dart';
|
||||
import 'package:fladder/util/sliver_list_padding.dart';
|
||||
import 'package:fladder/widgets/navigation_scaffold/components/floating_player_bar.dart';
|
||||
import 'package:fladder/widgets/navigation_scaffold/components/settings_user_icon.dart';
|
||||
|
|
@ -41,9 +46,6 @@ import 'package:fladder/widgets/shared/pull_to_refresh.dart';
|
|||
import 'package:fladder/widgets/shared/scroll_position.dart';
|
||||
import 'package:fladder/widgets/shared/shapes.dart';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
@RoutePage()
|
||||
class LibrarySearchScreen extends ConsumerStatefulWidget {
|
||||
final String? viewModelId;
|
||||
|
|
@ -67,9 +69,6 @@ class LibrarySearchScreen extends ConsumerStatefulWidget {
|
|||
}
|
||||
|
||||
class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
||||
late final Key uniqueKey = Key(widget.folderId?.join(',').toString() ?? widget.viewModelId ?? UniqueKey().toString());
|
||||
late final providerKey = librarySearchProvider(uniqueKey);
|
||||
late final libraryProvider = ref.read(providerKey.notifier);
|
||||
final SearchController searchController = SearchController();
|
||||
final Debouncer debouncer = Debouncer(const Duration(seconds: 1));
|
||||
final GlobalKey<RefreshIndicatorState> refreshKey = GlobalKey<RefreshIndicatorState>();
|
||||
|
|
@ -78,9 +77,26 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
|||
|
||||
bool loadOnStart = false;
|
||||
|
||||
Key get uniqueKey => Key(widget.folderId?.join(',').toString() ?? widget.viewModelId ?? "EmptySearch");
|
||||
AutoDisposeStateNotifierProvider<LibrarySearchNotifier, LibrarySearchModel> get providerKey =>
|
||||
librarySearchProvider(uniqueKey);
|
||||
LibrarySearchNotifier get libraryProvider => ref.read(librarySearchProvider(uniqueKey).notifier);
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant LibrarySearchScreen oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (kIsWeb && ref.read(librarySearchProvider(uniqueKey)).posters.isEmpty) {
|
||||
initLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initLibrary();
|
||||
}
|
||||
|
||||
void initLibrary() {
|
||||
searchController.addListener(() {
|
||||
debouncer.run(() {
|
||||
ref.read(providerKey.notifier).setSearch(searchController.text);
|
||||
|
|
@ -89,7 +105,9 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
|||
|
||||
Future.microtask(
|
||||
() async {
|
||||
if (libraryProvider.mounted) {
|
||||
libraryProvider.setDefaultOptions(widget.sortOrder, widget.sortingOptions);
|
||||
}
|
||||
await refreshKey.currentState?.show();
|
||||
SystemChrome.setEnabledSystemUIMode(
|
||||
SystemUiMode.edgeToEdge,
|
||||
|
|
@ -115,10 +133,8 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
|||
Widget build(BuildContext context) {
|
||||
final isEmptySearchScreen = widget.viewModelId == null && widget.favourites == null && widget.folderId == null;
|
||||
final librarySearchResults = ref.watch(providerKey);
|
||||
final libraryProvider = ref.read(providerKey.notifier);
|
||||
final postersList = librarySearchResults.posters.hideEmptyChildren(librarySearchResults.hideEmtpyShows);
|
||||
final playerState = ref.watch(mediaPlaybackProvider.select((value) => value.state));
|
||||
|
||||
final libraryViewType = ref.watch(libraryViewTypeProvider);
|
||||
|
||||
ref.listen(
|
||||
|
|
@ -132,6 +148,7 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
|||
);
|
||||
|
||||
return PopScope(
|
||||
key: uniqueKey,
|
||||
canPop: !librarySearchResults.selecteMode,
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
if (librarySearchResults.selecteMode) {
|
||||
|
|
@ -210,8 +227,12 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
|||
refreshKey: refreshKey,
|
||||
autoFocus: false,
|
||||
contextRefresh: false,
|
||||
onRefresh: () async =>
|
||||
libraryProvider.initRefresh(widget.folderId, widget.viewModelId, widget.favourites),
|
||||
onRefresh: () async {
|
||||
if (libraryProvider.mounted) {
|
||||
return libraryProvider.initRefresh(
|
||||
widget.folderId, widget.viewModelId, widget.favourites);
|
||||
}
|
||||
},
|
||||
refreshOnStart: false,
|
||||
child: CustomScrollView(
|
||||
physics: const AlwaysScrollableNoImplicitScrollPhysics(),
|
||||
|
|
@ -220,10 +241,11 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
|||
SliverAppBar(
|
||||
floating: !AdaptiveLayout.of(context).isDesktop,
|
||||
collapsedHeight: 80,
|
||||
automaticallyImplyLeading: true,
|
||||
automaticallyImplyLeading: false,
|
||||
pinned: AdaptiveLayout.of(context).isDesktop,
|
||||
primary: true,
|
||||
elevation: 5,
|
||||
leading: context.router.backButton(),
|
||||
surfaceTintColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:ficonsax/ficonsax.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
|
|
@ -12,8 +16,6 @@ import 'package:fladder/util/localization_helper.dart';
|
|||
import 'package:fladder/util/map_bool_helper.dart';
|
||||
import 'package:fladder/util/refresh_state.dart';
|
||||
import 'package:fladder/widgets/shared/scroll_position.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class LibraryFilterChips extends ConsumerWidget {
|
||||
final Key uniqueKey;
|
||||
|
|
@ -188,12 +190,12 @@ List<Widget> libraryFilterChips(
|
|||
if (librarySearchResults.types[FladderItemType.series] == true)
|
||||
FilterChip(
|
||||
avatar: Icon(
|
||||
librarySearchResults.hideEmtpyShows ? Icons.visibility_rounded : Icons.visibility_off_rounded,
|
||||
librarySearchResults.hideEmtpyShows ? Icons.visibility_off_rounded : Icons.visibility_rounded,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
selected: librarySearchResults.hideEmtpyShows,
|
||||
showCheckmark: false,
|
||||
label: Text(librarySearchResults.hideEmtpyShows ? context.localized.showEmpty : context.localized.hideEmpty),
|
||||
label: Text(context.localized.hideEmpty),
|
||||
onSelected: libraryProvider.setHideEmpty,
|
||||
),
|
||||
if (librarySearchResults.officialRatings.isNotEmpty)
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ class _LoginPageState extends ConsumerState<LoginScreen> {
|
|||
serverTextController.text = value;
|
||||
startAddingNewUser();
|
||||
});
|
||||
context.router.maybePop();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:ficonsax/ficonsax.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/providers/items/identify_provider.dart';
|
||||
import 'package:fladder/screens/shared/adaptive_dialog.dart';
|
||||
|
|
@ -9,8 +13,6 @@ import 'package:fladder/screens/shared/focused_outlined_text_field.dart';
|
|||
import 'package:fladder/screens/shared/media/external_urls.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/util/string_extensions.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
Future<void> showIdentifyScreen(BuildContext context, ItemBaseModel item) async {
|
||||
return showDialogAdaptive(
|
||||
|
|
@ -30,7 +32,7 @@ class IdentifyScreen extends ConsumerStatefulWidget {
|
|||
}
|
||||
|
||||
class _IdentifyScreenState extends ConsumerState<IdentifyScreen> with TickerProviderStateMixin {
|
||||
late AutoDisposeStateNotifierProvider<IdentifyNotifier, IdentifyModel> provider = identifyProvider(widget.item.id);
|
||||
AutoDisposeStateNotifierProvider<IdentifyNotifier, IdentifyModel> get provider => identifyProvider(widget.item.id);
|
||||
late final TabController tabController = TabController(length: 2, vsync: this);
|
||||
|
||||
TextEditingController? currentController;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:ficonsax/ficonsax.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/models/information_model.dart';
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/providers/items/information_provider.dart';
|
||||
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/widgets/shared/clickable_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
Future<void> showInfoScreen(BuildContext context, ItemBaseModel item) async {
|
||||
return showDialog(
|
||||
|
|
@ -27,7 +29,7 @@ class ItemInfoScreen extends ConsumerStatefulWidget {
|
|||
}
|
||||
|
||||
class ItemInfoScreenState extends ConsumerState<ItemInfoScreen> {
|
||||
late AutoDisposeStateNotifierProvider<InformationNotifier, InformationProviderModel> provider =
|
||||
AutoDisposeStateNotifierProvider<InformationNotifier, InformationProviderModel> get provider =>
|
||||
informationProvider(widget.item.id);
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -133,10 +133,10 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
|||
(context) async {
|
||||
await ref.read(syncProvider.notifier).clear();
|
||||
setState(() {});
|
||||
context.router.maybePop();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
context.localized.clear,
|
||||
(context) => context.router.maybePop(),
|
||||
(context) => Navigator.of(context).pop(),
|
||||
context.localized.cancel,
|
||||
);
|
||||
},
|
||||
|
|
@ -451,7 +451,7 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
FilledButton(
|
||||
onPressed: () => context.router.maybePop(),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(context.localized.cancel),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/screens/shared/user_icon.dart';
|
||||
import 'package:fladder/util/adaptive_layout.dart';
|
||||
import 'package:fladder/util/router_extension.dart';
|
||||
|
||||
class SettingsScaffold extends ConsumerWidget {
|
||||
final String label;
|
||||
|
|
@ -39,6 +41,7 @@ class SettingsScaffold extends ConsumerWidget {
|
|||
if (AdaptiveLayout.of(context).size == ScreenLayout.single)
|
||||
SliverAppBar.large(
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
leading: context.router.backButton(),
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
titlePadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16)
|
||||
.add(EdgeInsets.only(left: padding.left, right: padding.right)),
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import 'package:fladder/screens/shared/fladder_icon.dart';
|
|||
import 'package:fladder/util/adaptive_layout.dart';
|
||||
import 'package:fladder/util/application_info.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/util/router_extension.dart';
|
||||
import 'package:fladder/util/theme_extensions.dart';
|
||||
import 'package:fladder/widgets/shared/hide_on_scroll.dart';
|
||||
|
||||
|
|
@ -90,7 +91,7 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
|||
scrollController: scrollController,
|
||||
showUserIcon: true,
|
||||
items: [
|
||||
if (context.router.canPop() && AdaptiveLayout.of(context).size == ScreenLayout.dual)
|
||||
if (context.router.canNavigateBack && AdaptiveLayout.of(context).size == ScreenLayout.dual)
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
|
|
@ -99,7 +100,7 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
|||
style: IconButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface.withOpacity(0.8),
|
||||
),
|
||||
onPressed: () => context.router.maybePop(),
|
||||
onPressed: () => context.router.popBack(),
|
||||
icon: Padding(
|
||||
padding: EdgeInsets.all(AdaptiveLayout.of(context).inputDevice == InputDevice.pointer ? 0 : 4),
|
||||
child: const Icon(IconsaxOutline.arrow_left_2),
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/images_models.dart';
|
||||
import 'package:fladder/models/media_playback_model.dart';
|
||||
import 'package:fladder/providers/items/item_details_provider.dart';
|
||||
import 'package:fladder/providers/video_player_provider.dart';
|
||||
import 'package:fladder/routes/auto_router.gr.dart';
|
||||
import 'package:fladder/theme.dart';
|
||||
|
|
@ -15,74 +14,13 @@ import 'package:fladder/util/adaptive_layout.dart';
|
|||
import 'package:fladder/util/fladder_image.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/util/refresh_state.dart';
|
||||
import 'package:fladder/util/router_extension.dart';
|
||||
import 'package:fladder/widgets/navigation_scaffold/components/floating_player_bar.dart';
|
||||
import 'package:fladder/widgets/navigation_scaffold/components/settings_user_icon.dart';
|
||||
import 'package:fladder/widgets/shared/item_actions.dart';
|
||||
import 'package:fladder/widgets/shared/modal_bottom_sheet.dart';
|
||||
import 'package:fladder/widgets/shared/pull_to_refresh.dart';
|
||||
|
||||
class DetailScreen extends ConsumerStatefulWidget {
|
||||
final String id;
|
||||
final ItemBaseModel? item;
|
||||
const DetailScreen({required this.id, this.item, super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<ConsumerStatefulWidget> createState() => _DetailScreenState();
|
||||
}
|
||||
|
||||
class _DetailScreenState extends ConsumerState<DetailScreen> {
|
||||
late Widget currentWidget = const Center(
|
||||
key: Key("progress-indicator"),
|
||||
child: CircularProgressIndicator.adaptive(strokeCap: StrokeCap.round),
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Future.microtask(() async {
|
||||
if (widget.item != null) {
|
||||
setState(() {
|
||||
currentWidget = widget.item!.detailScreenWidget;
|
||||
});
|
||||
} else {
|
||||
final response = await ref.read(itemDetailsProvider.notifier).fetchDetails(widget.id);
|
||||
if (context.mounted) {
|
||||
if (response != null) {
|
||||
setState(() {
|
||||
currentWidget = response.detailScreenWidget;
|
||||
});
|
||||
} else {
|
||||
context.router.navigate(const DashboardRoute());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Hero(
|
||||
tag: widget.id,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface.withOpacity(1.0),
|
||||
),
|
||||
//Small offset to match detailscaffold
|
||||
child: Transform.translate(
|
||||
offset: const Offset(0, -5), child: FladderImage(image: widget.item?.getPosters?.primary)),
|
||||
),
|
||||
),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(seconds: 1),
|
||||
child: currentWidget,
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DetailScaffold extends ConsumerStatefulWidget {
|
||||
final String label;
|
||||
final ItemBaseModel? item;
|
||||
|
|
@ -212,7 +150,7 @@ class _DetailScaffoldState extends ConsumerState<DetailScaffold> {
|
|||
style: IconButton.styleFrom(
|
||||
backgroundColor: backGroundColor,
|
||||
),
|
||||
onPressed: () => context.router.maybePop(),
|
||||
onPressed: () => context.router.popBack(),
|
||||
icon: Padding(
|
||||
padding:
|
||||
EdgeInsets.all(AdaptiveLayout.of(context).inputDevice == InputDevice.pointer ? 0 : 4),
|
||||
|
|
@ -269,8 +207,8 @@ class _DetailScaffoldState extends ConsumerState<DetailScaffold> {
|
|||
icon: const Icon(IconsaxOutline.refresh),
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
),
|
||||
if (AdaptiveLayout.of(context).size == ScreenLayout.single)
|
||||
const SizedBox(height: 30, width: 30, child: SettingsUserIcon()),
|
||||
Tooltip(
|
||||
message: context.localized.home,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:fladder/models/account_model.dart';
|
||||
import 'package:fladder/providers/shared_provider.dart';
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/routes/auto_router.gr.dart';
|
||||
import 'package:fladder/screens/shared/fladder_logo.dart';
|
||||
|
||||
@RoutePage()
|
||||
|
|
@ -28,19 +29,16 @@ class _SplashScreenState extends ConsumerState<SplashScreen> {
|
|||
|
||||
if (context.mounted) {
|
||||
if (lastUsedAccount == null) {
|
||||
widget.loggedIn?.call(false);
|
||||
context.router.maybePop(false);
|
||||
callBackOrNavigate(false);
|
||||
} else {
|
||||
switch (lastUsedAccount.authMethod) {
|
||||
case Authentication.autoLogin:
|
||||
widget.loggedIn?.call(true);
|
||||
context.router.maybePop(true);
|
||||
callBackOrNavigate(true);
|
||||
break;
|
||||
case Authentication.biometrics:
|
||||
case Authentication.none:
|
||||
case Authentication.passcode:
|
||||
widget.loggedIn?.call(false);
|
||||
context.router.maybePop(false);
|
||||
callBackOrNavigate(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -48,6 +46,19 @@ class _SplashScreenState extends ConsumerState<SplashScreen> {
|
|||
});
|
||||
}
|
||||
|
||||
void callBackOrNavigate(bool loggedIn) {
|
||||
if (widget.loggedIn == null) {
|
||||
if (loggedIn) {
|
||||
context.router.replace(const DashboardRoute());
|
||||
} else {
|
||||
context.router.replace(const LoginRoute());
|
||||
}
|
||||
} else {
|
||||
widget.loggedIn?.call(loggedIn);
|
||||
context.router.maybePop(loggedIn);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Scaffold(
|
||||
|
|
|
|||
30
lib/util/router_extension.dart
Normal file
30
lib/util/router_extension.dart
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
|
||||
extension RouterExtension on StackRouter {
|
||||
Future<bool> popBack() async {
|
||||
if (kIsWeb) {
|
||||
back();
|
||||
return canNavigateBack;
|
||||
} else {
|
||||
return maybePop();
|
||||
}
|
||||
}
|
||||
|
||||
Widget? backButton() {
|
||||
if (kIsWeb && canNavigateBack) {
|
||||
return IconButton(
|
||||
onPressed: back,
|
||||
icon: const BackButtonIcon(),
|
||||
);
|
||||
} else if (canPop()) {
|
||||
return IconButton(
|
||||
onPressed: maybePop,
|
||||
icon: const BackButtonIcon(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -154,7 +154,8 @@ class NestedNavigationDrawer extends ConsumerWidget {
|
|||
|
||||
bool checkLibrary(BuildContext context, String id) {
|
||||
try {
|
||||
return context.routeData.queryParams.isNotEmpty && context.routeData.queryParams.getString('parentId') == id;
|
||||
return context.router.current.name == LibrarySearchRoute().routeName &&
|
||||
(context.routeData.queryParams.isNotEmpty && context.routeData.queryParams.getString('parentId') == id);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/models/media_playback_model.dart';
|
||||
import 'package:fladder/providers/video_player_provider.dart';
|
||||
import 'package:fladder/providers/views_provider.dart';
|
||||
|
|
@ -9,8 +13,6 @@ import 'package:fladder/widgets/navigation_scaffold/components/floating_player_b
|
|||
import 'package:fladder/widgets/navigation_scaffold/components/navigation_body.dart';
|
||||
import 'package:fladder/widgets/navigation_scaffold/components/navigation_drawer.dart';
|
||||
import 'package:fladder/widgets/shared/hide_on_scroll.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class NavigationScaffold extends ConsumerStatefulWidget {
|
||||
final String? currentRouteName;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'dart:ui' as ui;
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
|
|
@ -34,12 +35,12 @@ class _TrickplayImageState extends ConsumerState<TrickplayImage> {
|
|||
|
||||
@override
|
||||
void didUpdateWidget(covariant TrickplayImage oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.position?.inMilliseconds != widget.position?.inMilliseconds) {
|
||||
time = widget.position ?? Duration.zero;
|
||||
model = widget.trickplay;
|
||||
loadImage();
|
||||
}
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue