uaestats_fe/lib/presentation/components/dialogs.dart
2026-05-16 11:31:32 +05:30

187 lines
5.7 KiB
Dart
Executable File

import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
extension Dialogs on BuildContext {
Future<BuildContext> _pushLoaderDialog() {
final ctxWaiter = Completer<BuildContext>();
showDialog(
barrierDismissible: false,
context: this,
builder: (loaderContext) {
if (!ctxWaiter.isCompleted) ctxWaiter.complete(loaderContext);
return Dialog(
child: LinearProgressIndicator(),
);
},
);
return ctxWaiter.future;
}
/// For an async or other function, shows a
/// loading dialog as the computation processes.
///
/// If successful, the loading dialog is dismissed
/// and the value is returned.
///
/// If there is an error, an error dialog replaces
/// the loading dialog. Upon dismissal of this
/// error dialog, the error is rethrown.
///
/// A generic error dialog is shown if
/// [errorDialogBuilder] is null, or if the method
/// returns null.
///
/// If [errorDialogBuilder] is not given and [showLogBtn]
/// is, the errorDialog will have a 'Show logs' button
/// that shows logs in a new dialog when pressed.
///
/// If [errorDialogBuilder] is provided, an
/// [showLogBtn] is also true exception is thrown;
Future<T> loaderWithErrorDialog<T>(
FutureOr<T> Function() computation, {
Future<void> Function(
Object error, [
StackTrace stackTrace,
])? errorDialogBuilder,
}) async {
final loaderContext = await _pushLoaderDialog();
late final T value;
try {
value = await computation();
} catch (error, stackTrace) {
Future<bool> attemptParameterErrorBuilder() async {
if (errorDialogBuilder == null) return false;
await errorDialogBuilder(error, stackTrace);
return true;
}
Future<void> showGenericOrUserDefinedErrorDialog() async {
final didPEBSucceed = await attemptParameterErrorBuilder();
if (didPEBSucceed) return;
if (kDebugMode) {
return simpleDialog(
error: error,
stackTrace: stackTrace,
);
}
return simpleDialog();
}
if (!loaderContext.mounted) rethrow;
Navigator.of(
loaderContext,
rootNavigator: true,
).pop();
await showGenericOrUserDefinedErrorDialog();
rethrow;
}
if (loaderContext.mounted) {
Navigator.of(
loaderContext,
rootNavigator: true,
).pop();
}
return value;
}
Future<void> simpleDialog({
String title = 'An error occurred',
String content = 'Please try again later',
Widget? extraAction,
dynamic error,
StackTrace? stackTrace,
}) =>
showDialog(
context: this,
barrierDismissible: false,
builder: (context) {
final contentText = Text(content);
return AlertDialog(
title: Text(title,
style: TextStyle(
color: Color(0xFF898C81), fontWeight: FontWeight.w700)),
content: content.length > 300
? SingleChildScrollView(
child: Text(content,
style: TextStyle(color: Color(0xFF898C81))),
)
: Text(
content,
style: TextStyle(
color: Color(0xFF898C81)), // Set content text color
),
actions: [
TextButton(
onPressed: Navigator.of(
this,
rootNavigator: true,
).pop,
style: TextButton.styleFrom(
// backgroundColor: Color(0xFFAA8E83), // Background color
backgroundColor: Color(0xFFB68A34), // Background color
foregroundColor: Colors.white, // Font (text) color
side: BorderSide(
// color: Color(0xFF92722A)), // Border outline color
color: Color(0xFFB68A34)), // Border outline color
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(8), // Optional: Rounded corners
),
padding: EdgeInsets.symmetric(
horizontal: 16, vertical: 12), // Optional: Padding
),
child: const Text('Return'),
),
if (extraAction != null) extraAction,
if (error != null)
ElevatedButton(
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
simpleDialog(
title: 'Logs',
content: '$error\n\nStackTrace: $stackTrace',
);
},
child: const Text('Show Logs'),
),
],
);
},
);
Future<bool?> confirmationDialog(
String titleText,
String contentText, {
String cancelText = 'Cancel',
String confirmText = 'Confirm',
}) =>
showDialog<bool>(
context: this,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: Text(titleText),
content: Text(contentText),
actions: [
TextButton(
onPressed: () => Navigator.of(
this,
rootNavigator: true,
).pop(false),
child: Text(cancelText),
),
ElevatedButton(
onPressed: () => Navigator.of(
this,
rootNavigator: true,
).pop(true),
child: Text(
confirmText,
),
),
],
),
);
}