showDialog<T> method

Future<T?> showDialog<T>({
  1. required WidgetBuilder builder,
  2. bool barrierDismissible = true,
  3. double barrierScalar = 0.5,
  4. int width = 40,
  5. int height = 10,
})

Displays a modal dialog by adding it as a higher layer. Returns a Future that completes when the dialog is dismissed/completed.

Implementation

Future<T?> showDialog<T>({
  required WidgetBuilder builder,
  bool barrierDismissible = true,
  double barrierScalar = 0.5,
  int width = 40,
  int height = 10,
}) {
  final completer = Completer<T?>();

  late final PromptRunner<T> runner;

  final barrierWidget = Stack([
    Positioned(
      left: 0,
      top: 0,
      right: 0,
      bottom: 0,
      child: DimmingBarrier(scalar: barrierScalar),
    ),
    Positioned(
      left: 0,
      top: 0,
      right: 0,
      bottom: 0,
      child: ModalDismissBarrier(
        onDismiss: () {
          if (barrierDismissible) {
            runner.abort();
          }
        },
        child: const SizedBox.expand(),
      ),
    ),
  ]);

  final barrierRunner = PromptRunner<void>(
    terminal: terminal,
    widget: barrierWidget,
    alternateScreen: false,
    mode: ExecutionMode.managed,
    onFramePainted: (_) => render(),
  );

  final dialogWidget = Builder(builder: builder);

  final previousFocusedLayer = focusedLayer;

  runner = PromptRunner<T>(
    terminal: terminal,
    widget: dialogWidget,
    alternateScreen: false,
    mode: ExecutionMode.managed,
    onFramePainted: (_) => render(),
  );

  final baseZIndex = 1000 + layers.length * 2;

  final barrierLayer = SceneLayer(
    renderer: barrierRunner,
    sizing: LayerSizing.fullscreen,
    zIndex: baseZIndex,
  );

  final terminalSize = terminal.backend.size;
  final x = max(0, (terminalSize.x - width) ~/ 2);
  final y = max(0, (terminalSize.y - height) ~/ 2);

  late final SceneLayer dialogLayer;
  dialogLayer = SceneLayer(
    renderer: runner,
    sizing: LayerSizing.fixed,
    x: x,
    y: y,
    width: width,
    height: height,
    zIndex: baseZIndex + 1,
    onResize: (newSize) {
      dialogLayer.x = max(0, (newSize.x - width) ~/ 2);
      dialogLayer.y = max(0, (newSize.y - height) ~/ 2);
    },
  );

  layers.add(barrierLayer);
  layers.add(dialogLayer);
  focusedLayer = dialogLayer;
  render();

  barrierRunner.run();
  runner
      .run()
      .then((result) {
        completer.complete(result);
      })
      .catchError((err) {
        if (err is PromptAbortedException) {
          completer.complete(null);
        } else {
          completer.completeError(err);
        }
      })
      .whenComplete(() {
        layers.remove(dialogLayer);
        layers.remove(barrierLayer);
        runner.dispose();
        barrierRunner.dispose();
        if (focusedLayer == dialogLayer || focusedLayer == barrierLayer) {
          focusedLayer =
              previousFocusedLayer ??
              (layers.isNotEmpty ? layers.last : null);
        }
        render();
      });

  return completer.future;
}