extract SettingsCheckboxTile for settings toggles
This commit is contained in:
@@ -6,13 +6,13 @@ import 'package:hydrated_bloc/hydrated_bloc.dart';
|
|||||||
import '../../../../routing/app_routes.dart';
|
import '../../../../routing/app_routes.dart';
|
||||||
import '../../../../state/app/modules/settings/bloc/settings_cubit.dart';
|
import '../../../../state/app/modules/settings/bloc/settings_cubit.dart';
|
||||||
import '../../../../storage/settings.dart' as model;
|
import '../../../../storage/settings.dart' as model;
|
||||||
import '../../../../utils/haptics.dart';
|
|
||||||
import '../../../../widget/centered_leading.dart';
|
import '../../../../widget/centered_leading.dart';
|
||||||
import '../../../../widget/confirm_dialog.dart';
|
import '../../../../widget/confirm_dialog.dart';
|
||||||
import '../../../../widget/debug/cache_view.dart';
|
import '../../../../widget/debug/cache_view.dart';
|
||||||
import '../../../../widget/debug/json_viewer.dart';
|
import '../../../../widget/debug/json_viewer.dart';
|
||||||
import '../../../../widget/details_bottom_sheet.dart';
|
import '../../../../widget/details_bottom_sheet.dart';
|
||||||
import '../widgets/endpoint_picker.dart';
|
import '../widgets/endpoint_picker.dart';
|
||||||
|
import '../widgets/settings_checkbox_tile.dart';
|
||||||
|
|
||||||
class DevToolsSection extends StatefulWidget {
|
class DevToolsSection extends StatefulWidget {
|
||||||
final SettingsCubit settings;
|
final SettingsCubit settings;
|
||||||
@@ -41,49 +41,32 @@ class _DevToolsSectionState extends State<DevToolsSection> {
|
|||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
SettingsCheckboxTile(
|
||||||
leading: const Icon(Icons.auto_graph_outlined),
|
icon: Icons.auto_graph_outlined,
|
||||||
title: const Text('Performance graph'),
|
title: 'Performance graph',
|
||||||
trailing: Checkbox(
|
value: dev.showPerformanceOverlay,
|
||||||
value: dev.showPerformanceOverlay,
|
onChanged: (e) => widget.settings
|
||||||
onChanged: (e) {
|
.val(write: true)
|
||||||
Haptics.selection();
|
.devToolsSettings
|
||||||
widget.settings
|
.showPerformanceOverlay = e,
|
||||||
.val(write: true)
|
|
||||||
.devToolsSettings
|
|
||||||
.showPerformanceOverlay = e!;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
SettingsCheckboxTile(
|
||||||
leading: const Icon(
|
icon: Icons.screen_search_desktop_outlined,
|
||||||
Icons.screen_search_desktop_outlined,
|
title: 'Indicate offscreen layers',
|
||||||
),
|
value: dev.checkerboardOffscreenLayers,
|
||||||
title: const Text('Indicate offscreen layers'),
|
onChanged: (e) => widget.settings
|
||||||
trailing: Checkbox(
|
.val(write: true)
|
||||||
value: dev.checkerboardOffscreenLayers,
|
.devToolsSettings
|
||||||
onChanged: (e) {
|
.checkerboardOffscreenLayers = e,
|
||||||
Haptics.selection();
|
|
||||||
widget.settings
|
|
||||||
.val(write: true)
|
|
||||||
.devToolsSettings
|
|
||||||
.checkerboardOffscreenLayers = e!;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
SettingsCheckboxTile(
|
||||||
leading: const Icon(Icons.imagesearch_roller_outlined),
|
icon: Icons.imagesearch_roller_outlined,
|
||||||
title: const Text('Indicate raster cache images'),
|
title: 'Indicate raster cache images',
|
||||||
trailing: Checkbox(
|
value: dev.checkerboardRasterCacheImages,
|
||||||
value: dev.checkerboardRasterCacheImages,
|
onChanged: (e) => widget.settings
|
||||||
onChanged: (e) {
|
.val(write: true)
|
||||||
Haptics.selection();
|
.devToolsSettings
|
||||||
widget.settings
|
.checkerboardRasterCacheImages = e,
|
||||||
.val(write: true)
|
|
||||||
.devToolsSettings
|
|
||||||
.checkerboardRasterCacheImages = e!;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
import '../../../../state/app/modules/settings/bloc/settings_cubit.dart';
|
import '../../../../state/app/modules/settings/bloc/settings_cubit.dart';
|
||||||
import '../../../../utils/haptics.dart';
|
import '../widgets/settings_checkbox_tile.dart';
|
||||||
|
|
||||||
class FilesSection extends StatelessWidget {
|
class FilesSection extends StatelessWidget {
|
||||||
const FilesSection({super.key});
|
const FilesSection({super.key});
|
||||||
@@ -12,30 +12,20 @@ class FilesSection extends StatelessWidget {
|
|||||||
final settings = context.watch<SettingsCubit>();
|
final settings = context.watch<SettingsCubit>();
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
SettingsCheckboxTile(
|
||||||
leading: const Icon(Icons.drive_folder_upload_outlined),
|
icon: Icons.drive_folder_upload_outlined,
|
||||||
title: const Text('Ordner in Dateien nach oben sortieren'),
|
title: 'Ordner in Dateien nach oben sortieren',
|
||||||
trailing: Checkbox(
|
value: settings.val().fileSettings.sortFoldersToTop,
|
||||||
value: settings.val().fileSettings.sortFoldersToTop,
|
onChanged: (e) =>
|
||||||
onChanged: (e) {
|
settings.val(write: true).fileSettings.sortFoldersToTop = e,
|
||||||
Haptics.selection();
|
|
||||||
settings.val(write: true).fileSettings.sortFoldersToTop = e!;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
SettingsCheckboxTile(
|
||||||
leading: const Icon(Icons.open_in_new_outlined),
|
icon: Icons.open_in_new_outlined,
|
||||||
title: const Text('Dateien immer mit Systemdialog öffnen'),
|
title: 'Dateien immer mit Systemdialog öffnen',
|
||||||
trailing: Checkbox(
|
value: settings.val().fileViewSettings.alwaysOpenExternally,
|
||||||
value: settings.val().fileViewSettings.alwaysOpenExternally,
|
onChanged: (e) =>
|
||||||
onChanged: (e) {
|
settings.val(write: true).fileViewSettings.alwaysOpenExternally =
|
||||||
Haptics.selection();
|
e,
|
||||||
settings
|
|
||||||
.val(write: true)
|
|
||||||
.fileViewSettings
|
|
||||||
.alwaysOpenExternally = e!;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import '../../../../push/push_registration.dart';
|
import '../../../../push/push_registration.dart';
|
||||||
import '../../../../routing/app_routes.dart';
|
import '../../../../routing/app_routes.dart';
|
||||||
import '../../../../state/app/modules/settings/bloc/settings_cubit.dart';
|
import '../../../../state/app/modules/settings/bloc/settings_cubit.dart';
|
||||||
import '../../../../utils/haptics.dart';
|
|
||||||
import '../../../../widget/centered_leading.dart';
|
import '../../../../widget/centered_leading.dart';
|
||||||
import '../widgets/push_status_sheet.dart';
|
import '../widgets/push_status_sheet.dart';
|
||||||
|
import '../widgets/settings_checkbox_tile.dart';
|
||||||
|
|
||||||
class TalkSection extends StatelessWidget {
|
class TalkSection extends StatelessWidget {
|
||||||
const TalkSection({super.key});
|
const TalkSection({super.key});
|
||||||
@@ -20,27 +20,19 @@ class TalkSection extends StatelessWidget {
|
|||||||
final notificationSettings = settings.val().notificationSettings;
|
final notificationSettings = settings.val().notificationSettings;
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
SettingsCheckboxTile(
|
||||||
leading: const Icon(Icons.star_border),
|
icon: Icons.star_border,
|
||||||
title: const Text('Favoriten im Talk nach oben sortieren'),
|
title: 'Favoriten im Talk nach oben sortieren',
|
||||||
trailing: Checkbox(
|
value: talkSettings.sortFavoritesToTop,
|
||||||
value: talkSettings.sortFavoritesToTop,
|
onChanged: (e) =>
|
||||||
onChanged: (e) {
|
settings.val(write: true).talkSettings.sortFavoritesToTop = e,
|
||||||
Haptics.selection();
|
|
||||||
settings.val(write: true).talkSettings.sortFavoritesToTop = e!;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
SettingsCheckboxTile(
|
||||||
leading: const Icon(Icons.mark_email_unread_outlined),
|
icon: Icons.mark_email_unread_outlined,
|
||||||
title: const Text('Ungelesene Chats nach oben sortieren'),
|
title: 'Ungelesene Chats nach oben sortieren',
|
||||||
trailing: Checkbox(
|
value: talkSettings.sortUnreadToTop,
|
||||||
value: talkSettings.sortUnreadToTop,
|
onChanged: (e) =>
|
||||||
onChanged: (e) {
|
settings.val(write: true).talkSettings.sortUnreadToTop = e,
|
||||||
Haptics.selection();
|
|
||||||
settings.val(write: true).talkSettings.sortUnreadToTop = e!;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.wallpaper_outlined),
|
leading: const Icon(Icons.wallpaper_outlined),
|
||||||
@@ -49,46 +41,38 @@ class TalkSection extends StatelessWidget {
|
|||||||
trailing: const Icon(Icons.arrow_right),
|
trailing: const Icon(Icons.arrow_right),
|
||||||
onTap: () => AppRoutes.openChatBackgroundSettings(context),
|
onTap: () => AppRoutes.openChatBackgroundSettings(context),
|
||||||
),
|
),
|
||||||
ListTile(
|
SettingsCheckboxTile(
|
||||||
leading: const CenteredLeading(
|
icon: Icons.notifications_active_outlined,
|
||||||
Icon(Icons.notifications_active_outlined),
|
title: 'Push-Benachrichtigungen',
|
||||||
),
|
subtitle: 'Benachrichtigungen bei neuen Talk-Nachrichten erhalten',
|
||||||
title: const Text('Push-Benachrichtigungen'),
|
value: notificationSettings.enabled,
|
||||||
subtitle: const Text(
|
onChanged: (enabled) {
|
||||||
'Benachrichtigungen bei neuen Talk-Nachrichten erhalten',
|
settings.val(write: true).notificationSettings.enabled = enabled;
|
||||||
),
|
// Turning off does NOT unregister: the device stays subscribed so
|
||||||
trailing: Checkbox(
|
// silent sync pushes keep arriving; the message handler and iOS
|
||||||
value: notificationSettings.enabled,
|
// NSE suppress only the visible notification (via the mirrored
|
||||||
onChanged: (e) {
|
// flag). Enabling (re-)registers and ensures the OS permission.
|
||||||
Haptics.selection();
|
if (enabled) {
|
||||||
final enabled = e ?? false;
|
final messenger = ScaffoldMessenger.of(context);
|
||||||
settings.val(write: true).notificationSettings.enabled = enabled;
|
unawaited(() async {
|
||||||
// Turning off does NOT unregister: the device stays subscribed so
|
// Only register when the OS permission isn't explicitly
|
||||||
// silent sync pushes keep arriving; the message handler and iOS
|
// denied — otherwise NC + proxy would push into the void.
|
||||||
// NSE suppress only the visible notification (via the mirrored
|
if (await PushRegistration.requestOsPermission()) {
|
||||||
// flag). Enabling (re-)registers and ensures the OS permission.
|
await PushRegistration().register();
|
||||||
if (enabled) {
|
} else {
|
||||||
final messenger = ScaffoldMessenger.of(context);
|
messenger.showSnackBar(
|
||||||
unawaited(() async {
|
const SnackBar(
|
||||||
// Only register when the OS permission isn't explicitly
|
content: Text(
|
||||||
// denied — otherwise NC + proxy would push into the void.
|
'Die Benachrichtigungsberechtigung wurde in den '
|
||||||
if (await PushRegistration.requestOsPermission()) {
|
'Systemeinstellungen deaktiviert. Bitte aktiviere '
|
||||||
await PushRegistration().register();
|
'sie dort, um Push-Benachrichtigungen zu erhalten.',
|
||||||
} else {
|
|
||||||
messenger.showSnackBar(
|
|
||||||
const SnackBar(
|
|
||||||
content: Text(
|
|
||||||
'Die Benachrichtigungsberechtigung wurde in den '
|
|
||||||
'Systemeinstellungen deaktiviert. Bitte aktiviere '
|
|
||||||
'sie dort, um Push-Benachrichtigungen zu erhalten.',
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
}
|
);
|
||||||
}());
|
}
|
||||||
}
|
}());
|
||||||
},
|
}
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const CenteredLeading(Icon(Icons.monitor_heart_outlined)),
|
leading: const CenteredLeading(Icon(Icons.monitor_heart_outlined)),
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
|
|
||||||
import '../../../../routing/app_routes.dart';
|
import '../../../../routing/app_routes.dart';
|
||||||
import '../../../../state/app/modules/settings/bloc/settings_cubit.dart';
|
import '../../../../state/app/modules/settings/bloc/settings_cubit.dart';
|
||||||
import '../../../../utils/haptics.dart';
|
|
||||||
import '../../../../view/pages/timetable/data/timetable_name_mode.dart';
|
import '../../../../view/pages/timetable/data/timetable_name_mode.dart';
|
||||||
|
import '../widgets/settings_checkbox_tile.dart';
|
||||||
|
|
||||||
class TimetableSection extends StatelessWidget {
|
class TimetableSection extends StatelessWidget {
|
||||||
const TimetableSection({super.key});
|
const TimetableSection({super.key});
|
||||||
@@ -43,19 +43,13 @@ class TimetableSection extends StatelessWidget {
|
|||||||
value!,
|
value!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
SettingsCheckboxTile(
|
||||||
leading: const Icon(Icons.calendar_view_day_outlined),
|
icon: Icons.calendar_view_day_outlined,
|
||||||
title: const Text('Doppelstunden zusammenhängend anzeigen'),
|
title: 'Doppelstunden zusammenhängend anzeigen',
|
||||||
trailing: Checkbox(
|
value: timetableSettings.connectDoubleLessons,
|
||||||
value: timetableSettings.connectDoubleLessons,
|
onChanged: (e) =>
|
||||||
onChanged: (e) {
|
settings.val(write: true).timetableSettings.connectDoubleLessons =
|
||||||
Haptics.selection();
|
e,
|
||||||
settings
|
|
||||||
.val(write: true)
|
|
||||||
.timetableSettings
|
|
||||||
.connectDoubleLessons = e!;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.palette_outlined),
|
leading: const Icon(Icons.palette_outlined),
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../../../utils/haptics.dart';
|
||||||
|
import '../../../../widget/centered_leading.dart';
|
||||||
|
|
||||||
|
/// Settings row with a trailing checkbox. Fires [Haptics.selection] before
|
||||||
|
/// invoking [onChanged] (with the resolved non-null value), so the sections
|
||||||
|
/// don't repeat that. The leading icon is vertically centered when a [subtitle]
|
||||||
|
/// is present, matching the surrounding settings styling.
|
||||||
|
class SettingsCheckboxTile extends StatelessWidget {
|
||||||
|
final IconData icon;
|
||||||
|
final String title;
|
||||||
|
final String? subtitle;
|
||||||
|
final bool value;
|
||||||
|
final ValueChanged<bool> onChanged;
|
||||||
|
|
||||||
|
const SettingsCheckboxTile({
|
||||||
|
required this.icon,
|
||||||
|
required this.title,
|
||||||
|
required this.value,
|
||||||
|
required this.onChanged,
|
||||||
|
this.subtitle,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final leadingIcon = Icon(icon);
|
||||||
|
return ListTile(
|
||||||
|
leading: subtitle == null ? leadingIcon : CenteredLeading(leadingIcon),
|
||||||
|
title: Text(title),
|
||||||
|
subtitle: subtitle == null ? null : Text(subtitle!),
|
||||||
|
trailing: Checkbox(
|
||||||
|
value: value,
|
||||||
|
onChanged: (e) {
|
||||||
|
Haptics.selection();
|
||||||
|
onChanged(e ?? false);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user