Make your CLI program interactive
In this chapter, you'll get hands-on practice with Dart syntax. You'll learn how to read user input, print usage information, and create a basic command-line interaction.
Prerequisites
#Before you begin this chapter, ensure you have:
- Completed Chapter 1 and have a working Dart development environment.
- Familiarity with basic programming concepts (variables, data types, control flow).
Tasks
#Add some basic functionality to your Dartpedia command-line application and then explore the Dart syntax for it.
Task 1: Implement version and help commands
#-
Implement the
versioncommand incli/bin/cli.dart: Add logic to handle aversioncommand, which prints the current version of the CLI. Use anifstatement to check if the first argument provided isversion. You'll also need aversionconstant.First, above your
mainfunction, declare aconstvariable for the version. The value of aconstvariable can never be changed after it's been set:dartconst version = '0.0.1'; // Add this lineNext, modify your
mainfunction to check for theversionargument:dartvoid main(List<String> arguments) { if (arguments.isEmpty) { print('Hello, Dart!'); } else if (arguments.first == 'version') { print('Dartpedia CLI version $version'); } }The
$versionsyntax is called string interpolation. It lets you embed the value of the variable directly into a string by prefixing the variable name with a$sign. -
Test the
versioncommand: Run your application with the version argument:bashdart bin/cli.dart versionYou should now see:
bashDartpedia CLI version 0.0.1If you run your app without arguments, you'll still see "Hello, Dart!".
-
Add a
printUsagefunction: To make the output more user-friendly, create a separate function to display usage information. Place this function outside and below yourmainfunction.dartvoid printUsage() { // Add this new function print( "The following commands are valid: 'help', 'version', 'search <ARTICLE-TITLE>'" ); }searchis the command that will eventually search from Wikipedia. -
Implement the
helpcommand and refinemain: Now, integrate thehelpcommand using anelse ifstatement, and clean up the default behavior to call theprintUsagefunction.Modify your
mainfunction to look like this:dartvoid main(List<String> arguments) { if (arguments.isEmpty || arguments.first == 'help') { printUsage(); // Change this from 'Hello, Dart!' } else if (arguments.first == 'version') { print('Dartpedia CLI version $version'); } else { printUsage(); // Catch all for any unrecognized command. } } -
Understand the
if/elsestructure and variables: Now that you've implemented control flow in themainfunction, review the code that was added for it.arguments.isEmptychecks if no command-line arguments were provided.arguments.firstaccesses the very first argument, which you're using as our command.versionis declared as aconst. This means its value is known at compile time and you can't change it during runtime.argumentsis a regular (non-constant) variable because its content can change during runtime based on user input.
Run your application with the help argument. You should see the usage information printed:
bashdart bin/cli.dart helpAlso, try running it without any arguments:
bashdart bin/cli.dartNotice that it continues to display usage information. At this point, any command you haven't defined will also print usage information. This is expected behavior for now.
Task 2: Implement the search command
#
Next, implement a basic search command that takes an article title as
input. As you build this functionality, you'll work with List manipulation,
null checks, and string interpolation.
-
Integrate the
searchcommand intomain: First, modify themainfunction incli/bin/cli.dartto include anelse ifbranch that handles thesearchcommand. For now, just print a placeholder message.dartvoid main(List<String> arguments) { if (arguments.isEmpty || arguments.first == 'help') { printUsage(); } else if (arguments.first == 'version') { print('Dartpedia CLI version $version'); } else if (arguments.first == 'search') { // Add this new block: print('Search command recognized!'); } else { printUsage(); } } -
Test the new command: Run your application with the
searchcommand:bashdart bin/cli.dart searchYou should see:
bashSearch command recognized! -
Define the
searchWikipediafunction: Thesearchcommand will eventually run the core logic of your application by calling a function calledsearchWikipedia. For now, havesearchWikipediaprint the arguments passed into it with thesearchcommand. Place this new function belowmain.dart// ... (your existing main function) void searchWikipedia(List<String>? arguments) { // Add this new function and add ? to arguments type print('searchWikipedia received arguments: $arguments'); } // ... (your existing printUsage() function)Highlights from the preceding code:
List<String>? argumentsmeans that theargumentslist itself can benull.:::note Dart enforces sound null safety, which means you have to explicity state when a variable can be null. Any variable that isn't marked as nullable is guarateed to never be null, even in production. The purpose of null-safety isn't to stop you from ever using null in your code, because representing the absense of a value can be valuable. Rather, it's to force you to consider nullability and therefore be more careful about it. Along with the analyzer, this helps prevent one of the most common runtime crashes in programming: null-pointer errors. :::
-
Call the
searchWikipediafunction from themainfunction: Now, modify thesearchcommand block inmainto callsearchWikipediaand pass it any arguments that come after thesearchcommand itself. Usearguments.sublist(1)to get all arguments starting from the second one. If no arguments are provided aftersearch, passnulltosearchWikipedia.dartvoid main(List<String> arguments) { if (arguments.isEmpty || arguments.first == 'help') { printUsage(); } else if (arguments.first == 'version') { print('Dartpedia CLI version $version'); } else if (arguments.first == 'search') { // Add this new block: final inputArgs = arguments.length > 1 ? arguments.sublist(1) : null; searchWikipedia(inputArgs); } else { printUsage(); } }Highlights from the preceding code:
finalvariables can only be set once and are used when you never intend to change the variable again in the code.arguments.sublist(1)creates a new list containing all elements of theargumentslist after the first element (which wassearch).arguments.length > 1 ? ... : null;is a conditional (ternary) operator. It ensures that if no arguments are provided after thesearchcommand,inputArgsbecomesnull, matching the sample code's behavior forsearchWikipedia'sargumentsparameter ofList<String>?.
-
Test
searchWikipediawith arguments: Using the command line, run the application with a test article title:bashdart bin/cli.dart search Dart ProgrammingYou should see:
bashsearchWikipedia received arguments: [Dart, Programming]Next, run the same command without the extra arguments:
bashdart bin/cli.dart searchYou should see:
bashsearchWikipedia received arguments: null -
Handle the missing article title and user input with the
stdincommand: It's more user-friendly to prompt the user if they don't provide an article title on the command line. Usestdin.readLineSync()for this.First, add the necessary import at the top of your
cli/bin/cli.dartfile:dartimport 'dart:io'; // Add this line at the topdart:iois core library in the Dart SDK, and provides APIs to deal with files, directories, sockets, and HTTP clients and servers, and more.Now, update your
searchWikipediafunction.dartvoid searchWikipedia(List<String>? arguments) { final String articleTitle; // If the user didn't pass in arguments, request an article title. if (arguments == null || arguments.isEmpty) { print('Please provide an article title.'); // Await input and provide a default empty string if the input is null. articleTitle = stdin.readLineSync() ?? ''; } else { // Otherwise, join the arguments into the CLI into a single string articleTitle = arguments.join(' '); } print('Current article title: $articleTitle'); }This preceding code block introduces a few key concepts:
- It declares a
final String articleTitlevariable. This allows static analysis to detect thatarticleTitlewill be aStringand won't be null. - An
if/elsestatement then checks if command-line arguments for the search were provided. - If arguments are missing, it prompts the user, reads input using
stdin.readLineSync(), and safely handles cases where no input is given. - If arguments are present, it uses
arguments.join(' ')to combine them into a single search string.
Highlights from the preceding code:
stdin.readLineSync() ?? ''reads the input from the user. Whilestdin.readLineSync()can return null, the null-coalescing operator (??) provides a default empty string (''). This is a concise way to ensure that the variable is a non-null String.arguments.join(' '): concatenates all elements of theargumentslist into a single string, using a space as the separator. For example,['Dart', 'Programming']becomes"Dart Programming". This is crucial for treating multi-word command-line inputs as a single search phrase.- Dart static analysis can detect that
articleTitleis guaranteed to be initialized when the print statement is executed. No matter which path is taken through this function body, the variable is non-nullable.
- It declares a
-
Finish
searchWikipediato print mock search results: UpdatesearchWikipediato display messages that look like our program found something. This helps us see what our finished program will do without actually building everything right now. You'll only see these messages if you include a search query when you run the program.For example:
dart bin/cli.dart search Dart Programming.dartvoid searchWikipedia(List<String>? arguments) { final String articleTitle; // If the user didn't pass in arguments, request an article title. if (arguments == null || arguments.isEmpty) { print('Please provide an article title.'); // Await input and provide a default empty string if the input is null. articleTitle = stdin.readLineSync() ?? ''; } else { // Otherwise, join the arguments into the CLI into a single string articleTitle = arguments.join(' '); } print('Looking up articles about "$articleTitle". Please wait.'); print('Here ya go!'); print('(Pretend this is an article about "$articleTitle")'); } -
Final Test Run with both scenarios:
Now that the article simulation is set up, test the
searchWikipediafunction in a few different ways:bashdart bin/cli.dart search Dart ProgrammingYou should see:
bashLooking up articles about "Dart Programming". Please wait. Here ya go! (Pretend this is an article about "Dart Programming")Run without arguments (type "Flutter Framework" when prompted):
bashdart bin/cli.dart searchbashPlease provide an article title. Flutter FrameworkYou have now successfully built the basic
searchcommand with user input handling, correctly treating multi-word command-line inputs as a single search phrase in the output.
Review
#In this chapter, you learned:
-
Control flow: Using
if/elsestatements to control the execution flow of your program. -
Variables and Constants: Declaring variables with
var,const, andfinal String. -
Lists: Creating and manipulating lists using
.isEmpty,.first,.sublist, and.join(). -
Null Safety: Understanding nullability (
?) and using null checks. Handling potential null values with the null-coalescing operator (??) to provide default values. - Functions: Defining and calling functions.
- String interpolation: Embedding variables in strings using
$. -
Input/Output: Reading user input from the console using
stdin.readLineSync().
Quiz
#Question 1: Which keyword is used to declare a constant variable in Dart whose value is known at compile time?
- A)
var - B)
final - C)
const - D)
static
Question 2: What is the primary purpose of stdin.readLineSync() in a CLI application?
- A) To print output to the console.
- B) To read a single line of text input from the user.
- C) To execute a command.
- D) To check if a file exists.
Next lesson
#
In the next chapter, you'll dive into asynchronous programming and learn how to
fetch data from the Wikipedia API using the http package. This will allow your
application to retrieve real data and display it to the user.
Unless stated otherwise, the documentation on this site reflects Dart 3.9.2. Page last updated on 2025-9-15. View source or report an issue.