mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-08 23:18:16 -07:00
feat: UI 2.0 and other Improvements (#357)
Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
parent
9ca06eaa37
commit
e7b5bb40ff
169 changed files with 4584 additions and 3626 deletions
|
|
@ -1,9 +1,9 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||
|
||||
import 'package:fladder/screens/crash_screen/crash_screen.dart';
|
||||
import 'package:fladder/screens/settings/settings_scaffold.dart';
|
||||
|
|
@ -42,75 +42,79 @@ class AboutSettingsPage extends ConsumerWidget {
|
|||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final applicationInfo = ref.watch(applicationInfoProvider);
|
||||
return Card(
|
||||
child: SettingsScaffold(
|
||||
label: "",
|
||||
items: [
|
||||
const FladderLogo(),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(context.localized.aboutVersion(applicationInfo.versionAndPlatform)),
|
||||
Text(context.localized.aboutBuild(applicationInfo.buildNumber)),
|
||||
const SizedBox(height: 16),
|
||||
Text(context.localized.aboutCreatedBy),
|
||||
],
|
||||
return SettingsScaffold(
|
||||
label: "",
|
||||
items: [
|
||||
const FladderLogo(),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(context.localized.aboutVersion(applicationInfo.versionAndPlatform)),
|
||||
Text(context.localized.aboutBuild(applicationInfo.buildNumber)),
|
||||
const SizedBox(height: 16),
|
||||
Text(context.localized.aboutCreatedBy),
|
||||
],
|
||||
),
|
||||
const FractionallySizedBox(
|
||||
widthFactor: 0.25,
|
||||
child: Divider(
|
||||
indent: 16,
|
||||
endIndent: 16,
|
||||
),
|
||||
const Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
context.localized.aboutSocials,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: socials
|
||||
.map(
|
||||
(e) => IconButton.filledTonal(
|
||||
onPressed: () => launchUrl(context, e.url),
|
||||
icon: Column(
|
||||
children: [
|
||||
Icon(e.icon),
|
||||
Text(e.label),
|
||||
],
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
context.localized.aboutSocials,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: socials
|
||||
.map(
|
||||
(e) => IconButton.filledTonal(
|
||||
onPressed: () => launchUrl(context, e.url),
|
||||
icon: Column(
|
||||
children: [
|
||||
Icon(e.icon),
|
||||
Text(e.label),
|
||||
],
|
||||
),
|
||||
)
|
||||
.toList()
|
||||
.addInBetween(const SizedBox(width: 16)),
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
FilledButton.tonal(
|
||||
onPressed: () => showLicensePage(
|
||||
context: context,
|
||||
applicationIcon: const FladderIcon(size: 55),
|
||||
applicationVersion: applicationInfo.versionPlatformBuild,
|
||||
applicationLegalese: "DonutWare",
|
||||
),
|
||||
child: Text(context.localized.aboutLicenses),
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
FilledButton.tonal(
|
||||
onPressed: () => showDialog(
|
||||
context: context,
|
||||
builder: (context) => const CrashScreen(),
|
||||
),
|
||||
child: Text(context.localized.errorLogs),
|
||||
)
|
||||
],
|
||||
),
|
||||
].addInBetween(const SizedBox(height: 16)),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList()
|
||||
.addInBetween(const SizedBox(width: 16)),
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
FilledButton.tonal(
|
||||
onPressed: () => showLicensePage(
|
||||
context: context,
|
||||
applicationIcon: const FladderIcon(size: 55),
|
||||
applicationVersion: applicationInfo.versionPlatformBuild,
|
||||
applicationLegalese: "DonutWare",
|
||||
),
|
||||
child: Text(context.localized.aboutLicenses),
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
FilledButton.tonal(
|
||||
onPressed: () => showDialog(
|
||||
context: context,
|
||||
builder: (context) => const CrashScreen(),
|
||||
),
|
||||
child: Text(context.localized.errorLogs),
|
||||
)
|
||||
],
|
||||
),
|
||||
].addInBetween(const SizedBox(height: 16)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,79 +2,83 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/models/settings/home_settings_model.dart';
|
||||
import 'package:fladder/providers/settings/home_settings_provider.dart';
|
||||
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
||||
import 'package:fladder/screens/settings/widgets/settings_label_divider.dart';
|
||||
import 'package:fladder/screens/settings/widgets/settings_list_group.dart';
|
||||
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/util/option_dialogue.dart';
|
||||
|
||||
List<Widget> buildClientSettingsAdvanced(BuildContext context, WidgetRef ref) {
|
||||
return [
|
||||
return settingsListGroup(
|
||||
context,
|
||||
SettingsLabelDivider(label: context.localized.advanced),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsLayoutSizesTitle),
|
||||
subLabel: Text(context.localized.settingsLayoutSizesDesc),
|
||||
onTap: () async {
|
||||
final newItems = await openMultiSelectOptions<ViewSize>(
|
||||
context,
|
||||
label: context.localized.settingsLayoutSizesTitle,
|
||||
items: ViewSize.values,
|
||||
allowMultiSelection: true,
|
||||
selected: ref.read(homeSettingsProvider.select((value) => value.layoutStates.toList())),
|
||||
itemBuilder: (type, selected, tap) => CheckboxListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
value: selected,
|
||||
onChanged: (value) => tap(),
|
||||
title: Text(type.label(context)),
|
||||
[
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsLayoutSizesTitle),
|
||||
subLabel: Text(context.localized.settingsLayoutSizesDesc),
|
||||
onTap: () async {
|
||||
final newItems = await openMultiSelectOptions<ViewSize>(
|
||||
context,
|
||||
label: context.localized.settingsLayoutSizesTitle,
|
||||
items: ViewSize.values,
|
||||
allowMultiSelection: true,
|
||||
selected: ref.read(homeSettingsProvider.select((value) => value.layoutStates.toList())),
|
||||
itemBuilder: (type, selected, tap) => CheckboxListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
value: selected,
|
||||
onChanged: (value) => tap(),
|
||||
title: Text(type.label(context)),
|
||||
),
|
||||
);
|
||||
ref.read(homeSettingsProvider.notifier).setViewSize(newItems.toSet());
|
||||
},
|
||||
trailing: Card(
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
shadowColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(ref
|
||||
.watch(homeSettingsProvider.select((value) => value.layoutStates.toList()))
|
||||
.map((e) => e.label(context))
|
||||
.join(', ')),
|
||||
),
|
||||
);
|
||||
ref.read(homeSettingsProvider.notifier).setViewSize(newItems.toSet());
|
||||
},
|
||||
trailing: Card(
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
shadowColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(ref
|
||||
.watch(homeSettingsProvider.select((value) => value.layoutStates.toList()))
|
||||
.map((e) => e.label(context))
|
||||
.join(', ')),
|
||||
),
|
||||
),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsLayoutModesTitle),
|
||||
subLabel: Text(context.localized.settingsLayoutModesDesc),
|
||||
onTap: () async {
|
||||
final newItems = await openMultiSelectOptions<LayoutMode>(
|
||||
context,
|
||||
label: context.localized.settingsLayoutModesTitle,
|
||||
items: LayoutMode.values,
|
||||
allowMultiSelection: true,
|
||||
selected: ref.read(homeSettingsProvider.select((value) => value.screenLayouts.toList())),
|
||||
itemBuilder: (type, selected, tap) => CheckboxListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
value: selected,
|
||||
onChanged: (value) => tap(),
|
||||
title: Text(type.label(context)),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsLayoutModesTitle),
|
||||
subLabel: Text(context.localized.settingsLayoutModesDesc),
|
||||
onTap: () async {
|
||||
final newItems = await openMultiSelectOptions<LayoutMode>(
|
||||
context,
|
||||
label: context.localized.settingsLayoutModesTitle,
|
||||
items: LayoutMode.values,
|
||||
allowMultiSelection: true,
|
||||
selected: ref.read(homeSettingsProvider.select((value) => value.screenLayouts.toList())),
|
||||
itemBuilder: (type, selected, tap) => CheckboxListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
value: selected,
|
||||
onChanged: (value) => tap(),
|
||||
title: Text(type.label(context)),
|
||||
),
|
||||
);
|
||||
ref.read(homeSettingsProvider.notifier).setLayoutModes(newItems.toSet());
|
||||
},
|
||||
trailing: Card(
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
shadowColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(ref
|
||||
.watch(homeSettingsProvider.select((value) => value.screenLayouts.toList()))
|
||||
.map((e) => e.label(context))
|
||||
.join(', ')),
|
||||
),
|
||||
);
|
||||
ref.read(homeSettingsProvider.notifier).setLayoutModes(newItems.toSet());
|
||||
},
|
||||
trailing: Card(
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
shadowColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(ref
|
||||
.watch(homeSettingsProvider.select((value) => value.screenLayouts.toList()))
|
||||
.map((e) => e.label(context))
|
||||
.join(', ')),
|
||||
),
|
||||
),
|
||||
),
|
||||
];
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,89 +7,92 @@ import 'package:fladder/providers/settings/client_settings_provider.dart';
|
|||
import 'package:fladder/providers/settings/home_settings_provider.dart';
|
||||
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
||||
import 'package:fladder/screens/settings/widgets/settings_label_divider.dart';
|
||||
import 'package:fladder/screens/settings/widgets/settings_list_group.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/widgets/shared/enum_selection.dart';
|
||||
|
||||
List<Widget> buildClientSettingsDashboard(BuildContext context, WidgetRef ref) {
|
||||
final clientSettings = ref.watch(clientSettingsProvider);
|
||||
return [
|
||||
return settingsListGroup(
|
||||
context,
|
||||
SettingsLabelDivider(label: context.localized.dashboard),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsHomeBannerTitle),
|
||||
subLabel: Text(context.localized.settingsHomeBannerDescription),
|
||||
trailing: EnumBox(
|
||||
current: ref.watch(
|
||||
homeSettingsProvider.select(
|
||||
(value) => value.homeBanner.label(context),
|
||||
),
|
||||
),
|
||||
itemBuilder: (context) => HomeBanner.values
|
||||
.map(
|
||||
(entry) => PopupMenuItem(
|
||||
value: entry,
|
||||
child: Text(entry.label(context)),
|
||||
onTap: () =>
|
||||
ref.read(homeSettingsProvider.notifier).update((context) => context.copyWith(homeBanner: entry)),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
if (ref.watch(homeSettingsProvider.select((value) => value.homeBanner)) != HomeBanner.hide)
|
||||
[
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsHomeBannerInformationTitle),
|
||||
subLabel: Text(context.localized.settingsHomeBannerInformationDesc),
|
||||
label: Text(context.localized.settingsHomeBannerTitle),
|
||||
subLabel: Text(context.localized.settingsHomeBannerDescription),
|
||||
trailing: EnumBox(
|
||||
current: ref.watch(
|
||||
homeSettingsProvider.select((value) => value.carouselSettings.label(context)),
|
||||
homeSettingsProvider.select(
|
||||
(value) => value.homeBanner.label(context),
|
||||
),
|
||||
),
|
||||
itemBuilder: (context) => HomeCarouselSettings.values
|
||||
itemBuilder: (context) => HomeBanner.values
|
||||
.map(
|
||||
(entry) => PopupMenuItem(
|
||||
value: entry,
|
||||
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(homeBanner: entry)),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsHomeNextUpTitle),
|
||||
subLabel: Text(context.localized.settingsHomeNextUpDesc),
|
||||
trailing: EnumBox(
|
||||
current: ref.watch(
|
||||
homeSettingsProvider.select(
|
||||
(value) => value.nextUp.label(context),
|
||||
if (ref.watch(homeSettingsProvider.select((value) => value.homeBanner)) != HomeBanner.hide)
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsHomeBannerInformationTitle),
|
||||
subLabel: Text(context.localized.settingsHomeBannerInformationDesc),
|
||||
trailing: EnumBox(
|
||||
current: ref.watch(
|
||||
homeSettingsProvider.select((value) => value.carouselSettings.label(context)),
|
||||
),
|
||||
itemBuilder: (context) => HomeCarouselSettings.values
|
||||
.map(
|
||||
(entry) => PopupMenuItem(
|
||||
value: entry,
|
||||
child: Text(entry.label(context)),
|
||||
onTap: () => ref
|
||||
.read(homeSettingsProvider.notifier)
|
||||
.update((context) => context.copyWith(carouselSettings: entry)),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
itemBuilder: (context) => HomeNextUp.values
|
||||
.map(
|
||||
(entry) => PopupMenuItem(
|
||||
value: entry,
|
||||
child: Text(entry.label(context)),
|
||||
onTap: () =>
|
||||
ref.read(homeSettingsProvider.notifier).update((context) => context.copyWith(nextUp: entry)),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsHomeNextUpTitle),
|
||||
subLabel: Text(context.localized.settingsHomeNextUpDesc),
|
||||
trailing: EnumBox(
|
||||
current: ref.watch(
|
||||
homeSettingsProvider.select(
|
||||
(value) => value.nextUp.label(context),
|
||||
),
|
||||
),
|
||||
itemBuilder: (context) => HomeNextUp.values
|
||||
.map(
|
||||
(entry) => PopupMenuItem(
|
||||
value: entry,
|
||||
child: Text(entry.label(context)),
|
||||
onTap: () =>
|
||||
ref.read(homeSettingsProvider.notifier).update((context) => context.copyWith(nextUp: entry)),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.clientSettingsShowAllCollectionsTitle),
|
||||
subLabel: Text(context.localized.clientSettingsShowAllCollectionsDesc),
|
||||
onTap: () => ref
|
||||
.read(clientSettingsProvider.notifier)
|
||||
.update((current) => current.copyWith(showAllCollectionTypes: !current.showAllCollectionTypes)),
|
||||
trailing: Switch(
|
||||
value: clientSettings.showAllCollectionTypes,
|
||||
onChanged: (value) => ref
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.clientSettingsShowAllCollectionsTitle),
|
||||
subLabel: Text(context.localized.clientSettingsShowAllCollectionsDesc),
|
||||
onTap: () => ref
|
||||
.read(clientSettingsProvider.notifier)
|
||||
.update((current) => current.copyWith(showAllCollectionTypes: value)),
|
||||
.update((current) => current.copyWith(showAllCollectionTypes: !current.showAllCollectionTypes)),
|
||||
trailing: Switch(
|
||||
value: clientSettings.showAllCollectionTypes,
|
||||
onChanged: (value) => ref
|
||||
.read(clientSettingsProvider.notifier)
|
||||
.update((current) => current.copyWith(showAllCollectionTypes: value)),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
];
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,10 @@ import 'package:fladder/providers/sync_provider.dart';
|
|||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
||||
import 'package:fladder/screens/settings/widgets/settings_label_divider.dart';
|
||||
import 'package:fladder/screens/settings/widgets/settings_list_group.dart';
|
||||
import 'package:fladder/screens/shared/default_alert_dialog.dart';
|
||||
import 'package:fladder/screens/shared/input_fields.dart';
|
||||
import 'package:fladder/util/adaptive_layout.dart';
|
||||
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/util/size_formatting.dart';
|
||||
|
||||
|
|
@ -24,121 +25,122 @@ List<Widget> buildClientSettingsDownload(BuildContext context, WidgetRef ref, Fu
|
|||
|
||||
return [
|
||||
if (canSync && !kIsWeb) ...[
|
||||
SettingsLabelDivider(label: context.localized.downloadsTitle),
|
||||
if (AdaptiveLayout.of(context).isDesktop) ...[
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.downloadsPath),
|
||||
subLabel: Text(currentFolder ?? "-"),
|
||||
onTap: currentFolder != null
|
||||
? () async => await showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(context.localized.pathEditTitle),
|
||||
content: Text(context.localized.pathEditDesc),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
String? selectedDirectory = await FilePicker.platform.getDirectoryPath(
|
||||
dialogTitle: context.localized.pathEditSelect, initialDirectory: currentFolder);
|
||||
if (selectedDirectory != null) {
|
||||
ref.read(clientSettingsProvider.notifier).setSyncPath(selectedDirectory);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(context.localized.change),
|
||||
)
|
||||
],
|
||||
...settingsListGroup(context, SettingsLabelDivider(label: context.localized.downloadsTitle), [
|
||||
if (AdaptiveLayout.of(context).isDesktop) ...[
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.downloadsPath),
|
||||
subLabel: Text(currentFolder ?? "-"),
|
||||
onTap: currentFolder != null
|
||||
? () async => await showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(context.localized.pathEditTitle),
|
||||
content: Text(context.localized.pathEditDesc),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
String? selectedDirectory = await FilePicker.platform.getDirectoryPath(
|
||||
dialogTitle: context.localized.pathEditSelect, initialDirectory: currentFolder);
|
||||
if (selectedDirectory != null) {
|
||||
ref.read(clientSettingsProvider.notifier).setSyncPath(selectedDirectory);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(context.localized.change),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
: () async {
|
||||
String? selectedDirectory = await FilePicker.platform.getDirectoryPath(
|
||||
dialogTitle: context.localized.pathEditSelect, initialDirectory: currentFolder);
|
||||
if (selectedDirectory != null) {
|
||||
ref.read(clientSettingsProvider.notifier).setSyncPath(selectedDirectory);
|
||||
}
|
||||
},
|
||||
trailing: currentFolder?.isNotEmpty == true
|
||||
? IconButton(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
onPressed: () async => await showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(context.localized.pathClearTitle),
|
||||
content: Text(context.localized.pathEditDesc),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
ref.read(clientSettingsProvider.notifier).setSyncPath(null);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(context.localized.clear),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
icon: const Icon(IconsaxPlusLinear.folder_minus),
|
||||
)
|
||||
: () async {
|
||||
String? selectedDirectory = await FilePicker.platform
|
||||
.getDirectoryPath(dialogTitle: context.localized.pathEditSelect, initialDirectory: currentFolder);
|
||||
if (selectedDirectory != null) {
|
||||
ref.read(clientSettingsProvider.notifier).setSyncPath(selectedDirectory);
|
||||
: null,
|
||||
),
|
||||
],
|
||||
FutureBuilder(
|
||||
future: ref.watch(syncProvider.notifier).directorySize,
|
||||
builder: (context, snapshot) {
|
||||
final data = snapshot.data ?? 0;
|
||||
return SettingsListTile(
|
||||
label: Text(context.localized.downloadsSyncedData),
|
||||
subLabel: Text(data.byteFormat ?? ""),
|
||||
trailing: FilledButton(
|
||||
onPressed: () {
|
||||
showDefaultAlertDialog(
|
||||
context,
|
||||
context.localized.downloadsClearTitle,
|
||||
context.localized.downloadsClearDesc,
|
||||
(context) async {
|
||||
await ref.read(syncProvider.notifier).clear();
|
||||
setState(() {});
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
context.localized.clear,
|
||||
(context) => Navigator.of(context).pop(),
|
||||
context.localized.cancel,
|
||||
);
|
||||
},
|
||||
child: Text(context.localized.clear),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.clientSettingsRequireWifiTitle),
|
||||
subLabel: Text(context.localized.clientSettingsRequireWifiDesc),
|
||||
onTap: () => ref.read(clientSettingsProvider.notifier).setRequireWifi(!clientSettings.requireWifi),
|
||||
trailing: Switch(
|
||||
value: clientSettings.requireWifi,
|
||||
onChanged: (value) => ref.read(clientSettingsProvider.notifier).setRequireWifi(value),
|
||||
),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.maxConcurrentDownloadsTitle),
|
||||
subLabel: Text(context.localized.maxConcurrentDownloadsDesc),
|
||||
trailing: SizedBox(
|
||||
width: 100,
|
||||
child: IntInputField(
|
||||
controller: TextEditingController(text: clientSettings.maxConcurrentDownloads.toString()),
|
||||
onSubmitted: (value) {
|
||||
if (value != null) {
|
||||
ref.read(clientSettingsProvider.notifier).update(
|
||||
(current) => current.copyWith(
|
||||
maxConcurrentDownloads: value,
|
||||
),
|
||||
);
|
||||
|
||||
ref.read(backgroundDownloaderProvider.notifier).setMaxConcurrent(value);
|
||||
}
|
||||
},
|
||||
trailing: currentFolder?.isNotEmpty == true
|
||||
? IconButton(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
onPressed: () async => await showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(context.localized.pathClearTitle),
|
||||
content: Text(context.localized.pathEditDesc),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
ref.read(clientSettingsProvider.notifier).setSyncPath(null);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(context.localized.clear),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
icon: const Icon(IconsaxPlusLinear.folder_minus),
|
||||
)
|
||||
: null,
|
||||
)),
|
||||
),
|
||||
],
|
||||
FutureBuilder(
|
||||
future: ref.watch(syncProvider.notifier).directorySize,
|
||||
builder: (context, snapshot) {
|
||||
final data = snapshot.data ?? 0;
|
||||
return SettingsListTile(
|
||||
label: Text(context.localized.downloadsSyncedData),
|
||||
subLabel: Text(data.byteFormat ?? ""),
|
||||
trailing: FilledButton(
|
||||
onPressed: () {
|
||||
showDefaultAlertDialog(
|
||||
context,
|
||||
context.localized.downloadsClearTitle,
|
||||
context.localized.downloadsClearDesc,
|
||||
(context) async {
|
||||
await ref.read(syncProvider.notifier).clear();
|
||||
setState(() {});
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
context.localized.clear,
|
||||
(context) => Navigator.of(context).pop(),
|
||||
context.localized.cancel,
|
||||
);
|
||||
},
|
||||
child: Text(context.localized.clear),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.clientSettingsRequireWifiTitle),
|
||||
subLabel: Text(context.localized.clientSettingsRequireWifiDesc),
|
||||
onTap: () => ref.read(clientSettingsProvider.notifier).setRequireWifi(!clientSettings.requireWifi),
|
||||
trailing: Switch(
|
||||
value: clientSettings.requireWifi,
|
||||
onChanged: (value) => ref.read(clientSettingsProvider.notifier).setRequireWifi(value),
|
||||
),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.maxConcurrentDownloadsTitle),
|
||||
subLabel: Text(context.localized.maxConcurrentDownloadsDesc),
|
||||
trailing: SizedBox(
|
||||
width: 100,
|
||||
child: IntInputField(
|
||||
controller: TextEditingController(text: clientSettings.maxConcurrentDownloads.toString()),
|
||||
onSubmitted: (value) {
|
||||
if (value != null) {
|
||||
ref.read(clientSettingsProvider.notifier).update(
|
||||
(current) => current.copyWith(
|
||||
maxConcurrentDownloads: value,
|
||||
),
|
||||
);
|
||||
|
||||
ref.read(backgroundDownloaderProvider.notifier).setMaxConcurrent(value);
|
||||
}
|
||||
},
|
||||
)),
|
||||
),
|
||||
const Divider(),
|
||||
]),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
||||
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
||||
import 'package:fladder/screens/settings/widgets/settings_label_divider.dart';
|
||||
import 'package:fladder/screens/settings/widgets/settings_list_group.dart';
|
||||
import 'package:fladder/util/color_extensions.dart';
|
||||
import 'package:fladder/util/custom_color_themes.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
|
|
@ -13,8 +14,7 @@ import 'package:fladder/util/theme_mode_extension.dart';
|
|||
|
||||
List<Widget> buildClientSettingsTheme(BuildContext context, WidgetRef ref) {
|
||||
final clientSettings = ref.watch(clientSettingsProvider);
|
||||
return [
|
||||
SettingsLabelDivider(label: context.localized.theme),
|
||||
return settingsListGroup(context, SettingsLabelDivider(label: context.localized.theme), [
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.mode),
|
||||
subLabel: Text(clientSettings.themeMode.label(context)),
|
||||
|
|
@ -107,6 +107,5 @@ List<Widget> buildClientSettingsTheme(BuildContext context, WidgetRef ref) {
|
|||
onChanged: (value) => ref.read(clientSettingsProvider.notifier).setAmoledBlack(value),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
];
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
|
@ -8,8 +6,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
||||
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
||||
import 'package:fladder/screens/settings/widgets/settings_label_divider.dart';
|
||||
import 'package:fladder/screens/settings/widgets/settings_list_group.dart';
|
||||
import 'package:fladder/screens/shared/input_fields.dart';
|
||||
import 'package:fladder/util/adaptive_layout.dart';
|
||||
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/widgets/shared/enum_selection.dart';
|
||||
import 'package:fladder/widgets/shared/fladder_slider.dart';
|
||||
|
|
@ -22,136 +21,136 @@ List<Widget> buildClientSettingsVisual(
|
|||
) {
|
||||
final clientSettings = ref.watch(clientSettingsProvider);
|
||||
Locale currentLocale = WidgetsBinding.instance.platformDispatcher.locale;
|
||||
return [
|
||||
return settingsListGroup(
|
||||
context,
|
||||
SettingsLabelDivider(label: context.localized.settingsVisual),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.displayLanguage),
|
||||
trailing: Localizations.override(
|
||||
context: context,
|
||||
locale: ref.watch(clientSettingsProvider.select((value) => (value.selectedLocale ?? currentLocale))),
|
||||
child: Builder(builder: (context) {
|
||||
String language = "Unknown";
|
||||
try {
|
||||
language = context.localized.nativeName;
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
}
|
||||
return EnumBox(
|
||||
current: language,
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
...AppLocalizations.supportedLocales.map(
|
||||
(entry) => PopupMenuItem(
|
||||
value: entry,
|
||||
child: Localizations.override(
|
||||
context: context,
|
||||
locale: entry,
|
||||
child: Builder(builder: (context) {
|
||||
return Text("${context.localized.nativeName} (${entry.languageCode.toUpperCase()})");
|
||||
}),
|
||||
[
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.displayLanguage),
|
||||
trailing: Localizations.override(
|
||||
context: context,
|
||||
locale: ref.watch(clientSettingsProvider.select((value) => (value.selectedLocale ?? currentLocale))),
|
||||
child: Builder(builder: (context) {
|
||||
String language = "Unknown";
|
||||
try {
|
||||
language = context.localized.nativeName;
|
||||
} catch (_) {}
|
||||
return EnumBox(
|
||||
current: language,
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
...AppLocalizations.supportedLocales.map(
|
||||
(entry) => PopupMenuItem(
|
||||
value: entry,
|
||||
child: Localizations.override(
|
||||
context: context,
|
||||
locale: entry,
|
||||
child: Builder(builder: (context) {
|
||||
return Text("${context.localized.nativeName} (${entry.languageCode.toUpperCase()})");
|
||||
}),
|
||||
),
|
||||
onTap: () => ref
|
||||
.read(clientSettingsProvider.notifier)
|
||||
.update((state) => state.copyWith(selectedLocale: entry)),
|
||||
),
|
||||
onTap: () => ref
|
||||
.read(clientSettingsProvider.notifier)
|
||||
.update((state) => state.copyWith(selectedLocale: entry)),
|
||||
)
|
||||
];
|
||||
},
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsBlurredPlaceholderTitle),
|
||||
subLabel: Text(context.localized.settingsBlurredPlaceholderDesc),
|
||||
onTap: () => ref.read(clientSettingsProvider.notifier).setBlurPlaceholders(!clientSettings.blurPlaceHolders),
|
||||
trailing: Switch(
|
||||
value: clientSettings.blurPlaceHolders,
|
||||
onChanged: (value) => ref.read(clientSettingsProvider.notifier).setBlurPlaceholders(value),
|
||||
),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsBlurEpisodesTitle),
|
||||
subLabel: Text(context.localized.settingsBlurEpisodesDesc),
|
||||
onTap: () => ref.read(clientSettingsProvider.notifier).setBlurEpisodes(!clientSettings.blurUpcomingEpisodes),
|
||||
trailing: Switch(
|
||||
value: clientSettings.blurUpcomingEpisodes,
|
||||
onChanged: (value) => ref.read(clientSettingsProvider.notifier).setBlurEpisodes(value),
|
||||
),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsEnableOsMediaControls),
|
||||
onTap: () => ref.read(clientSettingsProvider.notifier).setMediaKeys(!clientSettings.enableMediaKeys),
|
||||
trailing: Switch(
|
||||
value: clientSettings.enableMediaKeys,
|
||||
onChanged: (value) => ref.read(clientSettingsProvider.notifier).setMediaKeys(value),
|
||||
),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsNextUpCutoffDays),
|
||||
trailing: SizedBox(
|
||||
width: 100,
|
||||
child: IntInputField(
|
||||
suffix: context.localized.days,
|
||||
controller: nextUpDaysEditor,
|
||||
onSubmitted: (value) {
|
||||
if (value != null) {
|
||||
ref.read(clientSettingsProvider.notifier).update((current) => current.copyWith(
|
||||
nextUpDateCutoff: Duration(days: value),
|
||||
));
|
||||
}
|
||||
},
|
||||
)),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.libraryPageSizeTitle),
|
||||
subLabel: Text(context.localized.libraryPageSizeDesc),
|
||||
trailing: SizedBox(
|
||||
width: 100,
|
||||
child: IntInputField(
|
||||
controller: libraryPageSizeController,
|
||||
placeHolder: "500",
|
||||
onSubmitted: (value) => ref.read(clientSettingsProvider.notifier).update(
|
||||
(current) => current.copyWith(libraryPageSize: value),
|
||||
),
|
||||
)
|
||||
];
|
||||
},
|
||||
);
|
||||
}),
|
||||
)),
|
||||
),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsBlurredPlaceholderTitle),
|
||||
subLabel: Text(context.localized.settingsBlurredPlaceholderDesc),
|
||||
onTap: () => ref.read(clientSettingsProvider.notifier).setBlurPlaceholders(!clientSettings.blurPlaceHolders),
|
||||
trailing: Switch(
|
||||
value: clientSettings.blurPlaceHolders,
|
||||
onChanged: (value) => ref.read(clientSettingsProvider.notifier).setBlurPlaceholders(value),
|
||||
),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsBlurEpisodesTitle),
|
||||
subLabel: Text(context.localized.settingsBlurEpisodesDesc),
|
||||
onTap: () => ref.read(clientSettingsProvider.notifier).setBlurEpisodes(!clientSettings.blurUpcomingEpisodes),
|
||||
trailing: Switch(
|
||||
value: clientSettings.blurUpcomingEpisodes,
|
||||
onChanged: (value) => ref.read(clientSettingsProvider.notifier).setBlurEpisodes(value),
|
||||
),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsEnableOsMediaControls),
|
||||
onTap: () => ref.read(clientSettingsProvider.notifier).setMediaKeys(!clientSettings.enableMediaKeys),
|
||||
trailing: Switch(
|
||||
value: clientSettings.enableMediaKeys,
|
||||
onChanged: (value) => ref.read(clientSettingsProvider.notifier).setMediaKeys(value),
|
||||
),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsNextUpCutoffDays),
|
||||
trailing: SizedBox(
|
||||
width: 100,
|
||||
child: IntInputField(
|
||||
suffix: context.localized.days,
|
||||
controller: nextUpDaysEditor,
|
||||
onSubmitted: (value) {
|
||||
if (value != null) {
|
||||
ref.read(clientSettingsProvider.notifier).update((current) => current.copyWith(
|
||||
nextUpDateCutoff: Duration(days: value),
|
||||
));
|
||||
}
|
||||
},
|
||||
)),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.libraryPageSizeTitle),
|
||||
subLabel: Text(context.localized.libraryPageSizeDesc),
|
||||
trailing: SizedBox(
|
||||
width: 100,
|
||||
child: IntInputField(
|
||||
controller: libraryPageSizeController,
|
||||
placeHolder: "500",
|
||||
onSubmitted: (value) => ref.read(clientSettingsProvider.notifier).update(
|
||||
(current) => current.copyWith(libraryPageSize: value),
|
||||
),
|
||||
)),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(AdaptiveLayout.of(context).isDesktop
|
||||
? context.localized.settingsShowScaleSlider
|
||||
: context.localized.settingsPosterPinch),
|
||||
onTap: () => ref.read(clientSettingsProvider.notifier).update(
|
||||
(current) => current.copyWith(pinchPosterZoom: !current.pinchPosterZoom),
|
||||
),
|
||||
trailing: Switch(
|
||||
value: clientSettings.pinchPosterZoom,
|
||||
onChanged: (value) => ref.read(clientSettingsProvider.notifier).update(
|
||||
(current) => current.copyWith(pinchPosterZoom: value),
|
||||
SettingsListTile(
|
||||
label: Text(AdaptiveLayout.of(context).isDesktop
|
||||
? context.localized.settingsShowScaleSlider
|
||||
: context.localized.settingsPosterPinch),
|
||||
onTap: () => ref.read(clientSettingsProvider.notifier).update(
|
||||
(current) => current.copyWith(pinchPosterZoom: !current.pinchPosterZoom),
|
||||
),
|
||||
trailing: Switch(
|
||||
value: clientSettings.pinchPosterZoom,
|
||||
onChanged: (value) => ref.read(clientSettingsProvider.notifier).update(
|
||||
(current) => current.copyWith(pinchPosterZoom: value),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsPosterSize),
|
||||
trailing: Text(
|
||||
clientSettings.posterSize.toString(),
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
Column(
|
||||
children: [
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsPosterSize),
|
||||
trailing: Text(
|
||||
clientSettings.posterSize.toString(),
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: FladderSlider(
|
||||
min: 0.5,
|
||||
max: 1.5,
|
||||
value: clientSettings.posterSize,
|
||||
divisions: 20,
|
||||
onChanged: (value) =>
|
||||
ref.read(clientSettingsProvider.notifier).update((current) => current.copyWith(posterSize: value)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: FladderSlider(
|
||||
min: 0.5,
|
||||
max: 1.5,
|
||||
value: clientSettings.posterSize,
|
||||
divisions: 20,
|
||||
onChanged: (value) =>
|
||||
ref.read(clientSettingsProvider.notifier).update((current) => current.copyWith(posterSize: value)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(),
|
||||
];
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/models/settings/home_settings_model.dart';
|
||||
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
||||
import 'package:fladder/providers/shared_provider.dart';
|
||||
import 'package:fladder/routes/auto_router.gr.dart';
|
||||
|
|
@ -16,7 +15,8 @@ import 'package:fladder/screens/settings/client_sections/client_settings_visual.
|
|||
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
||||
import 'package:fladder/screens/settings/settings_scaffold.dart';
|
||||
import 'package:fladder/screens/settings/widgets/settings_label_divider.dart';
|
||||
import 'package:fladder/util/adaptive_layout.dart';
|
||||
import 'package:fladder/screens/settings/widgets/settings_list_group.dart';
|
||||
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/util/simple_duration_picker.dart';
|
||||
|
||||
|
|
@ -38,16 +38,12 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final clientSettings = ref.watch(clientSettingsProvider);
|
||||
final showBackground = AdaptiveLayout.viewSizeOf(context) != ViewSize.phone &&
|
||||
AdaptiveLayout.layoutModeOf(context) != LayoutMode.single;
|
||||
|
||||
return Card(
|
||||
elevation: showBackground ? 2 : 0,
|
||||
child: SettingsScaffold(
|
||||
label: "Fladder",
|
||||
items: [
|
||||
...buildClientSettingsDownload(context, ref, setState),
|
||||
SettingsLabelDivider(label: context.localized.lockscreen),
|
||||
return SettingsScaffold(
|
||||
label: "Fladder",
|
||||
items: [
|
||||
...buildClientSettingsDownload(context, ref, setState),
|
||||
...settingsListGroup(context, SettingsLabelDivider(label: context.localized.lockscreen), [
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.timeOut),
|
||||
subLabel: Text(timePickerString(context, clientSettings.timeOut)),
|
||||
|
|
@ -64,12 +60,16 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
|||
: null);
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
...buildClientSettingsDashboard(context, ref),
|
||||
...buildClientSettingsVisual(context, ref, nextUpDaysEditor, libraryPageSizeController),
|
||||
...buildClientSettingsTheme(context, ref),
|
||||
if (AdaptiveLayout.inputDeviceOf(context) == InputDevice.pointer) ...[
|
||||
SettingsLabelDivider(label: context.localized.controls),
|
||||
]),
|
||||
const SizedBox(height: 12),
|
||||
...buildClientSettingsDashboard(context, ref),
|
||||
const SizedBox(height: 12),
|
||||
...buildClientSettingsVisual(context, ref, nextUpDaysEditor, libraryPageSizeController),
|
||||
const SizedBox(height: 12),
|
||||
...buildClientSettingsTheme(context, ref),
|
||||
const SizedBox(height: 12),
|
||||
if (AdaptiveLayout.inputDeviceOf(context) == InputDevice.pointer) ...[
|
||||
...settingsListGroup(context, SettingsLabelDivider(label: context.localized.controls), [
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.mouseDragSupport),
|
||||
subLabel: Text(clientSettings.mouseDragSupport ? context.localized.enabled : context.localized.disabled),
|
||||
|
|
@ -83,61 +83,61 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
|
|||
.update((current) => current.copyWith(mouseDragSupport: !clientSettings.mouseDragSupport)),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
],
|
||||
...buildClientSettingsAdvanced(context, ref),
|
||||
if (kDebugMode) ...[
|
||||
const SizedBox(height: 64),
|
||||
SettingsListTile(
|
||||
label: Text(
|
||||
context.localized.clearAllSettings,
|
||||
),
|
||||
contentColor: Theme.of(context).colorScheme.error,
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => Dialog(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
context.localized.clearAllSettingsQuestion,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
context.localized.unableToReverseAction,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(context.localized.cancel),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
await ref.read(sharedPreferencesProvider).clear();
|
||||
context.router.push(const LoginRoute());
|
||||
},
|
||||
child: Text(context.localized.clear),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
...buildClientSettingsAdvanced(context, ref),
|
||||
if (kDebugMode) ...[
|
||||
const SizedBox(height: 64),
|
||||
SettingsListTile(
|
||||
label: Text(
|
||||
context.localized.clearAllSettings,
|
||||
),
|
||||
contentColor: Theme.of(context).colorScheme.error,
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => Dialog(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
context.localized.clearAllSettingsQuestion,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
context.localized.unableToReverseAction,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(context.localized.cancel),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
await ref.read(sharedPreferencesProvider).clear();
|
||||
context.router.push(const LoginRoute());
|
||||
},
|
||||
child: Text(context.localized.clear),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,20 +8,20 @@ import 'package:collection/collection.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/models/items/media_segments_model.dart';
|
||||
import 'package:fladder/models/settings/home_settings_model.dart';
|
||||
import 'package:fladder/models/settings/video_player_settings.dart';
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/providers/connectivity_provider.dart';
|
||||
import 'package:fladder/providers/settings/video_player_settings_provider.dart';
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/screens/settings/settings_list_tile.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_list_group.dart';
|
||||
import 'package:fladder/screens/settings/widgets/settings_message_box.dart';
|
||||
import 'package:fladder/screens/settings/widgets/subtitle_editor.dart';
|
||||
import 'package:fladder/screens/shared/animated_fade_size.dart';
|
||||
import 'package:fladder/screens/shared/input_fields.dart';
|
||||
import 'package:fladder/screens/video_player/components/video_player_options_sheet.dart';
|
||||
import 'package:fladder/util/adaptive_layout.dart';
|
||||
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
|
||||
import 'package:fladder/util/bitrate_helper.dart';
|
||||
import 'package:fladder/util/box_fit_extension.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
|
|
@ -41,100 +41,105 @@ class _PlayerSettingsPageState extends ConsumerState<PlayerSettingsPage> {
|
|||
Widget build(BuildContext context) {
|
||||
final videoSettings = ref.watch(videoPlayerSettingsProvider);
|
||||
final provider = ref.read(videoPlayerSettingsProvider.notifier);
|
||||
final showBackground = AdaptiveLayout.viewSizeOf(context) != ViewSize.phone &&
|
||||
AdaptiveLayout.layoutModeOf(context) != LayoutMode.single;
|
||||
|
||||
final connectionState = ref.watch(connectivityStatusProvider);
|
||||
|
||||
return Card(
|
||||
elevation: showBackground ? 2 : 0,
|
||||
child: SettingsScaffold(
|
||||
label: context.localized.settingsPlayerTitle,
|
||||
items: [
|
||||
return SettingsScaffold(
|
||||
label: context.localized.settingsPlayerTitle,
|
||||
items: [
|
||||
...settingsListGroup(
|
||||
context,
|
||||
SettingsLabelDivider(label: context.localized.video),
|
||||
if (!AdaptiveLayout.of(context).isDesktop && !kIsWeb)
|
||||
[
|
||||
if (!AdaptiveLayout.of(context).isDesktop && !kIsWeb)
|
||||
Column(
|
||||
children: [
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.videoScalingFillScreenTitle),
|
||||
subLabel: Text(context.localized.videoScalingFillScreenDesc),
|
||||
onTap: () => provider.setFillScreen(!videoSettings.fillScreen),
|
||||
trailing: Switch(
|
||||
value: videoSettings.fillScreen,
|
||||
onChanged: (value) => provider.setFillScreen(value),
|
||||
),
|
||||
),
|
||||
AnimatedFadeSize(
|
||||
child: videoSettings.fillScreen
|
||||
? SettingsMessageBox(
|
||||
context.localized.videoScalingFillScreenNotif,
|
||||
messageType: MessageType.warning,
|
||||
)
|
||||
: Container(),
|
||||
),
|
||||
],
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.videoScalingFillScreenTitle),
|
||||
subLabel: Text(context.localized.videoScalingFillScreenDesc),
|
||||
onTap: () => provider.setFillScreen(!videoSettings.fillScreen),
|
||||
trailing: Switch(
|
||||
value: videoSettings.fillScreen,
|
||||
onChanged: (value) => provider.setFillScreen(value),
|
||||
subLabel: Text(videoSettings.videoFit.label(context)),
|
||||
onTap: () => openMultiSelectOptions(
|
||||
context,
|
||||
label: context.localized.videoScalingFillScreenTitle,
|
||||
items: BoxFit.values,
|
||||
selected: [ref.read(videoPlayerSettingsProvider.select((value) => value.videoFit))],
|
||||
onChanged: (values) => ref.read(videoPlayerSettingsProvider.notifier).setFitType(values.first),
|
||||
itemBuilder: (type, selected, tap) => RadioListTile(
|
||||
groupValue: ref.read(videoPlayerSettingsProvider.select((value) => value.videoFit)),
|
||||
title: Text(type.label(context)),
|
||||
value: type,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
onChanged: (value) => tap(),
|
||||
),
|
||||
),
|
||||
),
|
||||
AnimatedFadeSize(
|
||||
child: videoSettings.fillScreen
|
||||
? SettingsMessageBox(
|
||||
context.localized.videoScalingFillScreenNotif,
|
||||
messageType: MessageType.warning,
|
||||
)
|
||||
: Container(),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.videoScalingFillScreenTitle),
|
||||
subLabel: Text(videoSettings.videoFit.label(context)),
|
||||
onTap: () => openMultiSelectOptions(
|
||||
context,
|
||||
label: context.localized.videoScalingFillScreenTitle,
|
||||
items: BoxFit.values,
|
||||
selected: [ref.read(videoPlayerSettingsProvider.select((value) => value.videoFit))],
|
||||
onChanged: (values) => ref.read(videoPlayerSettingsProvider.notifier).setFitType(values.first),
|
||||
itemBuilder: (type, selected, tap) => RadioListTile(
|
||||
groupValue: ref.read(videoPlayerSettingsProvider.select((value) => value.videoFit)),
|
||||
title: Text(type.label(context)),
|
||||
value: type,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
onChanged: (value) => tap(),
|
||||
SettingsListTile(
|
||||
label: _StatusIndicator(
|
||||
homeInternet: connectionState.homeInternet,
|
||||
label: Text(context.localized.homeStreamingQualityTitle),
|
||||
),
|
||||
subLabel: Text(context.localized.homeStreamingQualityDesc),
|
||||
trailing: EnumBox(
|
||||
current: ref.watch(
|
||||
videoPlayerSettingsProvider.select((value) => value.maxHomeBitrate.label(context)),
|
||||
),
|
||||
itemBuilder: (context) => Bitrate.values
|
||||
.map(
|
||||
(entry) => PopupMenuItem(
|
||||
value: entry,
|
||||
child: Text(entry.label(context)),
|
||||
onTap: () => ref.read(videoPlayerSettingsProvider.notifier).state =
|
||||
ref.read(videoPlayerSettingsProvider).copyWith(maxHomeBitrate: entry),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: _StatusIndicator(
|
||||
homeInternet: connectionState.homeInternet,
|
||||
label: Text(context.localized.homeStreamingQualityTitle),
|
||||
),
|
||||
subLabel: Text(context.localized.homeStreamingQualityDesc),
|
||||
trailing: EnumBox(
|
||||
current: ref.watch(
|
||||
videoPlayerSettingsProvider.select((value) => value.maxHomeBitrate.label(context)),
|
||||
SettingsListTile(
|
||||
label: _StatusIndicator(
|
||||
homeInternet: !connectionState.homeInternet,
|
||||
label: Text(context.localized.internetStreamingQualityTitle),
|
||||
),
|
||||
itemBuilder: (context) => Bitrate.values
|
||||
.map(
|
||||
(entry) => PopupMenuItem(
|
||||
value: entry,
|
||||
child: Text(entry.label(context)),
|
||||
onTap: () => ref.read(videoPlayerSettingsProvider.notifier).state =
|
||||
ref.read(videoPlayerSettingsProvider).copyWith(maxHomeBitrate: entry),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: _StatusIndicator(
|
||||
homeInternet: !connectionState.homeInternet,
|
||||
label: Text(context.localized.internetStreamingQualityTitle),
|
||||
),
|
||||
subLabel: Text(context.localized.internetStreamingQualityDesc),
|
||||
trailing: EnumBox(
|
||||
current: ref.watch(
|
||||
videoPlayerSettingsProvider.select((value) => value.maxInternetBitrate.label(context)),
|
||||
subLabel: Text(context.localized.internetStreamingQualityDesc),
|
||||
trailing: EnumBox(
|
||||
current: ref.watch(
|
||||
videoPlayerSettingsProvider.select((value) => value.maxInternetBitrate.label(context)),
|
||||
),
|
||||
itemBuilder: (context) => Bitrate.values
|
||||
.map(
|
||||
(entry) => PopupMenuItem(
|
||||
value: entry,
|
||||
child: Text(entry.label(context)),
|
||||
onTap: () => ref.read(videoPlayerSettingsProvider.notifier).state =
|
||||
ref.read(videoPlayerSettingsProvider).copyWith(maxInternetBitrate: entry),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
itemBuilder: (context) => Bitrate.values
|
||||
.map(
|
||||
(entry) => PopupMenuItem(
|
||||
value: entry,
|
||||
child: Text(entry.label(context)),
|
||||
onTap: () => ref.read(videoPlayerSettingsProvider.notifier).state =
|
||||
ref.read(videoPlayerSettingsProvider).copyWith(maxInternetBitrate: entry),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
SettingsLabelDivider(label: context.localized.mediaSegmentActions),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
...settingsListGroup(context, SettingsLabelDivider(label: context.localized.mediaSegmentActions), [
|
||||
...videoSettings.segmentSkipSettings.entries.sorted((a, b) => b.key.index.compareTo(a.key.index)).map(
|
||||
(entry) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
|
|
@ -167,7 +172,9 @@ class _PlayerSettingsPageState extends ConsumerState<PlayerSettingsPage> {
|
|||
),
|
||||
),
|
||||
),
|
||||
SettingsLabelDivider(label: context.localized.playbackTrackSelection),
|
||||
]),
|
||||
const SizedBox(height: 12),
|
||||
...settingsListGroup(context, SettingsLabelDivider(label: context.localized.playbackTrackSelection), [
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.rememberAudioSelections),
|
||||
subLabel: Text(context.localized.rememberAudioSelectionsDesc),
|
||||
|
|
@ -190,8 +197,9 @@ class _PlayerSettingsPageState extends ConsumerState<PlayerSettingsPage> {
|
|||
onChanged: (_) => ref.read(userProvider.notifier).setRememberSubtitleSelections(),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
SettingsLabelDivider(label: context.localized.advanced),
|
||||
]),
|
||||
const SizedBox(height: 12),
|
||||
...settingsListGroup(context, SettingsLabelDivider(label: context.localized.advanced), [
|
||||
if (PlayerOptions.available.length != 1)
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.playerSettingsBackendTitle),
|
||||
|
|
@ -236,7 +244,7 @@ class _PlayerSettingsPageState extends ConsumerState<PlayerSettingsPage> {
|
|||
onChanged: (value) => provider.setHardwareAccel(value),
|
||||
),
|
||||
),
|
||||
if (!kIsWeb) ...[
|
||||
if (!kIsWeb)
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsPlayerNativeLibassAccelTitle),
|
||||
subLabel: Text(context.localized.settingsPlayerNativeLibassAccelDesc),
|
||||
|
|
@ -246,15 +254,14 @@ class _PlayerSettingsPageState extends ConsumerState<PlayerSettingsPage> {
|
|||
onChanged: (value) => provider.setUseLibass(value),
|
||||
),
|
||||
),
|
||||
AnimatedFadeSize(
|
||||
child: videoSettings.useLibass && videoSettings.hardwareAccel && Platform.isAndroid
|
||||
? SettingsMessageBox(
|
||||
context.localized.settingsPlayerMobileWarning,
|
||||
messageType: MessageType.warning,
|
||||
)
|
||||
: Container(),
|
||||
),
|
||||
],
|
||||
AnimatedFadeSize(
|
||||
child: videoSettings.useLibass && videoSettings.hardwareAccel && Platform.isAndroid
|
||||
? SettingsMessageBox(
|
||||
context.localized.settingsPlayerMobileWarning,
|
||||
messageType: MessageType.warning,
|
||||
)
|
||||
: Container(),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsPlayerBufferSizeTitle),
|
||||
subLabel: Text(context.localized.settingsPlayerBufferSizeDesc),
|
||||
|
|
@ -291,33 +298,37 @@ class _PlayerSettingsPageState extends ConsumerState<PlayerSettingsPage> {
|
|||
"${context.localized.noVideoPlayerOptions}\n${context.localized.mdkExperimental}")
|
||||
},
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsAutoNextTitle),
|
||||
subLabel: Text(context.localized.settingsAutoNextDesc),
|
||||
trailing: EnumBox(
|
||||
current: ref.watch(
|
||||
videoPlayerSettingsProvider.select(
|
||||
(value) => value.nextVideoType.label(context),
|
||||
Column(
|
||||
children: [
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsAutoNextTitle),
|
||||
subLabel: Text(context.localized.settingsAutoNextDesc),
|
||||
trailing: EnumBox(
|
||||
current: ref.watch(
|
||||
videoPlayerSettingsProvider.select(
|
||||
(value) => value.nextVideoType.label(context),
|
||||
),
|
||||
),
|
||||
itemBuilder: (context) => AutoNextType.values
|
||||
.map(
|
||||
(entry) => PopupMenuItem(
|
||||
value: entry,
|
||||
child: Text(entry.label(context)),
|
||||
onTap: () => ref.read(videoPlayerSettingsProvider.notifier).state =
|
||||
ref.read(videoPlayerSettingsProvider).copyWith(nextVideoType: entry),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
itemBuilder: (context) => AutoNextType.values
|
||||
.map(
|
||||
(entry) => PopupMenuItem(
|
||||
value: entry,
|
||||
child: Text(entry.label(context)),
|
||||
onTap: () => ref.read(videoPlayerSettingsProvider.notifier).state =
|
||||
ref.read(videoPlayerSettingsProvider).copyWith(nextVideoType: entry),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
AnimatedFadeSize(
|
||||
child: switch (ref.watch(videoPlayerSettingsProvider.select((value) => value.nextVideoType))) {
|
||||
AutoNextType.smart => SettingsMessageBox(AutoNextType.smart.desc(context)),
|
||||
AutoNextType.static => SettingsMessageBox(AutoNextType.static.desc(context)),
|
||||
_ => const SizedBox.shrink(),
|
||||
},
|
||||
AnimatedFadeSize(
|
||||
child: switch (ref.watch(videoPlayerSettingsProvider.select((value) => value.nextVideoType))) {
|
||||
AutoNextType.smart => SettingsMessageBox(AutoNextType.smart.desc(context)),
|
||||
AutoNextType.static => SettingsMessageBox(AutoNextType.static.desc(context)),
|
||||
_ => const SizedBox.shrink(),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!AdaptiveLayout.of(context).isDesktop && !kIsWeb)
|
||||
SettingsListTile(
|
||||
|
|
@ -325,8 +336,8 @@ class _PlayerSettingsPageState extends ConsumerState<PlayerSettingsPage> {
|
|||
subLabel: Text(context.localized.playerSettingsOrientationDesc),
|
||||
onTap: () => showOrientationOptions(context, ref),
|
||||
),
|
||||
],
|
||||
),
|
||||
]),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:fladder/models/settings/home_settings_model.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/screens/settings/settings_list_tile.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_list_group.dart';
|
||||
import 'package:fladder/screens/shared/authenticate_button_options.dart';
|
||||
import 'package:fladder/util/adaptive_layout.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
@RoutePage()
|
||||
class SecuritySettingsPage extends ConsumerStatefulWidget {
|
||||
|
|
@ -22,14 +23,10 @@ class _UserSettingsPageState extends ConsumerState<SecuritySettingsPage> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final user = ref.watch(userProvider);
|
||||
final showBackground = AdaptiveLayout.viewSizeOf(context) != ViewSize.phone &&
|
||||
AdaptiveLayout.layoutModeOf(context) != LayoutMode.single;
|
||||
return Card(
|
||||
elevation: showBackground ? 2 : 0,
|
||||
child: SettingsScaffold(
|
||||
label: context.localized.settingsProfileTitle,
|
||||
items: [
|
||||
SettingsLabelDivider(label: context.localized.settingSecurityApplockTitle),
|
||||
return SettingsScaffold(
|
||||
label: context.localized.settingsProfileTitle,
|
||||
items: [
|
||||
...settingsListGroup(context, SettingsLabelDivider(label: context.localized.settingSecurityApplockTitle), [
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingSecurityApplockTitle),
|
||||
subLabel: Text(user?.authMethod.name(context) ?? ""),
|
||||
|
|
@ -37,8 +34,8 @@ class _UserSettingsPageState extends ConsumerState<SecuritySettingsPage> {
|
|||
ref.read(userProvider.notifier).updateUser(newUser);
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
]),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class SettingsListTile extends StatelessWidget {
|
|||
),
|
||||
if (subLabel != null)
|
||||
Opacity(
|
||||
opacity: 0.75,
|
||||
opacity: 0.65,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
textStyle: Theme.of(context).textTheme.labelLarge,
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@ import 'package:flutter/material.dart';
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/models/settings/home_settings_model.dart';
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/screens/shared/user_icon.dart';
|
||||
import 'package:fladder/util/adaptive_layout.dart';
|
||||
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
|
||||
import 'package:fladder/util/router_extension.dart';
|
||||
|
||||
class SettingsScaffold extends ConsumerWidget {
|
||||
|
|
@ -34,7 +33,6 @@ class SettingsScaffold extends ConsumerWidget {
|
|||
final padding = MediaQuery.of(context).padding;
|
||||
final singleLayout = AdaptiveLayout.layoutModeOf(context) == LayoutMode.single;
|
||||
return Scaffold(
|
||||
backgroundColor: AdaptiveLayout.layoutModeOf(context) == LayoutMode.dual ? Colors.transparent : null,
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
||||
floatingActionButton: floatingActionButton,
|
||||
body: Column(
|
||||
|
|
@ -87,9 +85,10 @@ class SettingsScaffold extends ConsumerWidget {
|
|||
),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: MediaQuery.paddingOf(context).copyWith(top: AdaptiveLayout.of(context).isDesktop ? 0 : 8),
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildListDelegate(items),
|
||||
padding: MediaQuery.paddingOf(context).copyWith(top: 0),
|
||||
sliver: SliverList.builder(
|
||||
itemBuilder: (context, index) => items[index],
|
||||
itemCount: items.length,
|
||||
),
|
||||
),
|
||||
if (bottomActions.isEmpty)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||
|
||||
import 'package:fladder/models/settings/home_settings_model.dart';
|
||||
import 'package:fladder/providers/auth_provider.dart';
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/routes/auto_router.gr.dart';
|
||||
|
|
@ -12,7 +11,7 @@ import 'package:fladder/screens/settings/quick_connect_window.dart';
|
|||
import 'package:fladder/screens/settings/settings_list_tile.dart';
|
||||
import 'package:fladder/screens/settings/settings_scaffold.dart';
|
||||
import 'package:fladder/screens/shared/fladder_icon.dart';
|
||||
import 'package:fladder/util/adaptive_layout.dart';
|
||||
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/util/theme_extensions.dart';
|
||||
|
||||
|
|
@ -97,119 +96,122 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
|||
final quickConnectAvailable =
|
||||
ref.watch(userProvider.select((value) => value?.serverConfiguration?.quickConnectAvailable ?? false));
|
||||
|
||||
return Container(
|
||||
color: context.colors.surface,
|
||||
child: SettingsScaffold(
|
||||
label: context.localized.settings,
|
||||
scrollController: scrollController,
|
||||
showBackButtonNested: true,
|
||||
showUserIcon: true,
|
||||
items: [
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsClientTitle),
|
||||
subLabel: Text(context.localized.settingsClientDesc),
|
||||
selected: containsRoute(const ClientSettingsRoute()),
|
||||
icon: deviceIcon,
|
||||
onTap: () => navigateTo(const ClientSettingsRoute()),
|
||||
),
|
||||
if (quickConnectAvailable)
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(left: AdaptiveLayout.of(context).sideBarWidth),
|
||||
child: Container(
|
||||
color: context.colors.surface,
|
||||
child: SettingsScaffold(
|
||||
label: context.localized.settings,
|
||||
scrollController: scrollController,
|
||||
showBackButtonNested: true,
|
||||
showUserIcon: true,
|
||||
items: [
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsQuickConnectTitle),
|
||||
icon: IconsaxPlusLinear.password_check,
|
||||
onTap: () => openQuickConnectDialog(context),
|
||||
label: Text(context.localized.settingsClientTitle),
|
||||
subLabel: Text(context.localized.settingsClientDesc),
|
||||
selected: containsRoute(const ClientSettingsRoute()),
|
||||
icon: deviceIcon,
|
||||
onTap: () => navigateTo(const ClientSettingsRoute()),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsProfileTitle),
|
||||
subLabel: Text(context.localized.settingsProfileDesc),
|
||||
selected: containsRoute(const SecuritySettingsRoute()),
|
||||
icon: IconsaxPlusLinear.security_user,
|
||||
onTap: () => navigateTo(const SecuritySettingsRoute()),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsPlayerTitle),
|
||||
subLabel: Text(context.localized.settingsPlayerDesc),
|
||||
selected: containsRoute(const PlayerSettingsRoute()),
|
||||
icon: IconsaxPlusLinear.video_play,
|
||||
onTap: () => navigateTo(const PlayerSettingsRoute()),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.about),
|
||||
subLabel: const Text("Fladder"),
|
||||
selected: containsRoute(const AboutSettingsRoute()),
|
||||
suffix: Opacity(
|
||||
opacity: 1,
|
||||
child: FladderIconOutlined(
|
||||
size: 24,
|
||||
color: context.colors.onSurfaceVariant,
|
||||
if (quickConnectAvailable)
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsQuickConnectTitle),
|
||||
icon: IconsaxPlusLinear.password_check,
|
||||
onTap: () => openQuickConnectDialog(context),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsProfileTitle),
|
||||
subLabel: Text(context.localized.settingsProfileDesc),
|
||||
selected: containsRoute(const SecuritySettingsRoute()),
|
||||
icon: IconsaxPlusLinear.security_user,
|
||||
onTap: () => navigateTo(const SecuritySettingsRoute()),
|
||||
),
|
||||
onTap: () => navigateTo(const AboutSettingsRoute()),
|
||||
),
|
||||
],
|
||||
floatingActionButton: 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.replaceAll([const LoginRoute()]);
|
||||
},
|
||||
child: const Icon(
|
||||
IconsaxPlusLinear.arrow_swap_horizontal,
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.settingsPlayerTitle),
|
||||
subLabel: Text(context.localized.settingsPlayerDesc),
|
||||
selected: containsRoute(const PlayerSettingsRoute()),
|
||||
icon: IconsaxPlusLinear.video_play,
|
||||
onTap: () => navigateTo(const PlayerSettingsRoute()),
|
||||
),
|
||||
SettingsListTile(
|
||||
label: Text(context.localized.about),
|
||||
subLabel: const Text("Fladder"),
|
||||
selected: containsRoute(const AboutSettingsRoute()),
|
||||
suffix: Opacity(
|
||||
opacity: 1,
|
||||
child: FladderIconOutlined(
|
||||
size: 24,
|
||||
color: context.colors.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
FloatingActionButton(
|
||||
heroTag: context.localized.logout,
|
||||
key: Key(context.localized.logout),
|
||||
tooltip: context.localized.logout,
|
||||
backgroundColor: Theme.of(context).colorScheme.errorContainer,
|
||||
onPressed: () {
|
||||
final user = ref.read(userProvider);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(context.localized.logoutUserPopupTitle(user?.name ?? "")),
|
||||
scrollable: true,
|
||||
content: Text(
|
||||
context.localized.logoutUserPopupContent(user?.name ?? "", user?.server ?? ""),
|
||||
),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(context.localized.cancel),
|
||||
),
|
||||
onTap: () => navigateTo(const AboutSettingsRoute()),
|
||||
),
|
||||
],
|
||||
floatingActionButton: 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.replaceAll([const LoginRoute()]);
|
||||
},
|
||||
child: const Icon(
|
||||
IconsaxPlusLinear.arrow_swap_horizontal,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
FloatingActionButton(
|
||||
heroTag: context.localized.logout,
|
||||
key: Key(context.localized.logout),
|
||||
tooltip: context.localized.logout,
|
||||
backgroundColor: Theme.of(context).colorScheme.errorContainer,
|
||||
onPressed: () {
|
||||
final user = ref.read(userProvider);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(context.localized.logoutUserPopupTitle(user?.name ?? "")),
|
||||
scrollable: true,
|
||||
content: Text(
|
||||
context.localized.logoutUserPopupContent(user?.name ?? "", user?.server ?? ""),
|
||||
),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom().copyWith(
|
||||
iconColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.onErrorContainer),
|
||||
foregroundColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.onErrorContainer),
|
||||
backgroundColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.errorContainer),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(context.localized.cancel),
|
||||
),
|
||||
onPressed: () async {
|
||||
await ref.read(authProvider.notifier).logOutUser();
|
||||
if (context.mounted) {
|
||||
context.router.replaceAll([const LoginRoute()]);
|
||||
}
|
||||
},
|
||||
child: Text(context.localized.logout),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Icon(
|
||||
IconsaxPlusLinear.logout,
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom().copyWith(
|
||||
iconColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.onErrorContainer),
|
||||
foregroundColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.onErrorContainer),
|
||||
backgroundColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.errorContainer),
|
||||
),
|
||||
onPressed: () async {
|
||||
await ref.read(authProvider.notifier).logOutUser();
|
||||
if (context.mounted) {
|
||||
context.router.replaceAll([const LoginRoute()]);
|
||||
}
|
||||
},
|
||||
child: Text(context.localized.logout),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Icon(
|
||||
IconsaxPlusLinear.logout,
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
43
lib/screens/settings/widgets/settings_list_group.dart
Normal file
43
lib/screens/settings/widgets/settings_list_group.dart
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
|
||||
List<Widget> settingsListGroup(BuildContext context, Widget label, List<Widget> children) {
|
||||
final radius = BorderRadius.circular(24);
|
||||
final radiusSmall = const Radius.circular(6);
|
||||
final color = Theme.of(context).colorScheme.surfaceContainerLow.harmonizeWith(Colors.red);
|
||||
return [
|
||||
Card(
|
||||
elevation: 0,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
|
||||
color: color,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: radius.copyWith(
|
||||
bottomLeft: radiusSmall,
|
||||
bottomRight: radiusSmall,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: label,
|
||||
),
|
||||
),
|
||||
...children.map(
|
||||
(e) {
|
||||
return Card(
|
||||
elevation: 0,
|
||||
color: color,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: radius.copyWith(
|
||||
topLeft: radiusSmall,
|
||||
topRight: radiusSmall,
|
||||
bottomLeft: e != children.last ? radiusSmall : null,
|
||||
bottomRight: e != children.last ? radiusSmall : null,
|
||||
)),
|
||||
child: e,
|
||||
);
|
||||
},
|
||||
)
|
||||
];
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import 'package:fladder/models/settings/subtitle_settings_model.dart';
|
|||
import 'package:fladder/providers/settings/subtitle_settings_provider.dart';
|
||||
import 'package:fladder/providers/settings/video_player_settings_provider.dart';
|
||||
import 'package:fladder/screens/video_player/components/video_subtitle_controls.dart';
|
||||
import 'package:fladder/util/adaptive_layout.dart';
|
||||
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/widgets/navigation_scaffold/components/fladder_app_bar.dart';
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue