Organizing Dart code with packages and libraries
In this chapter, you'll level up from basic Dart syntax to building command-line applications "the Dart way," embracing best practices. You'll learn to refactor your code into reusable components by creating a dedicated package for handling command-line arguments. This step sets you up for building a more advanced command-line application in future chapters, which will integrate specialized packages for Wikipedia logic and a robust command_runner
framework. This chapter helps you understand Dart libraries, export statements, and how to structure your project for better organization and maintainability.
Prerequisites
#- Completion of Chapter 3, which covered asynchronous programming and HTTP requests.
Tasks
#In this chapter, you'll be refactoring the existing dartpedia
CLI application by extracting the command-line argument parsing logic into a separate package called command_runner
. This will improve the structure of your project, making it more modular and maintainable.
Task 1: Create the command_runner package
#First, create a new Dart package to house the command-line argument parsing logic.
Navigate to the root directory of your project (
/dartpedia
).Run the following command in your terminal:
bashdart create -t package command_runner
This command creates a new directory named
command_runner
with the basic structure of a Dart package. You should now see a new foldercommand_runner
in your project root, alongsidecli
.
Task 2: Implement the CommandRunner class
#Now that you have created the command_runner
package, add a placeholder class that will eventually handle the command-line argument parsing logic.
Open the
command_runner/lib/command_runner.dart
file. Remove any existing placeholder code and add the following:dart/// A simple command runner to handle command-line arguments. /// /// More extensive documentation for this library goes here. library; export 'src/command_runner_base.dart'; // TODO: Export any other libraries intended for clients of this package.
Highlights from the preceding code:
library;
declares this file as a library, which helps define the boundaries and public interface of a reusable unit of Dart code.export 'src/command_runner_base.dart';
is a crucial line that makes declarations fromcommand_runner_base.dart
available to other packages that import thecommand_runner
package. Without thisexport
statement, the classes and functions withincommand_runner_base.dart
would be private to thecommand_runner
package, and you wouldn't be able to use them in yourdartpedia
application.
Create the file
command_runner/lib/src/command_runner_base.dart
.Add the following
CommandRunner
class tocommand_runner/lib/src/command_runner_base.dart
:dartclass CommandRunner { /// Runs the command-line application logic with the given arguments. Future<void> run(List<String> input) async { print('CommandRunner received arguments: $input'); } }
Highlights from the preceding code:
CommandRunner
is a class that serves as a simplified stand-in for now. Itsrun
method currently just prints the arguments it receives. In later chapters, you'll expand this class to handle complex command parsing.Future<void>
is a return type that indicates that this method might perform asynchronous operations, but doesn't return a value.
Task 3: Add command_runner
as a dependency
#Now that you've created the command_runner
package and added a placeholder CommandRunner
class, you need to tell your cli
application that it depends on command_runner
. Because the command_runner
package is located locally within your project, use the path
dependency option.
Open the
cli/pubspec.yaml
file.Locate the
dependencies
section. Add the following lines:yamldependencies: http: ^1.3.0 # Keep your existing http dependency command_runner: path: ../command_runner # Points to your local command_runner package
This section tells the
cli
application to depend on thecommand_runner
package, and specifies that the package is located in the../command_runner
directory (relative to thecli
directory).Run
dart pub get
in thecli
directory of your terminal to fetch the new dependency.
Task 4: Import and use the command_runner
package
#Now that you've added command_runner
as a dependency, you can import it into your cli
application and replace your existing argument-handling logic with the new CommandRunner
class. This step also fixes the program exit behavior discussed at the end of Chapter 3.
Open the
cli/bin/cli.dart
file.Add the following import statement at the top of the file, alongside your other imports:
dartimport 'package:command_runner/command_runner.dart';
This statement imports the
command_runner
package, making theCommandRunner
class available for use.Refactor the
main
function and remove old logic: Currently, yourmain
function from Chapter 3 directly handles commands likeversion
,help
, andwikipedia
, and then callssearchWikipedia
. You'll now replace all of this custom command-handling logic with a single call to the newCommandRunner
class.Your
cli/bin/cli.dart
file (from Chapter 3) should currently look like this:dartimport 'dart:io'; import 'package:http/http.dart' as http; const version = '0.0.1'; void 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 == 'wikipedia') { final inputArgs = arguments.length > 1 ? arguments.sublist(1) : null; searchWikipedia(inputArgs); } else { printUsage(); } } void searchWikipedia(List<String>? arguments) async { /* ... existing logic ... */ } void printUsage() { /* ... existing logic ... */ } Future<String> getWikipediaArticle(String articleTitle) async { /* ... existing logic ... */ }
Now, replace the entire contents of
cli/bin/cli.dart
(except for thehttp
import) with the following updated version:dartimport 'package:http/http.dart' as http; // Keep this import import 'package:command_runner/command_runner.dart'; // Add this new import void main(List<String> arguments) async { // main is now async and awaits the runner var runner = CommandRunner(); // Create an instance of your new CommandRunner await runner.run(arguments); // Call its run method, awaiting its Future<void> }
Highlights from the preceding code:
void main(List<String> arguments) async
directly addresses the program not exiting cleanly issue from Chapter 3. Notice thatmain
is now declaredasync
. This is essential becauserunner.run()
returns aFuture
, andmain
mustawait
its completion to ensure the program waits for all asynchronous tasks to finish before exiting.var runner = CommandRunner();
creates an instance of theCommandRunner
class from your newcommand_runner
package.await runner.run(arguments);
calls therun
method on theCommandRunner
instance, passing in the command-line arguments.
Removed Functions:
The
printUsage
,searchWikipedia
, andgetWikipediaArticle
functions are now completely removed fromcli/bin/cli.dart
. Their logic will be redesigned and moved into thecommand_runner
package in future chapters, as part of building the full command-line framework.
Task 5: Run the application
#Now that you've refactored the code and updated the cli
application to use the command_runner
package, run the application to verify that everything is working correctly at this stage.
Open your terminal and navigate to the
cli
directory.Run the
wikipedia
command:bashdart run bin/cli.dart wikipedia Dart
Ensure that the application now executes without errors and print the arguments to the console, demonstrating that the control has successfully transferred to your new
command_runner
package.bashCommandRunner received arguments: [wikipedia, Dart]
Review
#In this chapter, you learned about:
- Creating Dart packages using
dart create -t package
. - Using
export
statements to make declarations from one library available in another. - Adding local packages as dependencies using the
path
option inpubspec.yaml
. - Importing packages into your Dart code using
import
statements. - Refactoring code to improve organization and maintainability, including making
main
async
to correctlyawait
asynchronous operations.
Quiz
#Question 1: What is the purpose of the export
statement in a Dart library?
- A) To hide declarations from other libraries.
- B) To make declarations available to other libraries.
- C) To specify the version of the Dart SDK required by the library.
- D) To define the entry point of the library.
Question 2: How do you add a local package as a dependency in pubspec.yaml
?
- A) By specifying the package name and version.
- B) By specifying the package name and the path to the package.
- C) By using the
git
option and specifying the URL of the Git repository. - D) By using the
hosted
option and specifying the URL of the package server.
Next lesson
#In the next chapter, you'll dive into object-oriented programming (OOP) concepts in Dart. You'll learn how to create classes, define inheritance relationships, and build a more robust command-line argument parsing framework using OOP principles within your new command_runner
package.
Unless stated otherwise, the documentation on this site reflects Dart 3.8.1. Page last updated on 2025-06-19. View source or report an issue.