搜索插件、代码仓与 APP 分类

interact 封面
插件详情v2.2.0

interact

一组可自定义的交互式命令行组件。

macOSWindowsLinux
近 30 天下载
37万
喜欢数
95
Pub 分数
140

安装插件

flutter pub add interact

插件介绍

基于插件公开信息、社区数据与项目使用情况整理的选型参考。

Interact 是一个 Flutter/Dart 包,提供了一组可自定义的交互式命令行组件,用于构建功能丰富、用户友好的 CLI 应用程序。它使开发者能够轻松创建提示、菜单和输入表单,支持验证、默认值和实时反馈等功能。该包专为 macOS、Windows 和 Linux 上的跨平台使用而设计,可无缝集成到基于 Dart 的 CLI 工具中。通过简化交互式终端体验的创建过程,无需处理底层终端操作,该包显著提升了开发者的生产力。

适合场景

  • 构建交互式 CLI 工具
  • 创建设置向导
  • 开发命令行配置工具
  • 自动化用户输入工作流程
  • 通过动态提示增强CLI用户体验
  • 支持多步骤终端交互

主要特点

  • 可自定义的提示组件
  • 输入验证和错误处理
  • 跨平台兼容性
  • 实时用户反馈
  • 支持默认值和建议值
  • 与 Flutter/Dart CLI 应用的简单集成

适合对象

  • CLI 工具开发者
  • DevOps 自动化脚本
  • 交互式设置流程
  • 基于 Dart 的命令行应用程序
  • 寻求直观终端界面的用户

使用建议

  • 需要终端环境
  • 仅限基于控制台的交互
  • 不适用于GUI应用程序
  • 依赖于平台特定的终端行为
  • 可能需要额外的样式来适应复杂布局

原始 README

以下为关联源码仓库的 README 原文快照,最新内容请访问源码仓库。

Interact

A collection of customizable interactive command-line components.


Table of Contents


Overview

The library contains a bunch of command-line components that are easy to use and customizable, including text and password inputs, radio or select inputs, checkbox or multiple select inputs, spinners, and progress bars. Examples for all the available components can be found in the example folder, and the API Documentation section will cover all about them.

As an overview, you can make a Select component like this.

final languages = ['Rust', 'Dart', 'TypeScript'];
final selection = Select(
  prompt: 'Your favorite programming language',
  options: languages,
).interact();

print('${languages[selection]}');

It will result in something like this,


Installation

Install the latest version of interact as a dependency as shown in pub.dev.


API Documentation

Components

These are the snippets of components with their properties and arguments. Check the pub documentation to get to know more about them in detail.


Confirm Component

A confirm component asks the user for a simple yes or no and will return a boolean accordingly.

final answer = Confirm(
  prompt: 'Does it work?',
  defaultValue: true, // this is optional
  waitForNewLine: true, // optional and will be false by default
).interact();

If waitForNewLine is true, the prompt will wait for an Enter key from the user regardless of the answer.


Input Component

An input component asks the user for a string that could be validated.

final email = Input(
  prompt: 'Your email',
  defaultValue: '', // optional, will provide the user as a hint
  initialText: '', // optional, will be autofilled in the input
  validator: (String x) { // optional
    if (x.contains('@')) {
      return true;
    } else {
      throw ValidationError('Not a valid email');
    }
  },
).interact();

The message passed in the ValidationError exception will be shown as an error until the validator returns true.


Password Component

A password component behaves pretty much the same as an input component, but the user input will be hidden and by default, it has a repeat password validator that checks if two password inputs are the same or not.

final password = Password(
  prompt: 'Password',
  confirmation: true, // optional and will be false by default
  confirmPrompt: 'Repeat password', // optional
  confirmError: 'Passwords do not match' // optional
).interact();

Select Component

A select component asks the user to choose between the options supplied and the index of the chosen option will be returned.

final languages = ['Rust', 'Dart', 'TypeScript'];

final selection = Select(
  prompt: 'Your favorite programming language',
  options: languages,
  initialIndex: 2, // optional, will be 0 by default
).interact();

MultiSelect Component

A multi-select component asks the user for multiple options check by using the SpaceBar. Similarly, the multi-select component will return a list of selected indexes.

final answers = MultiSelect(
  prompt: 'Let me know your answers',
  options: ['A', 'B', 'C'],
  defaults: [false, true, false], // optional, will be all false by default
).interact();

Sort Component

A sort component asks the user to sort the given list of options and returns the list ordered by the user.

final sorted = Sort(
  prompt: 'Sort Tesla models from favorite to least',
  options: ['S', '3', 'X', 'Y'],
  showOutput: false, // optional, will be false by default
).interact();

Sometimes the list given can be massive, so setting the showOutput to false makes the list not be shown in the success prompt.


Spinner and MultiSpinner Components

A spinner will show a spinning indicator until the user calls it's done method. When it's done, it shows the icon given in place of the spinner.

final gift = Spinner(
  icon: '🏆',
  leftPrompt: (done) => '', // prompts are optional
  rightPrompt: (done) => done
      ? 'here is a trophy for being patient'
      : 'searching a thing for you',
).interact();

await Future.delayed(const Duration(seconds: 5));
gift.done();

Using multiple spinners at once is a common practice, but because of the way the library renders things, it's not possible to have multiple them normally. It will break the rendering process of all components.

final spinners = MultiSpinner();

final horse = spinners.add(Spinner(
  icon: '🐴',
  rightPrompt: (done) => done ? 'finished' : 'waiting',
)); // notice how you don't need to call the `.interact()` function

await Future.delayed(const Duration(seconds: 5));
horse.done();

Now you can have multiple of them without breaking things.


Progress and MultiProgress Components

A progress component shows a progress bar.

final progress = Progress(
  length: 1000, // required, the length of the progress to use
  size: 0.5, // optional, will be 1 by default
  rightPrompt: (current) => ' ${current.toString().padLeft(3)}/$length',
).interact();

for (var i = 0; i < 500; i++) {
  await Future.delayed(const Duration(milliseconds: 5));
  progress.increase(2);
}

progress.done();

The size is the multiplier for the rendered progress bar and it will be 1 by default. 0.5 means the rendered progress bar will be half the width of the terminal.

A MultiProgress does pretty much the same as what MultiSpinner did for spinners.

final bars = MultiProgress();

final p1 = bars.add(Progress(
  length: 100,
  rightPrompt: (current) => ' ${current.toString().padLeft(3)}/$length',
)); // notice how you don't need to call the `.interact()` function

for (var i = 0; i < 500; i++) {
  await Future.delayed(const Duration(milliseconds: 5));
  p1.increase(2);
}

p1.done();

Customizing Themes

Because most of the visually rendered parts come from the theme object which is available for all components, you can customize a lot of them by changing the theme. Changing a theme for a component can be done by using the withTheme constructor.

final progress = Progress.withTheme(
  theme: Theme(),
  length: length,
  rightPrompt: (current) => ' ${current.toString().padLeft(3)}/$length',
).interact();

The components used the Theme.defaultTheme as the theme by default. The Theme object has two premade themes,

  • Theme.colorfulTheme which is made with colorful ASCII/emojis
  • Theme.basicTheme which is mostly text characters and without colors

and the Theme.defaultTheme is the colorful theme by default.

Because constructing a theme from scrap requires you to write a lot of properties, it might be easier to extend existing themes to create a new one which can be done using the copyWith method.

import 'package:tint/tint.dart'; // for extension methods
// ...
Theme customTheme = Theme.colorfulTheme.copyWith(
  activeItemPrefix: '👉'
  activeItemStyle: (x) => x.yellow().underline(),
);

Technically, you can also override Theme.defaultTheme as a shortcut.

Handling Exceptions

If your program throw exceptions and exit midway, interact's components won't be able to finish their tasks and gracefully quit therefore causing certain problems like cursors not showing up, terminal colors got modified etc. To fix these problems you should always try to catch exceptions and reset to terminal defaults using reset function.

try {
  Spinner(icon: '🚨').interact();
  throw Exception(); // spinner couldn't finished
} catch (e) {
  reset(); // Reset everything to terminal defaults
  rethrow;
}

Acknowledgement

This library is mostly inspired by dialouger library from Rust. The lack of a properly maintained library for Dart with a well-made API is what pushed me into making this library.

所属分类

依赖

依赖清单 3 项

源码仓库

使用此插件的项目