refactor: consolidate dialogs to DialogActionButton

This commit is contained in:
edde746
2026-04-21 12:05:21 +02:00
parent 4665f2a5d8
commit 1edf3da984
9 changed files with 128 additions and 221 deletions
+5 -3
View File
@@ -18,6 +18,7 @@ import '../utils/app_logger.dart';
import '../utils/platform_detector.dart'; import '../utils/platform_detector.dart';
import '../focus/focusable_button.dart'; import '../focus/focusable_button.dart';
import '../utils/navigation_transitions.dart'; import '../utils/navigation_transitions.dart';
import '../widgets/dialog_action_button.dart';
import 'main_screen.dart'; import 'main_screen.dart';
class AuthScreen extends StatefulWidget { class AuthScreen extends StatefulWidget {
@@ -261,8 +262,8 @@ class _AuthScreenState extends State<AuthScreen> {
], ],
), ),
actions: [ actions: [
TextButton(onPressed: () => Navigator.of(context).pop(), child: Text(t.common.cancel)), DialogActionButton(onPressed: () => Navigator.of(context).pop(), label: t.common.cancel),
ElevatedButton( DialogActionButton(
onPressed: () async { onPressed: () async {
final token = tokenController.text.trim(); final token = tokenController.text.trim();
if (token.isEmpty) { if (token.isEmpty) {
@@ -298,7 +299,8 @@ class _AuthScreenState extends State<AuthScreen> {
}); });
} }
}, },
child: Text(t.auth.authenticate), label: t.auth.authenticate,
isPrimary: true,
), ),
], ],
); );
@@ -11,6 +11,7 @@ import '../../providers/companion_remote_provider.dart';
import '../../utils/platform_detector.dart'; import '../../utils/platform_detector.dart';
import '../../theme/mono_tokens.dart'; import '../../theme/mono_tokens.dart';
import '../../utils/app_logger.dart'; import '../../utils/app_logger.dart';
import '../../utils/dialogs.dart';
import '../../widgets/companion_remote/discovery_view.dart'; import '../../widgets/companion_remote/discovery_view.dart';
import '../../widgets/overlay_sheet.dart'; import '../../widgets/overlay_sheet.dart';
import '../../widgets/pill_input_decoration.dart'; import '../../widgets/pill_input_decoration.dart';
@@ -36,19 +37,15 @@ class _MobileRemoteScreenState extends State<MobileRemoteScreen> {
return IconButton( return IconButton(
icon: const Icon(Icons.link_off), icon: const Icon(Icons.link_off),
onPressed: () async { onPressed: () async {
final confirmed = await showDialog<bool>( final confirmed = await showConfirmDialog(
context: context, context,
builder: (context) => AlertDialog( title: t.common.disconnect,
title: Text(t.common.disconnect), message: t.companionRemote.remote.disconnectConfirm,
content: Text(t.companionRemote.remote.disconnectConfirm), confirmText: t.common.disconnect,
actions: [ isDestructive: true,
TextButton(onPressed: () => Navigator.pop(context, false), child: Text(t.common.cancel)),
TextButton(onPressed: () => Navigator.pop(context, true), child: Text(t.common.disconnect)),
],
),
); );
if (confirmed == true && context.mounted) { if (confirmed && context.mounted) {
await context.read<CompanionRemoteProvider>().leaveSession(); await context.read<CompanionRemoteProvider>().leaveSession();
} }
}, },
+8 -23
View File
@@ -6,7 +6,6 @@ import 'package:flutter/services.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../focus/focus_theme.dart'; import '../../focus/focus_theme.dart';
import '../../focus/focusable_action_bar.dart'; import '../../focus/focusable_action_bar.dart';
import '../../focus/focusable_button.dart';
import '../../focus/dpad_navigator.dart'; import '../../focus/dpad_navigator.dart';
import '../../focus/input_mode_tracker.dart'; import '../../focus/input_mode_tracker.dart';
import '../../focus/key_event_utils.dart'; import '../../focus/key_event_utils.dart';
@@ -18,6 +17,7 @@ import '../../providers/hidden_libraries_provider.dart';
import '../../providers/libraries_provider.dart'; import '../../providers/libraries_provider.dart';
import '../../providers/multi_server_provider.dart'; import '../../providers/multi_server_provider.dart';
import '../../utils/app_logger.dart'; import '../../utils/app_logger.dart';
import '../../utils/dialogs.dart';
import '../../utils/platform_detector.dart'; import '../../utils/platform_detector.dart';
import '../../utils/provider_extensions.dart'; import '../../utils/provider_extensions.dart';
import '../../utils/snackbar_helper.dart'; import '../../utils/snackbar_helper.dart';
@@ -593,29 +593,14 @@ class _LibrariesScreenState extends State<LibrariesScreen>
if (item == null) return; if (item == null) return;
if (item.requiresConfirmation) { if (item.requiresConfirmation) {
final confirmed = await showDialog<bool>( final confirmed = await showConfirmDialog(
context: context, context,
builder: (context) => AlertDialog( title: item.confirmationTitle ?? t.dialog.confirmAction,
title: Text(item.confirmationTitle ?? t.dialog.confirmAction), message: item.confirmationMessage ?? t.libraries.confirmActionMessage,
content: Text(item.confirmationMessage ?? t.libraries.confirmActionMessage), confirmText: t.common.confirm,
actions: [ isDestructive: item.isDestructive,
FocusableButton(
autofocus: true,
onPressed: () => Navigator.pop(context, false),
child: TextButton(onPressed: () => Navigator.pop(context, false), child: Text(t.common.cancel)),
),
FocusableButton(
onPressed: () => Navigator.pop(context, true),
child: TextButton(
onPressed: () => Navigator.pop(context, true),
style: item.isDestructive ? TextButton.styleFrom(foregroundColor: Colors.red) : null,
child: Text(t.common.confirm),
),
),
],
),
); );
if (confirmed != true) return; if (!confirmed) return;
} }
switch (action) { switch (action) {
+7 -36
View File
@@ -12,7 +12,7 @@ import '../../services/plex_client.dart';
import '../i18n/strings.g.dart'; import '../i18n/strings.g.dart';
import '../services/update_service.dart'; import '../services/update_service.dart';
import '../utils/app_logger.dart'; import '../utils/app_logger.dart';
import '../focus/focusable_button.dart'; import '../widgets/dialog_action_button.dart';
import '../utils/dialogs.dart'; import '../utils/dialogs.dart';
import '../utils/provider_extensions.dart'; import '../utils/provider_extensions.dart';
import '../utils/platform_detector.dart'; import '../utils/platform_detector.dart';
@@ -254,36 +254,15 @@ class _MainScreenState extends State<MainScreen> with RouteAware, WindowListener
], ],
), ),
actions: [ actions: [
FocusableButton( DialogActionButton(onPressed: () => Navigator.pop(dialogContext), label: t.common.later),
autofocus: true, DialogActionButton(
onPressed: () => Navigator.pop(dialogContext),
child: TextButton(
onPressed: () => Navigator.pop(dialogContext),
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14),
shape: const StadiumBorder(),
),
child: Text(t.common.later),
),
),
FocusableButton(
onPressed: () async { onPressed: () async {
await UpdateService.skipVersion(updateInfo['latestVersion']); await UpdateService.skipVersion(updateInfo['latestVersion']);
if (dialogContext.mounted) Navigator.pop(dialogContext); if (dialogContext.mounted) Navigator.pop(dialogContext);
}, },
child: TextButton( label: t.update.skipVersion,
onPressed: () async {
await UpdateService.skipVersion(updateInfo['latestVersion']);
if (dialogContext.mounted) Navigator.pop(dialogContext);
},
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14),
shape: const StadiumBorder(),
),
child: Text(t.update.skipVersion),
),
), ),
FocusableButton( DialogActionButton(
onPressed: () async { onPressed: () async {
final url = Uri.parse(updateInfo['releaseUrl']); final url = Uri.parse(updateInfo['releaseUrl']);
if (await canLaunchUrl(url)) { if (await canLaunchUrl(url)) {
@@ -291,16 +270,8 @@ class _MainScreenState extends State<MainScreen> with RouteAware, WindowListener
} }
if (dialogContext.mounted) Navigator.pop(dialogContext); if (dialogContext.mounted) Navigator.pop(dialogContext);
}, },
child: FilledButton( label: t.update.viewRelease,
onPressed: () async { isPrimary: true,
final url = Uri.parse(updateInfo['releaseUrl']);
if (await canLaunchUrl(url)) {
await launchUrl(url, mode: LaunchMode.externalApplication);
}
if (dialogContext.mounted) Navigator.pop(dialogContext);
},
child: Text(t.update.viewRelease),
),
), ),
], ],
); );
@@ -591,7 +591,7 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
title: Text(t.settings.introPattern), title: Text(t.settings.introPattern),
subtitle: Text(t.settings.introPatternDescription), subtitle: Text(t.settings.introPatternDescription),
trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1), trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1),
onTap: () => showTextInputDialog( onTap: () => showRegexInputDialog(
context: context, context: context,
title: t.settings.introPattern, title: t.settings.introPattern,
currentValue: _introPattern, currentValue: _introPattern,
@@ -610,7 +610,7 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
title: Text(t.settings.creditsPattern), title: Text(t.settings.creditsPattern),
subtitle: Text(t.settings.creditsPatternDescription), subtitle: Text(t.settings.creditsPatternDescription),
trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1), trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1),
onTap: () => showTextInputDialog( onTap: () => showRegexInputDialog(
context: context, context: context,
title: t.settings.creditsPattern, title: t.settings.creditsPattern,
currentValue: _creditsPattern, currentValue: _creditsPattern,
+48 -80
View File
@@ -28,9 +28,11 @@ import '../../providers/trakt_account_provider.dart';
import '../../services/keyboard_shortcuts_service.dart'; import '../../services/keyboard_shortcuts_service.dart';
import '../../services/settings_service.dart' as settings; import '../../services/settings_service.dart' as settings;
import '../../services/update_service.dart'; import '../../services/update_service.dart';
import '../../utils/dialogs.dart';
import '../../utils/snackbar_helper.dart'; import '../../utils/snackbar_helper.dart';
import '../../utils/platform_detector.dart'; import '../../utils/platform_detector.dart';
import '../../widgets/desktop_app_bar.dart'; import '../../widgets/desktop_app_bar.dart';
import '../../widgets/dialog_action_button.dart';
import '../../widgets/settings_section.dart'; import '../../widgets/settings_section.dart';
import 'about_screen.dart'; import 'about_screen.dart';
import 'appearance_settings_screen.dart'; import 'appearance_settings_screen.dart';
@@ -557,20 +559,21 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab {
), ),
actions: [ actions: [
if (isCustom) if (isCustom)
TextButton( DialogActionButton(
onPressed: () async { onPressed: () async {
Navigator.pop(dialogContext); Navigator.pop(dialogContext);
await _resetDownloadLocation(); await _resetDownloadLocation();
}, },
child: Text(t.settings.resetToDefault), label: t.settings.resetToDefault,
), ),
TextButton(onPressed: () => Navigator.pop(dialogContext), child: Text(t.common.cancel)), DialogActionButton(onPressed: () => Navigator.pop(dialogContext), label: t.common.cancel),
FilledButton( DialogActionButton(
onPressed: () async { onPressed: () async {
Navigator.pop(dialogContext); Navigator.pop(dialogContext);
await _selectDownloadLocation(); await _selectDownloadLocation();
}, },
child: Text(t.settings.selectFolder), label: t.settings.selectFolder,
isPrimary: true,
), ),
], ],
), ),
@@ -654,17 +657,17 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab {
onEditingComplete: () => saveFocusNode.requestFocus(), onEditingComplete: () => saveFocusNode.requestFocus(),
), ),
actions: [ actions: [
TextButton( DialogActionButton(
onPressed: () async { onPressed: () async {
controller.clear(); controller.clear();
await _settingsService.setCustomRelayUrl(null); await _settingsService.setCustomRelayUrl(null);
if (mounted) setState(() => _customRelayUrl = null); if (mounted) setState(() => _customRelayUrl = null);
if (dialogContext.mounted) Navigator.pop(dialogContext); if (dialogContext.mounted) Navigator.pop(dialogContext);
}, },
child: Text(t.settings.resetToDefault), label: t.settings.resetToDefault,
), ),
TextButton(onPressed: () => Navigator.pop(dialogContext), child: Text(t.common.cancel)), DialogActionButton(onPressed: () => Navigator.pop(dialogContext), label: t.common.cancel),
TextButton( DialogActionButton(
focusNode: saveFocusNode, focusNode: saveFocusNode,
onPressed: () async { onPressed: () async {
final url = controller.text.trim().isEmpty ? null : controller.text.trim(); final url = controller.text.trim().isEmpty ? null : controller.text.trim();
@@ -672,7 +675,7 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab {
if (mounted) setState(() => _customRelayUrl = url); if (mounted) setState(() => _customRelayUrl = url);
if (dialogContext.mounted) Navigator.pop(dialogContext); if (dialogContext.mounted) Navigator.pop(dialogContext);
}, },
child: Text(t.common.save), label: t.common.save,
), ),
], ],
); );
@@ -683,58 +686,33 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab {
}); });
} }
void _showClearCacheDialog() { Future<void> _showClearCacheDialog() async {
showDialog( final confirmed = await showConfirmDialog(
context: context, context,
builder: (BuildContext context) { title: t.settings.clearCache,
return AlertDialog( message: t.settings.clearCacheDescription,
title: Text(t.settings.clearCache), confirmText: t.common.clear,
content: Text(t.settings.clearCacheDescription),
actions: [
TextButton(autofocus: true, onPressed: () => Navigator.pop(context), child: Text(t.common.cancel)),
TextButton(
onPressed: () async {
final navigator = Navigator.of(context);
await _settingsService.clearCache();
if (mounted) {
navigator.pop();
showSuccessSnackBar(this.context, t.settings.clearCacheSuccess);
}
},
child: Text(t.common.clear),
),
],
);
},
); );
if (!confirmed) return;
await _settingsService.clearCache();
if (mounted) showSuccessSnackBar(context, t.settings.clearCacheSuccess);
} }
void _showResetSettingsDialog() { Future<void> _showResetSettingsDialog() async {
showDialog( final confirmed = await showConfirmDialog(
context: context, context,
builder: (BuildContext context) { title: t.settings.resetSettings,
return AlertDialog( message: t.settings.resetSettingsDescription,
title: Text(t.settings.resetSettings), confirmText: t.common.reset,
content: Text(t.settings.resetSettingsDescription), isDestructive: true,
actions: [
TextButton(autofocus: true, onPressed: () => Navigator.pop(context), child: Text(t.common.cancel)),
TextButton(
onPressed: () async {
final navigator = Navigator.of(context);
await _settingsService.resetAllSettings();
await _keyboardService?.resetToDefaults();
if (mounted) {
navigator.pop();
showSuccessSnackBar(this.context, t.settings.resetSettingsSuccess);
_loadSettings();
}
},
child: Text(t.common.reset),
),
],
);
},
); );
if (!confirmed) return;
await _settingsService.resetAllSettings();
await _keyboardService?.resetToDefaults();
if (mounted) {
showSuccessSnackBar(context, t.settings.resetSettingsSuccess);
_loadSettings();
}
} }
Future<void> _handleExportSettings() async { Future<void> _handleExportSettings() async {
@@ -750,26 +728,15 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab {
} }
} }
void _showImportSettingsDialog() { Future<void> _showImportSettingsDialog() async {
showDialog( final confirmed = await showConfirmDialog(
context: context, context,
builder: (BuildContext dialogContext) { title: t.settings.importSettings,
return AlertDialog( message: t.settings.importSettingsConfirm,
title: Text(t.settings.importSettings), confirmText: t.settings.importSettings,
content: Text(t.settings.importSettingsConfirm),
actions: [
TextButton(autofocus: true, onPressed: () => Navigator.pop(dialogContext), child: Text(t.common.cancel)),
TextButton(
onPressed: () {
Navigator.pop(dialogContext);
_handleImportSettings();
},
child: Text(t.settings.importSettings),
),
],
);
},
); );
if (!confirmed) return;
await _handleImportSettings();
} }
Future<void> _handleImportSettings() async { Future<void> _handleImportSettings() async {
@@ -856,8 +823,8 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab {
], ],
), ),
actions: [ actions: [
TextButton(autofocus: true, onPressed: () => Navigator.pop(context), child: Text(t.common.close)), DialogActionButton(onPressed: () => Navigator.pop(context), label: t.common.close),
FilledButton( DialogActionButton(
onPressed: () async { onPressed: () async {
final url = Uri.parse(_updateInfo!['releaseUrl']); final url = Uri.parse(_updateInfo!['releaseUrl']);
if (await canLaunchUrl(url)) { if (await canLaunchUrl(url)) {
@@ -865,7 +832,8 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab {
} }
if (context.mounted) Navigator.pop(context); if (context.mounted) Navigator.pop(context);
}, },
child: Text(t.update.viewRelease), label: t.update.viewRelease,
isPrimary: true,
), ),
], ],
); );
+32 -28
View File
@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import '../../focus/input_mode_tracker.dart'; import '../../focus/input_mode_tracker.dart';
import '../../i18n/strings.g.dart'; import '../../i18n/strings.g.dart';
import '../../widgets/dialog_action_button.dart';
import '../../widgets/focusable_list_tile.dart';
import '../../widgets/tv_number_spinner.dart'; import '../../widgets/tv_number_spinner.dart';
/// Model for option selection dialogs. /// Model for option selection dialogs.
@@ -13,7 +15,7 @@ class DialogOption<T> {
const DialogOption({required this.value, required this.title, this.subtitle}); const DialogOption({required this.value, required this.title, this.subtitle});
} }
/// Shows an M3-conformant AlertDialog with RadioListTiles for multi-option selection. /// Shows a selection dialog with focusable rows for dpad/keyboard navigation.
/// Used for settings with 5+ options (language, buffer size, etc.). /// Used for settings with 5+ options (language, buffer size, etc.).
Future<T?> showSelectionDialog<T>({ Future<T?> showSelectionDialog<T>({
required BuildContext context, required BuildContext context,
@@ -21,27 +23,29 @@ Future<T?> showSelectionDialog<T>({
required List<DialogOption<T>> options, required List<DialogOption<T>> options,
required T currentValue, required T currentValue,
}) { }) {
final focusFirstItem = InputModeTracker.isKeyboardMode(context);
return showDialog<T>( return showDialog<T>(
context: context, context: context,
builder: (dialogContext) => AlertDialog( builder: (dialogContext) => AlertDialog(
title: Text(title), title: Text(title),
contentPadding: const EdgeInsets.only(top: 12, bottom: 24), contentPadding: const EdgeInsets.only(top: 12, bottom: 24),
content: SingleChildScrollView( content: SingleChildScrollView(
child: RadioGroup<T>( child: Column(
groupValue: currentValue, mainAxisSize: MainAxisSize.min,
onChanged: (value) => Navigator.pop(dialogContext, value), children: options.map((option) {
child: Column( final selected = option.value == currentValue;
mainAxisSize: MainAxisSize.min, return FocusableListTile(
children: options leading: Icon(
.map( selected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
(option) => RadioListTile<T>( color: selected ? Theme.of(dialogContext).colorScheme.primary : null,
title: Text(option.title), ),
subtitle: option.subtitle != null ? Text(option.subtitle!) : null, title: Text(option.title),
value: option.value, subtitle: option.subtitle != null ? Text(option.subtitle!) : null,
), selected: selected,
) autofocus: focusFirstItem && selected,
.toList(), onTap: () => Navigator.pop(dialogContext, option.value),
), );
}).toList(),
), ),
), ),
), ),
@@ -133,8 +137,8 @@ void _showNumericInputDialogTV({
], ],
), ),
actions: [ actions: [
TextButton(onPressed: () => Navigator.pop(dialogContext), child: Text(t.common.cancel)), DialogActionButton(onPressed: () => Navigator.pop(dialogContext), label: t.common.cancel),
TextButton( DialogActionButton(
focusNode: saveFocusNode, focusNode: saveFocusNode,
onPressed: () async { onPressed: () async {
await onSave(spinnerValue); await onSave(spinnerValue);
@@ -142,7 +146,7 @@ void _showNumericInputDialogTV({
Navigator.pop(dialogContext); Navigator.pop(dialogContext);
} }
}, },
child: Text(t.common.save), label: t.common.save,
), ),
], ],
); );
@@ -201,8 +205,8 @@ void _showNumericInputDialogStandard({
}, },
), ),
actions: [ actions: [
TextButton(onPressed: () => Navigator.pop(dialogContext), child: Text(t.common.cancel)), DialogActionButton(onPressed: () => Navigator.pop(dialogContext), label: t.common.cancel),
TextButton( DialogActionButton(
focusNode: saveFocusNode, focusNode: saveFocusNode,
onPressed: () async { onPressed: () async {
final parsed = int.tryParse(controller.text); final parsed = int.tryParse(controller.text);
@@ -213,7 +217,7 @@ void _showNumericInputDialogStandard({
} }
} }
}, },
child: Text(t.common.save), label: t.common.save,
), ),
], ],
); );
@@ -227,7 +231,7 @@ void _showNumericInputDialogStandard({
} }
/// Shows a text input dialog with regex validation and reset-to-default support. /// Shows a text input dialog with regex validation and reset-to-default support.
void showTextInputDialog({ void showRegexInputDialog({
required BuildContext context, required BuildContext context,
required String title, required String title,
required String currentValue, required String currentValue,
@@ -263,22 +267,22 @@ void showTextInputDialog({
}, },
), ),
actions: [ actions: [
TextButton( DialogActionButton(
onPressed: () { onPressed: () {
controller.text = defaultValue; controller.text = defaultValue;
setDialogState(() => errorText = null); setDialogState(() => errorText = null);
}, },
child: Text(t.settings.resetToDefault), label: t.settings.resetToDefault,
), ),
TextButton(onPressed: () => Navigator.pop(dialogContext), child: Text(t.common.cancel)), DialogActionButton(onPressed: () => Navigator.pop(dialogContext), label: t.common.cancel),
TextButton( DialogActionButton(
focusNode: saveFocusNode, focusNode: saveFocusNode,
onPressed: () async { onPressed: () async {
if (errorText != null) return; if (errorText != null) return;
await onSave(controller.text); await onSave(controller.text);
if (dialogContext.mounted) Navigator.pop(dialogContext); if (dialogContext.mounted) Navigator.pop(dialogContext);
}, },
child: Text(t.common.save), label: t.common.save,
), ),
], ],
); );
+11 -16
View File
@@ -14,7 +14,9 @@ import '../../services/settings_service.dart';
import '../../services/trakt/trakt_scrobble_service.dart'; import '../../services/trakt/trakt_scrobble_service.dart';
import '../../services/trakt/trakt_sync_service.dart'; import '../../services/trakt/trakt_sync_service.dart';
import '../../utils/app_logger.dart'; import '../../utils/app_logger.dart';
import '../../utils/dialogs.dart';
import '../../utils/snackbar_helper.dart'; import '../../utils/snackbar_helper.dart';
import '../../widgets/dialog_action_button.dart';
import '../../widgets/app_icon.dart'; import '../../widgets/app_icon.dart';
import '../../widgets/focused_scroll_scaffold.dart'; import '../../widgets/focused_scroll_scaffold.dart';
import '../../widgets/settings_section.dart'; import '../../widgets/settings_section.dart';
@@ -99,21 +101,14 @@ class _TraktSettingsScreenState extends State<TraktSettingsScreen> {
} }
Future<void> _disconnect(TraktAccountProvider account) async { Future<void> _disconnect(TraktAccountProvider account) async {
final confirmed = await showDialog<bool>( final confirmed = await showConfirmDialog(
context: context, context,
builder: (ctx) => AlertDialog( title: t.trakt.disconnectConfirm,
title: Text(t.trakt.disconnectConfirm), message: t.trakt.disconnectConfirmBody,
content: Text(t.trakt.disconnectConfirmBody), confirmText: t.common.disconnect,
actions: [ isDestructive: true,
TextButton(onPressed: () => Navigator.of(ctx).pop(false), child: Text(t.common.cancel)),
TextButton(
onPressed: () => Navigator.of(ctx).pop(true),
child: Text(t.common.disconnect, style: TextStyle(color: Theme.of(ctx).colorScheme.error)),
),
],
),
); );
if (confirmed != true) return; if (!confirmed) return;
await account.disconnect(); await account.disconnect();
// build()'s post-frame handler pops the screen once the provider rebuilds // build()'s post-frame handler pops the screen once the provider rebuilds
// with isConnected == false — don't pop here too. // with isConnected == false — don't pop here too.
@@ -262,12 +257,12 @@ class _DeviceCodeDialogState extends State<_DeviceCodeDialog> {
], ],
), ),
actions: [ actions: [
TextButton( DialogActionButton(
onPressed: () { onPressed: () {
widget.account.cancelConnect(); widget.account.cancelConnect();
Navigator.of(context, rootNavigator: true).pop(); Navigator.of(context, rootNavigator: true).pop();
}, },
child: Text(t.common.cancel), label: t.common.cancel,
), ),
], ],
); );
@@ -14,6 +14,7 @@ import '../../utils/app_logger.dart';
import '../../utils/provider_extensions.dart'; import '../../utils/provider_extensions.dart';
import '../../utils/dialogs.dart'; import '../../utils/dialogs.dart';
import '../../utils/snackbar_helper.dart'; import '../../utils/snackbar_helper.dart';
import '../../widgets/dialog_action_button.dart';
import '../../utils/video_player_navigation.dart'; import '../../utils/video_player_navigation.dart';
import '../../widgets/focused_scroll_scaffold.dart'; import '../../widgets/focused_scroll_scaffold.dart';
import '../models/watch_session.dart'; import '../models/watch_session.dart';
@@ -241,37 +242,21 @@ class _NotInSessionViewState extends State<_NotInSessionView> {
} }
Future<ControlMode?> _showControlModeDialog() { Future<ControlMode?> _showControlModeDialog() {
const buttonPadding = EdgeInsets.symmetric(horizontal: 18, vertical: 14);
const buttonShape = StadiumBorder();
return showDialog<ControlMode>( return showDialog<ControlMode>(
context: context, context: context,
builder: (context) => AlertDialog( builder: (context) => AlertDialog(
title: Text(t.watchTogether.controlMode), title: Text(t.watchTogether.controlMode),
content: Text(t.watchTogether.controlModeQuestion), content: Text(t.watchTogether.controlModeQuestion),
actions: [ actions: [
FocusableButton( DialogActionButton(onPressed: () => Navigator.pop(context), label: t.common.cancel),
autofocus: true, DialogActionButton(
onPressed: () => Navigator.pop(context),
child: TextButton(
onPressed: () => Navigator.pop(context),
style: TextButton.styleFrom(padding: buttonPadding, shape: buttonShape),
child: Text(t.common.cancel),
),
),
FocusableButton(
onPressed: () => Navigator.pop(context, ControlMode.hostOnly), onPressed: () => Navigator.pop(context, ControlMode.hostOnly),
child: TextButton( label: t.watchTogether.hostOnly,
onPressed: () => Navigator.pop(context, ControlMode.hostOnly),
style: TextButton.styleFrom(padding: buttonPadding, shape: buttonShape),
child: Text(t.watchTogether.hostOnly),
),
), ),
FocusableButton( DialogActionButton(
onPressed: () => Navigator.pop(context, ControlMode.anyone), onPressed: () => Navigator.pop(context, ControlMode.anyone),
child: FilledButton( label: t.watchTogether.anyone,
onPressed: () => Navigator.pop(context, ControlMode.anyone), isPrimary: true,
child: Text(t.watchTogether.anyone),
),
), ),
], ],
), ),