diff --git a/packages/neon/neon/lib/l10n/en.arb b/packages/neon/neon/lib/l10n/en.arb
index cfc0f28e21a..a542875506a 100644
--- a/packages/neon/neon/lib/l10n/en.arb
+++ b/packages/neon/neon/lib/l10n/en.arb
@@ -77,6 +77,7 @@
       }
     }
   },
+  "errorDialog": "An error has occurred",
   "actionYes": "Yes",
   "actionNo": "No",
   "actionClose": "Close",
@@ -84,6 +85,7 @@
   "actionShowSlashHide": "Show/Hide",
   "actionExit": "Exit",
   "actionContinue": "Continue",
+  "actionCancel": "Cancel",
   "firstLaunchGoToSettingsToEnablePushNotifications": "Go to the settings to enable push notifications",
   "nextPushSupported": "NextPush is supported!",
   "nextPushSupportedText": "NextPush is a FOSS way of receiving push notifications using the UnifiedPush protocol via a Nextcloud instance.\nYou can install NextPush from the F-Droid app store.",
@@ -97,9 +99,11 @@
   "settingsAccountManage": "Manage accounts",
   "settingsExport": "Export settings",
   "settingsImport": "Import settings",
+  "settingsReset": "Reset settings?",
   "settingsImportWrongFileExtension": "Settings import has wrong file extension (has to be .json.base64)",
   "settingsResetAll": "Reset all settings",
   "settingsResetAllConfirmation": "Do you want to reset all settings?",
+  "settingsResetAllExplanation": "This will reset all preferences back to their default settings.",
   "settingsResetFor": "Reset all settings for {name}",
   "@settingsResetFor": {
     "placeholders": {
@@ -108,6 +112,8 @@
       }
     }
   },
+  "settingsResetForExplanation": "This will reset your account preferences back to their default settings.",
+  "settingsResetForClientExplanation": "This will reset all preferences for the app back to their default settings.",
   "settingsResetForConfirmation": "Do you want to reset all settings for {name}?",
   "@settingsResetForConfirmation": {
     "placeholders": {
diff --git a/packages/neon/neon/lib/l10n/localizations.dart b/packages/neon/neon/lib/l10n/localizations.dart
index b588aaf2a57..4a0793ccf78 100644
--- a/packages/neon/neon/lib/l10n/localizations.dart
+++ b/packages/neon/neon/lib/l10n/localizations.dart
@@ -269,6 +269,12 @@ abstract class NeonLocalizations {
   /// **'Route not found: {route}'**
   String errorRouteNotFound(String route);
 
+  /// No description provided for @errorDialog.
+  ///
+  /// In en, this message translates to:
+  /// **'An error has occurred'**
+  String get errorDialog;
+
   /// No description provided for @actionYes.
   ///
   /// In en, this message translates to:
@@ -311,6 +317,12 @@ abstract class NeonLocalizations {
   /// **'Continue'**
   String get actionContinue;
 
+  /// No description provided for @actionCancel.
+  ///
+  /// In en, this message translates to:
+  /// **'Cancel'**
+  String get actionCancel;
+
   /// No description provided for @firstLaunchGoToSettingsToEnablePushNotifications.
   ///
   /// In en, this message translates to:
@@ -389,6 +401,12 @@ abstract class NeonLocalizations {
   /// **'Import settings'**
   String get settingsImport;
 
+  /// No description provided for @settingsReset.
+  ///
+  /// In en, this message translates to:
+  /// **'Reset settings?'**
+  String get settingsReset;
+
   /// No description provided for @settingsImportWrongFileExtension.
   ///
   /// In en, this message translates to:
@@ -407,12 +425,30 @@ abstract class NeonLocalizations {
   /// **'Do you want to reset all settings?'**
   String get settingsResetAllConfirmation;
 
+  /// No description provided for @settingsResetAllExplanation.
+  ///
+  /// In en, this message translates to:
+  /// **'This will reset all preferences back to their default settings.'**
+  String get settingsResetAllExplanation;
+
   /// No description provided for @settingsResetFor.
   ///
   /// In en, this message translates to:
   /// **'Reset all settings for {name}'**
   String settingsResetFor(String name);
 
+  /// No description provided for @settingsResetForExplanation.
+  ///
+  /// In en, this message translates to:
+  /// **'This will reset your account preferences back to their default settings.'**
+  String get settingsResetForExplanation;
+
+  /// No description provided for @settingsResetForClientExplanation.
+  ///
+  /// In en, this message translates to:
+  /// **'This will reset all preferences for the app back to their default settings.'**
+  String get settingsResetForClientExplanation;
+
   /// No description provided for @settingsResetForConfirmation.
   ///
   /// In en, this message translates to:
diff --git a/packages/neon/neon/lib/l10n/localizations_en.dart b/packages/neon/neon/lib/l10n/localizations_en.dart
index e8df967a511..18183d59b71 100644
--- a/packages/neon/neon/lib/l10n/localizations_en.dart
+++ b/packages/neon/neon/lib/l10n/localizations_en.dart
@@ -126,6 +126,9 @@ class NeonLocalizationsEn extends NeonLocalizations {
     return 'Route not found: $route';
   }
 
+  @override
+  String get errorDialog => 'An error has occurred';
+
   @override
   String get actionYes => 'Yes';
 
@@ -147,6 +150,9 @@ class NeonLocalizationsEn extends NeonLocalizations {
   @override
   String get actionContinue => 'Continue';
 
+  @override
+  String get actionCancel => 'Cancel';
+
   @override
   String get firstLaunchGoToSettingsToEnablePushNotifications => 'Go to the settings to enable push notifications';
 
@@ -187,6 +193,9 @@ class NeonLocalizationsEn extends NeonLocalizations {
   @override
   String get settingsImport => 'Import settings';
 
+  @override
+  String get settingsReset => 'Reset settings?';
+
   @override
   String get settingsImportWrongFileExtension => 'Settings import has wrong file extension (has to be .json.base64)';
 
@@ -196,11 +205,21 @@ class NeonLocalizationsEn extends NeonLocalizations {
   @override
   String get settingsResetAllConfirmation => 'Do you want to reset all settings?';
 
+  @override
+  String get settingsResetAllExplanation => 'This will reset all preferences back to their default settings.';
+
   @override
   String settingsResetFor(String name) {
     return 'Reset all settings for $name';
   }
 
+  @override
+  String get settingsResetForExplanation => 'This will reset your account preferences back to their default settings.';
+
+  @override
+  String get settingsResetForClientExplanation =>
+      'This will reset all preferences for the app back to their default settings.';
+
   @override
   String settingsResetForConfirmation(String name) {
     return 'Do you want to reset all settings for $name?';
diff --git a/packages/neon/neon/lib/src/pages/account_settings.dart b/packages/neon/neon/lib/src/pages/account_settings.dart
index 77f9868b97e..52360c5d32a 100644
--- a/packages/neon/neon/lib/src/pages/account_settings.dart
+++ b/packages/neon/neon/lib/src/pages/account_settings.dart
@@ -13,7 +13,7 @@ import 'package:neon/src/settings/widgets/settings_category.dart';
 import 'package:neon/src/settings/widgets/settings_list.dart';
 import 'package:neon/src/theme/dialog.dart';
 import 'package:neon/src/utils/adaptive.dart';
-import 'package:neon/src/utils/dialog.dart';
+import 'package:neon/src/widgets/dialog.dart';
 import 'package:neon/src/widgets/error.dart';
 import 'package:nextcloud/provisioning_api.dart' as provisioning_api;
 
@@ -46,15 +46,23 @@ class AccountSettingsPage extends StatelessWidget {
       actions: [
         IconButton(
           onPressed: () async {
-            if (await showConfirmationDialog(
-              context,
-              NeonLocalizations.of(context).accountOptionsRemoveConfirm(account.humanReadableID),
-            )) {
+            final decision = await showAdaptiveDialog<bool>(
+              context: context,
+              builder: (final context) => NeonConfirmationDialog(
+                icon: const Icon(Icons.logout),
+                title: NeonLocalizations.of(context).accountOptionsRemove,
+                content: Text(
+                  NeonLocalizations.of(context).accountOptionsRemoveConfirm(account.humanReadableID),
+                ),
+              ),
+            );
+
+            if (decision ?? false) {
               final isActive = bloc.activeAccount.valueOrNull == account;
 
+              options.reset();
               bloc.removeAccount(account);
 
-              // ignore: use_build_context_synchronously
               if (!context.mounted) {
                 return;
               }
@@ -71,10 +79,18 @@ class AccountSettingsPage extends StatelessWidget {
         ),
         IconButton(
           onPressed: () async {
-            if (await showConfirmationDialog(
-              context,
-              NeonLocalizations.of(context).settingsResetForConfirmation(name),
-            )) {
+            final content =
+                '${NeonLocalizations.of(context).settingsResetForConfirmation(name)} ${NeonLocalizations.of(context).settingsResetForExplanation}';
+            final decision = await showAdaptiveDialog<bool>(
+              context: context,
+              builder: (final context) => NeonConfirmationDialog(
+                icon: const Icon(Icons.restart_alt),
+                title: NeonLocalizations.of(context).settingsReset,
+                content: Text(content),
+              ),
+            );
+
+            if (decision ?? false) {
               options.reset();
             }
           },
diff --git a/packages/neon/neon/lib/src/pages/home.dart b/packages/neon/neon/lib/src/pages/home.dart
index c82cd0eb0b8..951dc7de076 100644
--- a/packages/neon/neon/lib/src/pages/home.dart
+++ b/packages/neon/neon/lib/src/pages/home.dart
@@ -2,7 +2,6 @@ import 'dart:async';
 
 import 'package:flutter/material.dart';
 import 'package:meta/meta.dart';
-import 'package:neon/l10n/localizations.dart';
 import 'package:neon/src/bloc/result.dart';
 import 'package:neon/src/blocs/accounts.dart';
 import 'package:neon/src/blocs/apps.dart';
@@ -11,11 +10,11 @@ import 'package:neon/src/models/app_implementation.dart';
 import 'package:neon/src/utils/global_options.dart';
 import 'package:neon/src/utils/global_options.dart' as global_options;
 import 'package:neon/src/utils/global_popups.dart';
-import 'package:neon/src/utils/provider.dart';
 import 'package:neon/src/widgets/app_bar.dart';
 import 'package:neon/src/widgets/drawer.dart';
 import 'package:neon/src/widgets/error.dart';
 import 'package:neon/src/widgets/unified_search_results.dart';
+import 'package:neon/utils.dart';
 import 'package:nextcloud/core.dart' as core;
 import 'package:provider/provider.dart';
 
@@ -68,7 +67,7 @@ class _HomePageState extends State<HomePage> {
       }
 
       final message = l10n.errorUnsupportedAppVersions(buffer.toString());
-      unawaited(_showProblem(message));
+      unawaited(showErrorDialog(context: context, message: message));
     });
 
     GlobalPopups().register(context);
@@ -86,10 +85,10 @@ class _HomePageState extends State<HomePage> {
   Future<void> _checkMaintenanceMode() async {
     try {
       final status = await _account.client.core.getStatus();
+
       if (status.body.maintenance && mounted) {
-        await _showProblem(
-          NeonLocalizations.of(context).errorServerInMaintenanceMode,
-        );
+        final message = NeonLocalizations.of(context).errorServerInMaintenanceMode;
+        await showErrorDialog(context: context, message: message);
       }
     } catch (e, s) {
       debugPrint(e.toString());
@@ -100,29 +99,6 @@ class _HomePageState extends State<HomePage> {
     }
   }
 
-  Future<void> _showProblem(final String title) async {
-    final colorScheme = Theme.of(context).colorScheme;
-
-    await showDialog<void>(
-      context: context,
-      builder: (final context) => AlertDialog(
-        title: Text(title),
-        actions: [
-          ElevatedButton(
-            style: ElevatedButton.styleFrom(
-              backgroundColor: colorScheme.error,
-              foregroundColor: colorScheme.onError,
-            ),
-            onPressed: () {
-              Navigator.of(context).pop();
-            },
-            child: Text(NeonLocalizations.of(context).actionClose),
-          ),
-        ],
-      ),
-    );
-  }
-
   @override
   Widget build(final BuildContext context) {
     const drawer = NeonDrawer();
diff --git a/packages/neon/neon/lib/src/pages/nextcloud_app_settings.dart b/packages/neon/neon/lib/src/pages/nextcloud_app_settings.dart
index a00b29a47b0..f4dccba3670 100644
--- a/packages/neon/neon/lib/src/pages/nextcloud_app_settings.dart
+++ b/packages/neon/neon/lib/src/pages/nextcloud_app_settings.dart
@@ -7,7 +7,7 @@ import 'package:neon/src/settings/widgets/option_settings_tile.dart';
 import 'package:neon/src/settings/widgets/settings_category.dart';
 import 'package:neon/src/settings/widgets/settings_list.dart';
 import 'package:neon/src/theme/dialog.dart';
-import 'package:neon/src/utils/dialog.dart';
+import 'package:neon/src/widgets/dialog.dart';
 
 @internal
 class NextcloudAppSettingsPage extends StatelessWidget {
@@ -25,10 +25,19 @@ class NextcloudAppSettingsPage extends StatelessWidget {
       actions: [
         IconButton(
           onPressed: () async {
-            if (await showConfirmationDialog(
-              context,
-              NeonLocalizations.of(context).settingsResetForConfirmation(appImplementation.name(context)),
-            )) {
+            final content =
+                '${NeonLocalizations.of(context).settingsResetForConfirmation(appImplementation.name(context))} ${NeonLocalizations.of(context).settingsResetForClientExplanation}';
+
+            final decision = await showAdaptiveDialog<bool>(
+              context: context,
+              builder: (final context) => NeonConfirmationDialog(
+                icon: const Icon(Icons.restart_alt),
+                title: NeonLocalizations.of(context).settingsReset,
+                content: Text(content),
+              ),
+            );
+
+            if (decision ?? false) {
               appImplementation.options.reset();
             }
           },
diff --git a/packages/neon/neon/lib/src/pages/settings.dart b/packages/neon/neon/lib/src/pages/settings.dart
index f92703f5513..017edbfc93e 100644
--- a/packages/neon/neon/lib/src/pages/settings.dart
+++ b/packages/neon/neon/lib/src/pages/settings.dart
@@ -19,10 +19,10 @@ import 'package:neon/src/settings/widgets/text_settings_tile.dart';
 import 'package:neon/src/theme/branding.dart';
 import 'package:neon/src/theme/dialog.dart';
 import 'package:neon/src/utils/adaptive.dart';
-import 'package:neon/src/utils/dialog.dart';
 import 'package:neon/src/utils/global_options.dart';
 import 'package:neon/src/utils/provider.dart';
 import 'package:neon/src/utils/save_file.dart';
+import 'package:neon/src/widgets/dialog.dart';
 import 'package:neon/src/widgets/error.dart';
 import 'package:package_info_plus/package_info_plus.dart';
 import 'package:url_launcher/url_launcher_string.dart';
@@ -96,7 +96,18 @@ class _SettingsPageState extends State<SettingsPage> {
       actions: [
         IconButton(
           onPressed: () async {
-            if (await showConfirmationDialog(context, NeonLocalizations.of(context).settingsResetAllConfirmation)) {
+            final content =
+                '${NeonLocalizations.of(context).settingsResetAllConfirmation} ${NeonLocalizations.of(context).settingsResetAllExplanation}';
+            final decision = await showAdaptiveDialog<bool>(
+              context: context,
+              builder: (final context) => NeonConfirmationDialog(
+                icon: const Icon(Icons.restart_alt),
+                title: NeonLocalizations.of(context).settingsReset,
+                content: Text(content),
+              ),
+            );
+
+            if (decision ?? false) {
               globalOptions.reset();
 
               for (final appImplementation in appImplementations) {
diff --git a/packages/neon/neon/lib/src/theme/dialog.dart b/packages/neon/neon/lib/src/theme/dialog.dart
index 8273d98176b..335042abc4b 100644
--- a/packages/neon/neon/lib/src/theme/dialog.dart
+++ b/packages/neon/neon/lib/src/theme/dialog.dart
@@ -15,6 +15,7 @@ class NeonDialogTheme {
       minWidth: 280,
       maxWidth: 560,
     ),
+    this.padding = const EdgeInsets.all(24),
   });
 
   /// Used to configure the [BoxConstraints] for the [NeonDialog] widget.
@@ -23,13 +24,21 @@ class NeonDialogTheme {
   /// By default it follows the default [m3 dialog specification](https://m3.material.io/components/dialogs/specs).
   final BoxConstraints constraints;
 
+  /// Padding around the content.
+  ///
+  /// This property defaults to providing a padding of 24 pixels on all sides
+  /// to separate the content from the edges of the dialog.
+  final EdgeInsets padding;
+
   /// Creates a copy of this object but with the given fields replaced with the
   /// new values.
   NeonDialogTheme copyWith({
     final BoxConstraints? constraints,
+    final EdgeInsets? padding,
   }) =>
       NeonDialogTheme(
         constraints: constraints ?? this.constraints,
+        padding: padding ?? this.padding,
       );
 
   /// The data from the closest [NeonDialogTheme] instance given the build context.
@@ -45,11 +54,15 @@ class NeonDialogTheme {
     }
     return NeonDialogTheme(
       constraints: BoxConstraints.lerp(a.constraints, b.constraints, t)!,
+      padding: EdgeInsets.lerp(a.padding, b.padding, t)!,
     );
   }
 
   @override
-  int get hashCode => constraints.hashCode;
+  int get hashCode => Object.hashAll([
+        constraints,
+        padding,
+      ]);
 
   @override
   bool operator ==(final Object other) {
@@ -57,6 +70,6 @@ class NeonDialogTheme {
       return true;
     }
 
-    return other is NeonDialogTheme && other.constraints == constraints;
+    return other is NeonDialogTheme && other.constraints == constraints && other.padding == padding;
   }
 }
diff --git a/packages/neon/neon/lib/src/utils/confirmation_dialog.dart b/packages/neon/neon/lib/src/utils/confirmation_dialog.dart
deleted file mode 100644
index 944b994d4b9..00000000000
--- a/packages/neon/neon/lib/src/utils/confirmation_dialog.dart
+++ /dev/null
@@ -1,35 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:neon/l10n/localizations.dart';
-import 'package:neon/src/theme/colors.dart';
-
-Future<bool> showConfirmationDialog(final BuildContext context, final String title) async =>
-    await showDialog<bool>(
-      context: context,
-      builder: (final context) => AlertDialog(
-        title: Text(title),
-        actionsAlignment: MainAxisAlignment.spaceEvenly,
-        actions: [
-          ElevatedButton(
-            style: ElevatedButton.styleFrom(
-              backgroundColor: NcColors.decline,
-              foregroundColor: Theme.of(context).colorScheme.onPrimary,
-            ),
-            onPressed: () {
-              Navigator.of(context).pop(false);
-            },
-            child: Text(NeonLocalizations.of(context).actionNo),
-          ),
-          ElevatedButton(
-            style: ElevatedButton.styleFrom(
-              backgroundColor: NcColors.accept,
-              foregroundColor: Theme.of(context).colorScheme.onPrimary,
-            ),
-            onPressed: () {
-              Navigator.of(context).pop(true);
-            },
-            child: Text(NeonLocalizations.of(context).actionYes),
-          ),
-        ],
-      ),
-    ) ??
-    false;
diff --git a/packages/neon/neon/lib/src/utils/dialog.dart b/packages/neon/neon/lib/src/utils/dialog.dart
index 35275f32406..82aa0851143 100644
--- a/packages/neon/neon/lib/src/utils/dialog.dart
+++ b/packages/neon/neon/lib/src/utils/dialog.dart
@@ -1,27 +1,69 @@
 import 'package:flutter/material.dart';
-
+import 'package:neon/l10n/localizations.dart';
 import 'package:neon/src/widgets/dialog.dart';
 
-Future<bool> showConfirmationDialog(final BuildContext context, final String title) async =>
-    await showDialog<bool>(
+/// Displays a simple [NeonConfirmationDialog] with the given [title].
+///
+/// Returns a future whether the action has been accepted.
+Future<bool> showConfirmationDialog({
+  required final BuildContext context,
+  required final String title,
+}) async =>
+    await showAdaptiveDialog<bool>(
       context: context,
-      builder: (final context) => NeonConfirmationDialog(
-        title: title,
-      ),
+      builder: (final context) => NeonConfirmationDialog(title: title),
     ) ??
     false;
 
+/// Displays a [NeonRenameDialog] with the given [title] and [initialValue].
+///
+/// Returns a future with the new name of name.
 Future<String?> showRenameDialog({
   required final BuildContext context,
   required final String title,
-  required final String value,
-  final Key? key,
+  required final String initialValue,
 }) async =>
-    showDialog<String?>(
+    showAdaptiveDialog<String?>(
       context: context,
-      builder: (final context) => RenameDialog(
+      builder: (final context) => NeonRenameDialog(
         title: title,
-        value: value,
-        key: key,
+        value: initialValue,
+      ),
+    );
+
+/// Displays a [NeonErrorDialog] with the given [message].
+Future<void> showErrorDialog({
+  required final BuildContext context,
+  required final String message,
+  final String? title,
+}) =>
+    showAdaptiveDialog<void>(
+      context: context,
+      builder: (final context) => NeonErrorDialog(content: message),
+    );
+
+/// Displays a [NeonDialog] with the given [title] informing the user that a
+/// feature is not implemented yet.
+Future<void> showUnimplementedDialog({
+  required final BuildContext context,
+  required final String title,
+}) =>
+    showAdaptiveDialog<void>(
+      context: context,
+      builder: (final context) => NeonDialog(
+        automaticallyShowCancel: false,
+        title: Text(title),
+        actions: [
+          NeonDialogAction(
+            isDestructiveAction: true,
+            onPressed: () {
+              Navigator.of(context).pop();
+            },
+            child: Text(
+              NeonLocalizations.of(context).actionClose,
+              textAlign: TextAlign.end,
+            ),
+          ),
+        ],
       ),
     );
diff --git a/packages/neon/neon/lib/src/utils/global_popups.dart b/packages/neon/neon/lib/src/utils/global_popups.dart
index 897e28f5a12..945765f3bd9 100644
--- a/packages/neon/neon/lib/src/utils/global_popups.dart
+++ b/packages/neon/neon/lib/src/utils/global_popups.dart
@@ -10,7 +10,7 @@ import 'package:neon/src/platform/platform.dart';
 import 'package:neon/src/router.dart';
 import 'package:neon/src/utils/global_options.dart';
 import 'package:neon/src/utils/provider.dart';
-import 'package:url_launcher/url_launcher_string.dart';
+import 'package:neon/src/widgets/dialog.dart';
 
 /// Singleton class managing global popups.
 @internal
@@ -88,30 +88,9 @@ class GlobalPopups {
             return;
           }
 
-          await showDialog<void>(
+          await showAdaptiveDialog<void>(
             context: _context,
-            builder: (final context) => AlertDialog(
-              title: Text(NeonLocalizations.of(context).nextPushSupported),
-              content: Text(NeonLocalizations.of(context).nextPushSupportedText),
-              actions: [
-                OutlinedButton(
-                  onPressed: () {
-                    Navigator.of(context).pop();
-                  },
-                  child: Text(NeonLocalizations.of(context).actionNo),
-                ),
-                ElevatedButton(
-                  onPressed: () {
-                    Navigator.of(context).pop();
-                    launchUrlString(
-                      'https://f-droid.org/packages/$unifiedPushNextPushID',
-                      mode: LaunchMode.externalApplication,
-                    );
-                  },
-                  child: Text(NeonLocalizations.of(context).nextPushSupportedInstall),
-                ),
-              ],
-            ),
+            builder: (final context) => const NeonUnifiedPushDialog(),
           );
         }),
       ]);
diff --git a/packages/neon/neon/lib/src/widgets/dialog.dart b/packages/neon/neon/lib/src/widgets/dialog.dart
index 3fc96406942..01a65c6edd2 100644
--- a/packages/neon/neon/lib/src/widgets/dialog.dart
+++ b/packages/neon/neon/lib/src/widgets/dialog.dart
@@ -1,77 +1,283 @@
+import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:meta/meta.dart';
-import 'package:neon/blocs.dart';
+import 'package:neon/l10n/localizations.dart';
+import 'package:neon/src/blocs/accounts.dart';
+import 'package:neon/src/theme/dialog.dart';
+import 'package:neon/src/utils/global_options.dart';
+import 'package:neon/src/utils/provider.dart';
+import 'package:neon/src/utils/validators.dart';
 import 'package:neon/src/widgets/account_tile.dart';
-import 'package:neon/theme.dart';
-import 'package:neon/utils.dart';
+import 'package:url_launcher/url_launcher_string.dart';
 
-/// A Neon material design dialog based on [SimpleDialog].
+/// An button typically used in an [AlertDialog.adaptive].
+///
+/// It adaptively creates an [CupertinoDialogAction] based on the closest
+/// [ThemeData.platform].
+
+class NeonDialogAction extends StatelessWidget {
+  /// Creates a new adaptive Neon dialog action.
+  const NeonDialogAction({
+    required this.onPressed,
+    required this.child,
+    this.isDefaultAction = false,
+    this.isDestructiveAction = false,
+    super.key,
+  });
+
+  /// The callback that is called when the button is tapped or otherwise
+  /// activated.
+  ///
+  /// If this is set to null, the button will be disabled.
+  final VoidCallback? onPressed;
+
+  /// The widget below this widget in the tree.
+  ///
+  /// Typically a [Text] widget.
+  final Widget child;
+
+  /// Set to true if button is the default choice in the dialog.
+  ///
+  /// Default buttons have higher emphasis. Similar to
+  /// [CupertinoDialogAction.isDefaultAction]. More than one action can have
+  /// this attribute set to true in the same [Dialog].
+  ///
+  /// This parameters defaults to false and cannot be null.
+  final bool isDefaultAction;
+
+  /// Whether this action destroys an object.
+  ///
+  /// For example, an action that deletes an email is destructive.
+  ///
+  /// Defaults to false and cannot be null.
+  final bool isDestructiveAction;
+
+  @override
+  Widget build(final BuildContext context) {
+    final theme = Theme.of(context);
+    final colorScheme = theme.colorScheme;
+
+    switch (theme.platform) {
+      case TargetPlatform.android:
+      case TargetPlatform.fuchsia:
+      case TargetPlatform.linux:
+      case TargetPlatform.windows:
+        if (isDestructiveAction) {
+          return ElevatedButton(
+            style: ElevatedButton.styleFrom(
+              backgroundColor: colorScheme.errorContainer,
+              foregroundColor: colorScheme.onErrorContainer,
+            ),
+            onPressed: onPressed,
+            child: child,
+          );
+        }
+
+        if (isDefaultAction) {
+          return ElevatedButton(onPressed: onPressed, child: child);
+        }
+
+        return OutlinedButton(onPressed: onPressed, child: child);
+
+      case TargetPlatform.iOS:
+      case TargetPlatform.macOS:
+        return CupertinoDialogAction(
+          onPressed: onPressed,
+          isDefaultAction: isDefaultAction,
+          isDestructiveAction: isDestructiveAction,
+          child: child,
+        );
+    }
+  }
+}
+
+/// A Neon design dialog based on [AlertDialog.adaptive].
+///
+/// THis widget enforces the closest [NeonDialogTheme] and constraints the
+/// [content] width accordingly. The [title] should never be larger than the
+/// [NeonDialogTheme.constraints] and it it up to the caller to handle this.
 class NeonDialog extends StatelessWidget {
   /// Creates a Neon dialog.
   ///
   /// Typically used in conjunction with [showDialog].
   const NeonDialog({
+    this.icon,
     this.title,
-    this.children,
+    this.content,
+    this.actions,
+    this.automaticallyShowCancel = true,
     super.key,
   });
 
+  /// {@template NeonDialog.icon}
+  /// An optional icon to display at the top of the dialog.
+  ///
+  /// Typically, an [Icon] widget. Providing an icon centers the [title]'s text.
+  /// {@endtemplate}
+  final Widget? icon;
+
   /// The (optional) title of the dialog is displayed in a large font at the top
   /// of the dialog.
   ///
+  /// It is up to the caller to enforce [NeonDialogTheme.constraints] is meat.
+  ///
   /// Typically a [Text] widget.
   final Widget? title;
 
-  /// The (optional) content of the dialog is displayed in a
-  /// [SingleChildScrollView] underneath the title.
+  /// {@template NeonDialog.content}
+  /// The (optional) content of the dialog is displayed in the center of the
+  /// dialog in a lighter font.
   ///
-  /// Typically a list of [SimpleDialogOption]s.
-  final List<Widget>? children;
+  /// Typically this is a [SingleChildScrollView] that contains the dialog's
+  /// message. As noted in the [AlertDialog] documentation, it's important
+  /// to use a [SingleChildScrollView] if there's any risk that the content
+  /// will not fit, as the contents will otherwise overflow the dialog.
+  ///
+  /// The horizontal dimension of this widget is constrained by the closest
+  /// [NeonDialogTheme.constraints].
+  /// {@endtemplate}
+  final Widget? content;
+
+  /// The (optional) set of actions that are displayed at the bottom of the
+  /// dialog with an [OverflowBar].
+  ///
+  /// Typically this is a list of [NeonDialogAction] widgets. It is recommended
+  /// to set the [Text.textAlign] to [TextAlign.end] for the [Text] within the
+  /// [TextButton], so that buttons whose labels wrap to an extra line align
+  /// with the overall [OverflowBar]'s alignment within the dialog.
+  ///
+  /// If the [title] is not null but the [content] _is_ null, then an extra 20
+  /// pixels of padding is added above the [OverflowBar] to separate the [title]
+  /// from the [actions].
+  final List<Widget>? actions;
+
+  /// Whether to automatically show a cancel button when only less than two
+  /// actions are supplied.
+  ///
+  /// This is needed for the ios where dialogs are not dismissible by tapping
+  /// outside their boundary.
+  ///
+  /// Defaults to `true`.
+  final bool automaticallyShowCancel;
 
   @override
-  Widget build(final BuildContext context) => SimpleDialog(
-        titlePadding: const EdgeInsets.all(10),
-        contentPadding: const EdgeInsets.all(10),
-        title: title,
-        children: children,
+  Widget build(final BuildContext context) {
+    final theme = Theme.of(context);
+    final dialogTheme = NeonDialogTheme.of(context);
+
+    var content = this.content;
+    if (content != null) {
+      content = ConstrainedBox(
+        constraints: dialogTheme.constraints,
+        child: content,
       );
+    }
+
+    final needsCancelAction = automaticallyShowCancel &&
+        (actions == null || actions!.length <= 1) &&
+        (theme.platform == TargetPlatform.iOS || theme.platform == TargetPlatform.macOS);
+
+    return AlertDialog.adaptive(
+      icon: icon,
+      title: title,
+      content: content,
+      actions: [
+        if (needsCancelAction)
+          NeonDialogAction(
+            onPressed: () {
+              Navigator.pop(context);
+            },
+            child: Text(
+              NeonLocalizations.of(context).actionCancel,
+              textAlign: TextAlign.end,
+            ),
+          ),
+        ...?actions,
+      ],
+    );
+  }
 }
 
+/// A [NeonDialog] with predefined `actions` to confirm or decline.
 class NeonConfirmationDialog extends StatelessWidget {
+  /// Creates a new confirmation dialog.
   const NeonConfirmationDialog({
     required this.title,
+    this.content,
+    this.icon,
+    this.confirmAction,
+    this.declineAction,
+    this.isDestructive = true,
     super.key,
   });
 
+  /// The title of the dialog is displayed in a large font at the top of the
+  /// dialog.
+  ///
+  /// It is up to the caller to enforce [NeonDialogTheme.constraints] is meat
+  /// and the text does not overflow.
   final String title;
 
+  /// {@macro NeonDialog.icon}
+  final Widget? icon;
+
+  /// {@macro NeonDialog.content}
+  final Widget? content;
+
+  /// An optional override for the confirming action.
+  ///
+  /// It is advised to wrap the action in a [Builder] to retain an up to date
+  /// `context` for the Navigator.
+  ///
+  /// Typically this is a [NeonDialogAction] widget.
+  final Widget? confirmAction;
+
+  /// An optional override for the declining action.
+  ///
+  /// It is advised to wrap the action in a [Builder] to retain an up to date
+  /// `context` for the Navigator.
+  ///
+  /// Typically this is a [NeonDialogAction] widget.
+  final Widget? declineAction;
+
+  /// Whether confirming this dialog destroys an object.
+  ///
+  /// For example, a warning dialog that when accepted deletes an email is
+  /// considered destructive.
+  /// This value will set the default confirming action to being destructive.
+  ///
+  /// Defaults to true and cannot be null.
+  final bool isDestructive;
+
   @override
   Widget build(final BuildContext context) {
-    final confirm = ElevatedButton(
-      style: ElevatedButton.styleFrom(
-        backgroundColor: NcColors.accept,
-        foregroundColor: Theme.of(context).colorScheme.onPrimary,
-      ),
-      onPressed: () {
-        Navigator.of(context).pop(true);
-      },
-      child: Text(NeonLocalizations.of(context).actionYes),
-    );
+    final confirm = confirmAction ??
+        NeonDialogAction(
+          isDestructiveAction: isDestructive,
+          onPressed: () {
+            Navigator.of(context).pop(true);
+          },
+          child: Text(
+            NeonLocalizations.of(context).actionContinue,
+            textAlign: TextAlign.end,
+          ),
+        );
 
-    final decline = ElevatedButton(
-      style: ElevatedButton.styleFrom(
-        backgroundColor: NcColors.decline,
-        foregroundColor: Theme.of(context).colorScheme.onPrimary,
-      ),
-      onPressed: () {
-        Navigator.of(context).pop(false);
-      },
-      child: Text(NeonLocalizations.of(context).actionNo),
-    );
+    final decline = declineAction ??
+        NeonDialogAction(
+          onPressed: () {
+            Navigator.of(context).pop(false);
+          },
+          child: Text(
+            NeonLocalizations.of(context).actionCancel,
+            textAlign: TextAlign.end,
+          ),
+        );
 
-    return AlertDialog(
+    return NeonDialog(
+      icon: icon,
       title: Text(title),
-      actionsAlignment: MainAxisAlignment.spaceEvenly,
+      content: content,
       actions: [
         decline,
         confirm,
@@ -80,23 +286,33 @@ class NeonConfirmationDialog extends StatelessWidget {
   }
 }
 
-class RenameDialog extends StatefulWidget {
-  const RenameDialog({
+/// A [NeonDialog] that shows for renaming an object.
+///
+/// Use `showRenameDialog` to display this dialog.
+///
+/// When submitted the new value will be popped as a `String`.
+class NeonRenameDialog extends StatefulWidget {
+  /// Creates a new Neon rename dialog.
+  const NeonRenameDialog({
     required this.title,
     required this.value,
     super.key,
   });
 
+  /// The title of the dialog.
   final String title;
+
+  /// The initial value of the rename field.
+  ///
+  /// This is the current name of the object to be renamed.
   final String value;
 
   @override
-  State<RenameDialog> createState() => _RenameDialogState();
+  State<NeonRenameDialog> createState() => _NeonRenameDialogState();
 }
 
-class _RenameDialogState extends State<RenameDialog> {
+class _NeonRenameDialogState extends State<NeonRenameDialog> {
   final formKey = GlobalKey<FormState>();
-
   final controller = TextEditingController();
 
   @override
@@ -118,42 +334,99 @@ class _RenameDialogState extends State<RenameDialog> {
   }
 
   @override
-  Widget build(final BuildContext context) => NeonDialog(
-        title: Text(widget.title),
-        children: [
-          Form(
-            key: formKey,
-            child: Column(
-              crossAxisAlignment: CrossAxisAlignment.end,
-              children: [
-                TextFormField(
-                  autofocus: true,
-                  controller: controller,
-                  validator: (final input) => validateNotEmpty(context, input),
-                  onFieldSubmitted: (final _) {
-                    submit();
-                  },
-                ),
-                ElevatedButton(
-                  onPressed: submit,
-                  child: Text(widget.title),
-                ),
-              ],
-            ),
+  Widget build(final BuildContext context) {
+    final content = Material(
+      child: TextFormField(
+        autofocus: true,
+        controller: controller,
+        validator: (final input) => validateNotEmpty(context, input),
+        onFieldSubmitted: (final _) {
+          submit();
+        },
+      ),
+    );
+
+    return NeonDialog(
+      title: Text(widget.title),
+      content: Form(key: formKey, child: content),
+      actions: [
+        NeonDialogAction(
+          isDefaultAction: true,
+          onPressed: submit,
+          child: Text(
+            widget.title,
+            textAlign: TextAlign.end,
           ),
-        ],
-      );
+        ),
+      ],
+    );
+  }
+}
+
+/// A [NeonDialog] that informs the user about an error.
+///
+/// Use `showErrorDialog` to display this dialog.
+class NeonErrorDialog extends StatelessWidget {
+  /// Creates a new error dialog.
+  const NeonErrorDialog({
+    required this.content,
+    this.title,
+    super.key,
+  });
+
+  /// The (optional) title for the dialog.
+  ///
+  /// Defaults to [NeonLocalizations.errorDialog].
+  final String? title;
+
+  /// The content of the dialog.
+  final String content;
+
+  @override
+  Widget build(final BuildContext context) {
+    final title = this.title ?? NeonLocalizations.of(context).errorDialog;
+
+    final closeAction = NeonDialogAction(
+      isDestructiveAction: true,
+      onPressed: () {
+        Navigator.of(context).pop();
+      },
+      child: Text(
+        NeonLocalizations.of(context).actionClose,
+        textAlign: TextAlign.end,
+      ),
+    );
+
+    return NeonDialog(
+      automaticallyShowCancel: false,
+      icon: const Icon(Icons.error),
+      title: Text(title),
+      content: Text(content),
+      actions: [
+        closeAction,
+      ],
+    );
+  }
 }
 
+/// Account selection dialog.
+///
+/// Displays a list of all logged in accounts.
+///
+/// When one is selected the dialog gets pooped with the selected `Account`.
 @internal
 class NeonAccountSelectionDialog extends StatelessWidget {
+  /// Creates a new account selection dialog.
   const NeonAccountSelectionDialog({
     this.highlightActiveAccount = false,
     this.children,
     super.key,
   });
 
+  /// Whether the selected account is highlighted with a leading check icon.
   final bool highlightActiveAccount;
+
+  /// The (optional) trailing children of this dialog.
   final List<Widget>? children;
 
   @override
@@ -195,7 +468,7 @@ class NeonAccountSelectionDialog extends StatelessWidget {
     return Dialog(
       child: IntrinsicHeight(
         child: Container(
-          padding: const EdgeInsets.all(24),
+          padding: dialogTheme.padding,
           constraints: dialogTheme.constraints,
           child: body,
         ),
@@ -203,3 +476,43 @@ class NeonAccountSelectionDialog extends StatelessWidget {
     );
   }
 }
+
+/// A [NeonDialog] to inform the user about the UnifiedPush feature of neon.
+@internal
+class NeonUnifiedPushDialog extends StatelessWidget {
+  /// Creates a new UnifiedPush dialog.
+  const NeonUnifiedPushDialog({
+    super.key,
+  });
+
+  @override
+  Widget build(final BuildContext context) => NeonDialog(
+        title: Text(NeonLocalizations.of(context).nextPushSupported),
+        content: Text(NeonLocalizations.of(context).nextPushSupportedText),
+        actions: [
+          NeonDialogAction(
+            onPressed: () {
+              Navigator.pop(context);
+            },
+            child: Text(
+              NeonLocalizations.of(context).actionCancel,
+              textAlign: TextAlign.end,
+            ),
+          ),
+          NeonDialogAction(
+            isDefaultAction: true,
+            onPressed: () async {
+              Navigator.pop(context);
+              await launchUrlString(
+                'https://f-droid.org/packages/$unifiedPushNextPushID',
+                mode: LaunchMode.externalApplication,
+              );
+            },
+            child: Text(
+              NeonLocalizations.of(context).nextPushSupportedInstall,
+              textAlign: TextAlign.end,
+            ),
+          ),
+        ],
+      );
+}
diff --git a/packages/neon/neon/lib/widgets.dart b/packages/neon/neon/lib/widgets.dart
index 523fd2c7e72..c3820cad021 100644
--- a/packages/neon/neon/lib/widgets.dart
+++ b/packages/neon/neon/lib/widgets.dart
@@ -1,4 +1,4 @@
-export 'package:neon/src/widgets/dialog.dart' hide NeonAccountSelectionDialog;
+export 'package:neon/src/widgets/dialog.dart' show NeonConfirmationDialog, NeonDialog, NeonDialogAction;
 export 'package:neon/src/widgets/error.dart';
 export 'package:neon/src/widgets/image.dart';
 export 'package:neon/src/widgets/linear_progress_indicator.dart';
diff --git a/packages/neon/neon_files/lib/l10n/en.arb b/packages/neon/neon_files/lib/l10n/en.arb
index 5ec332df087..671aa881038 100644
--- a/packages/neon/neon_files/lib/l10n/en.arb
+++ b/packages/neon/neon_files/lib/l10n/en.arb
@@ -1,7 +1,5 @@
 {
   "@@locale": "en",
-  "actionYes": "Yes",
-  "actionNo": "No",
   "actionDelete": "Delete",
   "actionRename": "Rename",
   "actionMove": "Move",
@@ -43,6 +41,8 @@
       }
     }
   },
+  "actionDeleteTitle": "Permanently delete?",
+  "filesChooseCreate": "Add to Nextcloud",
   "folderCreate": "Create folder",
   "folderName": "Folder name",
   "folderRename": "Rename folder",
diff --git a/packages/neon/neon_files/lib/l10n/localizations.dart b/packages/neon/neon_files/lib/l10n/localizations.dart
index 6db0be3ca5d..1ce9f9ac30d 100644
--- a/packages/neon/neon_files/lib/l10n/localizations.dart
+++ b/packages/neon/neon_files/lib/l10n/localizations.dart
@@ -89,18 +89,6 @@ abstract class FilesLocalizations {
   /// A list of this localizations delegate's supported locales.
   static const List<Locale> supportedLocales = <Locale>[Locale('en')];
 
-  /// No description provided for @actionYes.
-  ///
-  /// In en, this message translates to:
-  /// **'Yes'**
-  String get actionYes;
-
-  /// No description provided for @actionNo.
-  ///
-  /// In en, this message translates to:
-  /// **'No'**
-  String get actionNo;
-
   /// No description provided for @actionDelete.
   ///
   /// In en, this message translates to:
@@ -185,6 +173,18 @@ abstract class FilesLocalizations {
   /// **'Are you sure you want to download a file that is bigger than {warningSize} ({actualSize})?'**
   String downloadConfirmSizeWarning(String warningSize, String actualSize);
 
+  /// No description provided for @actionDeleteTitle.
+  ///
+  /// In en, this message translates to:
+  /// **'Permanently delete?'**
+  String get actionDeleteTitle;
+
+  /// No description provided for @filesChooseCreate.
+  ///
+  /// In en, this message translates to:
+  /// **'Add to Nextcloud'**
+  String get filesChooseCreate;
+
   /// No description provided for @folderCreate.
   ///
   /// In en, this message translates to:
diff --git a/packages/neon/neon_files/lib/l10n/localizations_en.dart b/packages/neon/neon_files/lib/l10n/localizations_en.dart
index 397b08f8860..1513b83015f 100644
--- a/packages/neon/neon_files/lib/l10n/localizations_en.dart
+++ b/packages/neon/neon_files/lib/l10n/localizations_en.dart
@@ -4,12 +4,6 @@ import 'localizations.dart';
 class FilesLocalizationsEn extends FilesLocalizations {
   FilesLocalizationsEn([String locale = 'en']) : super(locale);
 
-  @override
-  String get actionYes => 'Yes';
-
-  @override
-  String get actionNo => 'No';
-
   @override
   String get actionDelete => 'Delete';
 
@@ -58,6 +52,12 @@ class FilesLocalizationsEn extends FilesLocalizations {
     return 'Are you sure you want to download a file that is bigger than $warningSize ($actualSize)?';
   }
 
+  @override
+  String get actionDeleteTitle => 'Permanently delete?';
+
+  @override
+  String get filesChooseCreate => 'Add to Nextcloud';
+
   @override
   String get folderCreate => 'Create folder';
 
diff --git a/packages/neon/neon_files/lib/neon_files.dart b/packages/neon/neon_files/lib/neon_files.dart
index f2eda4d94d0..025e2737caa 100644
--- a/packages/neon/neon_files/lib/neon_files.dart
+++ b/packages/neon/neon_files/lib/neon_files.dart
@@ -42,7 +42,7 @@ import 'package:neon/utils.dart';
 import 'package:neon/widgets.dart';
 import 'package:neon_files/l10n/localizations.dart';
 import 'package:neon_files/routes.dart';
-import 'package:neon_files/widgets/dialog.dart';
+import 'package:neon_files/utils/dialog.dart';
 import 'package:neon_files/widgets/file_list_tile.dart';
 import 'package:nextcloud/core.dart' as core;
 import 'package:nextcloud/nextcloud.dart';
diff --git a/packages/neon/neon_files/lib/pages/details.dart b/packages/neon/neon_files/lib/pages/details.dart
index 36d50c1f9bc..a8d3c8c54ac 100644
--- a/packages/neon/neon_files/lib/pages/details.dart
+++ b/packages/neon/neon_files/lib/pages/details.dart
@@ -56,8 +56,8 @@ class FilesDetailsPage extends StatelessWidget {
                     },
                     if (details.isFavorite != null) ...{
                       FilesLocalizations.of(context).detailsIsFavorite: details.isFavorite!
-                          ? FilesLocalizations.of(context).actionYes
-                          : FilesLocalizations.of(context).actionNo,
+                          ? NeonLocalizations.of(context).actionYes
+                          : NeonLocalizations.of(context).actionNo,
                     },
                   }.entries) ...[
                     DataRow(
diff --git a/packages/neon/neon_files/lib/pages/main.dart b/packages/neon/neon_files/lib/pages/main.dart
index 4e55a0c0724..37b3579d3b6 100644
--- a/packages/neon/neon_files/lib/pages/main.dart
+++ b/packages/neon/neon_files/lib/pages/main.dart
@@ -29,15 +29,7 @@ class _FilesMainPageState extends State<FilesMainPage> {
           filesBloc: bloc,
         ),
         floatingActionButton: FloatingActionButton(
-          onPressed: () async {
-            await showDialog<void>(
-              context: context,
-              builder: (final context) => FilesChooseCreateDialog(
-                bloc: bloc,
-                basePath: bloc.browser.path.value,
-              ),
-            );
-          },
+          onPressed: () async => showFilesCreateModal(context),
           tooltip: FilesLocalizations.of(context).uploadFiles,
           child: const Icon(Icons.add),
         ),
diff --git a/packages/neon/neon_files/lib/utils/dialog.dart b/packages/neon/neon_files/lib/utils/dialog.dart
new file mode 100644
index 00000000000..5ac349d78c5
--- /dev/null
+++ b/packages/neon/neon_files/lib/utils/dialog.dart
@@ -0,0 +1,128 @@
+import 'package:filesize/filesize.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:neon/utils.dart';
+import 'package:neon/widgets.dart';
+import 'package:neon_files/l10n/localizations.dart';
+import 'package:neon_files/neon_files.dart';
+import 'package:neon_files/widgets/dialog.dart';
+
+/// Displays a [FilesCreateFolderDialog] for creating a new folder.
+///
+/// Returns a future with the folder name split by `/`.
+Future<String?> showFolderCreateDialog({
+  required final BuildContext context,
+}) =>
+    showAdaptiveDialog<String>(
+      context: context,
+      builder: (final context) => const FilesCreateFolderDialog(),
+    );
+
+/// Displays a [NeonConfirmationDialog] to confirm downloading a file larger
+/// than the configured limit.
+///
+/// Returns a future whether the action has been accepted.
+Future<bool> showDownloadConfirmationDialog(
+  final BuildContext context,
+  final int warningSize,
+  final int actualSize,
+) async =>
+    await showAdaptiveDialog<bool>(
+      context: context,
+      builder: (final context) => NeonConfirmationDialog(
+        title: FilesLocalizations.of(context).optionsDownloadSizeWarning,
+        content: Text(
+          FilesLocalizations.of(context).downloadConfirmSizeWarning(
+            filesize(warningSize),
+            filesize(actualSize),
+          ),
+        ),
+      ),
+    ) ??
+    false;
+
+/// Displays a [NeonConfirmationDialog] to confirm uploading a file larger than
+/// the configured limit.
+///
+/// Returns a future whether the action has been accepted.
+Future<bool> showUploadConfirmationDialog(
+  final BuildContext context,
+  final int warningSize,
+  final int actualSize,
+) async =>
+    await showAdaptiveDialog<bool>(
+      context: context,
+      builder: (final context) => NeonConfirmationDialog(
+        title: FilesLocalizations.of(context).optionsUploadSizeWarning,
+        content: Text(
+          FilesLocalizations.of(context).uploadConfirmSizeWarning(
+            filesize(warningSize),
+            filesize(actualSize),
+          ),
+        ),
+      ),
+    ) ??
+    false;
+
+/// Displays a [FilesChooseFolderDialog] to choose a new location for a file with the given [details].
+///
+/// Returns a future with the new location.
+Future<List<String>?> showChooseFolderDialog(final BuildContext context, final FileDetails details) async {
+  final bloc = NeonProvider.of<FilesBloc>(context);
+
+  final originalPath = details.path;
+  final b = bloc.getNewFilesBrowserBloc(initialPath: originalPath);
+  final result = await showDialog<List<String>>(
+    context: context,
+    builder: (final context) => FilesChooseFolderDialog(
+      bloc: b,
+      filesBloc: bloc,
+      originalPath: originalPath,
+    ),
+  );
+  b.dispose();
+
+  return result;
+}
+
+/// Displays a [NeonConfirmationDialog] to confirm deleting a file or folder with the given [details].
+///
+/// Returns a future whether the action has been accepted.
+Future<bool> showDeleteConfirmationDialog(final BuildContext context, final FileDetails details) async =>
+    await showAdaptiveDialog<bool>(
+      context: context,
+      builder: (final context) => NeonConfirmationDialog(
+        title: FilesLocalizations.of(context).actionDeleteTitle,
+        icon: const Icon(Icons.delete_outlined),
+        content: Text(
+          details.isDirectory
+              ? FilesLocalizations.of(context).folderDeleteConfirm(details.name)
+              : FilesLocalizations.of(context).fileDeleteConfirm(details.name),
+        ),
+      ),
+    ) ??
+    false;
+
+/// Displays an adaptive modal to select or create a file.
+Future<void> showFilesCreateModal(final BuildContext context) {
+  final theme = Theme.of(context);
+  final bloc = NeonProvider.of<FilesBloc>(context);
+
+  switch (theme.platform) {
+    case TargetPlatform.android:
+    case TargetPlatform.fuchsia:
+    case TargetPlatform.linux:
+    case TargetPlatform.windows:
+      return showModalBottomSheet(
+        context: context,
+        builder: (final _) => FilesChooseCreateModal(bloc: bloc),
+      );
+
+    case TargetPlatform.iOS:
+    case TargetPlatform.macOS:
+      return showCupertinoModalPopup(
+        context: context,
+        builder: (final _) => FilesChooseCreateModal(bloc: bloc),
+      );
+  }
+}
diff --git a/packages/neon/neon_files/lib/widgets/actions.dart b/packages/neon/neon_files/lib/widgets/actions.dart
index 2547d70b690..6f777111945 100644
--- a/packages/neon/neon_files/lib/widgets/actions.dart
+++ b/packages/neon/neon_files/lib/widgets/actions.dart
@@ -1,10 +1,9 @@
-import 'package:filesize/filesize.dart';
 import 'package:flutter/material.dart';
 import 'package:neon/platform.dart';
 import 'package:neon/utils.dart';
 import 'package:neon_files/l10n/localizations.dart';
 import 'package:neon_files/neon_files.dart';
-import 'package:neon_files/widgets/dialog.dart';
+import 'package:neon_files/utils/dialog.dart';
 
 class FileActions extends StatelessWidget {
   const FileActions({
@@ -44,7 +43,7 @@ class FileActions extends StatelessWidget {
           title: details.isDirectory
               ? FilesLocalizations.of(context).folderRename
               : FilesLocalizations.of(context).fileRename,
-          value: details.name,
+          initialValue: details.name,
         );
         if (result != null) {
           bloc.rename(details.path, result);
@@ -53,17 +52,8 @@ class FileActions extends StatelessWidget {
         if (!context.mounted) {
           return;
         }
-        final originalPath = details.path.sublist(0, details.path.length - 1);
-        final b = bloc.getNewFilesBrowserBloc(initialPath: originalPath);
-        final result = await showDialog<List<String>?>(
-          context: context,
-          builder: (final context) => FilesChooseFolderDialog(
-            bloc: b,
-            filesBloc: bloc,
-            originalPath: originalPath,
-          ),
-        );
-        b.dispose();
+        final result = await showChooseFolderDialog(context, details);
+
         if (result != null) {
           bloc.move(details.path, result..add(details.name));
         }
@@ -71,17 +61,8 @@ class FileActions extends StatelessWidget {
         if (!context.mounted) {
           return;
         }
-        final originalPath = details.path.sublist(0, details.path.length - 1);
-        final b = bloc.getNewFilesBrowserBloc(initialPath: originalPath);
-        final result = await showDialog<List<String>?>(
-          context: context,
-          builder: (final context) => FilesChooseFolderDialog(
-            bloc: b,
-            filesBloc: bloc,
-            originalPath: originalPath,
-          ),
-        );
-        b.dispose();
+
+        final result = await showChooseFolderDialog(context, details);
         if (result != null) {
           bloc.copy(details.path, result..add(details.name));
         }
@@ -91,13 +72,9 @@ class FileActions extends StatelessWidget {
         }
         final sizeWarning = browserBloc.options.downloadSizeWarning.value;
         if (sizeWarning != null && details.size != null && details.size! > sizeWarning) {
-          if (!(await showConfirmationDialog(
-            context,
-            FilesLocalizations.of(context).downloadConfirmSizeWarning(
-              filesize(sizeWarning),
-              filesize(details.size),
-            ),
-          ))) {
+          final decision = await showDownloadConfirmationDialog(context, sizeWarning, details.size!);
+
+          if (!decision) {
             return;
           }
         }
@@ -106,12 +83,8 @@ class FileActions extends StatelessWidget {
         if (!context.mounted) {
           return;
         }
-        if (await showConfirmationDialog(
-          context,
-          details.isDirectory
-              ? FilesLocalizations.of(context).folderDeleteConfirm(details.name)
-              : FilesLocalizations.of(context).fileDeleteConfirm(details.name),
-        )) {
+        final decision = await showDeleteConfirmationDialog(context, details);
+        if (decision) {
           bloc.delete(details.path);
         }
     }
@@ -120,13 +93,12 @@ class FileActions extends StatelessWidget {
   @override
   Widget build(final BuildContext context) => PopupMenuButton<FilesFileAction>(
         itemBuilder: (final context) => [
-          if (!details.isDirectory && NeonPlatform.instance.canUseSharing) ...[
+          if (!details.isDirectory && NeonPlatform.instance.canUseSharing)
             PopupMenuItem(
               value: FilesFileAction.share,
               child: Text(FilesLocalizations.of(context).actionShare),
             ),
-          ],
-          if (details.isFavorite != null) ...[
+          if (details.isFavorite != null)
             PopupMenuItem(
               value: FilesFileAction.toggleFavorite,
               child: Text(
@@ -135,7 +107,7 @@ class FileActions extends StatelessWidget {
                     : FilesLocalizations.of(context).addToFavorites,
               ),
             ),
-          ],
+
           PopupMenuItem(
             value: FilesFileAction.details,
             child: Text(FilesLocalizations.of(context).details),
@@ -153,12 +125,12 @@ class FileActions extends StatelessWidget {
             child: Text(FilesLocalizations.of(context).actionCopy),
           ),
           // TODO: https://github.com/provokateurin/nextcloud-neon/issues/4
-          if (!details.isDirectory) ...[
+          if (!details.isDirectory)
             PopupMenuItem(
               value: FilesFileAction.sync,
               child: Text(FilesLocalizations.of(context).actionSync),
             ),
-          ],
+
           PopupMenuItem(
             value: FilesFileAction.delete,
             child: Text(FilesLocalizations.of(context).actionDelete),
diff --git a/packages/neon/neon_files/lib/widgets/dialog.dart b/packages/neon/neon_files/lib/widgets/dialog.dart
index bdec62ce3f6..3daabc1ef1e 100644
--- a/packages/neon/neon_files/lib/widgets/dialog.dart
+++ b/packages/neon/neon_files/lib/widgets/dialog.dart
@@ -3,37 +3,54 @@ import 'dart:io';
 
 import 'package:collection/collection.dart';
 import 'package:file_picker/file_picker.dart';
-import 'package:filesize/filesize.dart';
+import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
 import 'package:image_picker/image_picker.dart';
 import 'package:neon/platform.dart';
+import 'package:neon/theme.dart';
 import 'package:neon/utils.dart';
 import 'package:neon/widgets.dart';
 import 'package:neon_files/l10n/localizations.dart';
 import 'package:neon_files/neon_files.dart';
+import 'package:neon_files/utils/dialog.dart';
 import 'package:path/path.dart' as p;
 
-class FilesChooseCreateDialog extends StatefulWidget {
-  const FilesChooseCreateDialog({
+/// Creates an adaptive bottom sheet to select an action to add a file.
+class FilesChooseCreateModal extends StatefulWidget {
+  /// Creates a new add files modal.
+  const FilesChooseCreateModal({
     required this.bloc,
-    required this.basePath,
     super.key,
   });
 
+  /// The bloc of the flies client.
   final FilesBloc bloc;
-  final List<String> basePath;
 
   @override
-  State<FilesChooseCreateDialog> createState() => _FilesChooseCreateDialogState();
+  State<FilesChooseCreateModal> createState() => _FilesChooseCreateModalState();
 }
 
-class _FilesChooseCreateDialogState extends State<FilesChooseCreateDialog> {
+class _FilesChooseCreateModalState extends State<FilesChooseCreateModal> {
+  late List<String> basePath;
+
+  @override
+  void initState() {
+    basePath = widget.bloc.browser.path.value;
+
+    super.initState();
+  }
+
   Future<void> uploadFromPick(final FileType type) async {
     final result = await FilePicker.platform.pickFiles(
       allowMultiple: true,
       type: type,
     );
+
+    if (mounted) {
+      Navigator.of(context).pop();
+    }
+
     if (result != null) {
       for (final file in result.files) {
         await upload(File(file.path!));
@@ -46,92 +63,147 @@ class _FilesChooseCreateDialogState extends State<FilesChooseCreateDialog> {
     if (sizeWarning != null) {
       final stat = file.statSync();
       if (stat.size > sizeWarning) {
-        if (!(await showConfirmationDialog(
-          context,
-          FilesLocalizations.of(context).uploadConfirmSizeWarning(
-            filesize(sizeWarning),
-            filesize(stat.size),
-          ),
-        ))) {
+        final result = await showUploadConfirmationDialog(context, sizeWarning, stat.size);
+
+        if (!result) {
           return;
         }
       }
     }
-    widget.bloc.uploadFile([...widget.basePath, p.basename(file.path)], file.path);
+    widget.bloc.uploadFile([...basePath, p.basename(file.path)], file.path);
+  }
+
+  Widget wrapAction({
+    required final Widget icon,
+    required final Widget message,
+    required final VoidCallback onPressed,
+  }) {
+    final theme = Theme.of(context);
+
+    switch (theme.platform) {
+      case TargetPlatform.android:
+      case TargetPlatform.fuchsia:
+      case TargetPlatform.linux:
+      case TargetPlatform.windows:
+        return ListTile(
+          leading: icon,
+          title: message,
+          onTap: onPressed,
+        );
+
+      case TargetPlatform.iOS:
+      case TargetPlatform.macOS:
+        return CupertinoActionSheetAction(
+          onPressed: onPressed,
+          child: message,
+        );
+    }
   }
 
   @override
-  Widget build(final BuildContext context) => NeonDialog(
-        children: [
-          ListTile(
-            leading: Icon(
-              MdiIcons.filePlus,
-              color: Theme.of(context).colorScheme.primary,
-            ),
-            title: Text(FilesLocalizations.of(context).uploadFiles),
-            onTap: () async {
-              await uploadFromPick(FileType.any);
+  Widget build(final BuildContext context) {
+    final theme = Theme.of(context);
+    final title = FilesLocalizations.of(context).filesChooseCreate;
 
-              if (mounted) {
-                Navigator.of(context).pop();
-              }
-            },
+    final actions = [
+      wrapAction(
+        icon: Icon(
+          MdiIcons.filePlus,
+          color: Theme.of(context).colorScheme.primary,
+        ),
+        message: Text(FilesLocalizations.of(context).uploadFiles),
+        onPressed: () async => uploadFromPick(FileType.any),
+      ),
+      wrapAction(
+        icon: Icon(
+          MdiIcons.fileImagePlus,
+          color: Theme.of(context).colorScheme.primary,
+        ),
+        message: Text(FilesLocalizations.of(context).uploadImages),
+        onPressed: () async => uploadFromPick(FileType.image),
+      ),
+      if (NeonPlatform.instance.canUseCamera)
+        wrapAction(
+          icon: Icon(
+            MdiIcons.cameraPlus,
+            color: Theme.of(context).colorScheme.primary,
           ),
-          ListTile(
-            leading: Icon(
-              MdiIcons.fileImagePlus,
-              color: Theme.of(context).colorScheme.primary,
-            ),
-            title: Text(FilesLocalizations.of(context).uploadImages),
-            onTap: () async {
-              await uploadFromPick(FileType.image);
+          message: Text(FilesLocalizations.of(context).uploadCamera),
+          onPressed: () async {
+            Navigator.of(context).pop();
 
-              if (mounted) {
-                Navigator.of(context).pop();
-              }
-            },
-          ),
-          if (NeonPlatform.instance.canUseCamera) ...[
-            ListTile(
-              leading: Icon(
-                MdiIcons.cameraPlus,
-                color: Theme.of(context).colorScheme.primary,
-              ),
-              title: Text(FilesLocalizations.of(context).uploadCamera),
-              onTap: () async {
-                Navigator.of(context).pop();
-
-                final picker = ImagePicker();
-                final result = await picker.pickImage(source: ImageSource.camera);
-                if (result != null) {
-                  await upload(File(result.path));
-                }
-              },
-            ),
-          ],
-          ListTile(
-            leading: Icon(
-              MdiIcons.folderPlus,
-              color: Theme.of(context).colorScheme.primary,
+            final picker = ImagePicker();
+            final result = await picker.pickImage(source: ImageSource.camera);
+            if (result != null) {
+              await upload(File(result.path));
+            }
+          },
+        ),
+      wrapAction(
+        icon: Icon(
+          MdiIcons.folderPlus,
+          color: Theme.of(context).colorScheme.primary,
+        ),
+        message: Text(FilesLocalizations.of(context).folderCreate),
+        onPressed: () async {
+          Navigator.of(context).pop();
+
+          final result = await showFolderCreateDialog(context: context);
+          if (result != null) {
+            widget.bloc.browser.createFolder([...basePath, ...result.split('/')]);
+          }
+        },
+      ),
+    ];
+
+    switch (theme.platform) {
+      case TargetPlatform.android:
+      case TargetPlatform.fuchsia:
+      case TargetPlatform.linux:
+      case TargetPlatform.windows:
+        return BottomSheet(
+          onClosing: () {},
+          builder: (final context) => Padding(
+            padding: const EdgeInsets.all(24),
+            child: Column(
+              mainAxisSize: MainAxisSize.min,
+              children: [
+                Padding(
+                  padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
+                  child: Align(
+                    alignment: AlignmentDirectional.centerStart,
+                    child: Text(
+                      title,
+                      style: theme.textTheme.titleLarge,
+                    ),
+                  ),
+                ),
+                ...actions,
+              ],
             ),
-            title: Text(FilesLocalizations.of(context).folderCreate),
-            onTap: () async {
-              Navigator.of(context).pop();
-
-              final result = await showDialog<List<String>>(
-                context: context,
-                builder: (final context) => const FilesCreateFolderDialog(),
-              );
-              if (result != null) {
-                widget.bloc.browser.createFolder([...widget.basePath, ...result]);
-              }
-            },
           ),
-        ],
-      );
+        );
+
+      case TargetPlatform.iOS:
+      case TargetPlatform.macOS:
+        return CupertinoActionSheet(
+          actions: actions,
+          title: Text(title),
+          cancelButton: CupertinoActionSheetAction(
+            onPressed: () => Navigator.pop(context),
+            isDestructiveAction: true,
+            child: Text(NeonLocalizations.of(context).actionCancel),
+          ),
+        );
+    }
+  }
 }
 
+/// A dialog for choosing a folder.
+///
+/// This dialog is not adaptive and always builds a material design dialog.
 class FilesChooseFolderDialog extends StatelessWidget {
+  /// Creates a new folder chooser dialog.
   const FilesChooseFolderDialog({
     required this.bloc,
     required this.filesBloc,
@@ -142,61 +214,68 @@ class FilesChooseFolderDialog extends StatelessWidget {
   final FilesBrowserBloc bloc;
   final FilesBloc filesBloc;
 
+  /// The initial path to start at.
   final List<String> originalPath;
 
   @override
-  Widget build(final BuildContext context) => AlertDialog(
-        title: Text(FilesLocalizations.of(context).folderChoose),
-        contentPadding: EdgeInsets.zero,
-        content: SizedBox(
-          width: double.maxFinite,
-          child: Column(
-            children: [
-              Expanded(
-                child: FilesBrowserView(
-                  bloc: bloc,
-                  filesBloc: filesBloc,
-                  mode: FilesBrowserMode.selectDirectory,
-                ),
-              ),
-              StreamBuilder<List<String>>(
-                stream: bloc.path,
-                builder: (final context, final pathSnapshot) => pathSnapshot.hasData
-                    ? Container(
-                        margin: const EdgeInsets.all(10),
-                        child: Row(
-                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
-                          children: [
-                            ElevatedButton(
-                              onPressed: () async {
-                                final result = await showDialog<List<String>>(
-                                  context: context,
-                                  builder: (final context) => const FilesCreateFolderDialog(),
-                                );
-                                if (result != null) {
-                                  bloc.createFolder([...pathSnapshot.requireData, ...result]);
-                                }
-                              },
-                              child: Text(FilesLocalizations.of(context).folderCreate),
-                            ),
-                            ElevatedButton(
-                              onPressed: !(const ListEquality<String>().equals(originalPath, pathSnapshot.data))
-                                  ? () => Navigator.of(context).pop(pathSnapshot.data)
-                                  : null,
-                              child: Text(FilesLocalizations.of(context).folderChoose),
-                            ),
-                          ],
-                        ),
-                      )
-                    : const SizedBox(),
+  Widget build(final BuildContext context) {
+    final dialogTheme = NeonDialogTheme.of(context);
+
+    return StreamBuilder<List<String>>(
+      stream: bloc.path,
+      builder: (final context, final pathSnapshot) {
+        final actions = [
+          OutlinedButton(
+            onPressed: () async {
+              final result = await showFolderCreateDialog(context: context);
+
+              if (result != null) {
+                bloc.createFolder([...pathSnapshot.requireData, ...result.split('/')]);
+              }
+            },
+            child: Text(
+              FilesLocalizations.of(context).folderCreate,
+              textAlign: TextAlign.end,
+            ),
+          ),
+          ElevatedButton(
+            onPressed: !(const ListEquality<String>().equals(originalPath, pathSnapshot.data))
+                ? () => Navigator.of(context).pop(pathSnapshot.data)
+                : null,
+            child: Text(
+              FilesLocalizations.of(context).folderChoose,
+              textAlign: TextAlign.end,
+            ),
+          ),
+        ];
+
+        return AlertDialog(
+          title: Text(FilesLocalizations.of(context).folderChoose),
+          content: ConstrainedBox(
+            constraints: dialogTheme.constraints,
+            child: SizedBox(
+              width: double.maxFinite,
+              child: FilesBrowserView(
+                bloc: bloc,
+                filesBloc: filesBloc,
+                mode: FilesBrowserMode.selectDirectory,
               ),
-            ],
+            ),
           ),
-        ),
-      );
+          actions: pathSnapshot.hasData ? actions : null,
+        );
+      },
+    );
+  }
 }
 
+/// A [NeonDialog] that shows for renaming creating a new folder.
+///
+/// Use `showFolderCreateDialog` to display this dialog.
+///
+/// When submitted the folder name will be popped as a `String`.
 class FilesCreateFolderDialog extends StatefulWidget {
+  /// Creates a new NeonDialog for creating a folder.
   const FilesCreateFolderDialog({
     super.key,
   });
@@ -207,7 +286,6 @@ class FilesCreateFolderDialog extends StatefulWidget {
 
 class _FilesCreateFolderDialogState extends State<FilesCreateFolderDialog> {
   final formKey = GlobalKey<FormState>();
-
   final controller = TextEditingController();
 
   @override
@@ -218,37 +296,42 @@ class _FilesCreateFolderDialogState extends State<FilesCreateFolderDialog> {
 
   void submit() {
     if (formKey.currentState!.validate()) {
-      Navigator.of(context).pop(controller.text.split('/'));
+      Navigator.of(context).pop(controller.text);
     }
   }
 
   @override
-  Widget build(final BuildContext context) => NeonDialog(
-        title: Text(FilesLocalizations.of(context).folderCreate),
-        children: [
-          Form(
-            key: formKey,
-            child: Column(
-              crossAxisAlignment: CrossAxisAlignment.end,
-              children: [
-                TextFormField(
-                  controller: controller,
-                  decoration: InputDecoration(
-                    hintText: FilesLocalizations.of(context).folderName,
-                  ),
-                  autofocus: true,
-                  validator: (final input) => validateNotEmpty(context, input),
-                  onFieldSubmitted: (final _) {
-                    submit();
-                  },
-                ),
-                ElevatedButton(
-                  onPressed: submit,
-                  child: Text(FilesLocalizations.of(context).folderCreate),
-                ),
-              ],
-            ),
+  Widget build(final BuildContext context) {
+    final content = Material(
+      child: TextFormField(
+        controller: controller,
+        decoration: InputDecoration(
+          hintText: FilesLocalizations.of(context).folderName,
+        ),
+        autofocus: true,
+        validator: (final input) => validateNotEmpty(context, input),
+        onFieldSubmitted: (final _) {
+          submit();
+        },
+      ),
+    );
+
+    return NeonDialog(
+      title: Text(FilesLocalizations.of(context).folderCreate),
+      content: Form(
+        key: formKey,
+        child: content,
+      ),
+      actions: [
+        NeonDialogAction(
+          isDefaultAction: true,
+          onPressed: submit,
+          child: Text(
+            FilesLocalizations.of(context).folderCreate,
+            textAlign: TextAlign.end,
           ),
-        ],
-      );
+        ),
+      ],
+    );
+  }
 }
diff --git a/packages/neon/neon_files/lib/widgets/file_list_tile.dart b/packages/neon/neon_files/lib/widgets/file_list_tile.dart
index 85426f9fec5..f609c39de87 100644
--- a/packages/neon/neon_files/lib/widgets/file_list_tile.dart
+++ b/packages/neon/neon_files/lib/widgets/file_list_tile.dart
@@ -2,10 +2,9 @@ import 'package:filesize/filesize.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
 import 'package:neon/theme.dart';
-import 'package:neon/utils.dart';
 import 'package:neon/widgets.dart';
-import 'package:neon_files/l10n/localizations.dart';
 import 'package:neon_files/neon_files.dart';
+import 'package:neon_files/utils/dialog.dart';
 import 'package:neon_files/widgets/actions.dart';
 
 class FileListTile extends StatelessWidget {
@@ -28,13 +27,9 @@ class FileListTile extends StatelessWidget {
     } else if (mode == FilesBrowserMode.browser) {
       final sizeWarning = bloc.options.downloadSizeWarning.value;
       if (sizeWarning != null && details.size != null && details.size! > sizeWarning) {
-        if (!(await showConfirmationDialog(
-          context,
-          FilesLocalizations.of(context).downloadConfirmSizeWarning(
-            filesize(sizeWarning),
-            filesize(details.size),
-          ),
-        ))) {
+        final decision = await showDownloadConfirmationDialog(context, sizeWarning, details.size!);
+
+        if (!decision) {
           return;
         }
       }
diff --git a/packages/neon/neon_news/lib/l10n/en.arb b/packages/neon/neon_news/lib/l10n/en.arb
index cdafa96614b..4b520c595f2 100644
--- a/packages/neon/neon_news/lib/l10n/en.arb
+++ b/packages/neon/neon_news/lib/l10n/en.arb
@@ -1,8 +1,6 @@
 {
   "@@locale": "en",
-  "actionClose": "Close",
   "actionDelete": "Delete",
-  "actionRemove": "Remove",
   "actionRename": "Rename",
   "actionMove": "Move",
   "general": "General",
@@ -19,6 +17,7 @@
       }
     }
   },
+  "actionDeleteTitle": "Permanently delete?",
   "folderRename": "Rename folder",
   "feeds": "Feeds",
   "feedAdd": "Add feed",
diff --git a/packages/neon/neon_news/lib/l10n/localizations.dart b/packages/neon/neon_news/lib/l10n/localizations.dart
index aab0f2da1b1..8b5e07a1a81 100644
--- a/packages/neon/neon_news/lib/l10n/localizations.dart
+++ b/packages/neon/neon_news/lib/l10n/localizations.dart
@@ -89,24 +89,12 @@ abstract class NewsLocalizations {
   /// A list of this localizations delegate's supported locales.
   static const List<Locale> supportedLocales = <Locale>[Locale('en')];
 
-  /// No description provided for @actionClose.
-  ///
-  /// In en, this message translates to:
-  /// **'Close'**
-  String get actionClose;
-
   /// No description provided for @actionDelete.
   ///
   /// In en, this message translates to:
   /// **'Delete'**
   String get actionDelete;
 
-  /// No description provided for @actionRemove.
-  ///
-  /// In en, this message translates to:
-  /// **'Remove'**
-  String get actionRemove;
-
   /// No description provided for @actionRename.
   ///
   /// In en, this message translates to:
@@ -161,6 +149,12 @@ abstract class NewsLocalizations {
   /// **'Are you sure you want to delete the folder \'{name}\'?'**
   String folderDeleteConfirm(String name);
 
+  /// No description provided for @actionDeleteTitle.
+  ///
+  /// In en, this message translates to:
+  /// **'Permanently delete?'**
+  String get actionDeleteTitle;
+
   /// No description provided for @folderRename.
   ///
   /// In en, this message translates to:
diff --git a/packages/neon/neon_news/lib/l10n/localizations_en.dart b/packages/neon/neon_news/lib/l10n/localizations_en.dart
index af1adcbecfb..3a9cae952b0 100644
--- a/packages/neon/neon_news/lib/l10n/localizations_en.dart
+++ b/packages/neon/neon_news/lib/l10n/localizations_en.dart
@@ -4,15 +4,9 @@ import 'localizations.dart';
 class NewsLocalizationsEn extends NewsLocalizations {
   NewsLocalizationsEn([String locale = 'en']) : super(locale);
 
-  @override
-  String get actionClose => 'Close';
-
   @override
   String get actionDelete => 'Delete';
 
-  @override
-  String get actionRemove => 'Remove';
-
   @override
   String get actionRename => 'Rename';
 
@@ -42,6 +36,9 @@ class NewsLocalizationsEn extends NewsLocalizations {
     return 'Are you sure you want to delete the folder \'$name\'?';
   }
 
+  @override
+  String get actionDeleteTitle => 'Permanently delete?';
+
   @override
   String get folderRename => 'Rename folder';
 
diff --git a/packages/neon/neon_news/lib/neon_news.dart b/packages/neon/neon_news/lib/neon_news.dart
index 8b292e6b9f7..8df743dd4b2 100644
--- a/packages/neon/neon_news/lib/neon_news.dart
+++ b/packages/neon/neon_news/lib/neon_news.dart
@@ -43,6 +43,7 @@ import 'package:neon/utils.dart';
 import 'package:neon/widgets.dart';
 import 'package:neon_news/l10n/localizations.dart';
 import 'package:neon_news/routes.dart';
+import 'package:neon_news/utils/dialog.dart';
 import 'package:neon_news/widgets/dialog.dart';
 import 'package:nextcloud/core.dart' as core;
 import 'package:nextcloud/news.dart' as news;
diff --git a/packages/neon/neon_news/lib/utils/dialog.dart b/packages/neon/neon_news/lib/utils/dialog.dart
new file mode 100644
index 00000000000..ea5ec88e98e
--- /dev/null
+++ b/packages/neon/neon_news/lib/utils/dialog.dart
@@ -0,0 +1,67 @@
+import 'package:flutter/material.dart';
+import 'package:neon/utils.dart';
+import 'package:neon/widgets.dart';
+import 'package:neon_news/l10n/localizations.dart';
+import 'package:neon_news/widgets/dialog.dart';
+import 'package:nextcloud/news.dart';
+
+/// Displays a [NeonConfirmationDialog] to confirm the deletion of the given [feed].
+///
+/// Returns a future whether the action has been accepted.
+Future<bool> showDeleteFeedDialog(final BuildContext context, final Feed feed) async {
+  final content = NewsLocalizations.of(context).feedRemoveConfirm(feed.title);
+
+  final result = await showAdaptiveDialog<bool>(
+    context: context,
+    builder: (final context) => NeonConfirmationDialog(
+      title: NewsLocalizations.of(context).actionDeleteTitle,
+      content: Text(content),
+    ),
+  );
+
+  return result ?? false;
+}
+
+/// Displays a [NewsCreateFolderDialog] for creating a new folder.
+///
+/// Returns a future with the folder name split by `/`.
+Future<String?> showFolderCreateDialog({
+  required final BuildContext context,
+}) =>
+    showAdaptiveDialog<String>(
+      context: context,
+      builder: (final context) => const NewsCreateFolderDialog(),
+    );
+
+/// Displays a [NeonConfirmationDialog] for deleting a folder.
+///
+/// Returns a future whether the action has been accepted.
+Future<bool> showFolderDeleteDialog({
+  required final BuildContext context,
+  required final String folderName,
+}) async {
+  final content = NewsLocalizations.of(context).folderDeleteConfirm(folderName);
+
+  final result = await showAdaptiveDialog<bool>(
+    context: context,
+    builder: (final context) => NeonConfirmationDialog(
+      title: NewsLocalizations.of(context).actionDeleteTitle,
+      content: Text(content),
+    ),
+  );
+
+  return result ?? false;
+}
+
+/// Displays a `NeonRenameDialog` for renaming a folder.
+///
+/// Returns a future with the new name of name.
+Future<String?> showFolderRenameDialog({
+  required final BuildContext context,
+  required final String folderName,
+}) async =>
+    showRenameDialog(
+      context: context,
+      title: NewsLocalizations.of(context).folderRename,
+      initialValue: folderName,
+    );
diff --git a/packages/neon/neon_news/lib/widgets/dialog.dart b/packages/neon/neon_news/lib/widgets/dialog.dart
index b1cdce0a14b..5dd0a589700 100644
--- a/packages/neon/neon_news/lib/widgets/dialog.dart
+++ b/packages/neon/neon_news/lib/widgets/dialog.dart
@@ -1,5 +1,6 @@
 import 'dart:async';
 
+import 'package:collection/collection.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:neon/blocs.dart';
@@ -9,14 +10,21 @@ import 'package:neon_news/l10n/localizations.dart';
 import 'package:neon_news/neon_news.dart';
 import 'package:nextcloud/news.dart' as news;
 
+/// A dialog for adding a news feed by url.
+///
+/// When created a record with `(String url, int? folderId)` will be popped.
 class NewsAddFeedDialog extends StatefulWidget {
+  /// Creates a new add feed dialog.
   const NewsAddFeedDialog({
     required this.bloc,
     this.folderID,
     super.key,
   });
 
+  /// The active client bloc.
   final NewsBloc bloc;
+
+  /// The initial id of the folder the feed is in.
   final int? folderID;
 
   @override
@@ -58,146 +66,114 @@ class _NewsAddFeedDialogState extends State<NewsAddFeedDialog> {
   }
 
   @override
-  Widget build(final BuildContext context) => ResultBuilder<List<news.Folder>>.behaviorSubject(
-        subject: widget.bloc.folders,
-        builder: (final context, final folders) => NeonDialog(
-          title: Text(NewsLocalizations.of(context).feedAdd),
-          children: [
-            Form(
-              key: formKey,
-              child: Column(
-                crossAxisAlignment: CrossAxisAlignment.end,
-                children: [
-                  TextFormField(
-                    autofocus: true,
-                    controller: controller,
-                    decoration: const InputDecoration(
-                      hintText: 'https://...',
-                    ),
-                    keyboardType: TextInputType.url,
-                    validator: (final input) => validateHttpUrl(context, input),
-                    onFieldSubmitted: (final _) {
-                      submit();
-                    },
-                  ),
-                  if (widget.folderID == null) ...[
-                    Center(
-                      child: NeonError(
-                        folders.error,
-                        onRetry: widget.bloc.refresh,
-                      ),
-                    ),
-                    Center(
-                      child: NeonLinearProgressIndicator(
-                        visible: folders.isLoading,
-                      ),
-                    ),
-                    if (folders.hasData) ...[
-                      NewsFolderSelect(
-                        folders: folders.requireData,
-                        value: folder,
-                        onChanged: (final f) {
-                          setState(() {
-                            folder = f;
-                          });
-                        },
-                      ),
-                    ],
-                  ],
-                  ElevatedButton(
-                    onPressed: folders.hasData ? submit : null,
-                    child: Text(NewsLocalizations.of(context).feedAdd),
-                  ),
-                ],
-              ),
-            ),
-          ],
+  Widget build(final BuildContext context) {
+    final urlField = Form(
+      key: formKey,
+      child: TextFormField(
+        autofocus: true,
+        controller: controller,
+        decoration: const InputDecoration(
+          hintText: 'https://...',
         ),
-      );
-}
-
-class NewsCreateFolderDialog extends StatefulWidget {
-  const NewsCreateFolderDialog({
-    super.key,
-  });
-
-  @override
-  State<NewsCreateFolderDialog> createState() => _NewsCreateFolderDialogState();
-}
-
-class _NewsCreateFolderDialogState extends State<NewsCreateFolderDialog> {
-  final formKey = GlobalKey<FormState>();
-
-  final controller = TextEditingController();
+        keyboardType: TextInputType.url,
+        validator: (final input) => validateHttpUrl(context, input),
+        onFieldSubmitted: (final _) {
+          submit();
+        },
+        autofillHints: const [AutofillHints.url],
+      ),
+    );
 
-  @override
-  void dispose() {
-    controller.dispose();
-    super.dispose();
-  }
+    final folderSelector = ResultBuilder<List<news.Folder>>.behaviorSubject(
+      subject: widget.bloc.folders,
+      builder: (final context, final folders) {
+        if (folders.hasError) {
+          return Center(
+            child: NeonError(
+              folders.error,
+              onRetry: widget.bloc.refresh,
+            ),
+          );
+        }
+        if (!folders.hasData) {
+          return Center(
+            child: NeonLinearProgressIndicator(
+              visible: folders.isLoading,
+            ),
+          );
+        }
 
-  void submit() {
-    if (formKey.currentState!.validate()) {
-      Navigator.of(context).pop(controller.text);
-    }
-  }
+        return NewsFolderSelect(
+          folders: folders.requireData,
+          value: folder,
+          onChanged: (final f) {
+            setState(() {
+              folder = f;
+            });
+          },
+        );
+      },
+    );
 
-  @override
-  Widget build(final BuildContext context) => NeonDialog(
-        title: Text(NewsLocalizations.of(context).folderCreate),
-        children: [
-          Form(
-            key: formKey,
-            child: Column(
-              crossAxisAlignment: CrossAxisAlignment.end,
-              children: [
-                TextFormField(
-                  autofocus: true,
-                  controller: controller,
-                  decoration: InputDecoration(
-                    hintText: NewsLocalizations.of(context).folderCreateName,
-                  ),
-                  validator: (final input) => validateNotEmpty(context, input),
-                  onFieldSubmitted: (final _) {
-                    submit();
-                  },
-                ),
-                ElevatedButton(
-                  onPressed: submit,
-                  child: Text(NewsLocalizations.of(context).folderCreate),
-                ),
-              ],
-            ),
+    return NeonDialog(
+      title: Text(NewsLocalizations.of(context).feedAdd),
+      content: Material(
+        child: Column(
+          mainAxisSize: MainAxisSize.min,
+          children: [
+            urlField,
+            const SizedBox(height: 8),
+            folderSelector,
+          ],
+        ),
+      ),
+      actions: [
+        NeonDialogAction(
+          isDefaultAction: true,
+          onPressed: submit,
+          child: Text(
+            NewsLocalizations.of(context).feedAdd,
+            textAlign: TextAlign.end,
           ),
-        ],
-      );
+        ),
+      ],
+    );
+  }
 }
 
-class NewsFeedShowURLDialog extends StatefulWidget {
+/// A dialog for displaying the url of a news feed.
+class NewsFeedShowURLDialog extends StatelessWidget {
+  /// Creates a new display url dialog.
   const NewsFeedShowURLDialog({
     required this.feed,
     super.key,
   });
 
+  /// The feed to display the url for.
   final news.Feed feed;
 
   @override
-  State<NewsFeedShowURLDialog> createState() => _NewsFeedShowURLDialogState();
-}
-
-class _NewsFeedShowURLDialogState extends State<NewsFeedShowURLDialog> {
-  @override
-  Widget build(final BuildContext context) => AlertDialog(
-        title: Text(widget.feed.url),
+  Widget build(final BuildContext context) => NeonDialog(
+        title: Text(feed.url),
         actions: [
-          ElevatedButton(
+          NeonDialogAction(
+            onPressed: () {
+              Navigator.of(context).pop();
+            },
+            child: Text(
+              NeonLocalizations.of(context).actionClose,
+              textAlign: TextAlign.end,
+            ),
+          ),
+          NeonDialogAction(
+            isDefaultAction: true,
             onPressed: () async {
               await Clipboard.setData(
                 ClipboardData(
-                  text: widget.feed.url,
+                  text: feed.url,
                 ),
               );
-              if (mounted) {
+              if (context.mounted) {
                 ScaffoldMessenger.of(context).showSnackBar(
                   SnackBar(
                     content: Text(NewsLocalizations.of(context).feedCopiedURL),
@@ -206,19 +182,16 @@ class _NewsFeedShowURLDialogState extends State<NewsFeedShowURLDialog> {
                 Navigator.of(context).pop();
               }
             },
-            child: Text(NewsLocalizations.of(context).feedCopyURL),
-          ),
-          ElevatedButton(
-            onPressed: () {
-              Navigator.of(context).pop();
-            },
-            child: Text(NewsLocalizations.of(context).actionClose),
+            child: Text(
+              NewsLocalizations.of(context).feedCopyURL,
+              textAlign: TextAlign.end,
+            ),
           ),
         ],
       );
 }
 
-class NewsFeedUpdateErrorDialog extends StatefulWidget {
+class NewsFeedUpdateErrorDialog extends StatelessWidget {
   const NewsFeedUpdateErrorDialog({
     required this.feed,
     super.key,
@@ -227,22 +200,27 @@ class NewsFeedUpdateErrorDialog extends StatefulWidget {
   final news.Feed feed;
 
   @override
-  State<NewsFeedUpdateErrorDialog> createState() => _NewsFeedUpdateErrorDialogState();
-}
-
-class _NewsFeedUpdateErrorDialogState extends State<NewsFeedUpdateErrorDialog> {
-  @override
-  Widget build(final BuildContext context) => AlertDialog(
-        title: Text(widget.feed.lastUpdateError!),
+  Widget build(final BuildContext context) => NeonDialog(
+        title: Text(feed.lastUpdateError!),
         actions: [
-          ElevatedButton(
+          NeonDialogAction(
+            onPressed: () {
+              Navigator.of(context).pop();
+            },
+            child: Text(
+              NeonLocalizations.of(context).actionClose,
+              textAlign: TextAlign.end,
+            ),
+          ),
+          NeonDialogAction(
+            isDefaultAction: true,
             onPressed: () async {
               await Clipboard.setData(
                 ClipboardData(
-                  text: widget.feed.lastUpdateError!,
+                  text: feed.lastUpdateError!,
                 ),
               );
-              if (mounted) {
+              if (context.mounted) {
                 ScaffoldMessenger.of(context).showSnackBar(
                   SnackBar(
                     content: Text(NewsLocalizations.of(context).feedCopiedErrorMessage),
@@ -251,26 +229,30 @@ class _NewsFeedUpdateErrorDialogState extends State<NewsFeedUpdateErrorDialog> {
                 Navigator.of(context).pop();
               }
             },
-            child: Text(NewsLocalizations.of(context).feedCopyErrorMessage),
-          ),
-          ElevatedButton(
-            onPressed: () {
-              Navigator.of(context).pop();
-            },
-            child: Text(NewsLocalizations.of(context).actionClose),
+            child: Text(
+              NewsLocalizations.of(context).feedCopyErrorMessage,
+              textAlign: TextAlign.end,
+            ),
           ),
         ],
       );
 }
 
+/// A dialog for moving a news feed by into a different folder.
+///
+/// When moved the id of the new folder will be popped.
 class NewsMoveFeedDialog extends StatefulWidget {
+  /// Creates a new move feed dialog.
   const NewsMoveFeedDialog({
     required this.folders,
     required this.feed,
     super.key,
   });
 
+  /// The list of available folders.
   final List<news.Folder> folders;
+
+  /// The feed to move.
   final news.Feed feed;
 
   @override
@@ -284,37 +266,110 @@ class _NewsMoveFeedDialogState extends State<NewsMoveFeedDialog> {
 
   void submit() {
     if (formKey.currentState!.validate()) {
-      Navigator.of(context).pop([folder?.id]);
+      Navigator.of(context).pop(folder?.id);
     }
   }
 
+  @override
+  void initState() {
+    folder = widget.folders.singleWhereOrNull((final folder) => folder.id == widget.feed.folderId);
+
+    super.initState();
+  }
+
   @override
   Widget build(final BuildContext context) => NeonDialog(
         title: Text(NewsLocalizations.of(context).feedMove),
-        children: [
-          Form(
+        content: Material(
+          child: Form(
             key: formKey,
-            child: Column(
-              crossAxisAlignment: CrossAxisAlignment.end,
-              children: [
-                NewsFolderSelect(
-                  folders: widget.folders,
-                  value: widget.feed.folderId != null
-                      ? widget.folders.singleWhere((final folder) => folder.id == widget.feed.folderId)
-                      : null,
-                  onChanged: (final f) {
-                    setState(() {
-                      folder = f;
-                    });
-                  },
-                ),
-                ElevatedButton(
-                  onPressed: submit,
-                  child: Text(NewsLocalizations.of(context).feedMove),
-                ),
-              ],
+            child: NewsFolderSelect(
+              folders: widget.folders,
+              value: folder,
+              onChanged: (final f) {
+                setState(() {
+                  folder = f;
+                });
+              },
+            ),
+          ),
+        ),
+        actions: [
+          NeonDialogAction(
+            isDefaultAction: true,
+            onPressed: submit,
+            child: Text(
+              NewsLocalizations.of(context).feedMove,
+              textAlign: TextAlign.end,
             ),
           ),
         ],
       );
 }
+
+/// A [NeonDialog] that shows for renaming creating a new folder.
+///
+/// Use `showFolderCreateDialog` to display this dialog.
+///
+/// When submitted the folder name will be popped as a `String`.
+class NewsCreateFolderDialog extends StatefulWidget {
+  /// Creates a new NeonDialog for creating a folder.
+  const NewsCreateFolderDialog({
+    super.key,
+  });
+
+  @override
+  State<NewsCreateFolderDialog> createState() => _NewsCreateFolderDialogState();
+}
+
+class _NewsCreateFolderDialogState extends State<NewsCreateFolderDialog> {
+  final formKey = GlobalKey<FormState>();
+  final controller = TextEditingController();
+
+  @override
+  void dispose() {
+    controller.dispose();
+    super.dispose();
+  }
+
+  void submit() {
+    if (formKey.currentState!.validate()) {
+      Navigator.of(context).pop(controller.text);
+    }
+  }
+
+  @override
+  Widget build(final BuildContext context) {
+    final content = Material(
+      child: TextFormField(
+        controller: controller,
+        decoration: InputDecoration(
+          hintText: NewsLocalizations.of(context).folderCreateName,
+        ),
+        autofocus: true,
+        validator: (final input) => validateNotEmpty(context, input),
+        onFieldSubmitted: (final _) {
+          submit();
+        },
+      ),
+    );
+
+    return NeonDialog(
+      title: Text(NewsLocalizations.of(context).folderCreate),
+      content: Form(
+        key: formKey,
+        child: content,
+      ),
+      actions: [
+        NeonDialogAction(
+          isDefaultAction: true,
+          onPressed: submit,
+          child: Text(
+            NewsLocalizations.of(context).folderCreate,
+            textAlign: TextAlign.end,
+          ),
+        ),
+      ],
+    );
+  }
+}
diff --git a/packages/neon/neon_news/lib/widgets/feed_floating_action_button.dart b/packages/neon/neon_news/lib/widgets/feed_floating_action_button.dart
index 79a8293ce57..fd62575fb0b 100644
--- a/packages/neon/neon_news/lib/widgets/feed_floating_action_button.dart
+++ b/packages/neon/neon_news/lib/widgets/feed_floating_action_button.dart
@@ -13,7 +13,7 @@ class NewsFeedFloatingActionButton extends StatelessWidget {
   @override
   Widget build(final BuildContext context) => FloatingActionButton(
         onPressed: () async {
-          final result = await showDialog<(String, int?)>(
+          final result = await showAdaptiveDialog<(String, int?)>(
             context: context,
             builder: (final context) => NewsAddFeedDialog(
               bloc: bloc,
diff --git a/packages/neon/neon_news/lib/widgets/feeds_view.dart b/packages/neon/neon_news/lib/widgets/feeds_view.dart
index f36b6672e56..0f85ff30c5e 100644
--- a/packages/neon/neon_news/lib/widgets/feeds_view.dart
+++ b/packages/neon/neon_news/lib/widgets/feeds_view.dart
@@ -57,16 +57,14 @@ class NewsFeedsView extends StatelessWidget {
         trailing: Row(
           mainAxisSize: MainAxisSize.min,
           children: [
-            if (feed.updateErrorCount > 0) ...[
+            if (feed.updateErrorCount > 0)
               IconButton(
-                onPressed: () async {
-                  await showDialog<void>(
-                    context: context,
-                    builder: (final context) => NewsFeedUpdateErrorDialog(
-                      feed: feed,
-                    ),
-                  );
-                },
+                onPressed: () async => showAdaptiveDialog<void>(
+                  context: context,
+                  builder: (final context) => NewsFeedUpdateErrorDialog(
+                    feed: feed,
+                  ),
+                ),
                 tooltip: NewsLocalizations.of(context).feedShowErrorMessage,
                 iconSize: 30,
                 icon: Text(
@@ -76,7 +74,6 @@ class NewsFeedsView extends StatelessWidget {
                   ),
                 ),
               ),
-            ],
             PopupMenuButton<NewsFeedAction>(
               itemBuilder: (final context) => [
                 PopupMenuItem(
@@ -91,17 +88,16 @@ class NewsFeedsView extends StatelessWidget {
                   value: NewsFeedAction.rename,
                   child: Text(NewsLocalizations.of(context).actionRename),
                 ),
-                if (folders.isNotEmpty) ...[
+                if (folders.isNotEmpty)
                   PopupMenuItem(
                     value: NewsFeedAction.move,
                     child: Text(NewsLocalizations.of(context).actionMove),
                   ),
-                ],
               ],
               onSelected: (final action) async {
                 switch (action) {
                   case NewsFeedAction.showURL:
-                    await showDialog<void>(
+                    await showAdaptiveDialog<void>(
                       context: context,
                       builder: (final context) => NewsFeedShowURLDialog(
                         feed: feed,
@@ -111,10 +107,9 @@ class NewsFeedsView extends StatelessWidget {
                     if (!context.mounted) {
                       return;
                     }
-                    if (await showConfirmationDialog(
-                      context,
-                      NewsLocalizations.of(context).feedRemoveConfirm(feed.title),
-                    )) {
+                    final result = await showDeleteFeedDialog(context, feed);
+
+                    if (result) {
                       bloc.removeFeed(feed.id);
                     }
                   case NewsFeedAction.rename:
@@ -124,7 +119,7 @@ class NewsFeedsView extends StatelessWidget {
                     final result = await showRenameDialog(
                       context: context,
                       title: NewsLocalizations.of(context).feedRename,
-                      value: feed.title,
+                      initialValue: feed.title,
                     );
                     if (result != null) {
                       bloc.renameFeed(feed.id, result);
@@ -133,15 +128,15 @@ class NewsFeedsView extends StatelessWidget {
                     if (!context.mounted) {
                       return;
                     }
-                    final result = await showDialog<List<int?>>(
+                    final result = await showAdaptiveDialog<int?>(
                       context: context,
                       builder: (final context) => NewsMoveFeedDialog(
                         folders: folders,
                         feed: feed,
                       ),
                     );
-                    if (result != null) {
-                      bloc.moveFeed(feed.id, result[0]);
+                    if (result != feed.folderId) {
+                      bloc.moveFeed(feed.id, result);
                     }
                 }
               },
diff --git a/packages/neon/neon_news/lib/widgets/folder_floating_action_button.dart b/packages/neon/neon_news/lib/widgets/folder_floating_action_button.dart
index cfb99bf8d09..867ca3c0a23 100644
--- a/packages/neon/neon_news/lib/widgets/folder_floating_action_button.dart
+++ b/packages/neon/neon_news/lib/widgets/folder_floating_action_button.dart
@@ -11,10 +11,8 @@ class NewsFolderFloatingActionButton extends StatelessWidget {
   @override
   Widget build(final BuildContext context) => FloatingActionButton(
         onPressed: () async {
-          final result = await showDialog<String>(
-            context: context,
-            builder: (final context) => const NewsCreateFolderDialog(),
-          );
+          final result = await showFolderCreateDialog(context: context);
+
           if (result != null) {
             bloc.createFolder(result);
           }
diff --git a/packages/neon/neon_news/lib/widgets/folders_view.dart b/packages/neon/neon_news/lib/widgets/folders_view.dart
index c23425e274d..78ee308326d 100644
--- a/packages/neon/neon_news/lib/widgets/folders_view.dart
+++ b/packages/neon/neon_news/lib/widgets/folders_view.dart
@@ -88,21 +88,15 @@ class NewsFoldersView extends StatelessWidget {
         onSelected: (final action) async {
           switch (action) {
             case NewsFolderAction.delete:
-              if (await showConfirmationDialog(
-                context,
-                NewsLocalizations.of(context).folderDeleteConfirm(folder.name),
-              )) {
+              final result = await showFolderDeleteDialog(context: context, folderName: folder.name);
+              if (result) {
                 bloc.deleteFolder(folder.id);
               }
             case NewsFolderAction.rename:
               if (!context.mounted) {
                 return;
               }
-              final result = await showRenameDialog(
-                context: context,
-                title: NewsLocalizations.of(context).folderRename,
-                value: folder.name,
-              );
+              final result = await showFolderRenameDialog(context: context, folderName: folder.name);
               if (result != null) {
                 bloc.renameFolder(folder.id, result);
               }
diff --git a/packages/neon/neon_news/pubspec.yaml b/packages/neon/neon_news/pubspec.yaml
index 8917a777f3b..ef1fe7b03ae 100644
--- a/packages/neon/neon_news/pubspec.yaml
+++ b/packages/neon/neon_news/pubspec.yaml
@@ -7,6 +7,7 @@ environment:
   flutter: '>=3.13.0'
 
 dependencies:
+  collection: ^1.0.0
   flutter:
     sdk: flutter
   flutter_html: ^3.0.0-beta.2
diff --git a/packages/neon/neon_notes/lib/pages/note.dart b/packages/neon/neon_notes/lib/pages/note.dart
index cb99dd82c43..ec0169d7fa6 100644
--- a/packages/neon/neon_notes/lib/pages/note.dart
+++ b/packages/neon/neon_notes/lib/pages/note.dart
@@ -120,7 +120,7 @@ class _NotesNotePageState extends State<NotesNotePage> {
 
                   return IconButton(
                     onPressed: () async {
-                      final result = await showDialog<String>(
+                      final result = await showAdaptiveDialog<String>(
                         context: context,
                         builder: (final context) => NotesSelectCategoryDialog(
                           bloc: widget.notesBloc,
diff --git a/packages/neon/neon_notes/lib/widgets/dialog.dart b/packages/neon/neon_notes/lib/widgets/dialog.dart
index 90de24c4427..5e29b8a0214 100644
--- a/packages/neon/neon_notes/lib/widgets/dialog.dart
+++ b/packages/neon/neon_notes/lib/widgets/dialog.dart
@@ -6,15 +6,20 @@ import 'package:neon_notes/l10n/localizations.dart';
 import 'package:neon_notes/neon_notes.dart';
 import 'package:nextcloud/notes.dart' as notes;
 
+/// A dialog for creating a note.
 class NotesCreateNoteDialog extends StatefulWidget {
+  /// Creates a new create note dialog.
   const NotesCreateNoteDialog({
     required this.bloc,
-    this.category,
+    this.initialCategory,
     super.key,
   });
 
+  /// The active notes bloc.
   final NotesBloc bloc;
-  final String? category;
+
+  /// The initial category of the note.
+  final String? initialCategory;
 
   @override
   State<NotesCreateNoteDialog> createState() => _NotesCreateNoteDialogState();
@@ -33,74 +38,96 @@ class _NotesCreateNoteDialogState extends State<NotesCreateNoteDialog> {
 
   void submit() {
     if (formKey.currentState!.validate()) {
-      Navigator.of(context).pop((controller.text, widget.category ?? selectedCategory));
+      Navigator.of(context).pop((controller.text, widget.initialCategory ?? selectedCategory));
     }
   }
 
   @override
-  Widget build(final BuildContext context) => ResultBuilder<List<notes.Note>>.behaviorSubject(
-        subject: widget.bloc.notesList,
-        builder: (final context, final notes) => NeonDialog(
-          title: Text(NotesLocalizations.of(context).noteCreate),
-          children: [
-            Form(
-              key: formKey,
-              child: Column(
-                crossAxisAlignment: CrossAxisAlignment.end,
-                children: [
-                  TextFormField(
-                    autofocus: true,
-                    controller: controller,
-                    decoration: InputDecoration(
-                      hintText: NotesLocalizations.of(context).noteTitle,
-                    ),
-                    validator: (final input) => validateNotEmpty(context, input),
-                    onFieldSubmitted: (final _) {
-                      submit();
-                    },
-                  ),
-                  if (widget.category == null) ...[
-                    Center(
-                      child: NeonError(
-                        notes.error,
-                        onRetry: widget.bloc.refresh,
-                      ),
-                    ),
-                    Center(
-                      child: NeonLinearProgressIndicator(
-                        visible: notes.isLoading,
-                      ),
-                    ),
-                    if (notes.hasData) ...[
-                      NotesCategorySelect(
-                        categories: notes.requireData.map((final note) => note.category).toSet().toList(),
-                        onChanged: (final category) {
-                          selectedCategory = category;
-                        },
-                        onSubmitted: submit,
-                      ),
-                    ],
-                  ],
-                  ElevatedButton(
-                    onPressed: submit,
-                    child: Text(NotesLocalizations.of(context).noteCreate),
-                  ),
-                ],
-              ),
+  Widget build(final BuildContext context) {
+    final titleField = Form(
+      key: formKey,
+      child: TextFormField(
+        autofocus: true,
+        controller: controller,
+        decoration: InputDecoration(
+          hintText: NotesLocalizations.of(context).noteTitle,
+        ),
+        validator: (final input) => validateNotEmpty(context, input),
+        onFieldSubmitted: (final _) {
+          submit();
+        },
+      ),
+    );
+
+    final folderSelector = ResultBuilder<List<notes.Note>>.behaviorSubject(
+      subject: widget.bloc.notesList,
+      builder: (final context, final notes) {
+        if (notes.hasError) {
+          return Center(
+            child: NeonError(
+              notes.error,
+              onRetry: widget.bloc.refresh,
+            ),
+          );
+        }
+        if (!notes.hasData) {
+          return Center(
+            child: NeonLinearProgressIndicator(
+              visible: notes.isLoading,
             ),
+          );
+        }
+
+        return NotesCategorySelect(
+          categories: notes.requireData.map((final note) => note.category).toSet().toList(),
+          onChanged: (final category) {
+            selectedCategory = category;
+          },
+          onSubmitted: submit,
+        );
+      },
+    );
+
+    return NeonDialog(
+      title: Text(NotesLocalizations.of(context).noteCreate),
+      content: Material(
+        child: Column(
+          mainAxisSize: MainAxisSize.min,
+          crossAxisAlignment: CrossAxisAlignment.end,
+          children: [
+            titleField,
+            const SizedBox(height: 8),
+            folderSelector,
           ],
         ),
-      );
+      ),
+      actions: [
+        NeonDialogAction(
+          isDefaultAction: true,
+          onPressed: submit,
+          child: Text(
+            NotesLocalizations.of(context).noteCreate,
+            textAlign: TextAlign.end,
+          ),
+        ),
+      ],
+    );
+  }
 }
 
+/// A dialog for selecting a category for a note.
 class NotesSelectCategoryDialog extends StatefulWidget {
+  /// Creates a new category selection dialog.
   const NotesSelectCategoryDialog({
     required this.bloc,
     this.initialCategory,
     super.key,
   });
 
+  /// The active notes bloc.
   final NotesBloc bloc;
+
+  /// The initial category of the note.
   final String? initialCategory;
 
   @override
@@ -119,45 +146,55 @@ class _NotesSelectCategoryDialogState extends State<NotesSelectCategoryDialog> {
   }
 
   @override
-  Widget build(final BuildContext context) => ResultBuilder<List<notes.Note>>.behaviorSubject(
-        subject: widget.bloc.notesList,
-        builder: (final context, final notes) => NeonDialog(
-          title: Text(NotesLocalizations.of(context).category),
-          children: [
-            Form(
-              key: formKey,
-              child: Column(
-                crossAxisAlignment: CrossAxisAlignment.end,
-                children: [
-                  Center(
-                    child: NeonError(
-                      notes.error,
-                      onRetry: widget.bloc.refresh,
-                    ),
-                  ),
-                  Center(
-                    child: NeonLinearProgressIndicator(
-                      visible: notes.isLoading,
-                    ),
-                  ),
-                  if (notes.hasData) ...[
-                    NotesCategorySelect(
-                      categories: notes.requireData.map((final note) => note.category).toSet().toList(),
-                      initialValue: widget.initialCategory,
-                      onChanged: (final category) {
-                        selectedCategory = category;
-                      },
-                      onSubmitted: submit,
-                    ),
-                  ],
-                  ElevatedButton(
-                    onPressed: submit,
-                    child: Text(NotesLocalizations.of(context).noteSetCategory),
-                  ),
-                ],
-              ),
+  Widget build(final BuildContext context) {
+    final folderSelector = ResultBuilder<List<notes.Note>>.behaviorSubject(
+      subject: widget.bloc.notesList,
+      builder: (final context, final notes) {
+        if (notes.hasError) {
+          return Center(
+            child: NeonError(
+              notes.error,
+              onRetry: widget.bloc.refresh,
             ),
-          ],
+          );
+        }
+        if (!notes.hasData) {
+          return Center(
+            child: NeonLinearProgressIndicator(
+              visible: notes.isLoading,
+            ),
+          );
+        }
+
+        return Form(
+          key: formKey,
+          child: NotesCategorySelect(
+            categories: notes.requireData.map((final note) => note.category).toSet().toList(),
+            initialValue: widget.initialCategory,
+            onChanged: (final category) {
+              selectedCategory = category;
+            },
+            onSubmitted: submit,
+          ),
+        );
+      },
+    );
+
+    return NeonDialog(
+      title: Text(NotesLocalizations.of(context).category),
+      content: Material(
+        child: folderSelector,
+      ),
+      actions: [
+        NeonDialogAction(
+          isDefaultAction: true,
+          onPressed: submit,
+          child: Text(
+            NotesLocalizations.of(context).noteSetCategory,
+            textAlign: TextAlign.end,
+          ),
         ),
-      );
+      ],
+    );
+  }
 }
diff --git a/packages/neon/neon_notes/lib/widgets/notes_floating_action_button.dart b/packages/neon/neon_notes/lib/widgets/notes_floating_action_button.dart
index 975daee452b..79b1ea584fa 100644
--- a/packages/neon/neon_notes/lib/widgets/notes_floating_action_button.dart
+++ b/packages/neon/neon_notes/lib/widgets/notes_floating_action_button.dart
@@ -13,11 +13,11 @@ class NotesFloatingActionButton extends StatelessWidget {
   @override
   Widget build(final BuildContext context) => FloatingActionButton(
         onPressed: () async {
-          final result = await showDialog<(String, String?)>(
+          final result = await showAdaptiveDialog<(String, String?)>(
             context: context,
             builder: (final context) => NotesCreateNoteDialog(
               bloc: bloc,
-              category: category,
+              initialCategory: category,
             ),
           );
           if (result != null) {
diff --git a/packages/neon/neon_notes/lib/widgets/notes_view.dart b/packages/neon/neon_notes/lib/widgets/notes_view.dart
index 7225ed1771f..2231472a49f 100644
--- a/packages/neon/neon_notes/lib/widgets/notes_view.dart
+++ b/packages/neon/neon_notes/lib/widgets/notes_view.dart
@@ -90,8 +90,8 @@ class NotesView extends StatelessWidget {
         },
         onLongPress: () async {
           final result = await showConfirmationDialog(
-            context,
-            NotesLocalizations.of(context).noteDeleteConfirm(note.title),
+            context: context,
+            title: NotesLocalizations.of(context).noteDeleteConfirm(note.title),
           );
           if (result) {
             bloc.deleteNote(note.id);
diff --git a/packages/neon/neon_notifications/lib/l10n/en.arb b/packages/neon/neon_notifications/lib/l10n/en.arb
index a4b62eb35af..d0382224804 100644
--- a/packages/neon/neon_notifications/lib/l10n/en.arb
+++ b/packages/neon/neon_notifications/lib/l10n/en.arb
@@ -1,6 +1,5 @@
 {
   "@@locale": "en",
-  "actionClose": "Close",
   "notificationsDismissAll": "Dismiss all notifications",
   "notificationAppNotImplementedYet": "Sorry, this Nextcloud app has not been implemented yet"
 }
diff --git a/packages/neon/neon_notifications/lib/l10n/localizations.dart b/packages/neon/neon_notifications/lib/l10n/localizations.dart
index a9716aa2523..3dbb713c0ce 100644
--- a/packages/neon/neon_notifications/lib/l10n/localizations.dart
+++ b/packages/neon/neon_notifications/lib/l10n/localizations.dart
@@ -89,12 +89,6 @@ abstract class NotificationsLocalizations {
   /// A list of this localizations delegate's supported locales.
   static const List<Locale> supportedLocales = <Locale>[Locale('en')];
 
-  /// No description provided for @actionClose.
-  ///
-  /// In en, this message translates to:
-  /// **'Close'**
-  String get actionClose;
-
   /// No description provided for @notificationsDismissAll.
   ///
   /// In en, this message translates to:
diff --git a/packages/neon/neon_notifications/lib/l10n/localizations_en.dart b/packages/neon/neon_notifications/lib/l10n/localizations_en.dart
index b8e6ff33b4f..48b2caa4daf 100644
--- a/packages/neon/neon_notifications/lib/l10n/localizations_en.dart
+++ b/packages/neon/neon_notifications/lib/l10n/localizations_en.dart
@@ -4,9 +4,6 @@ import 'localizations.dart';
 class NotificationsLocalizationsEn extends NotificationsLocalizations {
   NotificationsLocalizationsEn([String locale = 'en']) : super(locale);
 
-  @override
-  String get actionClose => 'Close';
-
   @override
   String get notificationsDismissAll => 'Dismiss all notifications';
 
diff --git a/packages/neon/neon_notifications/lib/pages/main.dart b/packages/neon/neon_notifications/lib/pages/main.dart
index 7783f2d3af2..426a456ab29 100644
--- a/packages/neon/neon_notifications/lib/pages/main.dart
+++ b/packages/neon/neon_notifications/lib/pages/main.dart
@@ -96,25 +96,9 @@ class _NotificationsMainPageState extends State<NotificationsMainPage> {
           final accountsBloc = NeonProvider.of<AccountsBloc>(context);
           await accountsBloc.activeAppsBloc.setActiveApp(app.id);
         } else {
-          final colorScheme = Theme.of(context).colorScheme;
-
-          await showDialog<void>(
+          await showUnimplementedDialog(
             context: context,
-            builder: (final context) => AlertDialog(
-              title: Text(NotificationsLocalizations.of(context).notificationAppNotImplementedYet),
-              actions: [
-                ElevatedButton(
-                  style: ElevatedButton.styleFrom(
-                    backgroundColor: colorScheme.error,
-                    foregroundColor: colorScheme.onError,
-                  ),
-                  onPressed: () {
-                    Navigator.of(context).pop();
-                  },
-                  child: Text(NotificationsLocalizations.of(context).actionClose),
-                ),
-              ],
-            ),
+            title: NotificationsLocalizations.of(context).notificationAppNotImplementedYet,
           );
         }
       },