187 lines
5.7 KiB
Dart
Executable File
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,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|