runCustom method
List<T>
runCustom({
- void beforeItems(
- FrameContext ctx
- required void renderItem(
- FrameContext ctx,
- T item,
- int absoluteIndex,
- bool isFocused,
- bool isSelected,
- void afterItems(
- FrameContext ctx
- KeyBindings? extraBindings,
- void onBeforeRender()?,
Runs the prompt with full customization hooks.
beforeItems: Content to render before the item list (e.g., search bar, summary).
renderItem: Called for each visible item.
afterItems: Content to render after the item list (e.g., footer, stats).
extraBindings: Additional key bindings to merge.
onBeforeRender: Called before each render (e.g., update nav.maxVisible).
Returns selected items on confirm, empty list on cancel.
Example:
final selected = prompt.runCustom(
beforeItems: (ctx) {
ctx.searchLine(queryInput.text, enabled: searchEnabled);
ctx.writeConnector();
},
renderItem: (ctx, item, index, focused, selected) {
ctx.checkboxItem(item.toString(), focused: focused, checked: selected);
},
afterItems: (ctx) {
if (items.isEmpty) ctx.emptyMessage('no matches');
},
);
Implementation
List<T> runCustom({
void Function(FrameContext ctx)? beforeItems,
required void Function(
FrameContext ctx,
T item,
int absoluteIndex,
bool isFocused,
bool isSelected,
) renderItem,
void Function(FrameContext ctx)? afterItems,
KeyBindings? extraBindings,
void Function()? onBeforeRender,
}) {
if (items.isEmpty) return [];
_initState();
if (_pendingExtraBindings != null) {
_bindings = _bindings + _pendingExtraBindings!;
}
if (extraBindings != null) {
_bindings = _bindings + extraBindings;
}
final frame = FrameView(
title: title,
theme: theme,
bindings: _bindings,
);
void render(RenderOutput out) {
// Hook for pre-render updates
onBeforeRender?.call();
// Responsive viewport adjustment
_nav.maxVisible =
(TerminalInfo.rows - reservedLines).clamp(5, maxVisible);
frame.render(out, (ctx) {
// Before items hook
beforeItems?.call(ctx);
// Get visible window
final window = _nav.visibleWindow(items);
// Overflow above indicator
if (window.hasOverflowAbove) {
ctx.overflowIndicator();
}
// Render visible items
for (var i = 0; i < window.items.length; i++) {
final absoluteIndex = window.start + i;
final isFocused = _nav.isSelected(absoluteIndex);
final isSelected = _selection.isSelected(absoluteIndex);
renderItem(
ctx, window.items[i], absoluteIndex, isFocused, isSelected);
}
// Overflow below indicator
if (window.hasOverflowBelow) {
ctx.overflowIndicator();
}
// After items hook
afterItems?.call(ctx);
});
}
final runner = PromptRunner(hideCursor: true);
final result = runner.runWithBindings(
render: render,
bindings: _bindings,
);
if (_cancelled || result == PromptResult.cancelled) {
return [];
}
return _selection.getSelectedMany(
items,
fallbackIndex: _nav.selectedIndex,
);
}