Feat: Fügt einen dynamisch erstell- und anzeigbaren Dialog hinzu
This commit is contained in:
11
lib/Entities/dialog_type_enum.dart
Normal file
11
lib/Entities/dialog_type_enum.dart
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/// Eine Enum, um den Typ eines Dialogs festzulegen
|
||||||
|
enum DialogTypeEnum {
|
||||||
|
/// Der Standarddialog
|
||||||
|
info,
|
||||||
|
|
||||||
|
/// Der Errordialog
|
||||||
|
error,
|
||||||
|
|
||||||
|
/// Der Erfolgreichdialog
|
||||||
|
success,
|
||||||
|
}
|
||||||
14
lib/Pages/Dialog/dialog_action.dart
Normal file
14
lib/Pages/Dialog/dialog_action.dart
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/// Action/Knopf für den Dialog
|
||||||
|
class DialogAction {
|
||||||
|
/// Erstellt eine neue Aktion für den Dialog
|
||||||
|
DialogAction({required this.label, this.isPrimary = false, this.onPressed});
|
||||||
|
|
||||||
|
/// Das Label der Aktion
|
||||||
|
final String label;
|
||||||
|
|
||||||
|
/// Ob es die primäre Aktion ist
|
||||||
|
final bool isPrimary;
|
||||||
|
|
||||||
|
/// Was bei einem Knopfdruck passieren soll
|
||||||
|
final void Function(Map<String, String> inputValues)? onPressed;
|
||||||
|
}
|
||||||
36
lib/Pages/Dialog/dialog_input_field.dart
Normal file
36
lib/Pages/Dialog/dialog_input_field.dart
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// Ein Input-Feld für den dynamischen Dialog
|
||||||
|
class DialogInputField {
|
||||||
|
/// Erstellt ein neues Input-Feld
|
||||||
|
const DialogInputField({
|
||||||
|
required this.id,
|
||||||
|
this.label,
|
||||||
|
this.initialValue,
|
||||||
|
this.keyboardType = TextInputType.text,
|
||||||
|
this.obscureText = false,
|
||||||
|
this.autoFocus = false,
|
||||||
|
this.onChanged,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Die Id des InputFelds
|
||||||
|
final String id;
|
||||||
|
|
||||||
|
/// Der Label des InputFelds
|
||||||
|
final String? label;
|
||||||
|
|
||||||
|
/// Der initiale Wert des InputFelds
|
||||||
|
final String? initialValue;
|
||||||
|
|
||||||
|
/// Der InputTyp des InputFeld
|
||||||
|
final TextInputType keyboardType;
|
||||||
|
|
||||||
|
/// Ob der Text obskur angezeigt werden soll
|
||||||
|
final bool obscureText;
|
||||||
|
|
||||||
|
/// Ob das Textfeld automatisch fokussiert werden soll
|
||||||
|
final bool autoFocus;
|
||||||
|
|
||||||
|
/// Was bei Veränderung des Textfeldes geschehen soll
|
||||||
|
final ValueChanged<String>? onChanged;
|
||||||
|
}
|
||||||
206
lib/Pages/Dialog/dynamic_dialog.dart
Normal file
206
lib/Pages/Dialog/dynamic_dialog.dart
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../Entities/dialog_type_enum.dart';
|
||||||
|
import '../../Services/theme_service.dart';
|
||||||
|
import 'dialog_action.dart';
|
||||||
|
import 'dialog_input_field.dart';
|
||||||
|
|
||||||
|
/// Erstellt einen neuen dynamischen Dialog
|
||||||
|
class DynamicDialog {
|
||||||
|
/// Erstellt eine neue Instanz dieser Klasse
|
||||||
|
DynamicDialog({
|
||||||
|
this.title,
|
||||||
|
this.icon,
|
||||||
|
this.content,
|
||||||
|
final List<DialogInputField>? inputFields,
|
||||||
|
final List<DialogAction>? actions,
|
||||||
|
this.backgroundColor,
|
||||||
|
this.borderRadius = 16,
|
||||||
|
this.barrierDismissible = true,
|
||||||
|
this.dialogType = DialogTypeEnum.info,
|
||||||
|
}) : inputFields = inputFields ?? const [],
|
||||||
|
actions = actions ?? [DialogAction(label: 'Schließen')];
|
||||||
|
|
||||||
|
/// Der Titel des Dialogs
|
||||||
|
final String? title;
|
||||||
|
|
||||||
|
/// Der Icon des Dialogs
|
||||||
|
final IconData? icon;
|
||||||
|
|
||||||
|
/// Der Inhalt des Dialogs
|
||||||
|
final Widget? content;
|
||||||
|
|
||||||
|
/// Die Hintergrundfarbe des Dialogs, Standard wenn nicht gesetzt
|
||||||
|
final Color? backgroundColor;
|
||||||
|
|
||||||
|
/// Der BorderRadius des Dialogs
|
||||||
|
final double borderRadius;
|
||||||
|
|
||||||
|
/// Ob der Dialog bei einem Klick auf die Barriere geschlossen werden kann
|
||||||
|
final bool barrierDismissible;
|
||||||
|
|
||||||
|
/// Die InputFelder des Dialogs
|
||||||
|
final List<DialogInputField> inputFields;
|
||||||
|
|
||||||
|
/// Die Aktionen des Dialogs
|
||||||
|
final List<DialogAction> actions;
|
||||||
|
|
||||||
|
/// Der Typ des Dialogs
|
||||||
|
final DialogTypeEnum dialogType;
|
||||||
|
|
||||||
|
Map<String, TextEditingController>? _controllers;
|
||||||
|
Map<String, FocusNode>? _focusNodes;
|
||||||
|
|
||||||
|
BuildContext? _dialogContext;
|
||||||
|
|
||||||
|
void _prepareControllers() {
|
||||||
|
_controllers = {
|
||||||
|
for (final field in inputFields)
|
||||||
|
field.id: TextEditingController(text: field.initialValue ?? ''),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void _prepareFocusNodes() {
|
||||||
|
_focusNodes = {for (final field in inputFields) field.id: FocusNode()};
|
||||||
|
}
|
||||||
|
|
||||||
|
void _disposeControllers() {
|
||||||
|
for (final TextEditingController controller in _controllers!.values) {
|
||||||
|
controller.dispose();
|
||||||
|
}
|
||||||
|
_controllers = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _disposeFocusNodes() {
|
||||||
|
for (final FocusNode node in _focusNodes!.values) {
|
||||||
|
node.dispose();
|
||||||
|
}
|
||||||
|
_focusNodes = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Zeigt den vorher zusammengebauten Dialog an
|
||||||
|
Future<void> show(final BuildContext context) async {
|
||||||
|
final ThemeData theme = Theme.of(context);
|
||||||
|
|
||||||
|
_prepareControllers();
|
||||||
|
_prepareFocusNodes();
|
||||||
|
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: barrierDismissible,
|
||||||
|
builder: (final BuildContext ctx) {
|
||||||
|
_dialogContext = ctx;
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
final DialogInputField? autoFocusField = inputFields
|
||||||
|
.where((final DialogInputField f) => f.autoFocus)
|
||||||
|
.cast<DialogInputField?>()
|
||||||
|
.firstWhere(
|
||||||
|
(final DialogInputField? f) => f != null,
|
||||||
|
orElse: () => null,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (autoFocusField != null) {
|
||||||
|
_focusNodes![autoFocusField.id]!.requestFocus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final DialogAction primaryAction = actions.firstWhere(
|
||||||
|
(final a) => a.isPrimary,
|
||||||
|
orElse: () => actions.first,
|
||||||
|
);
|
||||||
|
|
||||||
|
Color backgroundColor =
|
||||||
|
this.backgroundColor ?? theme.colorScheme.surface;
|
||||||
|
|
||||||
|
if (dialogType == DialogTypeEnum.error) {
|
||||||
|
backgroundColor = theme.colorScheme.errorContainer;
|
||||||
|
} else if (dialogType == DialogTypeEnum.success) {
|
||||||
|
backgroundColor = ThemeService.getSuccessColor(
|
||||||
|
brightness: theme.brightness,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AlertDialog(
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(borderRadius),
|
||||||
|
),
|
||||||
|
title: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (icon != null)
|
||||||
|
Icon(icon, size: 48, color: theme.colorScheme.primary),
|
||||||
|
if (title != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8),
|
||||||
|
child: Text(
|
||||||
|
title!,
|
||||||
|
style: theme.textTheme.titleLarge,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
?content,
|
||||||
|
...inputFields.map(
|
||||||
|
(final DialogInputField field) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 6),
|
||||||
|
child: TextField(
|
||||||
|
controller: _controllers![field.id],
|
||||||
|
focusNode: _focusNodes![field.id],
|
||||||
|
keyboardType: field.keyboardType,
|
||||||
|
obscureText: field.obscureText,
|
||||||
|
onChanged: field.onChanged,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: field.label,
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
isDense: true,
|
||||||
|
),
|
||||||
|
onSubmitted: (_) {
|
||||||
|
final Map<String, String> values = {
|
||||||
|
for (final entry in _controllers!.entries)
|
||||||
|
entry.key: entry.value.text,
|
||||||
|
};
|
||||||
|
|
||||||
|
close();
|
||||||
|
primaryAction.onPressed?.call(values);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: actions
|
||||||
|
.map(
|
||||||
|
(final action) => TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
final Map<String, String> values = {
|
||||||
|
for (final entry in _controllers!.entries)
|
||||||
|
entry.key: entry.value.text,
|
||||||
|
};
|
||||||
|
|
||||||
|
close();
|
||||||
|
action.onPressed?.call(values);
|
||||||
|
},
|
||||||
|
child: Text(action.label),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Schließt den dynamischen Dialog
|
||||||
|
void close() {
|
||||||
|
if (_dialogContext != null) {
|
||||||
|
Navigator.of(_dialogContext!).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposeControllers();
|
||||||
|
_disposeFocusNodes();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user