mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-07 21:48:14 -08:00
Merge branch 'feature/auto_router' into develop
This commit is contained in:
commit
e70858eb64
49 changed files with 1366 additions and 1102 deletions
18
build.yaml
18
build.yaml
|
|
@ -2,13 +2,27 @@ targets:
|
||||||
$default:
|
$default:
|
||||||
sources:
|
sources:
|
||||||
- lib/$lib$
|
- lib/$lib$
|
||||||
- "**/models/**.dart" # Matches models folder at any depth in lib
|
- "**/models/**.dart"
|
||||||
- "**/providers/**.dart" # Matches providers folder at any depth in lib
|
- "**/providers/**.dart"
|
||||||
- lib/util/**.dart
|
- lib/util/**.dart
|
||||||
- lib/jellyfin/**.dart
|
- lib/jellyfin/**.dart
|
||||||
|
- "**/**_screen.dart"
|
||||||
|
- "**/**_page.dart"
|
||||||
- swagger/**
|
- swagger/**
|
||||||
|
- lib/routes/auto_router.dart
|
||||||
- $package$
|
- $package$
|
||||||
builders:
|
builders:
|
||||||
|
auto_route_generator:auto_router_generator:
|
||||||
|
options:
|
||||||
|
enable_cached_builds: true
|
||||||
|
generate_for:
|
||||||
|
- lib/routes/auto_router.dart
|
||||||
|
auto_route_generator:auto_route_generator:
|
||||||
|
options:
|
||||||
|
enable_cached_builds: true
|
||||||
|
generate_for:
|
||||||
|
- "**/**_screen.dart"
|
||||||
|
- "**/**_page.dart"
|
||||||
freezed|freezed:
|
freezed|freezed:
|
||||||
options:
|
options:
|
||||||
generate_for:
|
generate_for:
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,15 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:fladder/models/syncing/i_synced_item.dart';
|
|
||||||
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
|
||||||
import 'package:fladder/providers/sync_provider.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:dynamic_color/dynamic_color.dart';
|
import 'package:dynamic_color/dynamic_color.dart';
|
||||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
// ignore: depend_on_referenced_packages
|
|
||||||
import 'package:flutter_web_plugins/url_strategy.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:media_kit/media_kit.dart';
|
import 'package:media_kit/media_kit.dart';
|
||||||
|
|
@ -23,21 +17,23 @@ import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:universal_html/html.dart' as html;
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
import 'package:fladder/models/account_model.dart';
|
import 'package:fladder/models/account_model.dart';
|
||||||
|
import 'package:fladder/models/syncing/i_synced_item.dart';
|
||||||
|
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
||||||
import 'package:fladder/providers/shared_provider.dart';
|
import 'package:fladder/providers/shared_provider.dart';
|
||||||
|
import 'package:fladder/providers/sync_provider.dart';
|
||||||
import 'package:fladder/providers/user_provider.dart';
|
import 'package:fladder/providers/user_provider.dart';
|
||||||
import 'package:fladder/providers/video_player_provider.dart';
|
import 'package:fladder/providers/video_player_provider.dart';
|
||||||
import 'package:fladder/routes/app_routes.dart';
|
import 'package:fladder/routes/auto_router.gr.dart';
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
|
||||||
import 'package:fladder/screens/login/lock_screen.dart';
|
import 'package:fladder/screens/login/lock_screen.dart';
|
||||||
import 'package:fladder/theme.dart';
|
import 'package:fladder/theme.dart';
|
||||||
import 'package:fladder/util/adaptive_layout.dart';
|
import 'package:fladder/util/adaptive_layout.dart';
|
||||||
import 'package:fladder/util/application_info.dart';
|
import 'package:fladder/util/application_info.dart';
|
||||||
import 'package:fladder/util/string_extensions.dart';
|
import 'package:fladder/util/string_extensions.dart';
|
||||||
import 'package:fladder/util/themes_data.dart';
|
import 'package:fladder/util/themes_data.dart';
|
||||||
import 'package:universal_html/html.dart' as html;
|
|
||||||
|
|
||||||
bool get _isDesktop {
|
bool get _isDesktop {
|
||||||
if (kIsWeb) return false;
|
if (kIsWeb) return false;
|
||||||
|
|
@ -63,9 +59,7 @@ class CustomCacheManager {
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
usePathUrlStrategy();
|
|
||||||
html.document.onContextMenu.listen((event) => event.preventDefault());
|
html.document.onContextMenu.listen((event) => event.preventDefault());
|
||||||
GoRouter.optionURLReflectsImperativeAPIs = true;
|
|
||||||
}
|
}
|
||||||
_setupLogging();
|
_setupLogging();
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
@ -89,9 +83,9 @@ void main() async {
|
||||||
}
|
}
|
||||||
|
|
||||||
final applicationInfo = ApplicationInfo(
|
final applicationInfo = ApplicationInfo(
|
||||||
name: kIsWeb ? "${packageInfo.appName.capitalize()} Web" : packageInfo.appName.capitalize(),
|
name: packageInfo.appName.capitalize(),
|
||||||
version: "${packageInfo.version}(${packageInfo.buildNumber})",
|
version: "${packageInfo.version}(${packageInfo.buildNumber})",
|
||||||
os: defaultTargetPlatform.name.capitalize(),
|
os: !kIsWeb ? defaultTargetPlatform.name.capitalize() : "${defaultTargetPlatform.name.capitalize()} Web",
|
||||||
);
|
);
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
|
|
@ -111,7 +105,7 @@ void main() async {
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
child: AdaptiveLayoutBuilder(
|
child: AdaptiveLayoutBuilder(
|
||||||
fallBack: LayoutState.phone,
|
fallBack: LayoutState.tablet,
|
||||||
layoutPoints: [
|
layoutPoints: [
|
||||||
LayoutPoints(start: 0, end: 599, type: LayoutState.phone),
|
LayoutPoints(start: 0, end: 599, type: LayoutState.phone),
|
||||||
LayoutPoints(start: 600, end: 1919, type: LayoutState.tablet),
|
LayoutPoints(start: 600, end: 1919, type: LayoutState.tablet),
|
||||||
|
|
@ -179,7 +173,7 @@ class _MainState extends ConsumerState<Main> with WindowListener, WidgetsBinding
|
||||||
await ref.read(videoPlayerProvider).pause();
|
await ref.read(videoPlayerProvider).pause();
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
AdaptiveLayout.of(context).router.push(LockScreenRoute().route);
|
AdaptiveLayout.of(context).router.push(const LockRoute());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -316,22 +310,12 @@ class _MainState extends ConsumerState<Main> with WindowListener, WidgetsBinding
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
themeMode: themeMode,
|
themeMode: themeMode,
|
||||||
routerConfig: AdaptiveLayout.of(context).router,
|
routerConfig: AdaptiveLayout.routerOf(context).config(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<RouteBase> getRoutes(LayoutState state) {
|
|
||||||
switch (state) {
|
|
||||||
case LayoutState.phone:
|
|
||||||
return AppRoutes.linearRoutes;
|
|
||||||
case LayoutState.tablet:
|
|
||||||
case LayoutState.desktop:
|
|
||||||
return AppRoutes.nestedRoutes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final currentTitleProvider = StateProvider<String>((ref) {
|
final currentTitleProvider = StateProvider<String>((ref) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:dart_mappable/dart_mappable.dart';
|
import 'package:dart_mappable/dart_mappable.dart';
|
||||||
import 'package:ficonsax/ficonsax.dart';
|
import 'package:ficonsax/ficonsax.dart';
|
||||||
import 'package:fladder/models/book_model.dart';
|
import 'package:fladder/models/book_model.dart';
|
||||||
|
|
@ -5,8 +6,7 @@ import 'package:fladder/models/boxset_model.dart';
|
||||||
import 'package:fladder/models/items/media_streams_model.dart';
|
import 'package:fladder/models/items/media_streams_model.dart';
|
||||||
import 'package:fladder/models/library_search/library_search_options.dart';
|
import 'package:fladder/models/library_search/library_search_options.dart';
|
||||||
import 'package:fladder/models/playlist_model.dart';
|
import 'package:fladder/models/playlist_model.dart';
|
||||||
import 'package:fladder/routes/build_routes/home_routes.dart';
|
import 'package:fladder/routes/auto_router.gr.dart';
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
|
||||||
import 'package:fladder/screens/details_screens/book_detail_screen.dart';
|
import 'package:fladder/screens/details_screens/book_detail_screen.dart';
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
import 'package:fladder/util/string_extensions.dart';
|
import 'package:fladder/util/string_extensions.dart';
|
||||||
|
|
@ -166,7 +166,7 @@ class ItemBaseModel with ItemBaseModelMappable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> navigateTo(BuildContext context) async => context.routePush(DetailsRoute(id: id), extra: this);
|
Future<void> navigateTo(BuildContext context) async => context.router.push(DetailsRoute(id: id, item: this));
|
||||||
|
|
||||||
factory ItemBaseModel.fromBaseDto(dto.BaseItemDto item, Ref ref) {
|
factory ItemBaseModel.fromBaseDto(dto.BaseItemDto item, Ref ref) {
|
||||||
return switch (item.type) {
|
return switch (item.type) {
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,32 @@
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:chopper/chopper.dart';
|
import 'package:chopper/chopper.dart';
|
||||||
import 'package:collection/collection.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/collection_types.dart';
|
||||||
|
import 'package:fladder/models/item_base_model.dart';
|
||||||
import 'package:fladder/models/items/folder_model.dart';
|
import 'package:fladder/models/items/folder_model.dart';
|
||||||
import 'package:fladder/models/items/item_shared_models.dart';
|
import 'package:fladder/models/items/item_shared_models.dart';
|
||||||
import 'package:fladder/models/items/photos_model.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/library_search/library_search_options.dart';
|
||||||
import 'package:fladder/models/playlist_model.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/service_provider.dart';
|
||||||
import 'package:fladder/providers/settings/client_settings_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/photo_viewer/photo_viewer_screen.dart';
|
||||||
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
||||||
import 'package:fladder/util/item_base_model/play_item_helpers.dart';
|
import 'package:fladder/util/item_base_model/play_item_helpers.dart';
|
||||||
import 'package:fladder/util/list_extensions.dart';
|
import 'package:fladder/util/list_extensions.dart';
|
||||||
import 'package:fladder/util/localization_helper.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:fladder/util/map_bool_helper.dart';
|
||||||
import 'package:page_transition/page_transition.dart';
|
|
||||||
|
|
||||||
final librarySearchProvider =
|
final librarySearchProvider =
|
||||||
StateNotifierProvider.family.autoDispose<LibrarySearchNotifier, LibrarySearchModel, Key>((ref, id) {
|
StateNotifierProvider.family.autoDispose<LibrarySearchNotifier, LibrarySearchModel, Key>((ref, id) {
|
||||||
|
|
@ -652,6 +653,10 @@ class LibrarySearchNotifier extends StateNotifier<LibrarySearchModel> {
|
||||||
items.firstOrNull?.navigateTo(context);
|
items.firstOrNull?.navigateTo(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateEverything() {
|
||||||
|
state = state.copyWith();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SimpleSorter on List<ItemBaseModel> {
|
extension SimpleSorter on List<ItemBaseModel> {
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ Map<String, dynamic> _$$SessionInfoModelImplToJson(
|
||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$sessionInfoHash() => r'ab5afcada1c9677cadda954c9abf7eb361dc057d';
|
String _$sessionInfoHash() => r'024da7f8d05fb98f6e2e5395ed06c1cc9d003f79';
|
||||||
|
|
||||||
/// See also [SessionInfo].
|
/// See also [SessionInfo].
|
||||||
@ProviderFor(SessionInfo)
|
@ProviderFor(SessionInfo)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ part of 'background_download_provider.dart';
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$backgroundDownloaderHash() =>
|
String _$backgroundDownloaderHash() =>
|
||||||
r'2bc7a06682cdcfa9a754dce9b7f7ea48f873682e';
|
r'9a9f91504ae4532ab37290ee9372d2e7a18380a9';
|
||||||
|
|
||||||
/// See also [backgroundDownloader].
|
/// See also [backgroundDownloader].
|
||||||
@ProviderFor(backgroundDownloader)
|
@ProviderFor(backgroundDownloader)
|
||||||
|
|
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
import 'package:fladder/providers/shared_provider.dart';
|
|
||||||
import 'package:fladder/providers/user_provider.dart';
|
|
||||||
import 'package:fladder/routes/build_routes/home_routes.dart';
|
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
|
||||||
import 'package:fladder/routes/build_routes/settings_routes.dart';
|
|
||||||
import 'package:fladder/screens/home.dart';
|
|
||||||
import 'package:fladder/screens/settings/settings_screen.dart';
|
|
||||||
import 'package:fladder/util/adaptive_layout.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
class AppRoutes {
|
|
||||||
static final parentKey = GlobalKey<NavigatorState>();
|
|
||||||
static final homeShellKey = GlobalKey<NavigatorState>();
|
|
||||||
static final settingsKey = GlobalKey<NavigatorState>();
|
|
||||||
static final loginRoute = LoginRoute();
|
|
||||||
static final splashRoute = SplashRoute();
|
|
||||||
|
|
||||||
static final scrollController = ScrollController();
|
|
||||||
|
|
||||||
static GoRouter routes({required WidgetRef ref, required ScreenLayout screenLayout}) {
|
|
||||||
return GoRouter(
|
|
||||||
navigatorKey: parentKey,
|
|
||||||
initialLocation: splashRoute.route,
|
|
||||||
//Only useful for web if the user is not in an active session yet
|
|
||||||
redirect: kIsWeb
|
|
||||||
? (context, state) async {
|
|
||||||
if (state.uri.toString() == loginRoute.route || state.uri.toString() == splashRoute.route) return null;
|
|
||||||
await Future.microtask(() {
|
|
||||||
final lastUsedAccount = ref.read(sharedUtilityProvider).getActiveAccount();
|
|
||||||
if (lastUsedAccount == null) return loginRoute.route;
|
|
||||||
if (ref.read(userProvider) == null) ref.read(userProvider.notifier).loginUser(lastUsedAccount);
|
|
||||||
});
|
|
||||||
if (ref.read(userProvider) == null) {
|
|
||||||
return loginRoute.route;
|
|
||||||
} else {
|
|
||||||
return state.uri.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
routes: AppRoutes().getRoutes(screenLayout),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<RouteBase> getRoutes(ScreenLayout screenLayout) {
|
|
||||||
switch (screenLayout) {
|
|
||||||
case ScreenLayout.single:
|
|
||||||
return linearRoutes;
|
|
||||||
case ScreenLayout.dual:
|
|
||||||
return nestedRoutes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<RouteBase> linearRoutes = [
|
|
||||||
loginRoute.go,
|
|
||||||
splashRoute.go,
|
|
||||||
LockScreenRoute().go,
|
|
||||||
ShellRoute(
|
|
||||||
navigatorKey: homeShellKey,
|
|
||||||
pageBuilder: (context, state, child) {
|
|
||||||
return NoTransitionPage(
|
|
||||||
child: Home(
|
|
||||||
key: state.pageKey,
|
|
||||||
location: state.uri.toString(),
|
|
||||||
nestedChild: child,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
routes: [
|
|
||||||
DashboardRoute().go,
|
|
||||||
FavouritesRoute().go,
|
|
||||||
SyncRoute().go,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
DetailsRoute(navKey: parentKey).go,
|
|
||||||
LibrarySearchRoute(navKey: parentKey).go,
|
|
||||||
|
|
||||||
// Settings routes
|
|
||||||
SettingsRoute(navKey: parentKey).go,
|
|
||||||
ClientSettingsRoute(navKey: parentKey).go,
|
|
||||||
SecuritySettingsRoute(navKey: parentKey).go,
|
|
||||||
PlayerSettingsRoute(navKey: parentKey).go,
|
|
||||||
];
|
|
||||||
|
|
||||||
static List<RouteBase> nestedRoutes = [
|
|
||||||
loginRoute.go,
|
|
||||||
splashRoute.go,
|
|
||||||
LockScreenRoute().go,
|
|
||||||
ShellRoute(
|
|
||||||
navigatorKey: homeShellKey,
|
|
||||||
pageBuilder: (context, state, child) => NoTransitionPage(
|
|
||||||
child: Home(
|
|
||||||
key: state.pageKey,
|
|
||||||
location: state.uri.toString(),
|
|
||||||
nestedChild: child,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
routes: [
|
|
||||||
ShellRoute(
|
|
||||||
navigatorKey: settingsKey,
|
|
||||||
pageBuilder: (context, state, child) => NoTransitionPage(
|
|
||||||
key: state.pageKey,
|
|
||||||
child: SettingsScreen(location: state.uri.toString(), child: child),
|
|
||||||
),
|
|
||||||
routes: [
|
|
||||||
ClientSettingsRoute(navKey: settingsKey).go,
|
|
||||||
SecuritySettingsRoute(navKey: settingsKey).go,
|
|
||||||
PlayerSettingsRoute(navKey: settingsKey).go,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
DashboardRoute().go,
|
|
||||||
FavouritesRoute().go,
|
|
||||||
SyncRoute().go,
|
|
||||||
DetailsRoute(navKey: homeShellKey).go,
|
|
||||||
LibrarySearchRoute(navKey: homeShellKey).go,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
138
lib/routes/auto_router.dart
Normal file
138
lib/routes/auto_router.dart
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
import 'package:flutter/foundation.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/routes/auto_router.gr.dart';
|
||||||
|
import 'package:fladder/screens/login/lock_screen.dart';
|
||||||
|
import 'package:fladder/util/adaptive_layout.dart';
|
||||||
|
|
||||||
|
@AutoRouterConfig(replaceInRouteName: 'Screen|Page,Route')
|
||||||
|
class AutoRouter extends RootStackRouter {
|
||||||
|
AutoRouter({
|
||||||
|
required this.layout,
|
||||||
|
required this.ref,
|
||||||
|
});
|
||||||
|
|
||||||
|
final WidgetRef ref;
|
||||||
|
final ScreenLayout layout;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<AutoRouteGuard> get guards => [...super.guards, AuthGuard(ref: ref)];
|
||||||
|
|
||||||
|
@override
|
||||||
|
RouteType get defaultRouteType => kIsWeb ? const RouteType.material() : const RouteType.adaptive();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<AutoRoute> get routes => [
|
||||||
|
..._defaultRoutes,
|
||||||
|
...(layout == ScreenLayout.dual ? desktopRoutes : mobileRoutes),
|
||||||
|
];
|
||||||
|
|
||||||
|
final List<AutoRoute> mobileRoutes = [
|
||||||
|
_homeRoute.copyWith(
|
||||||
|
children: [
|
||||||
|
_dashboardRoute,
|
||||||
|
_favouritesRoute,
|
||||||
|
_syncedRoute,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
AutoRoute(page: LockRoute.page, path: '/locked'),
|
||||||
|
];
|
||||||
|
final List<AutoRoute> desktopRoutes = [
|
||||||
|
_homeRoute.copyWith(
|
||||||
|
children: [
|
||||||
|
_dashboardRoute,
|
||||||
|
_favouritesRoute,
|
||||||
|
_syncedRoute,
|
||||||
|
AutoRoute(page: DetailsRoute.page, path: 'details', usesPathAsKey: true),
|
||||||
|
AutoRoute(page: LibrarySearchRoute.page, path: 'library', usesPathAsKey: true),
|
||||||
|
AutoRoute(
|
||||||
|
page: SettingsRoute.page,
|
||||||
|
path: 'settings',
|
||||||
|
children: _settingsChildren,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
AutoRoute(page: LockRoute.page, path: '/locked'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<AutoRoute> _defaultRoutes = [
|
||||||
|
AutoRoute(page: SplashRoute.page, path: '/splash'),
|
||||||
|
AutoRoute(page: LoginRoute.page, path: '/login'),
|
||||||
|
];
|
||||||
|
|
||||||
|
final AutoRoute _homeRoute = AutoRoute(page: HomeRoute.page, path: '/');
|
||||||
|
final AutoRoute _dashboardRoute = CustomRoute(
|
||||||
|
page: DashboardRoute.page,
|
||||||
|
transitionsBuilder: TransitionsBuilders.fadeIn,
|
||||||
|
initial: true,
|
||||||
|
path: 'dashboard',
|
||||||
|
maintainState: false,
|
||||||
|
);
|
||||||
|
final AutoRoute _favouritesRoute = CustomRoute(
|
||||||
|
page: FavouritesRoute.page,
|
||||||
|
transitionsBuilder: TransitionsBuilders.fadeIn,
|
||||||
|
path: 'favourites',
|
||||||
|
maintainState: false,
|
||||||
|
);
|
||||||
|
final AutoRoute _syncedRoute = CustomRoute(
|
||||||
|
page: SyncedRoute.page,
|
||||||
|
transitionsBuilder: TransitionsBuilders.fadeIn,
|
||||||
|
path: 'synced',
|
||||||
|
maintainState: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
final List<AutoRoute> _settingsChildren = [
|
||||||
|
CustomRoute(
|
||||||
|
page: ClientSettingsRoute.page, initial: true, transitionsBuilder: TransitionsBuilders.fadeIn, path: 'client'),
|
||||||
|
CustomRoute(page: SecuritySettingsRoute.page, transitionsBuilder: TransitionsBuilders.fadeIn, path: 'security'),
|
||||||
|
CustomRoute(page: PlayerSettingsRoute.page, transitionsBuilder: TransitionsBuilders.fadeIn, path: 'player'),
|
||||||
|
];
|
||||||
|
|
||||||
|
class LockScreenGuard extends AutoRouteGuard {
|
||||||
|
final WidgetRef ref;
|
||||||
|
|
||||||
|
const LockScreenGuard({required this.ref});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onNavigation(NavigationResolver resolver, StackRouter router) async {
|
||||||
|
if (ref.read(lockScreenActiveProvider) && resolver.routeName != const LockRoute().routeName) {
|
||||||
|
router.replace(const LockRoute());
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
return resolver.next(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AuthGuard extends AutoRouteGuard {
|
||||||
|
final WidgetRef ref;
|
||||||
|
|
||||||
|
const AuthGuard({required this.ref});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onNavigation(NavigationResolver resolver, StackRouter router) async {
|
||||||
|
if (ref.read(userProvider) != null ||
|
||||||
|
resolver.routeName == const LoginRoute().routeName ||
|
||||||
|
resolver.routeName == SplashRoute().routeName) {
|
||||||
|
return resolver.next(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolver.redirect<bool>(SplashRoute(loggedIn: (value) {
|
||||||
|
if (value) {
|
||||||
|
resolver.next(true);
|
||||||
|
} else {
|
||||||
|
router.navigate(const LoginRoute());
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
445
lib/routes/auto_router.gr.dart
Normal file
445
lib/routes/auto_router.gr.dart
Normal file
|
|
@ -0,0 +1,445 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// AutoRouterGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// coverage:ignore-file
|
||||||
|
|
||||||
|
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||||
|
import 'package:auto_route/auto_route.dart' as _i14;
|
||||||
|
import 'package:fladder/models/item_base_model.dart' as _i15;
|
||||||
|
import 'package:fladder/models/items/photos_model.dart' as _i18;
|
||||||
|
import 'package:fladder/models/library_search/library_search_options.dart'
|
||||||
|
as _i17;
|
||||||
|
import 'package:fladder/routes/nested_details_screen.dart' as _i3;
|
||||||
|
import 'package:fladder/screens/dashboard/dashboard_screen.dart' as _i2;
|
||||||
|
import 'package:fladder/screens/favourites/favourites_screen.dart' as _i4;
|
||||||
|
import 'package:fladder/screens/home_screen.dart' as _i5;
|
||||||
|
import 'package:fladder/screens/library_search/library_search_screen.dart'
|
||||||
|
as _i6;
|
||||||
|
import 'package:fladder/screens/login/lock_screen.dart' as _i7;
|
||||||
|
import 'package:fladder/screens/login/login_screen.dart' as _i8;
|
||||||
|
import 'package:fladder/screens/settings/client_settings_page.dart' as _i1;
|
||||||
|
import 'package:fladder/screens/settings/player_settings_page.dart' as _i9;
|
||||||
|
import 'package:fladder/screens/settings/security_settings_page.dart' as _i10;
|
||||||
|
import 'package:fladder/screens/settings/settings_screen.dart' as _i11;
|
||||||
|
import 'package:fladder/screens/splash_screen.dart' as _i12;
|
||||||
|
import 'package:fladder/screens/syncing/synced_screen.dart' as _i13;
|
||||||
|
import 'package:flutter/material.dart' as _i16;
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [_i1.ClientSettingsPage]
|
||||||
|
class ClientSettingsRoute extends _i14.PageRouteInfo<void> {
|
||||||
|
const ClientSettingsRoute({List<_i14.PageRouteInfo>? children})
|
||||||
|
: super(
|
||||||
|
ClientSettingsRoute.name,
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'ClientSettingsRoute';
|
||||||
|
|
||||||
|
static _i14.PageInfo page = _i14.PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const _i1.ClientSettingsPage();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [_i2.DashboardScreen]
|
||||||
|
class DashboardRoute extends _i14.PageRouteInfo<void> {
|
||||||
|
const DashboardRoute({List<_i14.PageRouteInfo>? children})
|
||||||
|
: super(
|
||||||
|
DashboardRoute.name,
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'DashboardRoute';
|
||||||
|
|
||||||
|
static _i14.PageInfo page = _i14.PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const _i2.DashboardScreen();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [_i3.DetailsScreen]
|
||||||
|
class DetailsRoute extends _i14.PageRouteInfo<DetailsRouteArgs> {
|
||||||
|
DetailsRoute({
|
||||||
|
String id = '',
|
||||||
|
_i15.ItemBaseModel? item,
|
||||||
|
_i16.Key? key,
|
||||||
|
List<_i14.PageRouteInfo>? children,
|
||||||
|
}) : super(
|
||||||
|
DetailsRoute.name,
|
||||||
|
args: DetailsRouteArgs(
|
||||||
|
id: id,
|
||||||
|
item: item,
|
||||||
|
key: key,
|
||||||
|
),
|
||||||
|
rawQueryParams: {'id': id},
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'DetailsRoute';
|
||||||
|
|
||||||
|
static _i14.PageInfo page = _i14.PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
final queryParams = data.queryParams;
|
||||||
|
final args = data.argsAs<DetailsRouteArgs>(
|
||||||
|
orElse: () => DetailsRouteArgs(
|
||||||
|
id: queryParams.getString(
|
||||||
|
'id',
|
||||||
|
'',
|
||||||
|
)));
|
||||||
|
return _i3.DetailsScreen(
|
||||||
|
id: args.id,
|
||||||
|
item: args.item,
|
||||||
|
key: args.key,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class DetailsRouteArgs {
|
||||||
|
const DetailsRouteArgs({
|
||||||
|
this.id = '',
|
||||||
|
this.item,
|
||||||
|
this.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String id;
|
||||||
|
|
||||||
|
final _i15.ItemBaseModel? item;
|
||||||
|
|
||||||
|
final _i16.Key? key;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DetailsRouteArgs{id: $id, item: $item, key: $key}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [_i4.FavouritesScreen]
|
||||||
|
class FavouritesRoute extends _i14.PageRouteInfo<void> {
|
||||||
|
const FavouritesRoute({List<_i14.PageRouteInfo>? children})
|
||||||
|
: super(
|
||||||
|
FavouritesRoute.name,
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'FavouritesRoute';
|
||||||
|
|
||||||
|
static _i14.PageInfo page = _i14.PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const _i4.FavouritesScreen();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [_i5.HomeScreen]
|
||||||
|
class HomeRoute extends _i14.PageRouteInfo<void> {
|
||||||
|
const HomeRoute({List<_i14.PageRouteInfo>? children})
|
||||||
|
: super(
|
||||||
|
HomeRoute.name,
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'HomeRoute';
|
||||||
|
|
||||||
|
static _i14.PageInfo page = _i14.PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const _i5.HomeScreen();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [_i6.LibrarySearchScreen]
|
||||||
|
class LibrarySearchRoute extends _i14.PageRouteInfo<LibrarySearchRouteArgs> {
|
||||||
|
LibrarySearchRoute({
|
||||||
|
String? viewModelId,
|
||||||
|
List<String>? folderId,
|
||||||
|
bool? favourites,
|
||||||
|
_i17.SortingOrder? sortOrder,
|
||||||
|
_i17.SortingOptions? sortingOptions,
|
||||||
|
_i18.PhotoModel? photoToView,
|
||||||
|
_i16.Key? key,
|
||||||
|
List<_i14.PageRouteInfo>? children,
|
||||||
|
}) : super(
|
||||||
|
LibrarySearchRoute.name,
|
||||||
|
args: LibrarySearchRouteArgs(
|
||||||
|
viewModelId: viewModelId,
|
||||||
|
folderId: folderId,
|
||||||
|
favourites: favourites,
|
||||||
|
sortOrder: sortOrder,
|
||||||
|
sortingOptions: sortingOptions,
|
||||||
|
photoToView: photoToView,
|
||||||
|
key: key,
|
||||||
|
),
|
||||||
|
rawQueryParams: {
|
||||||
|
'parentId': viewModelId,
|
||||||
|
'folderId': folderId,
|
||||||
|
'favourites': favourites,
|
||||||
|
'sortOrder': sortOrder,
|
||||||
|
'sortOptions': sortingOptions,
|
||||||
|
},
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'LibrarySearchRoute';
|
||||||
|
|
||||||
|
static _i14.PageInfo page = _i14.PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
final queryParams = data.queryParams;
|
||||||
|
final args = data.argsAs<LibrarySearchRouteArgs>(
|
||||||
|
orElse: () => LibrarySearchRouteArgs(
|
||||||
|
viewModelId: queryParams.optString('parentId'),
|
||||||
|
folderId: queryParams.optList('folderId'),
|
||||||
|
favourites: queryParams.optBool('favourites'),
|
||||||
|
sortOrder: queryParams.get('sortOrder'),
|
||||||
|
sortingOptions: queryParams.get('sortOptions'),
|
||||||
|
));
|
||||||
|
return _i6.LibrarySearchScreen(
|
||||||
|
viewModelId: args.viewModelId,
|
||||||
|
folderId: args.folderId,
|
||||||
|
favourites: args.favourites,
|
||||||
|
sortOrder: args.sortOrder,
|
||||||
|
sortingOptions: args.sortingOptions,
|
||||||
|
photoToView: args.photoToView,
|
||||||
|
key: args.key,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class LibrarySearchRouteArgs {
|
||||||
|
const LibrarySearchRouteArgs({
|
||||||
|
this.viewModelId,
|
||||||
|
this.folderId,
|
||||||
|
this.favourites,
|
||||||
|
this.sortOrder,
|
||||||
|
this.sortingOptions,
|
||||||
|
this.photoToView,
|
||||||
|
this.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String? viewModelId;
|
||||||
|
|
||||||
|
final List<String>? folderId;
|
||||||
|
|
||||||
|
final bool? favourites;
|
||||||
|
|
||||||
|
final _i17.SortingOrder? sortOrder;
|
||||||
|
|
||||||
|
final _i17.SortingOptions? sortingOptions;
|
||||||
|
|
||||||
|
final _i18.PhotoModel? photoToView;
|
||||||
|
|
||||||
|
final _i16.Key? key;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'LibrarySearchRouteArgs{viewModelId: $viewModelId, folderId: $folderId, favourites: $favourites, sortOrder: $sortOrder, sortingOptions: $sortingOptions, photoToView: $photoToView, key: $key}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [_i7.LockScreen]
|
||||||
|
class LockRoute extends _i14.PageRouteInfo<void> {
|
||||||
|
const LockRoute({List<_i14.PageRouteInfo>? children})
|
||||||
|
: super(
|
||||||
|
LockRoute.name,
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'LockRoute';
|
||||||
|
|
||||||
|
static _i14.PageInfo page = _i14.PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const _i7.LockScreen();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [_i8.LoginScreen]
|
||||||
|
class LoginRoute extends _i14.PageRouteInfo<void> {
|
||||||
|
const LoginRoute({List<_i14.PageRouteInfo>? children})
|
||||||
|
: super(
|
||||||
|
LoginRoute.name,
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'LoginRoute';
|
||||||
|
|
||||||
|
static _i14.PageInfo page = _i14.PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const _i8.LoginScreen();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [_i9.PlayerSettingsPage]
|
||||||
|
class PlayerSettingsRoute extends _i14.PageRouteInfo<void> {
|
||||||
|
const PlayerSettingsRoute({List<_i14.PageRouteInfo>? children})
|
||||||
|
: super(
|
||||||
|
PlayerSettingsRoute.name,
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'PlayerSettingsRoute';
|
||||||
|
|
||||||
|
static _i14.PageInfo page = _i14.PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const _i9.PlayerSettingsPage();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [_i10.SecuritySettingsPage]
|
||||||
|
class SecuritySettingsRoute extends _i14.PageRouteInfo<void> {
|
||||||
|
const SecuritySettingsRoute({List<_i14.PageRouteInfo>? children})
|
||||||
|
: super(
|
||||||
|
SecuritySettingsRoute.name,
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'SecuritySettingsRoute';
|
||||||
|
|
||||||
|
static _i14.PageInfo page = _i14.PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const _i10.SecuritySettingsPage();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [_i11.SettingsScreen]
|
||||||
|
class SettingsRoute extends _i14.PageRouteInfo<void> {
|
||||||
|
const SettingsRoute({List<_i14.PageRouteInfo>? children})
|
||||||
|
: super(
|
||||||
|
SettingsRoute.name,
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'SettingsRoute';
|
||||||
|
|
||||||
|
static _i14.PageInfo page = _i14.PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const _i11.SettingsScreen();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [_i12.SplashScreen]
|
||||||
|
class SplashRoute extends _i14.PageRouteInfo<SplashRouteArgs> {
|
||||||
|
SplashRoute({
|
||||||
|
dynamic Function(bool)? loggedIn,
|
||||||
|
_i16.Key? key,
|
||||||
|
List<_i14.PageRouteInfo>? children,
|
||||||
|
}) : super(
|
||||||
|
SplashRoute.name,
|
||||||
|
args: SplashRouteArgs(
|
||||||
|
loggedIn: loggedIn,
|
||||||
|
key: key,
|
||||||
|
),
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'SplashRoute';
|
||||||
|
|
||||||
|
static _i14.PageInfo page = _i14.PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
final args =
|
||||||
|
data.argsAs<SplashRouteArgs>(orElse: () => const SplashRouteArgs());
|
||||||
|
return _i12.SplashScreen(
|
||||||
|
loggedIn: args.loggedIn,
|
||||||
|
key: args.key,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SplashRouteArgs {
|
||||||
|
const SplashRouteArgs({
|
||||||
|
this.loggedIn,
|
||||||
|
this.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final dynamic Function(bool)? loggedIn;
|
||||||
|
|
||||||
|
final _i16.Key? key;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SplashRouteArgs{loggedIn: $loggedIn, key: $key}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [_i13.SyncedScreen]
|
||||||
|
class SyncedRoute extends _i14.PageRouteInfo<SyncedRouteArgs> {
|
||||||
|
SyncedRoute({
|
||||||
|
_i16.ScrollController? navigationScrollController,
|
||||||
|
_i16.Key? key,
|
||||||
|
List<_i14.PageRouteInfo>? children,
|
||||||
|
}) : super(
|
||||||
|
SyncedRoute.name,
|
||||||
|
args: SyncedRouteArgs(
|
||||||
|
navigationScrollController: navigationScrollController,
|
||||||
|
key: key,
|
||||||
|
),
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'SyncedRoute';
|
||||||
|
|
||||||
|
static _i14.PageInfo page = _i14.PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
final args =
|
||||||
|
data.argsAs<SyncedRouteArgs>(orElse: () => const SyncedRouteArgs());
|
||||||
|
return _i13.SyncedScreen(
|
||||||
|
navigationScrollController: args.navigationScrollController,
|
||||||
|
key: args.key,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SyncedRouteArgs {
|
||||||
|
const SyncedRouteArgs({
|
||||||
|
this.navigationScrollController,
|
||||||
|
this.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final _i16.ScrollController? navigationScrollController;
|
||||||
|
|
||||||
|
final _i16.Key? key;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SyncedRouteArgs{navigationScrollController: $navigationScrollController, key: $key}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,182 +0,0 @@
|
||||||
import 'package:animations/animations.dart';
|
|
||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
|
||||||
import 'package:fladder/models/item_base_model.dart';
|
|
||||||
import 'package:fladder/models/library_search/library_search_options.dart';
|
|
||||||
import 'package:fladder/routes/app_routes.dart';
|
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
|
||||||
import 'package:fladder/screens/dashboard/dashboard_screen.dart';
|
|
||||||
import 'package:fladder/screens/syncing/synced_screen.dart';
|
|
||||||
import 'package:fladder/screens/favourites/favourites_screen.dart';
|
|
||||||
import 'package:fladder/screens/library_search/library_search_screen.dart';
|
|
||||||
import 'package:fladder/screens/shared/detail_scaffold.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
class DashboardRoute extends CustomRoute {
|
|
||||||
DashboardRoute() : super(name: 'Dashboard', basePath: '/dashboard');
|
|
||||||
|
|
||||||
@override
|
|
||||||
GoRoute get go => GoRoute(
|
|
||||||
path: basePath,
|
|
||||||
parentNavigatorKey: AppRoutes.homeShellKey,
|
|
||||||
pageBuilder: (context, state) {
|
|
||||||
return CustomTransitionPage(
|
|
||||||
key: state.pageKey,
|
|
||||||
child: DashboardScreen(
|
|
||||||
key: Key(basePath),
|
|
||||||
navigationScrollController: AppRoutes.scrollController,
|
|
||||||
),
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
|
||||||
return FadeTransition(
|
|
||||||
opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class FavouritesRoute extends CustomRoute {
|
|
||||||
FavouritesRoute() : super(name: 'Favorites', basePath: '/favorites');
|
|
||||||
|
|
||||||
@override
|
|
||||||
GoRoute get go => GoRoute(
|
|
||||||
path: basePath,
|
|
||||||
parentNavigatorKey: AppRoutes.homeShellKey,
|
|
||||||
pageBuilder: (context, state) {
|
|
||||||
return CustomTransitionPage(
|
|
||||||
key: state.pageKey,
|
|
||||||
child: FavouritesScreen(
|
|
||||||
key: Key(basePath),
|
|
||||||
navigationScrollController: AppRoutes.scrollController,
|
|
||||||
),
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
|
||||||
return FadeTransition(
|
|
||||||
opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class SyncRoute extends CustomRoute {
|
|
||||||
SyncRoute() : super(name: 'Sync', basePath: '/sync');
|
|
||||||
|
|
||||||
@override
|
|
||||||
GoRoute get go => GoRoute(
|
|
||||||
path: basePath,
|
|
||||||
parentNavigatorKey: AppRoutes.homeShellKey,
|
|
||||||
pageBuilder: (context, state) {
|
|
||||||
return CustomTransitionPage(
|
|
||||||
key: state.pageKey,
|
|
||||||
child: SyncedScreen(
|
|
||||||
key: Key(basePath),
|
|
||||||
navigationScrollController: AppRoutes.scrollController,
|
|
||||||
),
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
|
||||||
return FadeTransition(
|
|
||||||
opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class DetailsRoute extends CustomRoute {
|
|
||||||
final String? id;
|
|
||||||
final GlobalKey<NavigatorState>? navKey;
|
|
||||||
DetailsRoute({this.id, this.navKey}) : super(name: 'Details', basePath: '/details', arguments: ':itemId');
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get route => "$basePath/$id";
|
|
||||||
|
|
||||||
@override
|
|
||||||
GoRoute get go => GoRoute(
|
|
||||||
path: path,
|
|
||||||
parentNavigatorKey: navKey,
|
|
||||||
pageBuilder: (context, state) {
|
|
||||||
final String id = state.pathParameters['itemId'] ?? "nothing";
|
|
||||||
ItemBaseModel? item = state.extra as ItemBaseModel?;
|
|
||||||
return CustomTransitionPage(
|
|
||||||
key: state.pageKey,
|
|
||||||
child: DetailScreen(
|
|
||||||
key: Key(id),
|
|
||||||
id: id,
|
|
||||||
item: item,
|
|
||||||
),
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
|
||||||
return FadeThroughTransition(
|
|
||||||
animation: animation,
|
|
||||||
secondaryAnimation: secondaryAnimation,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const _libraryKey = "libraryId";
|
|
||||||
const _sortOptionsKey = "sortOptions";
|
|
||||||
const _sortOrderKey = "sortOrder";
|
|
||||||
const _favoriteKey = "favorite";
|
|
||||||
const _folderKey = "folder";
|
|
||||||
|
|
||||||
class LibrarySearchRoute extends CustomRoute {
|
|
||||||
final String? id;
|
|
||||||
final bool? favorites;
|
|
||||||
final SortingOptions? sortOptions;
|
|
||||||
final SortOrder? sortOrder;
|
|
||||||
final String? folderId;
|
|
||||||
final GlobalKey<NavigatorState>? navKey;
|
|
||||||
LibrarySearchRoute({this.id, this.favorites, this.sortOptions, this.sortOrder, this.folderId, this.navKey})
|
|
||||||
: super(
|
|
||||||
name: 'LibrarySearch',
|
|
||||||
basePath: '/library',
|
|
||||||
queryParameters: {
|
|
||||||
_libraryKey: id,
|
|
||||||
_sortOptionsKey: sortOptions?.name,
|
|
||||||
_sortOrderKey: sortOrder?.name,
|
|
||||||
_favoriteKey: favorites,
|
|
||||||
_folderKey: folderId,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get route => "$basePath${parseUrlParameters(queryParameters)}";
|
|
||||||
|
|
||||||
@override
|
|
||||||
GoRoute get go => GoRoute(
|
|
||||||
path: basePath,
|
|
||||||
parentNavigatorKey: navKey,
|
|
||||||
pageBuilder: (context, state) {
|
|
||||||
final String? id = state.uri.queryParameters[_libraryKey];
|
|
||||||
final bool? favourites = bool.tryParse(state.uri.queryParameters[_favoriteKey] ?? "");
|
|
||||||
final String? folderId = state.uri.queryParameters[_folderKey];
|
|
||||||
final SortingOptions? sortingOptions = SortingOptions.values
|
|
||||||
.firstWhereOrNull((element) => element.name == state.uri.queryParameters[_sortOptionsKey]);
|
|
||||||
final SortingOrder? sortOrder = SortingOrder.values
|
|
||||||
.firstWhereOrNull((element) => element.name == state.uri.queryParameters[_sortOrderKey]);
|
|
||||||
return CustomTransitionPage(
|
|
||||||
key: state.pageKey,
|
|
||||||
child: LibrarySearchScreen(
|
|
||||||
key: Key(id ?? "librarySearch"),
|
|
||||||
viewModelId: id,
|
|
||||||
folderId: folderId?.split(','),
|
|
||||||
sortingOptions: sortingOptions,
|
|
||||||
sortOrder: sortOrder,
|
|
||||||
favourites: favourites,
|
|
||||||
),
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
|
||||||
return FadeTransition(
|
|
||||||
opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
import 'package:fladder/screens/login/login_screen.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
import 'package:fladder/routes/app_routes.dart';
|
|
||||||
import 'package:fladder/screens/login/lock_screen.dart';
|
|
||||||
import 'package:fladder/screens/splash_screen.dart';
|
|
||||||
import 'package:fladder/util/adaptive_layout.dart';
|
|
||||||
|
|
||||||
extension RouteContextBuilder on BuildContext {
|
|
||||||
/// Push a location onto the page stack.
|
|
||||||
Future<T?> routePush<T>(CustomRoute route, {Object? extra}) async {
|
|
||||||
return push(route.route, extra: extra);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Replaces the top-most page of the page stack with the given URL location
|
|
||||||
void routeReplace(CustomRoute route, {Object? extra}) {
|
|
||||||
replace(route.route, extra: extra);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Navigate to a location.
|
|
||||||
void routeGo(CustomRoute route, {Object? extra}) {
|
|
||||||
go(route.route, extra: extra);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [Pushed] if nested(single) else [Go]
|
|
||||||
void routePushOrGo(CustomRoute route, {Object? extra}) {
|
|
||||||
switch (AdaptiveLayout.of(this).size) {
|
|
||||||
case ScreenLayout.single:
|
|
||||||
routePush(route, extra: extra);
|
|
||||||
break;
|
|
||||||
case ScreenLayout.dual:
|
|
||||||
routeGo(route, extra: extra);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [Push] if nested(single) else [Replace]
|
|
||||||
void routeReplaceOrPush(CustomRoute route, {Object? extra}) {
|
|
||||||
switch (AdaptiveLayout.of(this).size) {
|
|
||||||
case ScreenLayout.single:
|
|
||||||
routePush(route, extra: extra);
|
|
||||||
break;
|
|
||||||
case ScreenLayout.dual:
|
|
||||||
routeReplace(route, extra: extra);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class CustomRoute {
|
|
||||||
final String name;
|
|
||||||
final String basePath;
|
|
||||||
final String? arguments;
|
|
||||||
final Map<String, dynamic>? queryParameters;
|
|
||||||
|
|
||||||
CustomRoute({required this.name, required this.basePath, this.arguments, this.queryParameters})
|
|
||||||
: assert(basePath.isNotEmpty, 'GoRoute path cannot be empty');
|
|
||||||
|
|
||||||
String get route => basePath;
|
|
||||||
|
|
||||||
String get path => "$basePath/${arguments ?? queryParameters}";
|
|
||||||
|
|
||||||
void navigate() {
|
|
||||||
AppRoutes.parentKey.currentContext?.routeGo(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void replaceRoot() {
|
|
||||||
AppRoutes.parentKey.currentContext?.routeReplace(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
RouteBase get go;
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoginRoute extends CustomRoute {
|
|
||||||
LoginRoute() : super(name: 'Login', basePath: '/login');
|
|
||||||
|
|
||||||
@override
|
|
||||||
GoRoute get go => GoRoute(
|
|
||||||
path: basePath,
|
|
||||||
builder: (context, state) {
|
|
||||||
return const LoginScreen();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class SplashRoute extends CustomRoute {
|
|
||||||
SplashRoute() : super(name: 'Splash', basePath: '/splash');
|
|
||||||
|
|
||||||
@override
|
|
||||||
GoRoute get go => GoRoute(
|
|
||||||
path: basePath,
|
|
||||||
builder: (context, state) {
|
|
||||||
return const SplashScreen();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class LockScreenRoute extends CustomRoute {
|
|
||||||
final bool selfLock;
|
|
||||||
LockScreenRoute({this.selfLock = false}) : super(name: 'Lock', basePath: '/lock');
|
|
||||||
|
|
||||||
@override
|
|
||||||
GoRoute get go => GoRoute(
|
|
||||||
parentNavigatorKey: AppRoutes.parentKey,
|
|
||||||
path: basePath,
|
|
||||||
builder: (context, state) {
|
|
||||||
return LockScreen(
|
|
||||||
selfLock: selfLock,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String parseUrlParameters(Map<String, dynamic>? parameters) {
|
|
||||||
if (parameters == null) return '';
|
|
||||||
String parameterString = '?';
|
|
||||||
for (String key in parameters.keys) {
|
|
||||||
if (parameters[key] != null) parameterString += '$key=${parameters[key]}&';
|
|
||||||
}
|
|
||||||
|
|
||||||
return parameterString.substring(0, parameterString.length - 1);
|
|
||||||
}
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
|
||||||
import 'package:fladder/screens/settings/client_settings_page.dart';
|
|
||||||
import 'package:fladder/screens/settings/player_settings_page.dart';
|
|
||||||
import 'package:fladder/screens/settings/settings_screen.dart';
|
|
||||||
import 'package:fladder/screens/settings/security_settings_page.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
class SettingsRoute extends CustomRoute {
|
|
||||||
final String? id;
|
|
||||||
final GlobalKey<NavigatorState>? navKey;
|
|
||||||
SettingsRoute({this.id, this.navKey}) : super(name: 'Settings', basePath: '/settings');
|
|
||||||
|
|
||||||
@override
|
|
||||||
GoRoute get go => GoRoute(
|
|
||||||
path: basePath,
|
|
||||||
parentNavigatorKey: navKey,
|
|
||||||
pageBuilder: (context, state) {
|
|
||||||
return CustomTransitionPage(
|
|
||||||
key: state.pageKey,
|
|
||||||
child: const SettingsScreen(),
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
|
||||||
return FadeTransition(
|
|
||||||
opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class ClientSettingsRoute extends CustomRoute {
|
|
||||||
final String? id;
|
|
||||||
final GlobalKey<NavigatorState>? navKey;
|
|
||||||
ClientSettingsRoute({this.id, this.navKey}) : super(name: 'ClientSettings', basePath: '/settings/client');
|
|
||||||
|
|
||||||
@override
|
|
||||||
GoRoute get go => GoRoute(
|
|
||||||
path: basePath,
|
|
||||||
parentNavigatorKey: navKey,
|
|
||||||
pageBuilder: (context, state) {
|
|
||||||
return CustomTransitionPage(
|
|
||||||
key: state.pageKey,
|
|
||||||
child: const ClientSettingsPage(),
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
|
||||||
return FadeTransition(
|
|
||||||
opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class SecuritySettingsRoute extends CustomRoute {
|
|
||||||
final String? id;
|
|
||||||
final GlobalKey<NavigatorState>? navKey;
|
|
||||||
SecuritySettingsRoute({this.id, this.navKey}) : super(name: 'SecuritySettings', basePath: '/settings/security');
|
|
||||||
|
|
||||||
@override
|
|
||||||
GoRoute get go => GoRoute(
|
|
||||||
path: basePath,
|
|
||||||
parentNavigatorKey: navKey,
|
|
||||||
pageBuilder: (context, state) {
|
|
||||||
return CustomTransitionPage(
|
|
||||||
key: state.pageKey,
|
|
||||||
child: const SecuritySettingsPage(),
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
|
||||||
return FadeTransition(
|
|
||||||
opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class PlayerSettingsRoute extends CustomRoute {
|
|
||||||
final String? id;
|
|
||||||
final GlobalKey<NavigatorState>? navKey;
|
|
||||||
PlayerSettingsRoute({this.id, this.navKey}) : super(name: 'PlayerSettings', basePath: '/settings/player');
|
|
||||||
|
|
||||||
@override
|
|
||||||
GoRoute get go => GoRoute(
|
|
||||||
path: basePath,
|
|
||||||
parentNavigatorKey: navKey,
|
|
||||||
pageBuilder: (context, state) {
|
|
||||||
return CustomTransitionPage(
|
|
||||||
key: state.pageKey,
|
|
||||||
child: const PlayerSettingsPage(),
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
|
||||||
return FadeTransition(
|
|
||||||
opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
86
lib/routes/nested_details_screen.dart
Normal file
86
lib/routes/nested_details_screen.dart
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
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';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class DetailsScreen extends ConsumerStatefulWidget {
|
||||||
|
final String id;
|
||||||
|
final ItemBaseModel? item;
|
||||||
|
const DetailsScreen({@QueryParam() this.id = '', this.item, super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<ConsumerStatefulWidget> createState() => _DetailsScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DetailsScreenState extends ConsumerState<DetailsScreen> {
|
||||||
|
late Widget currentWidget = const Center(
|
||||||
|
key: Key("progress-indicator"),
|
||||||
|
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(() {
|
||||||
|
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 {
|
||||||
|
const DashboardRoute().navigate(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Stack(
|
||||||
|
key: Key(widget.id),
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
||||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||||
import 'package:fladder/models/library_search/library_search_options.dart';
|
import 'package:fladder/models/library_search/library_search_options.dart';
|
||||||
|
|
@ -10,8 +15,7 @@ import 'package:fladder/providers/settings/client_settings_provider.dart';
|
||||||
import 'package:fladder/providers/settings/home_settings_provider.dart';
|
import 'package:fladder/providers/settings/home_settings_provider.dart';
|
||||||
import 'package:fladder/providers/user_provider.dart';
|
import 'package:fladder/providers/user_provider.dart';
|
||||||
import 'package:fladder/providers/views_provider.dart';
|
import 'package:fladder/providers/views_provider.dart';
|
||||||
import 'package:fladder/routes/build_routes/home_routes.dart';
|
import 'package:fladder/routes/auto_router.gr.dart';
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
|
||||||
import 'package:fladder/screens/shared/media/carousel_banner.dart';
|
import 'package:fladder/screens/shared/media/carousel_banner.dart';
|
||||||
import 'package:fladder/screens/shared/media/poster_row.dart';
|
import 'package:fladder/screens/shared/media/poster_row.dart';
|
||||||
import 'package:fladder/screens/shared/nested_scaffold.dart';
|
import 'package:fladder/screens/shared/nested_scaffold.dart';
|
||||||
|
|
@ -23,13 +27,10 @@ import 'package:fladder/util/sliver_list_padding.dart';
|
||||||
import 'package:fladder/widgets/shared/pinch_poster_zoom.dart';
|
import 'package:fladder/widgets/shared/pinch_poster_zoom.dart';
|
||||||
import 'package:fladder/widgets/shared/poster_size_slider.dart';
|
import 'package:fladder/widgets/shared/poster_size_slider.dart';
|
||||||
import 'package:fladder/widgets/shared/pull_to_refresh.dart';
|
import 'package:fladder/widgets/shared/pull_to_refresh.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
class DashboardScreen extends ConsumerStatefulWidget {
|
class DashboardScreen extends ConsumerStatefulWidget {
|
||||||
final ScrollController navigationScrollController;
|
|
||||||
const DashboardScreen({
|
const DashboardScreen({
|
||||||
required this.navigationScrollController,
|
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -91,8 +92,8 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
||||||
child: PinchPosterZoom(
|
child: PinchPosterZoom(
|
||||||
scaleDifference: (difference) => ref.read(clientSettingsProvider.notifier).addPosterSize(difference),
|
scaleDifference: (difference) => ref.read(clientSettingsProvider.notifier).addPosterSize(difference),
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
|
controller: AdaptiveLayout.scrollOf(context),
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
controller: widget.navigationScrollController,
|
|
||||||
slivers: [
|
slivers: [
|
||||||
if (AdaptiveLayout.of(context).layout == LayoutState.phone)
|
if (AdaptiveLayout.of(context).layout == LayoutState.phone)
|
||||||
NestedSliverAppBar(
|
NestedSliverAppBar(
|
||||||
|
|
@ -175,9 +176,9 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
||||||
.map((view) => SliverToBoxAdapter(
|
.map((view) => SliverToBoxAdapter(
|
||||||
child: PosterRow(
|
child: PosterRow(
|
||||||
label: context.localized.dashboardRecentlyAdded(view.name),
|
label: context.localized.dashboardRecentlyAdded(view.name),
|
||||||
onLabelClick: () => context.routePushOrGo(LibrarySearchRoute(
|
onLabelClick: () => context.router.push(LibrarySearchRoute(
|
||||||
id: view.id,
|
viewModelId: view.id,
|
||||||
sortOptions: switch (view.collectionType) {
|
sortingOptions: switch (view.collectionType) {
|
||||||
CollectionType.tvshows ||
|
CollectionType.tvshows ||
|
||||||
CollectionType.books ||
|
CollectionType.books ||
|
||||||
CollectionType.boxsets ||
|
CollectionType.boxsets ||
|
||||||
|
|
@ -186,7 +187,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
||||||
SortingOptions.dateLastContentAdded,
|
SortingOptions.dateLastContentAdded,
|
||||||
_ => SortingOptions.dateAdded,
|
_ => SortingOptions.dateAdded,
|
||||||
},
|
},
|
||||||
sortOrder: SortOrder.descending,
|
sortOrder: SortingOrder.descending,
|
||||||
)),
|
)),
|
||||||
posters: view.recentlyAdded,
|
posters: view.recentlyAdded,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:ficonsax/ficonsax.dart';
|
import 'package:ficonsax/ficonsax.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:fladder/models/book_model.dart';
|
import 'package:fladder/models/book_model.dart';
|
||||||
import 'package:fladder/providers/items/book_details_provider.dart';
|
import 'package:fladder/providers/items/book_details_provider.dart';
|
||||||
import 'package:fladder/providers/user_provider.dart';
|
import 'package:fladder/providers/user_provider.dart';
|
||||||
|
|
@ -12,13 +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/item_base_model/play_item_helpers.dart';
|
||||||
import 'package:fladder/util/list_padding.dart';
|
import 'package:fladder/util/list_padding.dart';
|
||||||
import 'package:fladder/util/localization_helper.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/util/widget_extensions.dart';
|
||||||
import 'package:fladder/widgets/shared/item_actions.dart';
|
import 'package:fladder/widgets/shared/item_actions.dart';
|
||||||
import 'package:fladder/widgets/shared/modal_bottom_sheet.dart';
|
import 'package:fladder/widgets/shared/modal_bottom_sheet.dart';
|
||||||
import 'package:fladder/widgets/shared/selectable_icon_button.dart';
|
import 'package:fladder/widgets/shared/selectable_icon_button.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
class BookDetailScreen extends ConsumerStatefulWidget {
|
class BookDetailScreen extends ConsumerStatefulWidget {
|
||||||
final BookModel item;
|
final BookModel item;
|
||||||
|
|
@ -29,7 +32,8 @@ class BookDetailScreen extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BookDetailScreenState extends ConsumerState<BookDetailScreen> {
|
class _BookDetailScreenState extends ConsumerState<BookDetailScreen> {
|
||||||
late final provider = bookDetailsProvider(widget.item.id);
|
AutoDisposeStateNotifierProvider<BookDetailsProviderNotifier, BookProviderModel> get provider =>
|
||||||
|
bookDetailsProvider(widget.item.id);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -47,7 +51,7 @@ class _BookDetailScreenState extends ConsumerState<BookDetailScreen> {
|
||||||
},
|
},
|
||||||
onDeleteSuccesFully: (item) {
|
onDeleteSuccesFully: (item) {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
context.pop();
|
context.router.popBack();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,28 @@
|
||||||
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:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:ficonsax/ficonsax.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:fladder/models/item_base_model.dart';
|
import 'package:fladder/models/item_base_model.dart';
|
||||||
import 'package:fladder/providers/items/episode_details_provider.dart';
|
import 'package:fladder/providers/items/episode_details_provider.dart';
|
||||||
import 'package:fladder/providers/user_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/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/detail_scaffold.dart';
|
||||||
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
||||||
import 'package:fladder/screens/shared/media/chapter_row.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_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/episode_posters.dart';
|
||||||
import 'package:fladder/screens/shared/media/expanding_overview.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/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/util/widget_extensions.dart';
|
||||||
import 'package:fladder/widgets/shared/selectable_icon_button.dart';
|
import 'package:fladder/widgets/shared/selectable_icon_button.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
class EpisodeDetailScreen extends ConsumerStatefulWidget {
|
class EpisodeDetailScreen extends ConsumerStatefulWidget {
|
||||||
final ItemBaseModel item;
|
final ItemBaseModel item;
|
||||||
|
|
@ -32,7 +33,8 @@ class EpisodeDetailScreen extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ItemDetailScreenState extends ConsumerState<EpisodeDetailScreen> {
|
class _ItemDetailScreenState extends ConsumerState<EpisodeDetailScreen> {
|
||||||
late final providerInstance = episodeDetailsProvider(widget.item.id);
|
AutoDisposeStateNotifierProvider<EpisodeDetailsProvider, EpisodeDetailModel> get providerInstance =>
|
||||||
|
episodeDetailsProvider(widget.item.id);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -52,7 +54,7 @@ class _ItemDetailScreenState extends ConsumerState<EpisodeDetailScreen> {
|
||||||
},
|
},
|
||||||
onDeleteSuccesFully: (item) {
|
onDeleteSuccesFully: (item) {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
context.pop();
|
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:ficonsax/ficonsax.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:fladder/models/item_base_model.dart';
|
import 'package:fladder/models/item_base_model.dart';
|
||||||
import 'package:fladder/providers/items/movies_details_provider.dart';
|
import 'package:fladder/providers/items/movies_details_provider.dart';
|
||||||
import 'package:fladder/providers/user_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/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/detail_scaffold.dart';
|
||||||
import 'package:fladder/screens/shared/media/chapter_row.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/components/media_play_button.dart';
|
||||||
import 'package:fladder/screens/shared/media/expanding_overview.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/people_row.dart';
|
||||||
import 'package:fladder/screens/shared/media/poster_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/item_base_model_extensions.dart';
|
||||||
import 'package:fladder/util/item_base_model/play_item_helpers.dart';
|
import 'package:fladder/util/item_base_model/play_item_helpers.dart';
|
||||||
|
|
||||||
import 'package:fladder/util/list_padding.dart';
|
import 'package:fladder/util/list_padding.dart';
|
||||||
import 'package:fladder/util/widget_extensions.dart';
|
import 'package:fladder/util/widget_extensions.dart';
|
||||||
import 'package:fladder/widgets/shared/selectable_icon_button.dart';
|
import 'package:fladder/widgets/shared/selectable_icon_button.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
class MovieDetailScreen extends ConsumerStatefulWidget {
|
class MovieDetailScreen extends ConsumerStatefulWidget {
|
||||||
final ItemBaseModel item;
|
final ItemBaseModel item;
|
||||||
|
|
@ -30,7 +32,7 @@ class MovieDetailScreen extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ItemDetailScreenState extends ConsumerState<MovieDetailScreen> {
|
class _ItemDetailScreenState extends ConsumerState<MovieDetailScreen> {
|
||||||
late final providerInstance = movieDetailsProvider(widget.item.id);
|
MovieDetailsProvider get providerInstance => movieDetailsProvider(widget.item.id);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -49,7 +51,7 @@ class _ItemDetailScreenState extends ConsumerState<MovieDetailScreen> {
|
||||||
},
|
},
|
||||||
onDeleteSuccesFully: (item) {
|
onDeleteSuccesFully: (item) {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
context.pop();
|
context.router.popBack();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:ficonsax/ficonsax.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/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/items/season_details_provider.dart';
|
||||||
import 'package:fladder/providers/user_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/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/string_extensions.dart';
|
||||||
import 'package:fladder/util/widget_extensions.dart';
|
import 'package:fladder/util/widget_extensions.dart';
|
||||||
import 'package:fladder/widgets/shared/selectable_icon_button.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 {
|
class SeasonDetailScreen extends ConsumerStatefulWidget {
|
||||||
final ItemBaseModel item;
|
final ItemBaseModel item;
|
||||||
|
|
@ -29,7 +32,8 @@ class SeasonDetailScreen extends ConsumerStatefulWidget {
|
||||||
|
|
||||||
class _SeasonDetailScreenState extends ConsumerState<SeasonDetailScreen> {
|
class _SeasonDetailScreenState extends ConsumerState<SeasonDetailScreen> {
|
||||||
Set<EpisodeDetailsViewType> viewOptions = {EpisodeDetailsViewType.grid};
|
Set<EpisodeDetailsViewType> viewOptions = {EpisodeDetailsViewType.grid};
|
||||||
late final providerId = seasonDetailsProvider(widget.item.id);
|
AutoDisposeStateNotifierProvider<SeasonDetailsNotifier, SeasonModel?> get providerId =>
|
||||||
|
seasonDetailsProvider(widget.item.id);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,30 @@
|
||||||
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:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:ficonsax/ficonsax.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:fladder/models/item_base_model.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/items/series_details_provider.dart';
|
||||||
import 'package:fladder/providers/user_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/detail_scaffold.dart';
|
||||||
import 'package:fladder/screens/shared/media/components/media_header.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/episode_posters.dart';
|
||||||
import 'package:fladder/screens/shared/media/expanding_overview.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/people_row.dart';
|
||||||
import 'package:fladder/screens/shared/media/poster_row.dart';
|
import 'package:fladder/screens/shared/media/poster_row.dart';
|
||||||
import 'package:fladder/screens/shared/media/season_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/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/util/widget_extensions.dart';
|
||||||
import 'package:fladder/widgets/shared/selectable_icon_button.dart';
|
import 'package:fladder/widgets/shared/selectable_icon_button.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
class SeriesDetailScreen extends ConsumerStatefulWidget {
|
class SeriesDetailScreen extends ConsumerStatefulWidget {
|
||||||
final ItemBaseModel item;
|
final ItemBaseModel item;
|
||||||
|
|
@ -33,7 +35,8 @@ class SeriesDetailScreen extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SeriesDetailScreenState extends ConsumerState<SeriesDetailScreen> {
|
class _SeriesDetailScreenState extends ConsumerState<SeriesDetailScreen> {
|
||||||
late final providerId = seriesDetailsProvider(widget.item.id);
|
AutoDisposeStateNotifierProvider<SeriesDetailViewNotifier, SeriesModel?> get providerId =>
|
||||||
|
seriesDetailsProvider(widget.item.id);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -51,7 +54,7 @@ class _SeriesDetailScreenState extends ConsumerState<SeriesDetailScreen> {
|
||||||
},
|
},
|
||||||
onDeleteSuccesFully: (item) {
|
onDeleteSuccesFully: (item) {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
context.pop();
|
context.router.popBack();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
||||||
import 'package:fladder/routes/build_routes/home_routes.dart';
|
import 'package:fladder/routes/auto_router.gr.dart';
|
||||||
import 'package:fladder/screens/shared/nested_scaffold.dart';
|
import 'package:fladder/screens/shared/nested_scaffold.dart';
|
||||||
import 'package:fladder/screens/shared/nested_sliver_appbar.dart';
|
import 'package:fladder/screens/shared/nested_sliver_appbar.dart';
|
||||||
import 'package:fladder/util/adaptive_layout.dart';
|
import 'package:fladder/util/adaptive_layout.dart';
|
||||||
|
|
@ -14,10 +15,9 @@ import 'package:fladder/screens/shared/media/poster_grid.dart';
|
||||||
import 'package:fladder/util/sliver_list_padding.dart';
|
import 'package:fladder/util/sliver_list_padding.dart';
|
||||||
import 'package:fladder/widgets/shared/pull_to_refresh.dart';
|
import 'package:fladder/widgets/shared/pull_to_refresh.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
class FavouritesScreen extends ConsumerWidget {
|
class FavouritesScreen extends ConsumerWidget {
|
||||||
final ScrollController navigationScrollController;
|
const FavouritesScreen({super.key});
|
||||||
|
|
||||||
const FavouritesScreen({required this.navigationScrollController, super.key});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
|
@ -30,13 +30,13 @@ class FavouritesScreen extends ConsumerWidget {
|
||||||
scaleDifference: (difference) => ref.read(clientSettingsProvider.notifier).addPosterSize(difference / 2),
|
scaleDifference: (difference) => ref.read(clientSettingsProvider.notifier).addPosterSize(difference / 2),
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
controller: navigationScrollController,
|
controller: AdaptiveLayout.scrollOf(context),
|
||||||
slivers: [
|
slivers: [
|
||||||
if (AdaptiveLayout.of(context).layout == LayoutState.phone)
|
if (AdaptiveLayout.of(context).layout == LayoutState.phone)
|
||||||
NestedSliverAppBar(
|
NestedSliverAppBar(
|
||||||
searchTitle: "${context.localized.search} ${context.localized.favorites.toLowerCase()}...",
|
searchTitle: "${context.localized.search} ${context.localized.favorites.toLowerCase()}...",
|
||||||
parent: context,
|
parent: context,
|
||||||
route: LibrarySearchRoute(favorites: true),
|
route: LibrarySearchRoute(favourites: true),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
const DefaultSliverTopBadding(),
|
const DefaultSliverTopBadding(),
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import 'package:ficonsax/ficonsax.dart';
|
|
||||||
import 'package:fladder/providers/user_provider.dart';
|
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:ficonsax/ficonsax.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:fladder/routes/build_routes/home_routes.dart';
|
import 'package:fladder/providers/user_provider.dart';
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
import 'package:fladder/routes/auto_router.gr.dart';
|
||||||
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
import 'package:fladder/util/string_extensions.dart';
|
import 'package:fladder/util/string_extensions.dart';
|
||||||
import 'package:fladder/widgets/navigation_scaffold/components/adaptive_fab.dart';
|
import 'package:fladder/widgets/navigation_scaffold/components/adaptive_fab.dart';
|
||||||
import 'package:fladder/widgets/navigation_scaffold/components/destination_model.dart';
|
import 'package:fladder/widgets/navigation_scaffold/components/destination_model.dart';
|
||||||
|
|
@ -19,11 +19,9 @@ enum HomeTabs {
|
||||||
sync;
|
sync;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Home extends ConsumerWidget {
|
@RoutePage()
|
||||||
final HomeTabs? currentTab;
|
class HomeScreen extends ConsumerWidget {
|
||||||
final Widget? nestedChild;
|
const HomeScreen({super.key});
|
||||||
final String? location;
|
|
||||||
const Home({this.currentTab, this.nestedChild, this.location, super.key});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
|
@ -35,13 +33,13 @@ class Home extends ConsumerWidget {
|
||||||
label: context.localized.navigationDashboard,
|
label: context.localized.navigationDashboard,
|
||||||
icon: const Icon(IconsaxOutline.home),
|
icon: const Icon(IconsaxOutline.home),
|
||||||
selectedIcon: const Icon(IconsaxBold.home),
|
selectedIcon: const Icon(IconsaxBold.home),
|
||||||
route: DashboardRoute(),
|
route: const DashboardRoute(),
|
||||||
action: () => context.routeGo(DashboardRoute()),
|
action: () => context.router.navigate(const DashboardRoute()),
|
||||||
floatingActionButton: AdaptiveFab(
|
floatingActionButton: AdaptiveFab(
|
||||||
context: context,
|
context: context,
|
||||||
title: context.localized.search,
|
title: context.localized.search,
|
||||||
key: Key(e.name.capitalize()),
|
key: Key(e.name.capitalize()),
|
||||||
onPressed: () => context.routePushOrGo(LibrarySearchRoute()),
|
onPressed: () => context.router.navigate(LibrarySearchRoute()),
|
||||||
child: const Icon(IconsaxOutline.search_normal_1),
|
child: const Icon(IconsaxOutline.search_normal_1),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -50,15 +48,15 @@ class Home extends ConsumerWidget {
|
||||||
label: context.localized.navigationFavorites,
|
label: context.localized.navigationFavorites,
|
||||||
icon: const Icon(IconsaxOutline.heart),
|
icon: const Icon(IconsaxOutline.heart),
|
||||||
selectedIcon: const Icon(IconsaxBold.heart),
|
selectedIcon: const Icon(IconsaxBold.heart),
|
||||||
route: FavouritesRoute(),
|
route: const FavouritesRoute(),
|
||||||
floatingActionButton: AdaptiveFab(
|
floatingActionButton: AdaptiveFab(
|
||||||
context: context,
|
context: context,
|
||||||
title: context.localized.filter(0),
|
title: context.localized.filter(0),
|
||||||
key: Key(e.name.capitalize()),
|
key: Key(e.name.capitalize()),
|
||||||
onPressed: () => context.routePushOrGo(LibrarySearchRoute(favorites: true)),
|
onPressed: () => context.router.navigate(LibrarySearchRoute(favourites: true)),
|
||||||
child: const Icon(IconsaxOutline.heart_search),
|
child: const Icon(IconsaxOutline.heart_search),
|
||||||
),
|
),
|
||||||
action: () => context.routeGo(FavouritesRoute()),
|
action: () => context.router.navigate(const FavouritesRoute()),
|
||||||
);
|
);
|
||||||
case HomeTabs.sync:
|
case HomeTabs.sync:
|
||||||
if (canDownload) {
|
if (canDownload) {
|
||||||
|
|
@ -66,8 +64,8 @@ class Home extends ConsumerWidget {
|
||||||
label: context.localized.navigationSync,
|
label: context.localized.navigationSync,
|
||||||
icon: const Icon(IconsaxOutline.cloud),
|
icon: const Icon(IconsaxOutline.cloud),
|
||||||
selectedIcon: const Icon(IconsaxBold.cloud),
|
selectedIcon: const Icon(IconsaxBold.cloud),
|
||||||
route: SyncRoute(),
|
route: SyncedRoute(),
|
||||||
action: () => context.routeGo(SyncRoute()),
|
action: () => context.router.navigate(SyncedRoute()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -75,12 +73,17 @@ class Home extends ConsumerWidget {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return HeroControllerScope(
|
||||||
return NavigationScaffold(
|
controller: HeroController(),
|
||||||
currentIndex: currentTab?.index ?? 0,
|
child: AutoRouter(
|
||||||
location: location,
|
builder: (context, child) {
|
||||||
nestedChild: nestedChild,
|
return NavigationScaffold(
|
||||||
destinations: destinations.whereNotNull().toList(),
|
destinations: destinations.whereNotNull().toList(),
|
||||||
|
currentRouteName: context.router.current.name,
|
||||||
|
nestedChild: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:ficonsax/ficonsax.dart';
|
import 'package:ficonsax/ficonsax.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
|
@ -30,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/list_padding.dart';
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
import 'package:fladder/util/refresh_state.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/util/sliver_list_padding.dart';
|
||||||
import 'package:fladder/widgets/navigation_scaffold/components/floating_player_bar.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/navigation_scaffold/components/settings_user_icon.dart';
|
||||||
|
|
@ -43,6 +46,7 @@ import 'package:fladder/widgets/shared/pull_to_refresh.dart';
|
||||||
import 'package:fladder/widgets/shared/scroll_position.dart';
|
import 'package:fladder/widgets/shared/scroll_position.dart';
|
||||||
import 'package:fladder/widgets/shared/shapes.dart';
|
import 'package:fladder/widgets/shared/shapes.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
class LibrarySearchScreen extends ConsumerStatefulWidget {
|
class LibrarySearchScreen extends ConsumerStatefulWidget {
|
||||||
final String? viewModelId;
|
final String? viewModelId;
|
||||||
final bool? favourites;
|
final bool? favourites;
|
||||||
|
|
@ -51,11 +55,11 @@ class LibrarySearchScreen extends ConsumerStatefulWidget {
|
||||||
final SortingOptions? sortingOptions;
|
final SortingOptions? sortingOptions;
|
||||||
final PhotoModel? photoToView;
|
final PhotoModel? photoToView;
|
||||||
const LibrarySearchScreen({
|
const LibrarySearchScreen({
|
||||||
this.viewModelId,
|
@QueryParam("parentId") this.viewModelId,
|
||||||
this.folderId,
|
@QueryParam("folderId") this.folderId,
|
||||||
this.favourites,
|
@QueryParam("favourites") this.favourites,
|
||||||
this.sortOrder,
|
@QueryParam("sortOrder") this.sortOrder,
|
||||||
this.sortingOptions,
|
@QueryParam("sortOptions") this.sortingOptions,
|
||||||
this.photoToView,
|
this.photoToView,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
@ -65,9 +69,6 @@ class LibrarySearchScreen extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
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 SearchController searchController = SearchController();
|
||||||
final Debouncer debouncer = Debouncer(const Duration(seconds: 1));
|
final Debouncer debouncer = Debouncer(const Duration(seconds: 1));
|
||||||
final GlobalKey<RefreshIndicatorState> refreshKey = GlobalKey<RefreshIndicatorState>();
|
final GlobalKey<RefreshIndicatorState> refreshKey = GlobalKey<RefreshIndicatorState>();
|
||||||
|
|
@ -76,9 +77,26 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
||||||
|
|
||||||
bool loadOnStart = false;
|
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
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
initLibrary();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initLibrary() {
|
||||||
searchController.addListener(() {
|
searchController.addListener(() {
|
||||||
debouncer.run(() {
|
debouncer.run(() {
|
||||||
ref.read(providerKey.notifier).setSearch(searchController.text);
|
ref.read(providerKey.notifier).setSearch(searchController.text);
|
||||||
|
|
@ -87,7 +105,9 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
||||||
|
|
||||||
Future.microtask(
|
Future.microtask(
|
||||||
() async {
|
() async {
|
||||||
libraryProvider.setDefaultOptions(widget.sortOrder, widget.sortingOptions);
|
if (libraryProvider.mounted) {
|
||||||
|
libraryProvider.setDefaultOptions(widget.sortOrder, widget.sortingOptions);
|
||||||
|
}
|
||||||
await refreshKey.currentState?.show();
|
await refreshKey.currentState?.show();
|
||||||
SystemChrome.setEnabledSystemUIMode(
|
SystemChrome.setEnabledSystemUIMode(
|
||||||
SystemUiMode.edgeToEdge,
|
SystemUiMode.edgeToEdge,
|
||||||
|
|
@ -113,10 +133,8 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isEmptySearchScreen = widget.viewModelId == null && widget.favourites == null && widget.folderId == null;
|
final isEmptySearchScreen = widget.viewModelId == null && widget.favourites == null && widget.folderId == null;
|
||||||
final librarySearchResults = ref.watch(providerKey);
|
final librarySearchResults = ref.watch(providerKey);
|
||||||
final libraryProvider = ref.read(providerKey.notifier);
|
|
||||||
final postersList = librarySearchResults.posters.hideEmptyChildren(librarySearchResults.hideEmtpyShows);
|
final postersList = librarySearchResults.posters.hideEmptyChildren(librarySearchResults.hideEmtpyShows);
|
||||||
final playerState = ref.watch(mediaPlaybackProvider.select((value) => value.state));
|
final playerState = ref.watch(mediaPlaybackProvider.select((value) => value.state));
|
||||||
|
|
||||||
final libraryViewType = ref.watch(libraryViewTypeProvider);
|
final libraryViewType = ref.watch(libraryViewTypeProvider);
|
||||||
|
|
||||||
ref.listen(
|
ref.listen(
|
||||||
|
|
@ -130,6 +148,7 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
||||||
);
|
);
|
||||||
|
|
||||||
return PopScope(
|
return PopScope(
|
||||||
|
key: uniqueKey,
|
||||||
canPop: !librarySearchResults.selecteMode,
|
canPop: !librarySearchResults.selecteMode,
|
||||||
onPopInvokedWithResult: (didPop, result) {
|
onPopInvokedWithResult: (didPop, result) {
|
||||||
if (librarySearchResults.selecteMode) {
|
if (librarySearchResults.selecteMode) {
|
||||||
|
|
@ -139,7 +158,8 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
extendBody: true,
|
extendBody: true,
|
||||||
extendBodyBehindAppBar: true,
|
extendBodyBehindAppBar: true,
|
||||||
floatingActionButtonLocation: playerState == VideoPlayerState.minimized ? FloatingActionButtonLocation.centerFloat : null,
|
floatingActionButtonLocation:
|
||||||
|
playerState == VideoPlayerState.minimized ? FloatingActionButtonLocation.centerFloat : null,
|
||||||
floatingActionButton: switch (playerState) {
|
floatingActionButton: switch (playerState) {
|
||||||
VideoPlayerState.minimized => const Padding(
|
VideoPlayerState.minimized => const Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
|
@ -197,8 +217,9 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
||||||
child: MediaQuery.removeViewInsets(
|
child: MediaQuery.removeViewInsets(
|
||||||
context: context,
|
context: context,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius:
|
borderRadius: AdaptiveLayout.of(context).layout == LayoutState.desktop
|
||||||
AdaptiveLayout.of(context).layout == LayoutState.desktop ? BorderRadius.circular(15) : BorderRadius.circular(0),
|
? BorderRadius.circular(15)
|
||||||
|
: BorderRadius.circular(0),
|
||||||
child: FladderScrollbar(
|
child: FladderScrollbar(
|
||||||
visible: AdaptiveLayout.of(context).inputDevice != InputDevice.pointer,
|
visible: AdaptiveLayout.of(context).inputDevice != InputDevice.pointer,
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
|
|
@ -206,7 +227,12 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
||||||
refreshKey: refreshKey,
|
refreshKey: refreshKey,
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
contextRefresh: 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,
|
refreshOnStart: false,
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
physics: const AlwaysScrollableNoImplicitScrollPhysics(),
|
physics: const AlwaysScrollableNoImplicitScrollPhysics(),
|
||||||
|
|
@ -215,10 +241,11 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
||||||
SliverAppBar(
|
SliverAppBar(
|
||||||
floating: !AdaptiveLayout.of(context).isDesktop,
|
floating: !AdaptiveLayout.of(context).isDesktop,
|
||||||
collapsedHeight: 80,
|
collapsedHeight: 80,
|
||||||
automaticallyImplyLeading: true,
|
automaticallyImplyLeading: false,
|
||||||
pinned: AdaptiveLayout.of(context).isDesktop,
|
pinned: AdaptiveLayout.of(context).isDesktop,
|
||||||
primary: true,
|
primary: true,
|
||||||
elevation: 5,
|
elevation: 5,
|
||||||
|
leading: context.router.backButton(),
|
||||||
surfaceTintColor: Colors.transparent,
|
surfaceTintColor: Colors.transparent,
|
||||||
shadowColor: Colors.transparent,
|
shadowColor: Colors.transparent,
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
|
|
@ -228,7 +255,8 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
||||||
actions: [
|
actions: [
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Builder(builder: (context) {
|
Builder(builder: (context) {
|
||||||
final isFavorite = librarySearchResults.nestedCurrentItem?.userData.isFavourite == true;
|
final isFavorite =
|
||||||
|
librarySearchResults.nestedCurrentItem?.userData.isFavourite == true;
|
||||||
final itemActions = librarySearchResults.nestedCurrentItem?.generateActions(
|
final itemActions = librarySearchResults.nestedCurrentItem?.generateActions(
|
||||||
context,
|
context,
|
||||||
ref,
|
ref,
|
||||||
|
|
@ -276,7 +304,8 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
||||||
(e) => FilledButton.tonal(
|
(e) => FilledButton.tonal(
|
||||||
style: FilledButtonTheme.of(context).style?.copyWith(
|
style: FilledButtonTheme.of(context).style?.copyWith(
|
||||||
padding: const WidgetStatePropertyAll(
|
padding: const WidgetStatePropertyAll(
|
||||||
EdgeInsets.symmetric(horizontal: 12, vertical: 24)),
|
EdgeInsets.symmetric(
|
||||||
|
horizontal: 12, vertical: 24)),
|
||||||
backgroundColor: WidgetStateProperty.resolveWith(
|
backgroundColor: WidgetStateProperty.resolveWith(
|
||||||
(states) {
|
(states) {
|
||||||
if (e != currentType) {
|
if (e != currentType) {
|
||||||
|
|
@ -312,8 +341,8 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
||||||
return Card(
|
return Card(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
message:
|
message: librarySearchResults.nestedCurrentItem?.type.label(context) ??
|
||||||
librarySearchResults.nestedCurrentItem?.type.label(context) ?? context.localized.library(1),
|
context.localized.library(1),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTapUp: (details) async {
|
onTapUp: (details) async {
|
||||||
if (AdaptiveLayout.of(context).inputDevice == InputDevice.pointer) {
|
if (AdaptiveLayout.of(context).inputDevice == InputDevice.pointer) {
|
||||||
|
|
@ -324,8 +353,9 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
||||||
position: RelativeRect.fromLTRB(left, top, 40, 100),
|
position: RelativeRect.fromLTRB(left, top, 40, 100),
|
||||||
items: <PopupMenuEntry>[
|
items: <PopupMenuEntry>[
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Text(librarySearchResults.nestedCurrentItem?.type.label(context) ??
|
child: Text(
|
||||||
context.localized.library(0))),
|
librarySearchResults.nestedCurrentItem?.type.label(context) ??
|
||||||
|
context.localized.library(0))),
|
||||||
itemCountWidget.toPopupMenuItem(useIcons: true),
|
itemCountWidget.toPopupMenuItem(useIcons: true),
|
||||||
refreshAction.toPopupMenuItem(useIcons: true),
|
refreshAction.toPopupMenuItem(useIcons: true),
|
||||||
itemViewAction.toPopupMenuItem(useIcons: true),
|
itemViewAction.toPopupMenuItem(useIcons: true),
|
||||||
|
|
@ -356,7 +386,8 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
||||||
child: Icon(
|
child: Icon(
|
||||||
isFavorite
|
isFavorite
|
||||||
? librarySearchResults.nestedCurrentItem?.type.selectedicon
|
? librarySearchResults.nestedCurrentItem?.type.selectedicon
|
||||||
: librarySearchResults.nestedCurrentItem?.type.icon ?? IconsaxOutline.document,
|
: librarySearchResults.nestedCurrentItem?.type.icon ??
|
||||||
|
IconsaxOutline.document,
|
||||||
color: isFavorite ? Theme.of(context).colorScheme.primary : null,
|
color: isFavorite ? Theme.of(context).colorScheme.primary : null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -433,7 +464,8 @@ class _LibrarySearchScreenState extends ConsumerState<LibrarySearchScreen> {
|
||||||
if (postersList.isNotEmpty)
|
if (postersList.isNotEmpty)
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
left: MediaQuery.of(context).padding.left, right: MediaQuery.of(context).padding.right),
|
left: MediaQuery.of(context).padding.left,
|
||||||
|
right: MediaQuery.of(context).padding.right),
|
||||||
sliver: LibraryViews(
|
sliver: LibraryViews(
|
||||||
key: uniqueKey,
|
key: uniqueKey,
|
||||||
items: postersList,
|
items: postersList,
|
||||||
|
|
@ -568,7 +600,8 @@ class _LibrarySearchBottomBar extends ConsumerWidget {
|
||||||
},
|
},
|
||||||
label: Text(context.localized.removeFromCollection),
|
label: Text(context.localized.removeFromCollection),
|
||||||
icon: Container(
|
icon: Container(
|
||||||
decoration: BoxDecoration(color: Theme.of(context).colorScheme.onPrimary, borderRadius: BorderRadius.circular(6)),
|
decoration:
|
||||||
|
BoxDecoration(color: Theme.of(context).colorScheme.onPrimary, borderRadius: BorderRadius.circular(6)),
|
||||||
child: const Padding(
|
child: const Padding(
|
||||||
padding: EdgeInsets.all(3.0),
|
padding: EdgeInsets.all(3.0),
|
||||||
child: Icon(IconsaxOutline.save_remove, size: 20),
|
child: Icon(IconsaxOutline.save_remove, size: 20),
|
||||||
|
|
@ -620,8 +653,8 @@ class _LibrarySearchBottomBar extends ConsumerWidget {
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
borderRadiusGeometry: BorderRadius.circular(6),
|
borderRadiusGeometry: BorderRadius.circular(6),
|
||||||
onTap: () =>
|
onTap: () => scrollController.animateTo(0,
|
||||||
scrollController.animateTo(0, duration: const Duration(milliseconds: 500), curve: Curves.easeInOutCubic),
|
duration: const Duration(milliseconds: 500), curve: Curves.easeInOutCubic),
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.primaryContainer,
|
color: Theme.of(context).colorScheme.primaryContainer,
|
||||||
|
|
@ -679,8 +712,9 @@ class _LibrarySearchBottomBar extends ConsumerWidget {
|
||||||
AnimatedFadeSize(
|
AnimatedFadeSize(
|
||||||
child: librarySearchResults.selecteMode
|
child: librarySearchResults.selecteMode
|
||||||
? Container(
|
? Container(
|
||||||
decoration:
|
decoration: BoxDecoration(
|
||||||
BoxDecoration(color: Theme.of(context).colorScheme.primaryContainer, borderRadius: BorderRadius.circular(16)),
|
color: Theme.of(context).colorScheme.primaryContainer,
|
||||||
|
borderRadius: BorderRadius.circular(16)),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Tooltip(
|
Tooltip(
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:ficonsax/ficonsax.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/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
||||||
import 'package:fladder/models/item_base_model.dart';
|
import 'package:fladder/models/item_base_model.dart';
|
||||||
import 'package:fladder/models/items/item_shared_models.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/map_bool_helper.dart';
|
||||||
import 'package:fladder/util/refresh_state.dart';
|
import 'package:fladder/util/refresh_state.dart';
|
||||||
import 'package:fladder/widgets/shared/scroll_position.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 {
|
class LibraryFilterChips extends ConsumerWidget {
|
||||||
final Key uniqueKey;
|
final Key uniqueKey;
|
||||||
|
|
@ -188,12 +190,12 @@ List<Widget> libraryFilterChips(
|
||||||
if (librarySearchResults.types[FladderItemType.series] == true)
|
if (librarySearchResults.types[FladderItemType.series] == true)
|
||||||
FilterChip(
|
FilterChip(
|
||||||
avatar: Icon(
|
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,
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
selected: librarySearchResults.hideEmtpyShows,
|
selected: librarySearchResults.hideEmtpyShows,
|
||||||
showCheckmark: false,
|
showCheckmark: false,
|
||||||
label: Text(librarySearchResults.hideEmtpyShows ? context.localized.showEmpty : context.localized.hideEmpty),
|
label: Text(context.localized.hideEmpty),
|
||||||
onSelected: libraryProvider.setHideEmpty,
|
onSelected: libraryProvider.setHideEmpty,
|
||||||
),
|
),
|
||||||
if (librarySearchResults.officialRatings.isNotEmpty)
|
if (librarySearchResults.officialRatings.isNotEmpty)
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,24 @@
|
||||||
import 'package:ficonsax/ficonsax.dart';
|
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.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';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
import 'package:fladder/models/account_model.dart';
|
import 'package:fladder/models/account_model.dart';
|
||||||
import 'package:fladder/providers/user_provider.dart';
|
import 'package:fladder/providers/user_provider.dart';
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
import 'package:fladder/routes/auto_router.gr.dart';
|
||||||
import 'package:fladder/screens/login/widgets/login_icon.dart';
|
import 'package:fladder/screens/login/widgets/login_icon.dart';
|
||||||
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
||||||
import 'package:fladder/screens/shared/passcode_input.dart';
|
import 'package:fladder/screens/shared/passcode_input.dart';
|
||||||
import 'package:fladder/util/auth_service.dart';
|
import 'package:fladder/util/auth_service.dart';
|
||||||
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
|
|
||||||
final lockScreenActiveProvider = StateProvider<bool>((ref) => false);
|
final lockScreenActiveProvider = StateProvider<bool>((ref) => false);
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
class LockScreen extends ConsumerStatefulWidget {
|
class LockScreen extends ConsumerStatefulWidget {
|
||||||
final bool selfLock;
|
const LockScreen({super.key});
|
||||||
const LockScreen({this.selfLock = false, super.key});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<ConsumerStatefulWidget> createState() => _LockScreenState();
|
ConsumerState<ConsumerStatefulWidget> createState() => _LockScreenState();
|
||||||
|
|
@ -52,10 +52,6 @@ class _LockScreenState extends ConsumerState<LockScreen> with WidgetsBindingObse
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
Future.microtask(() {
|
Future.microtask(() {
|
||||||
ref.read(lockScreenActiveProvider.notifier).update((state) => true);
|
ref.read(lockScreenActiveProvider.notifier).update((state) => true);
|
||||||
final user = ref.read(userProvider);
|
|
||||||
if (user != null && !widget.selfLock) {
|
|
||||||
tapLoggedInAccount(user);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
hackyFixForBlackNavbar();
|
hackyFixForBlackNavbar();
|
||||||
}
|
}
|
||||||
|
|
@ -63,7 +59,7 @@ class _LockScreenState extends ConsumerState<LockScreen> with WidgetsBindingObse
|
||||||
void handleLogin(AccountModel user) {
|
void handleLogin(AccountModel user) {
|
||||||
ref.read(lockScreenActiveProvider.notifier).update((state) => false);
|
ref.read(lockScreenActiveProvider.notifier).update((state) => false);
|
||||||
poppingLockScreen = true;
|
poppingLockScreen = true;
|
||||||
context.pop();
|
context.router.popForced();
|
||||||
}
|
}
|
||||||
|
|
||||||
void tapLoggedInAccount(AccountModel user) async {
|
void tapLoggedInAccount(AccountModel user) async {
|
||||||
|
|
@ -131,7 +127,7 @@ class _LockScreenState extends ConsumerState<LockScreen> with WidgetsBindingObse
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ref.read(lockScreenActiveProvider.notifier).update((state) => false);
|
ref.read(lockScreenActiveProvider.notifier).update((state) => false);
|
||||||
context.routeGo(LoginRoute());
|
context.router.push(const LoginRoute());
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.login_rounded),
|
icon: const Icon(Icons.login_rounded),
|
||||||
label: Text(context.localized.login),
|
label: Text(context.localized.login),
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,34 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:ficonsax/ficonsax.dart';
|
|
||||||
import 'package:fladder/screens/login/lock_screen.dart';
|
|
||||||
import 'package:fladder/screens/login/widgets/discover_servers_widget.dart';
|
|
||||||
import 'package:fladder/screens/shared/fladder_logo.dart';
|
|
||||||
import 'package:fladder/util/adaptive_layout.dart';
|
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
|
||||||
import 'package:fladder/util/string_extensions.dart';
|
|
||||||
import 'package:flutter/material.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:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
import 'package:fladder/models/account_model.dart';
|
import 'package:fladder/models/account_model.dart';
|
||||||
import 'package:fladder/providers/auth_provider.dart';
|
import 'package:fladder/providers/auth_provider.dart';
|
||||||
import 'package:fladder/providers/shared_provider.dart';
|
import 'package:fladder/providers/shared_provider.dart';
|
||||||
import 'package:fladder/providers/user_provider.dart';
|
import 'package:fladder/providers/user_provider.dart';
|
||||||
import 'package:fladder/routes/build_routes/home_routes.dart';
|
import 'package:fladder/routes/auto_router.gr.dart';
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
import 'package:fladder/screens/login/lock_screen.dart';
|
||||||
import 'package:fladder/screens/login/login_edit_user.dart';
|
import 'package:fladder/screens/login/login_edit_user.dart';
|
||||||
import 'package:fladder/screens/login/login_user_grid.dart';
|
import 'package:fladder/screens/login/login_user_grid.dart';
|
||||||
|
import 'package:fladder/screens/login/widgets/discover_servers_widget.dart';
|
||||||
import 'package:fladder/screens/shared/animated_fade_size.dart';
|
import 'package:fladder/screens/shared/animated_fade_size.dart';
|
||||||
|
import 'package:fladder/screens/shared/fladder_logo.dart';
|
||||||
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
||||||
import 'package:fladder/screens/shared/outlined_text_field.dart';
|
import 'package:fladder/screens/shared/outlined_text_field.dart';
|
||||||
import 'package:fladder/screens/shared/passcode_input.dart';
|
import 'package:fladder/screens/shared/passcode_input.dart';
|
||||||
|
import 'package:fladder/util/adaptive_layout.dart';
|
||||||
import 'package:fladder/util/auth_service.dart';
|
import 'package:fladder/util/auth_service.dart';
|
||||||
import 'package:fladder/util/list_padding.dart';
|
import 'package:fladder/util/list_padding.dart';
|
||||||
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
|
import 'package:fladder/util/string_extensions.dart';
|
||||||
import 'package:fladder/widgets/navigation_scaffold/components/fladder_appbar.dart';
|
import 'package:fladder/widgets/navigation_scaffold/components/fladder_appbar.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
class LoginScreen extends ConsumerStatefulWidget {
|
class LoginScreen extends ConsumerStatefulWidget {
|
||||||
const LoginScreen({super.key});
|
const LoginScreen({super.key});
|
||||||
|
|
||||||
|
|
@ -164,7 +164,7 @@ class _LoginPageState extends ConsumerState<LoginScreen> {
|
||||||
serverTextController.text = value;
|
serverTextController.text = value;
|
||||||
startAddingNewUser();
|
startAddingNewUser();
|
||||||
});
|
});
|
||||||
context.pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -211,7 +211,7 @@ class _LoginPageState extends ConsumerState<LoginScreen> {
|
||||||
void loggedInGoToHome() {
|
void loggedInGoToHome() {
|
||||||
ref.read(lockScreenActiveProvider.notifier).update((state) => false);
|
ref.read(lockScreenActiveProvider.notifier).update((state) => false);
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
context.routeGo(DashboardRoute());
|
context.router.navigate(const DashboardRoute());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:ficonsax/ficonsax.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/item_base_model.dart';
|
||||||
import 'package:fladder/providers/items/identify_provider.dart';
|
import 'package:fladder/providers/items/identify_provider.dart';
|
||||||
import 'package:fladder/screens/shared/adaptive_dialog.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/screens/shared/media/external_urls.dart';
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
import 'package:fladder/util/string_extensions.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 {
|
Future<void> showIdentifyScreen(BuildContext context, ItemBaseModel item) async {
|
||||||
return showDialogAdaptive(
|
return showDialogAdaptive(
|
||||||
|
|
@ -30,7 +32,7 @@ class IdentifyScreen extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _IdentifyScreenState extends ConsumerState<IdentifyScreen> with TickerProviderStateMixin {
|
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);
|
late final TabController tabController = TabController(length: 2, vsync: this);
|
||||||
|
|
||||||
TextEditingController? currentController;
|
TextEditingController? currentController;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:ficonsax/ficonsax.dart';
|
import 'package:ficonsax/ficonsax.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:fladder/models/information_model.dart';
|
import 'package:fladder/models/information_model.dart';
|
||||||
import 'package:fladder/models/item_base_model.dart';
|
import 'package:fladder/models/item_base_model.dart';
|
||||||
import 'package:fladder/providers/items/information_provider.dart';
|
import 'package:fladder/providers/items/information_provider.dart';
|
||||||
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
import 'package:fladder/widgets/shared/clickable_text.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 {
|
Future<void> showInfoScreen(BuildContext context, ItemBaseModel item) async {
|
||||||
return showDialog(
|
return showDialog(
|
||||||
|
|
@ -27,7 +29,7 @@ class ItemInfoScreen extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ItemInfoScreenState extends ConsumerState<ItemInfoScreen> {
|
class ItemInfoScreenState extends ConsumerState<ItemInfoScreen> {
|
||||||
late AutoDisposeStateNotifierProvider<InformationNotifier, InformationProviderModel> provider =
|
AutoDisposeStateNotifierProvider<InformationNotifier, InformationProviderModel> get provider =>
|
||||||
informationProvider(widget.item.id);
|
informationProvider(widget.item.id);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:ficonsax/ficonsax.dart';
|
import 'package:ficonsax/ficonsax.dart';
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
import 'package:fladder/models/settings/home_settings_model.dart';
|
import 'package:fladder/models/settings/home_settings_model.dart';
|
||||||
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
||||||
|
|
@ -13,7 +13,7 @@ import 'package:fladder/providers/settings/home_settings_provider.dart';
|
||||||
import 'package:fladder/providers/shared_provider.dart';
|
import 'package:fladder/providers/shared_provider.dart';
|
||||||
import 'package:fladder/providers/sync_provider.dart';
|
import 'package:fladder/providers/sync_provider.dart';
|
||||||
import 'package:fladder/providers/user_provider.dart';
|
import 'package:fladder/providers/user_provider.dart';
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
import 'package:fladder/routes/auto_router.gr.dart';
|
||||||
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
||||||
import 'package:fladder/screens/settings/settings_scaffold.dart';
|
import 'package:fladder/screens/settings/settings_scaffold.dart';
|
||||||
import 'package:fladder/screens/settings/widgets/settings_label_divider.dart';
|
import 'package:fladder/screens/settings/widgets/settings_label_divider.dart';
|
||||||
|
|
@ -30,6 +30,7 @@ import 'package:fladder/util/theme_mode_extension.dart';
|
||||||
import 'package:fladder/widgets/shared/enum_selection.dart';
|
import 'package:fladder/widgets/shared/enum_selection.dart';
|
||||||
import 'package:fladder/widgets/shared/fladder_slider.dart';
|
import 'package:fladder/widgets/shared/fladder_slider.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
class ClientSettingsPage extends ConsumerStatefulWidget {
|
class ClientSettingsPage extends ConsumerStatefulWidget {
|
||||||
const ClientSettingsPage({super.key});
|
const ClientSettingsPage({super.key});
|
||||||
|
|
||||||
|
|
@ -38,16 +39,17 @@ class ClientSettingsPage extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
||||||
late final nextUpDaysEditor =
|
late final nextUpDaysEditor = TextEditingController(
|
||||||
TextEditingController(text: ref.read(clientSettingsProvider.select((value) => value.nextUpDateCutoff?.inDays ?? 14)).toString());
|
text: ref.read(clientSettingsProvider.select((value) => value.nextUpDateCutoff?.inDays ?? 14)).toString());
|
||||||
|
|
||||||
late final libraryPageSizeController =
|
late final libraryPageSizeController = TextEditingController(
|
||||||
TextEditingController(text: ref.read(clientSettingsProvider.select((value) => value.libraryPageSize))?.toString() ?? "");
|
text: ref.read(clientSettingsProvider.select((value) => value.libraryPageSize))?.toString() ?? "");
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final clientSettings = ref.watch(clientSettingsProvider);
|
final clientSettings = ref.watch(clientSettingsProvider);
|
||||||
final showBackground = AdaptiveLayout.of(context).layout != LayoutState.phone && AdaptiveLayout.of(context).size != ScreenLayout.single;
|
final showBackground = AdaptiveLayout.of(context).layout != LayoutState.phone &&
|
||||||
|
AdaptiveLayout.of(context).size != ScreenLayout.single;
|
||||||
final currentFolder = ref.watch(syncProvider.notifier).savePath;
|
final currentFolder = ref.watch(syncProvider.notifier).savePath;
|
||||||
Locale currentLocale = WidgetsBinding.instance.platformDispatcher.locale;
|
Locale currentLocale = WidgetsBinding.instance.platformDispatcher.locale;
|
||||||
|
|
||||||
|
|
@ -72,8 +74,8 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
||||||
actions: [
|
actions: [
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
String? selectedDirectory = await FilePicker.platform
|
String? selectedDirectory = await FilePicker.platform.getDirectoryPath(
|
||||||
.getDirectoryPath(dialogTitle: context.localized.pathEditSelect, initialDirectory: currentFolder);
|
dialogTitle: context.localized.pathEditSelect, initialDirectory: currentFolder);
|
||||||
if (selectedDirectory != null) {
|
if (selectedDirectory != null) {
|
||||||
ref.read(clientSettingsProvider.notifier).setSyncPath(selectedDirectory);
|
ref.read(clientSettingsProvider.notifier).setSyncPath(selectedDirectory);
|
||||||
}
|
}
|
||||||
|
|
@ -85,8 +87,8 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: () async {
|
: () async {
|
||||||
String? selectedDirectory = await FilePicker.platform
|
String? selectedDirectory = await FilePicker.platform.getDirectoryPath(
|
||||||
.getDirectoryPath(dialogTitle: context.localized.pathEditSelect, initialDirectory: currentFolder);
|
dialogTitle: context.localized.pathEditSelect, initialDirectory: currentFolder);
|
||||||
if (selectedDirectory != null) {
|
if (selectedDirectory != null) {
|
||||||
ref.read(clientSettingsProvider.notifier).setSyncPath(selectedDirectory);
|
ref.read(clientSettingsProvider.notifier).setSyncPath(selectedDirectory);
|
||||||
}
|
}
|
||||||
|
|
@ -131,10 +133,10 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
||||||
(context) async {
|
(context) async {
|
||||||
await ref.read(syncProvider.notifier).clear();
|
await ref.read(syncProvider.notifier).clear();
|
||||||
setState(() {});
|
setState(() {});
|
||||||
context.pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
context.localized.clear,
|
context.localized.clear,
|
||||||
(context) => context.pop(),
|
(context) => Navigator.of(context).pop(),
|
||||||
context.localized.cancel,
|
context.localized.cancel,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -155,9 +157,9 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
||||||
initialValue: clientSettings.timeOut ?? const Duration(),
|
initialValue: clientSettings.timeOut ?? const Duration(),
|
||||||
);
|
);
|
||||||
|
|
||||||
ref
|
ref.read(clientSettingsProvider.notifier).setTimeOut(timePicker != null
|
||||||
.read(clientSettingsProvider.notifier)
|
? Duration(minutes: timePicker.inMinutes, seconds: timePicker.inSeconds % 60)
|
||||||
.setTimeOut(timePicker != null ? Duration(minutes: timePicker.inMinutes, seconds: timePicker.inSeconds % 60) : null);
|
: null);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
|
|
@ -176,7 +178,9 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
||||||
(entry) => PopupMenuItem(
|
(entry) => PopupMenuItem(
|
||||||
value: entry,
|
value: entry,
|
||||||
child: Text(entry.label(context)),
|
child: Text(entry.label(context)),
|
||||||
onTap: () => ref.read(homeSettingsProvider.notifier).update((context) => context.copyWith(carouselSettings: entry)),
|
onTap: () => ref
|
||||||
|
.read(homeSettingsProvider.notifier)
|
||||||
|
.update((context) => context.copyWith(carouselSettings: entry)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
|
|
@ -196,7 +200,8 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
||||||
(entry) => PopupMenuItem(
|
(entry) => PopupMenuItem(
|
||||||
value: entry,
|
value: entry,
|
||||||
child: Text(entry.label(context)),
|
child: Text(entry.label(context)),
|
||||||
onTap: () => ref.read(homeSettingsProvider.notifier).update((context) => context.copyWith(nextUp: entry)),
|
onTap: () =>
|
||||||
|
ref.read(homeSettingsProvider.notifier).update((context) => context.copyWith(nextUp: entry)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
|
|
@ -223,7 +228,9 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
||||||
fontWeight: currentLocale.languageCode == entry.languageCode ? FontWeight.bold : null,
|
fontWeight: currentLocale.languageCode == entry.languageCode ? FontWeight.bold : null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () => ref.read(clientSettingsProvider.notifier).update((state) => state.copyWith(selectedLocale: entry)),
|
onTap: () => ref
|
||||||
|
.read(clientSettingsProvider.notifier)
|
||||||
|
.update((state) => state.copyWith(selectedLocale: entry)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
@ -233,7 +240,8 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
||||||
SettingsListTile(
|
SettingsListTile(
|
||||||
label: Text(context.localized.settingsBlurredPlaceholderTitle),
|
label: Text(context.localized.settingsBlurredPlaceholderTitle),
|
||||||
subLabel: Text(context.localized.settingsBlurredPlaceholderDesc),
|
subLabel: Text(context.localized.settingsBlurredPlaceholderDesc),
|
||||||
onTap: () => ref.read(clientSettingsProvider.notifier).setBlurPlaceholders(!clientSettings.blurPlaceHolders),
|
onTap: () =>
|
||||||
|
ref.read(clientSettingsProvider.notifier).setBlurPlaceholders(!clientSettings.blurPlaceHolders),
|
||||||
trailing: Switch(
|
trailing: Switch(
|
||||||
value: clientSettings.blurPlaceHolders,
|
value: clientSettings.blurPlaceHolders,
|
||||||
onChanged: (value) => ref.read(clientSettingsProvider.notifier).setBlurPlaceholders(value),
|
onChanged: (value) => ref.read(clientSettingsProvider.notifier).setBlurPlaceholders(value),
|
||||||
|
|
@ -242,7 +250,8 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
||||||
SettingsListTile(
|
SettingsListTile(
|
||||||
label: Text(context.localized.settingsBlurEpisodesTitle),
|
label: Text(context.localized.settingsBlurEpisodesTitle),
|
||||||
subLabel: Text(context.localized.settingsBlurEpisodesDesc),
|
subLabel: Text(context.localized.settingsBlurEpisodesDesc),
|
||||||
onTap: () => ref.read(clientSettingsProvider.notifier).setBlurEpisodes(!clientSettings.blurUpcomingEpisodes),
|
onTap: () =>
|
||||||
|
ref.read(clientSettingsProvider.notifier).setBlurEpisodes(!clientSettings.blurUpcomingEpisodes),
|
||||||
trailing: Switch(
|
trailing: Switch(
|
||||||
value: clientSettings.blurUpcomingEpisodes,
|
value: clientSettings.blurUpcomingEpisodes,
|
||||||
onChanged: (value) => ref.read(clientSettingsProvider.notifier).setBlurEpisodes(value),
|
onChanged: (value) => ref.read(clientSettingsProvider.notifier).setBlurEpisodes(value),
|
||||||
|
|
@ -286,8 +295,9 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
SettingsListTile(
|
SettingsListTile(
|
||||||
label: Text(
|
label: Text(AdaptiveLayout.of(context).isDesktop
|
||||||
AdaptiveLayout.of(context).isDesktop ? context.localized.settingsShowScaleSlider : context.localized.settingsPosterPinch),
|
? context.localized.settingsShowScaleSlider
|
||||||
|
: context.localized.settingsPosterPinch),
|
||||||
onTap: () => ref.read(clientSettingsProvider.notifier).update(
|
onTap: () => ref.read(clientSettingsProvider.notifier).update(
|
||||||
(current) => current.copyWith(pinchPosterZoom: !current.pinchPosterZoom),
|
(current) => current.copyWith(pinchPosterZoom: !current.pinchPosterZoom),
|
||||||
),
|
),
|
||||||
|
|
@ -314,7 +324,9 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
||||||
max: 1.5,
|
max: 1.5,
|
||||||
value: clientSettings.posterSize,
|
value: clientSettings.posterSize,
|
||||||
divisions: 20,
|
divisions: 20,
|
||||||
onChanged: (value) => ref.read(clientSettingsProvider.notifier).update((current) => current.copyWith(posterSize: value)),
|
onChanged: (value) => ref
|
||||||
|
.read(clientSettingsProvider.notifier)
|
||||||
|
.update((current) => current.copyWith(posterSize: value)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
|
|
@ -439,14 +451,14 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: () => context.pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
child: Text(context.localized.cancel),
|
child: Text(context.localized.cancel),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await ref.read(sharedPreferencesProvider).clear();
|
await ref.read(sharedPreferencesProvider).clear();
|
||||||
context.routeGo(LoginRoute());
|
context.router.push(const LoginRoute());
|
||||||
},
|
},
|
||||||
child: Text(context.localized.clear),
|
child: Text(context.localized.clear),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:fladder/providers/settings/video_player_settings_provider.dart';
|
import 'package:fladder/providers/settings/video_player_settings_provider.dart';
|
||||||
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
||||||
import 'package:fladder/screens/settings/settings_scaffold.dart';
|
import 'package:fladder/screens/settings/settings_scaffold.dart';
|
||||||
|
|
@ -14,6 +15,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'dart:io' show Platform;
|
import 'dart:io' show Platform;
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
class PlayerSettingsPage extends ConsumerStatefulWidget {
|
class PlayerSettingsPage extends ConsumerStatefulWidget {
|
||||||
const PlayerSettingsPage({super.key});
|
const PlayerSettingsPage({super.key});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:fladder/providers/user_provider.dart';
|
import 'package:fladder/providers/user_provider.dart';
|
||||||
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
||||||
import 'package:fladder/screens/settings/settings_scaffold.dart';
|
import 'package:fladder/screens/settings/settings_scaffold.dart';
|
||||||
|
|
@ -8,6 +9,7 @@ import 'package:fladder/util/localization_helper.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
class SecuritySettingsPage extends ConsumerStatefulWidget {
|
class SecuritySettingsPage extends ConsumerStatefulWidget {
|
||||||
const SecuritySettingsPage({super.key});
|
const SecuritySettingsPage({super.key});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +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/providers/user_provider.dart';
|
||||||
import 'package:fladder/screens/shared/user_icon.dart';
|
import 'package:fladder/screens/shared/user_icon.dart';
|
||||||
import 'package:fladder/util/adaptive_layout.dart';
|
import 'package:fladder/util/adaptive_layout.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:fladder/util/router_extension.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
|
|
||||||
class SettingsScaffold extends ConsumerWidget {
|
class SettingsScaffold extends ConsumerWidget {
|
||||||
final String label;
|
final String label;
|
||||||
|
|
@ -25,7 +29,7 @@ class SettingsScaffold extends ConsumerWidget {
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final padding = MediaQuery.of(context).padding;
|
final padding = MediaQuery.of(context).padding;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: AdaptiveLayout.of(context).isDesktop ? Colors.transparent : null,
|
backgroundColor: AdaptiveLayout.of(context).size == ScreenLayout.dual ? Colors.transparent : null,
|
||||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
||||||
floatingActionButton: floatingActionButton,
|
floatingActionButton: floatingActionButton,
|
||||||
body: Column(
|
body: Column(
|
||||||
|
|
@ -36,8 +40,8 @@ class SettingsScaffold extends ConsumerWidget {
|
||||||
slivers: [
|
slivers: [
|
||||||
if (AdaptiveLayout.of(context).size == ScreenLayout.single)
|
if (AdaptiveLayout.of(context).size == ScreenLayout.single)
|
||||||
SliverAppBar.large(
|
SliverAppBar.large(
|
||||||
titleSpacing: 20,
|
|
||||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
|
leading: context.router.backButton(),
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
titlePadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16)
|
titlePadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16)
|
||||||
.add(EdgeInsets.only(left: padding.left, right: padding.right)),
|
.add(EdgeInsets.only(left: padding.left, right: padding.right)),
|
||||||
|
|
@ -54,10 +58,10 @@ class SettingsScaffold extends ConsumerWidget {
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
expandedTitleScale: 2,
|
expandedTitleScale: 1.2,
|
||||||
),
|
),
|
||||||
expandedHeight: 175,
|
expandedHeight: 100,
|
||||||
collapsedHeight: 100,
|
collapsedHeight: 80,
|
||||||
pinned: false,
|
pinned: false,
|
||||||
floating: true,
|
floating: true,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,27 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:ficonsax/ficonsax.dart';
|
import 'package:ficonsax/ficonsax.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
import 'package:fladder/providers/auth_provider.dart';
|
||||||
import 'package:fladder/providers/user_provider.dart';
|
import 'package:fladder/providers/user_provider.dart';
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
import 'package:fladder/routes/auto_router.gr.dart';
|
||||||
import 'package:fladder/routes/build_routes/settings_routes.dart';
|
|
||||||
import 'package:fladder/screens/settings/quick_connect_window.dart';
|
import 'package:fladder/screens/settings/quick_connect_window.dart';
|
||||||
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
||||||
import 'package:fladder/screens/settings/settings_scaffold.dart';
|
import 'package:fladder/screens/settings/settings_scaffold.dart';
|
||||||
import 'package:fladder/screens/shared/animated_fade_size.dart';
|
import 'package:fladder/screens/shared/animated_fade_size.dart';
|
||||||
import 'package:fladder/screens/shared/fladder_icon.dart';
|
import 'package:fladder/screens/shared/fladder_icon.dart';
|
||||||
import 'package:fladder/util/adaptive_layout.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/localization_helper.dart';
|
||||||
|
import 'package:fladder/util/router_extension.dart';
|
||||||
import 'package:fladder/util/theme_extensions.dart';
|
import 'package:fladder/util/theme_extensions.dart';
|
||||||
import 'package:fladder/widgets/shared/hide_on_scroll.dart';
|
import 'package:fladder/widgets/shared/hide_on_scroll.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
|
|
||||||
import 'package:fladder/providers/auth_provider.dart';
|
|
||||||
import 'package:fladder/util/application_info.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
class SettingsScreen extends ConsumerStatefulWidget {
|
class SettingsScreen extends ConsumerStatefulWidget {
|
||||||
final Widget? child;
|
const SettingsScreen({super.key});
|
||||||
final String? location;
|
|
||||||
const SettingsScreen({this.child, this.location, super.key});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<ConsumerStatefulWidget> createState() => _SettingsScreenState();
|
ConsumerState<ConsumerStatefulWidget> createState() => _SettingsScreenState();
|
||||||
|
|
@ -29,27 +29,30 @@ class SettingsScreen extends ConsumerStatefulWidget {
|
||||||
|
|
||||||
class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
||||||
final scrollController = ScrollController();
|
final scrollController = ScrollController();
|
||||||
late final singlePane = widget.child == null;
|
|
||||||
final minVerticalPadding = 20.0;
|
final minVerticalPadding = 20.0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (singlePane) {
|
if (AdaptiveLayout.of(context).size == ScreenLayout.single) {
|
||||||
return Card(
|
return Card(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
child: _leftPane(context),
|
child: _leftPane(context),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Row(
|
return AutoRouter(
|
||||||
mainAxisSize: MainAxisSize.max,
|
builder: (context, content) {
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
return Row(
|
||||||
children: [
|
mainAxisSize: MainAxisSize.max,
|
||||||
Expanded(flex: 1, child: _leftPane(context)),
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
Expanded(
|
children: [
|
||||||
flex: 2,
|
Expanded(flex: 1, child: _leftPane(context)),
|
||||||
child: widget.child ?? Container(),
|
Expanded(
|
||||||
),
|
flex: 2,
|
||||||
],
|
child: content,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -68,152 +71,167 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool containsRoute(CustomRoute route) => widget.location == route.route;
|
|
||||||
|
|
||||||
Widget _leftPane(BuildContext context) {
|
Widget _leftPane(BuildContext context) {
|
||||||
final quickConnectAvailable = ref.watch(userProvider.select((value) => value?.serverConfiguration?.quickConnectAvailable ?? false));
|
void navigateTo(PageRouteInfo route) {
|
||||||
return SettingsScaffold(
|
AdaptiveLayout.of(context).size == ScreenLayout.single
|
||||||
label: context.localized.settings,
|
? context.router.navigate(route)
|
||||||
scrollController: scrollController,
|
: context.router.replace(route);
|
||||||
showUserIcon: true,
|
}
|
||||||
items: [
|
|
||||||
if (context.canPop() && AdaptiveLayout.of(context).isDesktop)
|
bool containsRoute(PageRouteInfo route) {
|
||||||
Align(
|
return context.router.current.name == route.routeName;
|
||||||
alignment: Alignment.centerLeft,
|
}
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(16.0),
|
final quickConnectAvailable =
|
||||||
child: IconButton.filledTonal(
|
ref.watch(userProvider.select((value) => value?.serverConfiguration?.quickConnectAvailable ?? false));
|
||||||
style: IconButton.styleFrom(
|
return Container(
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface.withOpacity(0.8),
|
color: context.colors.surface,
|
||||||
),
|
child: SettingsScaffold(
|
||||||
onPressed: () {
|
label: context.localized.settings,
|
||||||
context.pop();
|
scrollController: scrollController,
|
||||||
},
|
showUserIcon: true,
|
||||||
icon: Padding(
|
items: [
|
||||||
padding: EdgeInsets.all(AdaptiveLayout.of(context).inputDevice == InputDevice.pointer ? 0 : 4),
|
if (context.router.canNavigateBack && AdaptiveLayout.of(context).size == ScreenLayout.dual)
|
||||||
child: const Icon(IconsaxOutline.arrow_left_2),
|
Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: IconButton.filledTonal(
|
||||||
|
style: IconButton.styleFrom(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.surface.withOpacity(0.8),
|
||||||
|
),
|
||||||
|
onPressed: () => context.router.popBack(),
|
||||||
|
icon: Padding(
|
||||||
|
padding: EdgeInsets.all(AdaptiveLayout.of(context).inputDevice == InputDevice.pointer ? 0 : 4),
|
||||||
|
child: const Icon(IconsaxOutline.arrow_left_2),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
SettingsListTile(
|
|
||||||
label: Text(context.localized.settingsClientTitle),
|
|
||||||
subLabel: Text(context.localized.settingsClientDesc),
|
|
||||||
selected: containsRoute(ClientSettingsRoute()),
|
|
||||||
icon: deviceIcon,
|
|
||||||
onTap: () => context.routeReplaceOrPush(ClientSettingsRoute()),
|
|
||||||
),
|
|
||||||
if (quickConnectAvailable)
|
|
||||||
SettingsListTile(
|
SettingsListTile(
|
||||||
label: Text(context.localized.settingsQuickConnectTitle),
|
label: Text(context.localized.settingsClientTitle),
|
||||||
icon: IconsaxOutline.password_check,
|
subLabel: Text(context.localized.settingsClientDesc),
|
||||||
onTap: () => openQuickConnectDialog(context),
|
selected: containsRoute(const ClientSettingsRoute()),
|
||||||
|
icon: deviceIcon,
|
||||||
|
onTap: () => navigateTo(const ClientSettingsRoute()),
|
||||||
),
|
),
|
||||||
SettingsListTile(
|
if (quickConnectAvailable)
|
||||||
label: Text(context.localized.settingsProfileTitle),
|
SettingsListTile(
|
||||||
subLabel: Text(context.localized.settingsProfileDesc),
|
label: Text(context.localized.settingsQuickConnectTitle),
|
||||||
selected: containsRoute(SecuritySettingsRoute()),
|
icon: IconsaxOutline.password_check,
|
||||||
icon: IconsaxOutline.security_user,
|
onTap: () => openQuickConnectDialog(context),
|
||||||
onTap: () => context.routeReplaceOrPush(SecuritySettingsRoute()),
|
),
|
||||||
),
|
SettingsListTile(
|
||||||
SettingsListTile(
|
label: Text(context.localized.settingsProfileTitle),
|
||||||
label: Text(context.localized.settingsPlayerTitle),
|
subLabel: Text(context.localized.settingsProfileDesc),
|
||||||
subLabel: Text(context.localized.settingsPlayerDesc),
|
selected: containsRoute(const SecuritySettingsRoute()),
|
||||||
selected: containsRoute(PlayerSettingsRoute()),
|
icon: IconsaxOutline.security_user,
|
||||||
icon: IconsaxOutline.video_play,
|
onTap: () => navigateTo(const SecuritySettingsRoute()),
|
||||||
onTap: () => context.routeReplaceOrPush(PlayerSettingsRoute()),
|
|
||||||
),
|
|
||||||
SettingsListTile(
|
|
||||||
label: Text(context.localized.about),
|
|
||||||
subLabel: const Text("Fladder"),
|
|
||||||
suffix: Opacity(
|
|
||||||
opacity: 1,
|
|
||||||
child: FladderIconOutlined(
|
|
||||||
size: 24,
|
|
||||||
color: context.colors.onSurfaceVariant,
|
|
||||||
)),
|
|
||||||
onTap: () => showAboutDialog(
|
|
||||||
context: context,
|
|
||||||
applicationIcon: const FladderIcon(size: 85),
|
|
||||||
applicationVersion: ref.watch(applicationInfoProvider).versionAndPlatform,
|
|
||||||
applicationLegalese: "Donut Factory",
|
|
||||||
),
|
),
|
||||||
),
|
SettingsListTile(
|
||||||
],
|
label: Text(context.localized.settingsPlayerTitle),
|
||||||
floatingActionButton: HideOnScroll(
|
subLabel: Text(context.localized.settingsPlayerDesc),
|
||||||
controller: scrollController,
|
selected: containsRoute(const PlayerSettingsRoute()),
|
||||||
visibleBuilder: (visible) {
|
icon: IconsaxOutline.video_play,
|
||||||
return AnimatedFadeSize(
|
onTap: () => navigateTo(const PlayerSettingsRoute()),
|
||||||
child: visible
|
),
|
||||||
? Padding(
|
SettingsListTile(
|
||||||
padding: EdgeInsets.symmetric(horizontal: MediaQuery.paddingOf(context).horizontal),
|
label: Text(context.localized.about),
|
||||||
child: Padding(
|
subLabel: const Text("Fladder"),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
suffix: Opacity(
|
||||||
child: Row(
|
opacity: 1,
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
child: FladderIconOutlined(
|
||||||
children: [
|
size: 24,
|
||||||
const Spacer(),
|
color: context.colors.onSurfaceVariant,
|
||||||
FloatingActionButton(
|
)),
|
||||||
key: Key(context.localized.switchUser),
|
onTap: () => showAboutDialog(
|
||||||
tooltip: context.localized.switchUser,
|
context: context,
|
||||||
onPressed: () {
|
applicationIcon: const FladderIcon(size: 85),
|
||||||
ref.read(userProvider.notifier).logoutUser();
|
applicationVersion: ref.watch(applicationInfoProvider).versionAndPlatform,
|
||||||
context.routeGo(LoginRoute());
|
applicationLegalese: "Donut Factory",
|
||||||
},
|
),
|
||||||
child: const Icon(
|
),
|
||||||
IconsaxOutline.arrow_swap_horizontal,
|
],
|
||||||
|
floatingActionButton: HideOnScroll(
|
||||||
|
controller: scrollController,
|
||||||
|
visibleBuilder: (visible) {
|
||||||
|
return AnimatedFadeSize(
|
||||||
|
child: visible
|
||||||
|
? Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: MediaQuery.paddingOf(context).horizontal),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
const Spacer(),
|
||||||
|
FloatingActionButton(
|
||||||
|
key: Key(context.localized.switchUser),
|
||||||
|
tooltip: context.localized.switchUser,
|
||||||
|
onPressed: () async {
|
||||||
|
await ref.read(userProvider.notifier).logoutUser();
|
||||||
|
context.router.navigate(const LoginRoute());
|
||||||
|
},
|
||||||
|
child: const Icon(
|
||||||
|
IconsaxOutline.arrow_swap_horizontal,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(width: 16),
|
||||||
const SizedBox(width: 16),
|
FloatingActionButton(
|
||||||
FloatingActionButton(
|
heroTag: context.localized.logout,
|
||||||
key: Key(context.localized.logout),
|
key: Key(context.localized.logout),
|
||||||
tooltip: context.localized.logout,
|
tooltip: context.localized.logout,
|
||||||
backgroundColor: Theme.of(context).colorScheme.errorContainer,
|
backgroundColor: Theme.of(context).colorScheme.errorContainer,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final user = ref.read(userProvider);
|
final user = ref.read(userProvider);
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: Text(context.localized.logoutUserPopupTitle(user?.name ?? "")),
|
title: Text(context.localized.logoutUserPopupTitle(user?.name ?? "")),
|
||||||
scrollable: true,
|
scrollable: true,
|
||||||
content: Text(
|
content: Text(
|
||||||
context.localized.logoutUserPopupContent(user?.name ?? "", user?.server ?? ""),
|
context.localized.logoutUserPopupContent(user?.name ?? "", user?.server ?? ""),
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
child: Text(context.localized.cancel),
|
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
actions: [
|
||||||
style: ElevatedButton.styleFrom().copyWith(
|
ElevatedButton(
|
||||||
foregroundColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.onErrorContainer),
|
onPressed: () => Navigator.pop(context),
|
||||||
backgroundColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.errorContainer),
|
child: Text(context.localized.cancel),
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
ElevatedButton(
|
||||||
await ref.read(authProvider.notifier).logOutUser();
|
style: ElevatedButton.styleFrom().copyWith(
|
||||||
if (context.mounted) context.routeGo(SplashRoute());
|
foregroundColor:
|
||||||
},
|
WidgetStatePropertyAll(Theme.of(context).colorScheme.onErrorContainer),
|
||||||
child: Text(context.localized.logout),
|
backgroundColor:
|
||||||
),
|
WidgetStatePropertyAll(Theme.of(context).colorScheme.errorContainer),
|
||||||
],
|
),
|
||||||
),
|
onPressed: () async {
|
||||||
);
|
await ref.read(authProvider.notifier).logOutUser();
|
||||||
},
|
if (context.mounted) {
|
||||||
child: Icon(
|
context.router.navigate(const LoginRoute());
|
||||||
IconsaxOutline.logout,
|
}
|
||||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
},
|
||||||
|
child: Text(context.localized.logout),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Icon(
|
||||||
|
IconsaxOutline.logout,
|
||||||
|
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
: Container(
|
||||||
|
height: 0,
|
||||||
|
key: UniqueKey(),
|
||||||
),
|
),
|
||||||
)
|
);
|
||||||
: Container(
|
},
|
||||||
height: 0,
|
),
|
||||||
key: UniqueKey(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,88 +1,26 @@
|
||||||
import 'package:ficonsax/ficonsax.dart';
|
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
|
||||||
import 'package:fladder/widgets/navigation_scaffold/components/settings_user_icon.dart';
|
|
||||||
import 'package:flutter/material.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:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
import 'package:fladder/models/item_base_model.dart';
|
import 'package:fladder/models/item_base_model.dart';
|
||||||
import 'package:fladder/models/items/images_models.dart';
|
import 'package:fladder/models/items/images_models.dart';
|
||||||
import 'package:fladder/models/media_playback_model.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/providers/video_player_provider.dart';
|
||||||
import 'package:fladder/routes/build_routes/home_routes.dart';
|
import 'package:fladder/routes/auto_router.gr.dart';
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
|
||||||
import 'package:fladder/theme.dart';
|
import 'package:fladder/theme.dart';
|
||||||
import 'package:fladder/util/adaptive_layout.dart';
|
import 'package:fladder/util/adaptive_layout.dart';
|
||||||
import 'package:fladder/util/fladder_image.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/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/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/item_actions.dart';
|
||||||
import 'package:fladder/widgets/shared/modal_bottom_sheet.dart';
|
import 'package:fladder/widgets/shared/modal_bottom_sheet.dart';
|
||||||
import 'package:fladder/widgets/shared/pull_to_refresh.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.routeGo(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 {
|
class DetailScaffold extends ConsumerStatefulWidget {
|
||||||
final String label;
|
final String label;
|
||||||
final ItemBaseModel? item;
|
final ItemBaseModel? item;
|
||||||
|
|
@ -212,13 +150,7 @@ class _DetailScaffoldState extends ConsumerState<DetailScaffold> {
|
||||||
style: IconButton.styleFrom(
|
style: IconButton.styleFrom(
|
||||||
backgroundColor: backGroundColor,
|
backgroundColor: backGroundColor,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () => context.router.popBack(),
|
||||||
if (context.canPop()) {
|
|
||||||
context.pop();
|
|
||||||
} else {
|
|
||||||
context.replace(DashboardRoute().route);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon: Padding(
|
icon: Padding(
|
||||||
padding:
|
padding:
|
||||||
EdgeInsets.all(AdaptiveLayout.of(context).inputDevice == InputDevice.pointer ? 0 : 4),
|
EdgeInsets.all(AdaptiveLayout.of(context).inputDevice == InputDevice.pointer ? 0 : 4),
|
||||||
|
|
@ -275,13 +207,13 @@ class _DetailScaffoldState extends ConsumerState<DetailScaffold> {
|
||||||
icon: const Icon(IconsaxOutline.refresh),
|
icon: const Icon(IconsaxOutline.refresh),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
else
|
if (AdaptiveLayout.of(context).size == ScreenLayout.single)
|
||||||
const SizedBox(height: 30, width: 30, child: SettingsUserIcon()),
|
const SizedBox(height: 30, width: 30, child: SettingsUserIcon()),
|
||||||
Tooltip(
|
Tooltip(
|
||||||
message: context.localized.home,
|
message: context.localized.home,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
onPressed: () => context.routeGo(DashboardRoute()),
|
onPressed: () => context.router.navigate(const DashboardRoute()),
|
||||||
icon: const Icon(IconsaxOutline.home),
|
icon: const Icon(IconsaxOutline.home),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
import 'package:animations/animations.dart';
|
import 'package:animations/animations.dart';
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:fladder/providers/user_provider.dart';
|
import 'package:fladder/providers/user_provider.dart';
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
import 'package:fladder/routes/auto_router.gr.dart';
|
||||||
import 'package:fladder/routes/build_routes/settings_routes.dart';
|
|
||||||
import 'package:fladder/screens/search/search_screen.dart';
|
import 'package:fladder/screens/search/search_screen.dart';
|
||||||
import 'package:fladder/util/string_extensions.dart';
|
import 'package:fladder/util/string_extensions.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
class FloatingSearchBar extends ConsumerStatefulWidget {
|
class FloatingSearchBar extends ConsumerStatefulWidget {
|
||||||
final List<Widget> trailing;
|
final List<Widget> trailing;
|
||||||
|
|
@ -58,9 +57,9 @@ class _FloatingSearchBarState extends ConsumerState<FloatingSearchBar> {
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (context.canPop())
|
if (context.router.canPop())
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => context.pop(),
|
onPressed: () => context.router.maybePop(),
|
||||||
icon: const Icon(Icons.arrow_back),
|
icon: const Icon(Icons.arrow_back),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
|
|
@ -77,7 +76,7 @@ class _FloatingSearchBarState extends ConsumerState<FloatingSearchBar> {
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.routeGo(SecuritySettingsRoute());
|
context.router.push(const SecuritySettingsRoute());
|
||||||
},
|
},
|
||||||
icon: ClipRRect(
|
icon: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(200),
|
borderRadius: BorderRadius.circular(200),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:ficonsax/ficonsax.dart';
|
import 'package:ficonsax/ficonsax.dart';
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
|
||||||
import 'package:fladder/util/list_padding.dart';
|
import 'package:fladder/util/list_padding.dart';
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
import 'package:fladder/widgets/navigation_scaffold/components/settings_user_icon.dart';
|
import 'package:fladder/widgets/navigation_scaffold/components/settings_user_icon.dart';
|
||||||
|
|
@ -10,7 +10,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
class NestedSliverAppBar extends ConsumerWidget {
|
class NestedSliverAppBar extends ConsumerWidget {
|
||||||
final BuildContext parent;
|
final BuildContext parent;
|
||||||
final String? searchTitle;
|
final String? searchTitle;
|
||||||
final CustomRoute? route;
|
final PageRouteInfo? route;
|
||||||
const NestedSliverAppBar({required this.parent, this.route, this.searchTitle, super.key});
|
const NestedSliverAppBar({required this.parent, this.route, this.searchTitle, super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -49,7 +49,7 @@ class NestedSliverAppBar extends ConsumerWidget {
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: route != null
|
onTap: route != null
|
||||||
? () {
|
? () {
|
||||||
context.routePushOrGo(route!);
|
route?.push(context);
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
@ -62,7 +62,8 @@ class NestedSliverAppBar extends ConsumerWidget {
|
||||||
const Icon(IconsaxOutline.search_normal),
|
const Icon(IconsaxOutline.search_normal),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
Transform.translate(
|
Transform.translate(
|
||||||
offset: const Offset(0, 2.5), child: Text(searchTitle ?? "${context.localized.search}...")),
|
offset: const Offset(0, 2.5),
|
||||||
|
child: Text(searchTitle ?? "${context.localized.search}...")),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:fladder/models/account_model.dart';
|
import 'package:fladder/models/account_model.dart';
|
||||||
import 'package:fladder/providers/shared_provider.dart';
|
import 'package:fladder/providers/shared_provider.dart';
|
||||||
import 'package:fladder/providers/user_provider.dart';
|
import 'package:fladder/providers/user_provider.dart';
|
||||||
import 'package:fladder/routes/build_routes/home_routes.dart';
|
import 'package:fladder/routes/auto_router.gr.dart';
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
|
||||||
import 'package:fladder/screens/shared/fladder_logo.dart';
|
import 'package:fladder/screens/shared/fladder_logo.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
class SplashScreen extends ConsumerStatefulWidget {
|
class SplashScreen extends ConsumerStatefulWidget {
|
||||||
const SplashScreen({super.key});
|
final Function(bool loggedIn)? loggedIn;
|
||||||
|
const SplashScreen({this.loggedIn, super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<ConsumerStatefulWidget> createState() => _SplashScreenState();
|
ConsumerState<ConsumerStatefulWidget> createState() => _SplashScreenState();
|
||||||
|
|
@ -18,23 +22,23 @@ class _SplashScreenState extends ConsumerState<SplashScreen> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
Future.microtask(() async {
|
WidgetsBinding.instance.addPostFrameCallback((value) async {
|
||||||
await Future.delayed(const Duration(milliseconds: 500));
|
await Future.delayed(const Duration(milliseconds: 500));
|
||||||
final AccountModel? lastUsedAccount = ref.read(sharedUtilityProvider).getActiveAccount();
|
final AccountModel? lastUsedAccount = ref.read(sharedUtilityProvider).getActiveAccount();
|
||||||
ref.read(userProvider.notifier).updateUser(lastUsedAccount);
|
ref.read(userProvider.notifier).updateUser(lastUsedAccount);
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
if (lastUsedAccount == null) {
|
if (lastUsedAccount == null) {
|
||||||
context.routeGo(LoginRoute());
|
callBackOrNavigate(false);
|
||||||
} else {
|
} else {
|
||||||
switch (lastUsedAccount.authMethod) {
|
switch (lastUsedAccount.authMethod) {
|
||||||
case Authentication.autoLogin:
|
case Authentication.autoLogin:
|
||||||
context.routeGo(DashboardRoute());
|
callBackOrNavigate(true);
|
||||||
break;
|
break;
|
||||||
case Authentication.biometrics:
|
case Authentication.biometrics:
|
||||||
case Authentication.none:
|
case Authentication.none:
|
||||||
case Authentication.passcode:
|
case Authentication.passcode:
|
||||||
context.routeReplace(LoginRoute());
|
callBackOrNavigate(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -42,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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return const Scaffold(
|
return const Scaffold(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:ficonsax/ficonsax.dart';
|
import 'package:ficonsax/ficonsax.dart';
|
||||||
import 'package:fladder/providers/sync_provider.dart';
|
import 'package:fladder/providers/sync_provider.dart';
|
||||||
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
||||||
import 'package:fladder/routes/build_routes/home_routes.dart';
|
import 'package:fladder/routes/auto_router.gr.dart';
|
||||||
import 'package:fladder/screens/shared/nested_scaffold.dart';
|
import 'package:fladder/screens/shared/nested_scaffold.dart';
|
||||||
import 'package:fladder/screens/shared/nested_sliver_appbar.dart';
|
import 'package:fladder/screens/shared/nested_sliver_appbar.dart';
|
||||||
import 'package:fladder/screens/syncing/sync_list_item.dart';
|
import 'package:fladder/screens/syncing/sync_list_item.dart';
|
||||||
|
|
@ -14,10 +15,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:fladder/util/sliver_list_padding.dart';
|
import 'package:fladder/util/sliver_list_padding.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
class SyncedScreen extends ConsumerStatefulWidget {
|
class SyncedScreen extends ConsumerStatefulWidget {
|
||||||
final ScrollController navigationScrollController;
|
final ScrollController? navigationScrollController;
|
||||||
|
|
||||||
const SyncedScreen({required this.navigationScrollController, super.key});
|
const SyncedScreen({this.navigationScrollController, super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<ConsumerStatefulWidget> createState() => _SyncedScreenState();
|
ConsumerState<ConsumerStatefulWidget> createState() => _SyncedScreenState();
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import 'package:fladder/util/poster_defaults.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
import 'package:fladder/routes/app_routes.dart';
|
import 'package:fladder/routes/auto_router.dart';
|
||||||
|
import 'package:fladder/util/poster_defaults.dart';
|
||||||
|
|
||||||
enum LayoutState {
|
enum LayoutState {
|
||||||
phone,
|
phone,
|
||||||
|
|
@ -65,8 +64,9 @@ class AdaptiveLayout extends InheritedWidget {
|
||||||
final InputDevice inputDevice;
|
final InputDevice inputDevice;
|
||||||
final TargetPlatform platform;
|
final TargetPlatform platform;
|
||||||
final bool isDesktop;
|
final bool isDesktop;
|
||||||
final GoRouter router;
|
final AutoRouter router;
|
||||||
final PosterDefaults posterDefaults;
|
final PosterDefaults posterDefaults;
|
||||||
|
final ScrollController controller;
|
||||||
|
|
||||||
const AdaptiveLayout({
|
const AdaptiveLayout({
|
||||||
super.key,
|
super.key,
|
||||||
|
|
@ -77,6 +77,7 @@ class AdaptiveLayout extends InheritedWidget {
|
||||||
required this.isDesktop,
|
required this.isDesktop,
|
||||||
required this.router,
|
required this.router,
|
||||||
required this.posterDefaults,
|
required this.posterDefaults,
|
||||||
|
required this.controller,
|
||||||
required super.child,
|
required super.child,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -94,7 +95,7 @@ class AdaptiveLayout extends InheritedWidget {
|
||||||
return result!.posterDefaults;
|
return result!.posterDefaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GoRouter routerOf(BuildContext context) {
|
static AutoRouter routerOf(BuildContext context) {
|
||||||
final AdaptiveLayout? result = maybeOf(context);
|
final AdaptiveLayout? result = maybeOf(context);
|
||||||
return result!.router;
|
return result!.router;
|
||||||
}
|
}
|
||||||
|
|
@ -104,6 +105,11 @@ class AdaptiveLayout extends InheritedWidget {
|
||||||
return result!;
|
return result!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ScrollController scrollOf(BuildContext context) {
|
||||||
|
final AdaptiveLayout? result = maybeOf(context);
|
||||||
|
return result!.controller;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool updateShouldNotify(AdaptiveLayout oldWidget) {
|
bool updateShouldNotify(AdaptiveLayout oldWidget) {
|
||||||
return layout != oldWidget.layout ||
|
return layout != oldWidget.layout ||
|
||||||
|
|
@ -128,8 +134,9 @@ class AdaptiveLayoutBuilder extends ConsumerStatefulWidget {
|
||||||
class _AdaptiveLayoutBuilderState extends ConsumerState<AdaptiveLayoutBuilder> {
|
class _AdaptiveLayoutBuilderState extends ConsumerState<AdaptiveLayoutBuilder> {
|
||||||
late LayoutState layout = widget.fallBack;
|
late LayoutState layout = widget.fallBack;
|
||||||
late ScreenLayout size = ScreenLayout.single;
|
late ScreenLayout size = ScreenLayout.single;
|
||||||
late GoRouter router = AppRoutes.routes(ref: ref, screenLayout: size);
|
late AutoRouter router = AutoRouter(layout: size, ref: ref);
|
||||||
late TargetPlatform currentPlatform = defaultTargetPlatform;
|
late TargetPlatform currentPlatform = defaultTargetPlatform;
|
||||||
|
late ScrollController controller = ScrollController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
|
|
@ -169,7 +176,7 @@ class _AdaptiveLayoutBuilderState extends ConsumerState<AdaptiveLayoutBuilder> {
|
||||||
}
|
}
|
||||||
if (size != newSize) {
|
if (size != newSize) {
|
||||||
size = newSize;
|
size = newSize;
|
||||||
router = AppRoutes.routes(ref: ref, screenLayout: size);
|
router = AutoRouter(layout: size, ref: ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,6 +184,7 @@ class _AdaptiveLayoutBuilderState extends ConsumerState<AdaptiveLayoutBuilder> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AdaptiveLayout(
|
return AdaptiveLayout(
|
||||||
layout: layout,
|
layout: layout,
|
||||||
|
controller: controller,
|
||||||
size: size,
|
size: size,
|
||||||
inputDevice: (isDesktop || kIsWeb) ? InputDevice.pointer : InputDevice.touch,
|
inputDevice: (isDesktop || kIsWeb) ? InputDevice.pointer : InputDevice.touch,
|
||||||
platform: currentPlatform,
|
platform: currentPlatform,
|
||||||
|
|
@ -191,3 +199,10 @@ class _AdaptiveLayoutBuilderState extends ConsumerState<AdaptiveLayoutBuilder> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double? get topPadding {
|
||||||
|
return switch (defaultTargetPlatform) {
|
||||||
|
TargetPlatform.linux || TargetPlatform.windows || TargetPlatform.macOS => 35,
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:fladder/widgets/navigation_scaffold/components/adaptive_fab.dart';
|
import 'package:fladder/widgets/navigation_scaffold/components/adaptive_fab.dart';
|
||||||
import 'package:fladder/widgets/navigation_scaffold/components/navigation_button.dart';
|
import 'package:fladder/widgets/navigation_scaffold/components/navigation_button.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
@ -7,7 +7,7 @@ class DestinationModel {
|
||||||
final String label;
|
final String label;
|
||||||
final Widget? icon;
|
final Widget? icon;
|
||||||
final Widget? selectedIcon;
|
final Widget? selectedIcon;
|
||||||
final CustomRoute? route;
|
final PageRouteInfo? route;
|
||||||
final Function()? action;
|
final Function()? action;
|
||||||
final String? tooltip;
|
final String? tooltip;
|
||||||
final Badge? badge;
|
final Badge? badge;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:fladder/screens/shared/default_titlebar.dart';
|
import 'package:fladder/screens/shared/default_titlebar.dart';
|
||||||
import 'package:fladder/util/adaptive_layout.dart';
|
import 'package:fladder/util/adaptive_layout.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
bool get _isDesktop {
|
bool get _isDesktop {
|
||||||
if (kIsWeb) return false;
|
if (kIsWeb) return false;
|
||||||
|
|
@ -29,7 +29,7 @@ class FladderAppbar extends StatelessWidget implements PreferredSize {
|
||||||
height: height,
|
height: height,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
if (automaticallyImplyLeading && context.canPop()) const BackButton(),
|
if (automaticallyImplyLeading && context.router.canPop()) const BackButton(),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DefaultTitleBar(
|
child: DefaultTitleBar(
|
||||||
label: label,
|
label: label,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import 'package:ficonsax/ficonsax.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:fladder/providers/views_provider.dart';
|
import 'package:fladder/providers/views_provider.dart';
|
||||||
import 'package:fladder/routes/build_routes/settings_routes.dart';
|
import 'package:fladder/routes/auto_router.gr.dart';
|
||||||
import 'package:fladder/screens/shared/animated_fade_size.dart';
|
import 'package:fladder/screens/shared/animated_fade_size.dart';
|
||||||
import 'package:fladder/util/adaptive_layout.dart';
|
import 'package:fladder/util/adaptive_layout.dart';
|
||||||
import 'package:fladder/widgets/navigation_scaffold/components/adaptive_fab.dart';
|
import 'package:fladder/widgets/navigation_scaffold/components/adaptive_fab.dart';
|
||||||
|
|
@ -110,7 +110,10 @@ class _NavigationBodyState extends ConsumerState<NavigationBody> {
|
||||||
style: Theme.of(context).textTheme.titleSmall,
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
if (AdaptiveLayout.of(context).platform == TargetPlatform.macOS) const SizedBox(height: 32) else const SizedBox(height: 16),
|
if (AdaptiveLayout.of(context).platform == TargetPlatform.macOS)
|
||||||
|
const SizedBox(height: 32)
|
||||||
|
else
|
||||||
|
const SizedBox(height: 16),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (AdaptiveLayout.layoutOf(context) != LayoutState.desktop) {
|
if (AdaptiveLayout.layoutOf(context) != LayoutState.desktop) {
|
||||||
|
|
@ -152,7 +155,7 @@ class _NavigationBodyState extends ConsumerState<NavigationBody> {
|
||||||
height: 48,
|
height: 48,
|
||||||
child: AnimatedSwitcher(
|
child: AnimatedSwitcher(
|
||||||
duration: const Duration(milliseconds: 250),
|
duration: const Duration(milliseconds: 250),
|
||||||
child: widget.currentLocation.contains(SettingsRoute().route)
|
child: widget.currentLocation.contains(const SettingsRoute().routeName)
|
||||||
? Card(
|
? Card(
|
||||||
color: Theme.of(context).colorScheme.primaryContainer,
|
color: Theme.of(context).colorScheme.primaryContainer,
|
||||||
child: const Padding(
|
child: const Padding(
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:ficonsax/ficonsax.dart';
|
import 'package:ficonsax/ficonsax.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:fladder/models/collection_types.dart';
|
import 'package:fladder/models/collection_types.dart';
|
||||||
import 'package:fladder/models/view_model.dart';
|
import 'package:fladder/models/view_model.dart';
|
||||||
import 'package:fladder/routes/build_routes/home_routes.dart';
|
import 'package:fladder/routes/auto_router.gr.dart';
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
|
||||||
import 'package:fladder/routes/build_routes/settings_routes.dart';
|
|
||||||
import 'package:fladder/screens/metadata/refresh_metadata.dart';
|
import 'package:fladder/screens/metadata/refresh_metadata.dart';
|
||||||
import 'package:fladder/screens/shared/animated_fade_size.dart';
|
import 'package:fladder/screens/shared/animated_fade_size.dart';
|
||||||
import 'package:fladder/util/adaptive_layout.dart';
|
import 'package:fladder/util/adaptive_layout.dart';
|
||||||
|
|
@ -74,7 +73,7 @@ class NestedNavigationDrawer extends ConsumerWidget {
|
||||||
),
|
),
|
||||||
...destinations.map((destination) => DrawerListButton(
|
...destinations.map((destination) => DrawerListButton(
|
||||||
label: destination.label,
|
label: destination.label,
|
||||||
selected: destination.route?.route == currentLocation,
|
selected: context.router.current.name == destination.route?.routeName,
|
||||||
selectedIcon: destination.selectedIcon!,
|
selectedIcon: destination.selectedIcon!,
|
||||||
icon: destination.icon!,
|
icon: destination.icon!,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
|
@ -93,7 +92,7 @@ class NestedNavigationDrawer extends ConsumerWidget {
|
||||||
),
|
),
|
||||||
...views.map((library) => DrawerListButton(
|
...views.map((library) => DrawerListButton(
|
||||||
label: library.name,
|
label: library.name,
|
||||||
selected: currentLocation.contains(library.id),
|
selected: checkLibrary(context, library.id),
|
||||||
actions: [
|
actions: [
|
||||||
ItemActionButton(
|
ItemActionButton(
|
||||||
label: Text(context.localized.scanLibrary),
|
label: Text(context.localized.scanLibrary),
|
||||||
|
|
@ -102,7 +101,7 @@ class NestedNavigationDrawer extends ConsumerWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.routePushOrGo(LibrarySearchRoute(id: library.id));
|
context.router.push(LibrarySearchRoute(viewModelId: library.id));
|
||||||
Scaffold.of(context).closeDrawer();
|
Scaffold.of(context).closeDrawer();
|
||||||
},
|
},
|
||||||
selectedIcon: Icon(library.collectionType.icon),
|
selectedIcon: Icon(library.collectionType.icon),
|
||||||
|
|
@ -115,15 +114,15 @@ class NestedNavigationDrawer extends ConsumerWidget {
|
||||||
child: DrawerListButton(
|
child: DrawerListButton(
|
||||||
label: context.localized.settings,
|
label: context.localized.settings,
|
||||||
selectedIcon: const Icon(IconsaxBold.setting_3),
|
selectedIcon: const Icon(IconsaxBold.setting_3),
|
||||||
selected: currentLocation.contains(SettingsRoute().basePath),
|
selected: currentLocation.contains(const SettingsRoute().routeName),
|
||||||
icon: const SizedBox(width: 35, height: 35, child: SettingsUserIcon()),
|
icon: const SizedBox(width: 35, height: 35, child: SettingsUserIcon()),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
switch (AdaptiveLayout.of(context).size) {
|
switch (AdaptiveLayout.of(context).size) {
|
||||||
case ScreenLayout.single:
|
case ScreenLayout.single:
|
||||||
context.routePush(SettingsRoute());
|
const SettingsRoute().push(context);
|
||||||
break;
|
break;
|
||||||
case ScreenLayout.dual:
|
case ScreenLayout.dual:
|
||||||
context.routeGo(ClientSettingsRoute());
|
context.router.push(const ClientSettingsRoute());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Scaffold.of(context).closeDrawer();
|
Scaffold.of(context).closeDrawer();
|
||||||
|
|
@ -135,14 +134,14 @@ class NestedNavigationDrawer extends ConsumerWidget {
|
||||||
label: context.localized.settings,
|
label: context.localized.settings,
|
||||||
selectedIcon: const Icon(IconsaxBold.setting_2),
|
selectedIcon: const Icon(IconsaxBold.setting_2),
|
||||||
icon: const Icon(IconsaxOutline.setting_2),
|
icon: const Icon(IconsaxOutline.setting_2),
|
||||||
selected: currentLocation.contains(SettingsRoute().basePath),
|
selected: currentLocation.contains(const SettingsRoute().routeName),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
switch (AdaptiveLayout.of(context).size) {
|
switch (AdaptiveLayout.of(context).size) {
|
||||||
case ScreenLayout.single:
|
case ScreenLayout.single:
|
||||||
context.routePush(SettingsRoute());
|
const SettingsRoute().push(context);
|
||||||
break;
|
break;
|
||||||
case ScreenLayout.dual:
|
case ScreenLayout.dual:
|
||||||
context.routeGo(ClientSettingsRoute());
|
context.router.push(const ClientSettingsRoute());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Scaffold.of(context).closeDrawer();
|
Scaffold.of(context).closeDrawer();
|
||||||
|
|
@ -152,4 +151,13 @@ class NestedNavigationDrawer extends ConsumerWidget {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool checkLibrary(BuildContext context, String id) {
|
||||||
|
try {
|
||||||
|
return context.router.current.name == LibrarySearchRoute().routeName &&
|
||||||
|
(context.routeData.queryParams.isNotEmpty && context.routeData.queryParams.getString('parentId') == id);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:fladder/providers/user_provider.dart';
|
import 'package:fladder/providers/user_provider.dart';
|
||||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
import 'package:fladder/routes/auto_router.gr.dart';
|
||||||
import 'package:fladder/routes/build_routes/settings_routes.dart';
|
|
||||||
import 'package:fladder/screens/shared/user_icon.dart';
|
import 'package:fladder/screens/shared/user_icon.dart';
|
||||||
import 'package:fladder/util/adaptive_layout.dart';
|
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
@ -19,11 +18,8 @@ class SettingsUserIcon extends ConsumerWidget {
|
||||||
child: UserIcon(
|
child: UserIcon(
|
||||||
user: users,
|
user: users,
|
||||||
cornerRadius: 200,
|
cornerRadius: 200,
|
||||||
onLongPress: () => context.routePush(LockScreenRoute()),
|
onLongPress: () => context.router.push(const LockRoute()),
|
||||||
onTap: () => switch (AdaptiveLayout.of(context).size) {
|
onTap: () => context.router.navigate(const SettingsRoute()),
|
||||||
ScreenLayout.single => context.routePush(SettingsRoute()),
|
|
||||||
ScreenLayout.dual => context.routePush(ClientSettingsRoute()),
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,26 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:fladder/models/media_playback_model.dart';
|
import 'package:fladder/models/media_playback_model.dart';
|
||||||
import 'package:fladder/providers/video_player_provider.dart';
|
import 'package:fladder/providers/video_player_provider.dart';
|
||||||
import 'package:fladder/providers/views_provider.dart';
|
import 'package:fladder/providers/views_provider.dart';
|
||||||
import 'package:fladder/routes/app_routes.dart';
|
|
||||||
import 'package:fladder/screens/shared/nested_bottom_appbar.dart';
|
import 'package:fladder/screens/shared/nested_bottom_appbar.dart';
|
||||||
import 'package:fladder/util/adaptive_layout.dart';
|
import 'package:fladder/util/adaptive_layout.dart';
|
||||||
import 'package:fladder/widgets/navigation_scaffold/components/floating_player_bar.dart';
|
|
||||||
import 'package:fladder/widgets/navigation_scaffold/components/destination_model.dart';
|
import 'package:fladder/widgets/navigation_scaffold/components/destination_model.dart';
|
||||||
import 'package:fladder/widgets/navigation_scaffold/components/fladder_appbar.dart';
|
import 'package:fladder/widgets/navigation_scaffold/components/fladder_appbar.dart';
|
||||||
|
import 'package:fladder/widgets/navigation_scaffold/components/floating_player_bar.dart';
|
||||||
import 'package:fladder/widgets/navigation_scaffold/components/navigation_body.dart';
|
import 'package:fladder/widgets/navigation_scaffold/components/navigation_body.dart';
|
||||||
import 'package:fladder/widgets/navigation_scaffold/components/navigation_drawer.dart';
|
import 'package:fladder/widgets/navigation_scaffold/components/navigation_drawer.dart';
|
||||||
import 'package:fladder/widgets/shared/hide_on_scroll.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 {
|
class NavigationScaffold extends ConsumerStatefulWidget {
|
||||||
final int? currentIndex;
|
final String? currentRouteName;
|
||||||
final String? location;
|
|
||||||
final Widget? nestedChild;
|
final Widget? nestedChild;
|
||||||
final List<DestinationModel> destinations;
|
final List<DestinationModel> destinations;
|
||||||
final GlobalKey<NavigatorState>? nestedNavigatorKey;
|
final GlobalKey<NavigatorState>? nestedNavigatorKey;
|
||||||
const NavigationScaffold({
|
const NavigationScaffold({
|
||||||
this.currentIndex,
|
this.currentRouteName,
|
||||||
this.location,
|
|
||||||
this.nestedChild,
|
this.nestedChild,
|
||||||
required this.destinations,
|
required this.destinations,
|
||||||
this.nestedNavigatorKey,
|
this.nestedNavigatorKey,
|
||||||
|
|
@ -35,8 +34,9 @@ class NavigationScaffold extends ConsumerStatefulWidget {
|
||||||
class _NavigationScaffoldState extends ConsumerState<NavigationScaffold> {
|
class _NavigationScaffoldState extends ConsumerState<NavigationScaffold> {
|
||||||
final GlobalKey<ScaffoldState> _key = GlobalKey();
|
final GlobalKey<ScaffoldState> _key = GlobalKey();
|
||||||
|
|
||||||
int get currentIndex => widget.destinations.indexWhere((element) => element.route?.route == widget.location);
|
int get currentIndex =>
|
||||||
String get currentLocation => widget.location ?? "Nothing";
|
widget.destinations.indexWhere((element) => element.route?.routeName == widget.currentRouteName);
|
||||||
|
String get currentLocation => widget.currentRouteName ?? "Nothing";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -50,6 +50,7 @@ class _NavigationScaffoldState extends ConsumerState<NavigationScaffold> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final playerState = ref.watch(mediaPlaybackProvider.select((value) => value.state));
|
final playerState = ref.watch(mediaPlaybackProvider.select((value) => value.state));
|
||||||
final views = ref.watch(viewsProvider.select((value) => value.views));
|
final views = ref.watch(viewsProvider.select((value) => value.views));
|
||||||
|
|
||||||
return PopScope(
|
return PopScope(
|
||||||
canPop: currentIndex == 0,
|
canPop: currentIndex == 0,
|
||||||
onPopInvokedWithResult: (didPop, result) {
|
onPopInvokedWithResult: (didPop, result) {
|
||||||
|
|
@ -70,21 +71,21 @@ class _NavigationScaffoldState extends ConsumerState<NavigationScaffold> {
|
||||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||||
child: FloatingPlayerBar(),
|
child: FloatingPlayerBar(),
|
||||||
),
|
),
|
||||||
_ => widget.destinations.elementAtOrNull(currentIndex)?.floatingActionButton?.normal,
|
_ => currentIndex != -1
|
||||||
|
? widget.destinations.elementAtOrNull(currentIndex)?.floatingActionButton?.normal
|
||||||
|
: null,
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
drawer: NestedNavigationDrawer(
|
drawer: NestedNavigationDrawer(
|
||||||
actionButton: null,
|
actionButton: null,
|
||||||
toggleExpanded: (value) {
|
toggleExpanded: (value) => _key.currentState?.closeDrawer(),
|
||||||
_key.currentState?.closeDrawer();
|
|
||||||
},
|
|
||||||
views: views,
|
views: views,
|
||||||
destinations: widget.destinations,
|
destinations: widget.destinations,
|
||||||
currentLocation: currentLocation,
|
currentLocation: currentLocation,
|
||||||
),
|
),
|
||||||
bottomNavigationBar: AdaptiveLayout.of(context).layout == LayoutState.phone
|
bottomNavigationBar: AdaptiveLayout.of(context).layout == LayoutState.phone
|
||||||
? HideOnScroll(
|
? HideOnScroll(
|
||||||
controller: AppRoutes.scrollController,
|
controller: AdaptiveLayout.scrollOf(context),
|
||||||
child: NestedBottomAppBar(
|
child: NestedBottomAppBar(
|
||||||
child: Transform.translate(
|
child: Transform.translate(
|
||||||
offset: const Offset(0, 8),
|
offset: const Offset(0, 8),
|
||||||
|
|
@ -93,8 +94,8 @@ class _NavigationScaffoldState extends ConsumerState<NavigationScaffold> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: widget.destinations
|
children: widget.destinations
|
||||||
.map(
|
.map(
|
||||||
(destination) =>
|
(destination) => destination.toNavigationButton(
|
||||||
destination.toNavigationButton(widget.location == destination.route?.route, false),
|
widget.currentRouteName == destination.route?.routeName, false),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import 'dart:ui' as ui;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
|
@ -34,12 +35,12 @@ class _TrickplayImageState extends ConsumerState<TrickplayImage> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(covariant TrickplayImage oldWidget) {
|
void didUpdateWidget(covariant TrickplayImage oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
if (oldWidget.position?.inMilliseconds != widget.position?.inMilliseconds) {
|
if (oldWidget.position?.inMilliseconds != widget.position?.inMilliseconds) {
|
||||||
time = widget.position ?? Duration.zero;
|
time = widget.position ?? Duration.zero;
|
||||||
model = widget.trickplay;
|
model = widget.trickplay;
|
||||||
loadImage();
|
loadImage();
|
||||||
}
|
}
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,22 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
import 'package:audio_service/audio_service.dart';
|
import 'package:audio_service/audio_service.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:media_kit/media_kit.dart';
|
||||||
|
import 'package:media_kit_video/media_kit_video.dart';
|
||||||
|
import 'package:smtc_windows/smtc_windows.dart';
|
||||||
|
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||||
|
|
||||||
import 'package:fladder/models/item_base_model.dart';
|
import 'package:fladder/models/item_base_model.dart';
|
||||||
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
||||||
import 'package:fladder/providers/settings/video_player_settings_provider.dart';
|
import 'package:fladder/providers/settings/video_player_settings_provider.dart';
|
||||||
import 'package:fladder/providers/video_player_provider.dart';
|
import 'package:fladder/providers/video_player_provider.dart';
|
||||||
import 'package:fladder/wrappers/media_control_base.dart';
|
import 'package:fladder/wrappers/media_control_base.dart';
|
||||||
import 'package:fladder/wrappers/media_wrapper_interface.dart';
|
import 'package:fladder/wrappers/media_wrapper_interface.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:media_kit/media_kit.dart';
|
|
||||||
import 'package:media_kit_video/media_kit_video.dart';
|
|
||||||
import 'package:smtc_windows/smtc_windows.dart';
|
|
||||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
|
||||||
|
|
||||||
class MediaControlsWrapper extends MediaPlayback implements MediaControlBase {
|
class MediaControlsWrapper extends MediaPlayback implements MediaControlBase {
|
||||||
MediaControlsWrapper({required this.ref});
|
MediaControlsWrapper({required this.ref});
|
||||||
|
|
|
||||||
97
pubspec.lock
97
pubspec.lock
|
|
@ -5,26 +5,31 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a
|
sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "61.0.0"
|
version: "72.0.0"
|
||||||
|
_macros:
|
||||||
|
dependency: transitive
|
||||||
|
description: dart
|
||||||
|
source: sdk
|
||||||
|
version: "0.3.2"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562
|
sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.13.0"
|
version: "6.7.0"
|
||||||
analyzer_plugin:
|
analyzer_plugin:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer_plugin
|
name: analyzer_plugin
|
||||||
sha256: c1d5f167683de03d5ab6c3b53fc9aeefc5d59476e7810ba7bbddff50c6f4392d
|
sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.11.2"
|
version: "0.11.3"
|
||||||
animations:
|
animations:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -97,6 +102,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.19"
|
version: "0.1.19"
|
||||||
|
auto_route:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: auto_route
|
||||||
|
sha256: b83e8ce46da7228cdd019b5a11205454847f0a971bca59a7529b98df9876889b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.2.2"
|
||||||
|
auto_route_generator:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: auto_route_generator
|
||||||
|
sha256: c9086eb07271e51b44071ad5cff34e889f3156710b964a308c2ab590769e79e6
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.0.0"
|
||||||
automatic_animated_list:
|
automatic_animated_list:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -317,10 +338,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cross_file
|
name: cross_file
|
||||||
sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32"
|
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.4+1"
|
version: "0.3.4+2"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -381,10 +402,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dart_style
|
name: dart_style
|
||||||
sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55"
|
sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.2"
|
version: "2.3.7"
|
||||||
dbus:
|
dbus:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -445,10 +466,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: extended_image_library
|
name: extended_image_library
|
||||||
sha256: c9caee8fe9b6547bd41c960c4f2d1ef8e34321804de6a1777f1d614a24247ad6
|
sha256: "9a94ec9314aa206cfa35f16145c3cd6e2c924badcc670eaaca8a3a8063a68cd7"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.4"
|
version: "4.0.5"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -773,14 +794,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
go_router:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: go_router
|
|
||||||
sha256: cdae1b9c8bd7efadcef6112e81c903662ef2ce105cbd220a04bbb7c3425b5554
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "14.2.0"
|
|
||||||
google_fonts:
|
google_fonts:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -929,10 +942,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: just_audio_web
|
name: just_audio_web
|
||||||
sha256: "0edb481ad4aa1ff38f8c40f1a3576013c3420bf6669b686fe661627d49bc606c"
|
sha256: "9a98035b8b24b40749507687520ec5ab404e291d2b0937823ff45d92cb18d448"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.11"
|
version: "0.4.13"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1013,6 +1026,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.0"
|
||||||
|
macros:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: macros
|
||||||
|
sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.2-main.4"
|
||||||
markdown:
|
markdown:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1161,18 +1182,18 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: package_info_plus
|
name: package_info_plus
|
||||||
sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0
|
sha256: a75164ade98cb7d24cfd0a13c6408927c6b217fa60dee5a7ff5c116a58f28918
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.0.0"
|
version: "8.0.2"
|
||||||
package_info_plus_platform_interface:
|
package_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_info_plus_platform_interface
|
name: package_info_plus_platform_interface
|
||||||
sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e
|
sha256: ac1f4a4847f1ade8e6a87d1f39f5d7c67490738642e2542f559ec38c37489a66
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "3.0.1"
|
||||||
page_transition:
|
page_transition:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -1345,10 +1366,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pointer_interceptor_web
|
name: pointer_interceptor_web
|
||||||
sha256: a6237528b46c411d8d55cdfad8fcb3269fc4cbb26060b14bff94879165887d1e
|
sha256: "7a7087782110f8c1827170660b09f8aa893e0e9a61431dbbe2ac3fc482e8c044"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.10.2"
|
version: "0.10.2+1"
|
||||||
pool:
|
pool:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1593,10 +1614,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_web
|
name: shared_preferences_web
|
||||||
sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a"
|
sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.2.1"
|
||||||
shared_preferences_windows:
|
shared_preferences_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1910,10 +1931,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_web
|
name: url_launcher_web
|
||||||
sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a"
|
sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.3.3"
|
||||||
url_launcher_windows:
|
url_launcher_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -2030,10 +2051,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: wakelock_plus
|
name: wakelock_plus
|
||||||
sha256: "14758533319a462ffb5aa3b7ddb198e59b29ac3b02da14173a1715d65d4e6e68"
|
sha256: bf4ee6f17a2fa373ed3753ad0e602b7603f8c75af006d5b9bdade263928c0484
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.5"
|
version: "1.2.8"
|
||||||
wakelock_plus_platform_interface:
|
wakelock_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -2062,18 +2083,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web
|
name: web
|
||||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.1"
|
version: "1.1.0"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42"
|
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.5"
|
version: "2.4.0"
|
||||||
webview_flutter:
|
webview_flutter:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ dependencies:
|
||||||
flutter_widget_from_html: ^0.14.11
|
flutter_widget_from_html: ^0.14.11
|
||||||
|
|
||||||
# Navigation
|
# Navigation
|
||||||
go_router: ^14.2.0
|
auto_route: ^9.2.2
|
||||||
url_launcher: ^6.1.10
|
url_launcher: ^6.1.10
|
||||||
flutter_custom_tabs: ^1.0.4
|
flutter_custom_tabs: ^1.0.4
|
||||||
|
|
||||||
|
|
@ -147,6 +147,7 @@ dev_dependencies:
|
||||||
swagger_dart_code_generator: ^2.15.2
|
swagger_dart_code_generator: ^2.15.2
|
||||||
riverpod_generator: ^2.4.0
|
riverpod_generator: ^2.4.0
|
||||||
dart_mappable_builder: ^4.2.3
|
dart_mappable_builder: ^4.2.3
|
||||||
|
auto_route_generator: ^9.0.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
# The following line ensures that the Material Icons font is
|
# The following line ensures that the Material Icons font is
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue