run method
Starts the inline prompt loop and returns a Future containing the final result.
Implementation
Future<T?> run() async {
final termSize = await terminal.size;
final width = termSize.x;
// Autosize the height dynamically if not explicitly specified.
final computedHeight = height ?? widget.getIntrinsicHeight(width);
// Create a temporary buffer and inline renderer.
final buffer = Buffer.blank(width, computedHeight);
final renderer = Renderer(
width,
computedHeight,
mode: RenderingMode.inline,
);
_completer = Completer<T?>();
_isDisposed = false;
// Wrap the widget tree in a PromptScope to expose the clean completion API
final scopedWidget = PromptScope(
onDone: (result) {
final comp = _completer;
if (comp != null && !comp.isCompleted) {
comp.complete(result as T?);
}
},
child: widget,
);
// Mount the widget tree element persistently so states and focus configuration
// are retained between keyboard event loop iterations.
final rootElement = scopedWidget.createElement()..mount(null);
_rootElement = rootElement;
void draw() {
if (_isDisposed) return;
// Rebuild the element tree to consume any new state modifications.
rootElement.rebuild();
buffer.clear();
// Render the rebuilt element tree on our double buffer canvas.
rootElement.render(buffer, Rect(0, 0, width, computedHeight));
final sb = StringBuffer();
renderer.render(buffer, sb);
if (sb.isNotEmpty) {
terminal.backend.write(sb.toString());
}
}
// Connect the static repainter callback so that inner setState() invocations
// (e.g. from typing inside a TextField) trigger screen updates.
State.onNeedRepaint = draw;
// Initial frame draw
draw();
final subscription = terminal.events.listen(
(event) {
if (_completer!.isCompleted) return;
if (event is term.KeyEvent) {
var isDone = false;
// Step 1: Custom Interceptor
if (onKeyEvent != null) {
isDone = onKeyEvent!(event);
}
// Step 2: Widget Event Routing
if (!isDone) {
isDone = _routeKeyEvent(rootElement, event);
}
// Step 3: Standard & System Exit Evaluation
if (!isDone) {
final trigger = _detectTrigger(event);
if (trigger != null && exitConditions.containsKey(trigger)) {
_handleAction(trigger, event);
return;
}
}
// Force a redraw to reflect any selections or edits.
draw();
}
},
onError: (e, stack) {
if (!_completer!.isCompleted) {
_completer!.completeError(e, stack);
}
},
);
try {
return await _completer!.future;
} finally {
_isDisposed = true;
await subscription.cancel();
// Restore cursor visibility
terminal.showCursor();
// Reset the repaint handler to avoid leaks.
State.onNeedRepaint = null;
_rootElement?.unmount();
}
}