showTaskLoader<T> method

Future<T?> showTaskLoader<T>({
  1. required Future<T?> task(),
  2. Duration? timeout,
  3. dynamic cancelTaskButton,
  4. dynamic loadingText,
  5. dynamic yesButton,
  6. dynamic noButton,
  7. dynamic cancelConfirmationText,
  8. void onError(
    1. Object?
    )?,
  9. void onCancel()?,
  10. Widget? remainTimeWidget(
    1. Duration
    )?,
})

Shows a task loader dialog and executes a task asynchronously.

The showTaskLoader function displays a dialog with a circular progress indicator and an optional loading text. It also provides an option to cancel the task by showing a cancel button.

The task parameter is a function that represents the task to be executed. It should return a future that resolves to a value of type T.

The cancelTaskButton parameter is the text to be displayed on the cancel button. It can be of any type and will be converted to a string.

The loadingText parameter is the text to be displayed below the progress indicator. It can be of any type and will be converted to a string.

The yesButton parameter is the text to be displayed on the OK button. It can be of any type and will be converted to a string.

The noButton parameter is the text to be displayed on the NO button. It can be of any type and will be converted to a string.

The cancelConfirmationText parameter is an optional message to be displayed before canceling the task. If provided, the user will be prompted to confirm the cancellation.

The onError parameter is a callback function that is invoked when an error occurs during the task execution.

The function returns a future that resolves to the result of the task execution, or null if the task was canceled.

Implementation

Future<T?> showTaskLoader<T>({
  required Future<T?> Function() task,
  Duration? timeout,
  dynamic cancelTaskButton,
  dynamic loadingText,
  dynamic yesButton,
  dynamic noButton,
  dynamic cancelConfirmationText,
  void Function(Object?)? onError,
  void Function()? onCancel,
  Widget? Function(Duration)? remainTimeWidget,
}) async {
  late CancelableOperation<T?> operation;
  Duration? remainTime = timeout;
  Timer? timer;
  var alertKey = GlobalKey();
  showGeneralDialog(
    context: this,
    barrierDismissible: false,
    pageBuilder: (BuildContext context, animation, secondaryAnimation) => PopScope(
      canPop: onCancel != null,
      onPopInvokedWithResult: (didPop, _) async {
        if (didPop) await operation.cancel();
      },
      child: StatefulBuilder(builder: (BuildContext context, setState) {
        if (cancelTaskButton == null && onCancel != null) {
          cancelTaskButton = context.translations.cancel;
        }
        if (timeout != null && timeout.inSeconds > 0) {
          timer ??= Timer.periodic(1.seconds, (timer) {
            if (remainTime != null) {
              remainTime = remainTime! - 1.seconds;
              if (remainTime!.inSeconds <= 0) {
                timer.cancel();
                operation.cancel();
              } else {
                if (context.mounted) setState(() {});
              }
            }
          });
        }
        return AlertDialog.adaptive(
          key: alertKey,
          actions: [
            if (cancelTaskButton != null) ...[
              ElevatedButton(
                onPressed: () async {
                  if (cancelConfirmationText == null || await context.confirm(cancelConfirmationText, textCancel: noButton, textOK: yesButton)) {
                    await operation.cancel();
                  }
                },
                child: forceWidget(cancelTaskButton),
              ),
            ],
          ],
          content: Padding(
            padding: const EdgeInsets.all(12.0),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                remainTime != null && remainTime!.inSeconds > 0
                    ? CircularProgressIndicator(
                        value: 1 - (remainTime!.inSeconds / timeout!.inSeconds),
                        backgroundColor: Colors.grey.withValues(alpha: .1),
                      )
                    : const CircularProgressIndicator(),
                if (loadingText != null) forceWidget(loadingText)!,
                if (remainTimeWidget != null && remainTime != null && remainTime!.inSeconds > 0) forceWidget(remainTimeWidget(remainTime!))!,
              ].nonNulls.toList()
                ..insertBetween(const Gap(20)),
            ),
          ),
        );
      }),
    ),
  );
  operation = CancelableOperation.fromFuture(() async {
    try {
      var result = await task();
      if (alertKey.currentContext != null) {
        pop(result);
      }
      return result;
    } catch (e) {
      if (onError != null) {
        if (alertKey.currentContext != null) {
          pop(null);
        }
        onError(e); // Call onError function if provided
      }
    }
    return null;
  }(), onCancel: onCancel);
  return await operation.valueOrCancellation();
}