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_runnerThis command creates a new directory named
command_runnerwith the basic structure of a Dart package. You should now see a new foldercommand_runnerin 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.dartfile. 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.dartavailable to other packages that import thecommand_runnerpackage. Without thisexportstatement, the classes and functions withincommand_runner_base.dartwould be private to thecommand_runnerpackage, and you wouldn't be able to use them in yourdartpediaapplication.
Open the file
command_runner/lib/src/command_runner_base.dart.-
Remove any existing placeholder code and add the following
CommandRunnerclass 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:
CommandRunneris a class that serves as a simplified stand-in for now. Itsrunmethod 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.yamlfile.-
Locate the
dependenciessection. 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 packageThis section tells the
cliapplication to depend on thecommand_runnerpackage, and specifies that the package is located in the../command_runnerdirectory (relative to theclidirectory). -
Run
dart pub getin the/dartpedia/clidirectory 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.dartfile.-
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_runnerpackage, making theCommandRunnerclass available for use. -
Refactor the
mainfunction and remove old logic: Currently, yourmainfunction 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 newCommandRunnerclass.Your
cli/bin/cli.dartfile (from Chapter 3) should currently look like this:dartimport 'dart:io'; import 'package:http/http.dart' as http; import 'package:command_runner/command_runner.dart'; 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 thehttpimport) with the following updated version:dartimport 'dart:io'; import 'package:http/http.dart' as http; import 'package:command_runner/command_runner.dart'; 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) asyncdirectly addresses the program not exiting cleanly issue from Chapter 3. Notice thatmainis now declaredasync. This is essential becauserunner.run()returns aFuture, andmainmustawaitits completion to ensure the program waits for all asynchronous tasks to finish before exiting.var runner = CommandRunner();creates an instance of theCommandRunnerclass from your newcommand_runnerpackage.await runner.run(arguments);calls therunmethod on theCommandRunnerinstance, passing in the command-line arguments.
Removed Functions:
The
printUsage,searchWikipedia, andgetWikipediaArticlefunctions are now completely removed fromcli/bin/cli.dart. Their logic will be redesigned and moved into thecommand_runnerpackage 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
clidirectory.-
Run the
wikipediacommand:bashdart run bin/cli.dart wikipedia Computer_programming -
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_runnerpackage.bashCommandRunner received arguments: [wikipedia, Computer_programming]
Review
#In this chapter, you learned about:
- Creating Dart packages using
dart create -t package. -
Using
exportstatements to make declarations from one library available in another. -
Adding local packages as dependencies using the
pathoption inpubspec.yaml. - Importing packages into your Dart code using
importstatements. -
Refactoring code to improve organization and maintainability, including making
mainasyncto correctlyawaitasynchronous 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
gitoption and specifying the URL of the Git repository. - D) By using the
hostedoption 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.9.2. Page last updated on 2025-9-15. View source or report an issue.