showWalletConfirm function

Future<bool> showWalletConfirm(
  1. BuildContext context, {
  2. required String title,
  3. required String body,
  4. required String confirmLabel,
  5. String? cancelLabel,
  6. required WalletConfirmKind kind,
  7. Key? confirmKey,
  8. Key? cancelKey,
})

Show a confirm dialog and return true ONLY when the user tapped confirmLabel. Cancel, a barrier tap, a back pop and a route removed from under it are all false, never null, so no caller can read a dismissal as consent.

Adaptive: a CupertinoAlertDialog on iOS and macOS (whose barrier is inert, the native and safer default), an AlertDialog elsewhere. The Material branch always scrolls, so a long warning at a large text size keeps its actions reachable on a small phone.

cancelLabel is required for every kind but WalletConfirmKind.info, whose one action is confirmLabel. confirmKey and cancelKey identify the actions for a test.

It unfocuses the primary focus first: a dialog opened over a focused field otherwise leaves the keyboard up behind it, and brings it back after Cancel.

Implementation

Future<bool> showWalletConfirm(
  BuildContext context, {
  required String title,
  required String body,
  required String confirmLabel,
  String? cancelLabel,
  required WalletConfirmKind kind,
  Key? confirmKey,
  Key? cancelKey,
}) async {
  assert(
    kind == WalletConfirmKind.info || cancelLabel != null,
    'a confirm dialog needs a Cancel label',
  );
  FocusManager.instance.primaryFocus?.unfocus();
  final result = await showAdaptiveDialog<bool>(
    context: context,
    builder: (dialogContext) {
      final platform = Theme.of(dialogContext).platform;
      final cupertino =
          platform == TargetPlatform.iOS || platform == TargetPlatform.macOS;
      void pop(bool value) => Navigator.of(dialogContext).pop(value);
      // The two dialogs are built directly rather than through
      // `AlertDialog.adaptive`, which does the same platform switch but
      // builds a private `AlertDialog` subtype: a test's
      // `find.byType(AlertDialog)` would stop finding the Material one.
      if (cupertino) {
        // Cupertino content always scrolls.
        return CupertinoAlertDialog(
          title: Text(title),
          content: Text(body),
          actions: _cupertinoActions(
            kind,
            confirmLabel: confirmLabel,
            cancelLabel: cancelLabel,
            confirmKey: confirmKey,
            cancelKey: cancelKey,
            pop: pop,
          ),
        );
      }
      return AlertDialog(
        title: Text(title),
        content: Text(body),
        // ALWAYS, not per site: the security review's M4 found a fund-loss
        // warning that did not set it.
        scrollable: true,
        actions: _materialActions(
          dialogContext,
          kind,
          confirmLabel: confirmLabel,
          cancelLabel: cancelLabel,
          confirmKey: confirmKey,
          cancelKey: cancelKey,
          pop: pop,
        ),
      );
    },
  );
  return result ?? false;
}