Define classes and objects
Learn about object-oriented programming in Dart, including classes, constructors, getters, and enums. Build data models for command-line arguments.
This chapter covers the fundamentals of object-oriented programming (OOP) in Dart. Explore how to create classes, define constructors and fields, compute properties with gettersGetterA special method that provides read access to an object's properties. Learn more, and use enums to represent fixed sets of values.
What you'll accomplish
Prerequisites
#Before starting this chapter:
-
Complete Chapter 4 and have a
working Dart development environment with the
dartpediaproject. - Understand basic programming concepts, such as variables, functions, and control flow.
- Understand packages and libraries in Dart.
Classes and objects in Dart
#Classes bundle two main components together:
-
State (fields):
The data each object holds,
such as an option's
nameorhelptext. -
Behavior (methods and getters):
The actions an object can perform or properties it computes,
such as formatting a
usagestring.
Instead of tracking loose strings or maps across an application, modeling command-line arguments as classes ensures compile-time type safety, editor autocomplete, and a consistent data structure.
Tasks
#A command-line interface (CLI) is defined by the commands, options, and arguments typed into a terminal.
This lesson and the next build a framework capable of parsing a command such as:
$ dartpedia help --verbose --command=search
Each part serves a distinct purpose:
-
dartpedia: The executable, or the name of the application. -
help: A command, representing an action to perform. -
--verbose: A flag (a boolean option without a value), modifying the command's behavior. -
--command=search: An option that accepts a value (search).
This chapter builds the data models representing options and parsed results.
Task 1: Define the OptionType enum
#
Command-line options fall into two distinct categories:
flags (which evaluate to boolean true or false) and
regular options (which accept a string value).
Using an enum ensures that code only allows these two valid categories.
Create
command_runner/lib/src/arguments.dart.-
Define the
OptionTypeenum:command_runner/lib/src/arguments.dartdartenum OptionType { flag, option }This enumeration defines two members:
flagandoption. Enums provide compile-time type safety, ensuring code accepts only valid option types.
Task 2: Define the Option class
#
Classes in Dart define blueprints for objects,
combining state (fields) and behavior (methods and getters).
The Option class models command-line options such as
--verbose or --command=search.
-
Start by defining a minimal
Optionclass with essential fields and a constructor. Add the following code tocommand_runner/lib/src/arguments.dartbelow the enum:command_runner/lib/src/arguments.dartdartclass Option { Option(this.name, {required this.type}); final String name; final OptionType type; }- Fields (
final String name;,final OptionType type;): Variables that store the object's data. Declaring them asfinalensures they cannot change after creation. - Constructor (
Option(...)): Instantiates newOptionobjects. Thethis.namesyntax is an initializing formal—a Dart shortcut that assigns the argument directly to the instance field before the constructor body runs. - Named parameters (
{required this.type}): Parameters inside curly braces{}are passed by name (for example,Option('verbose', type: OptionType.flag)). Therequiredkeyword makes the parameter mandatory.
- Fields (
-
Now, expand the
Optionclass with optional metadata fields and ausagegetter:command_runner/lib/src/arguments.dartdartclass Option { Option( this.name, { required this.type, this.help, this.abbr, this.defaultValue, this.valueHelp, }); final String name; final OptionType type; final String? help; final String? abbr; final Object? defaultValue; final String? valueHelp; String get usage { if (abbr != null) { return '-$abbr,--$name: $help'; } return '--$name: $help'; } }- Nullable types (
String?,Object?): The question mark?indicates that a field is optional and can holdnull. When callers omithelporabbr, they default tonull. - Getter (
get usage): A getter computes a value on demand when accessed, formatting the help string based on whether an abbreviation exists.
- Nullable types (
Task 3: Define the ArgResults class
#
The ArgResults class stores the output of parsing command-line input.
It maps each Option to its user-supplied value.
-
Add
ArgResultsto the bottom ofcommand_runner/lib/src/arguments.dart:command_runner/lib/src/arguments.dartdartclass ArgResults { String? command; String? commandArg; Map<Option, Object?> options = {}; // Returns true if the flag exists and is true. bool flag(String name) { for (var option in options.keys.where( (option) => option.type == OptionType.flag, )) { if (option.name == name) { return options[option] as bool; } } return false; } bool hasOption(String name) { return options.keys.any((option) => option.name == name); } ({Option option, Object? input}) getOption(String name) { var mapEntry = options.entries.firstWhere( (entry) => entry.key.name == name || entry.key.abbr == name, ); return (option: mapEntry.key, input: mapEntry.value); } }Highlights from the preceding code:
optionsmap (Map<Option, Object?>): Associates eachOptioninstance with its parsed user input.where()method inflag(): Filters map keys to inspect only boolean flags (option.type == OptionType.flag), ignoring options that take string arguments.- Type cast (
as bool): Tells the type checker to treat the value as aboolbecause flags always store boolean values. - Record return type (
({Option option, Object? input})): Returns a lightweight, named record grouping both theOptionand its value without declaring a separate class.
The
ArgResultsclass serves as the data contract for parsed output. Chapter 6 connects this class to the fullCommandRunner.
Task 4: Export arguments from the package
#
Export arguments.dart from the package entry point to
allow other packages to import Option and ArgResults.
Open
command_runner/lib/command_runner.dart.-
Export
arguments.dartalongsidecommand_runner_base.dart:command_runner/lib/command_runner.dartdart/// Support for command-line parsing and execution. library; export 'src/arguments.dart'; export 'src/command_runner_base.dart';This
exportstatement makes declarations inarguments.dartaccessible to any package that importspackage:command_runner/command_runner.dart.
Task 5: Test the classes in cli.dart
#Verify that Option instantiates and computes its usage string correctly.
Open
cli/bin/cli.dart.-
Replace the file contents with the following test code:
cli/bin/cli.dartdartimport 'package:command_runner/command_runner.dart'; void main() { final verboseOption = Option( 'verbose', type: OptionType.flag, abbr: 'v', help: 'Display extra logging information.', ); print('Defined option: ${verboseOption.name}'); print('Usage: ${verboseOption.usage}'); }This code calls the
Optionconstructor to create an instance, and then prints itsnamefield and computedusagegetter. -
Run the application from the
clidirectory:bashdart run bin/cli.dartThe console outputs:
bashDefined option: verbose Usage: -v,--verbose: Display extra logging information.This confirms that the
Optionclass and getter function as expected.
Review
#What you accomplished
A summary of the concepts and code introduced in this lesson.Defined classes to model data and behavior
Created the Option and ArgResults classes to structure command-line arguments and parsed output.
Created constructors with initializing formals and named parameters
Used this.fieldName syntax for automatic field initialization and defined optional and required named parameters with
{} and required.
Used getters to compute properties dynamically
Implemented the usage getter to format option help text on demand without storing duplicate string state.
Used enums to represent fixed sets of values
Defined the OptionType enum to represent valid option types (flag and option), ensuring compile-time safety.
Quiz
#Check your understanding
1 / 3Option(this.name, {required this.type}), what is the purpose of the this.name syntax?
-
It automatically assigns the constructor argument directly to the instance field
name.That's right!
In Dart,
this.fieldNamein a constructor's parameter list is an "initializing formal" that assigns the argument value directly to the field before the constructor body runs. -
It creates a new global variable named
name.Not quite.
this.namerefers specifically to the instance field of the class, not a global variable. -
It makes the
nameproperty private to the library.Not quite.
Privacy in Dart is designated by a leading underscore (
_name), not bythis. -
It marks the parameter as optional.
Not quite.
Optional parameters are enclosed in square brackets
[]or curly braces{}. Positionalthis.nameis required.
String get usage instead of storing a String usage field?
-
It dynamically computes the value whenever accessed, ensuring it reflects current state without redundant storage.
That's right!
Getters behave like properties from the caller's perspective, but execute code on demand to calculate their value.
-
Getters can only be accessed from within the same file.
Not quite.
Getters have the same visibility rules as normal fields and methods.
-
Getters run faster than reading a stored field.
Not quite.
Reading a stored field is typically faster than executing logic in a getter.
-
Getters allow the property to be modified from outside the class.
Not quite.
A getter provides read-only access unless a corresponding setter (
set usage(...)) is also defined.
enum instead of a class or a set of constants?
-
When you need a type that can only be one of a fixed, known set of values.
That's right!
Enums are perfect when you have a closed set of options, like
flagversusoption, days of the week, or status codes. The compiler ensures you handle all cases. -
When you need to store user input that could be any string.
Not quite.
User input is unpredictable and could be anything. Enums work best when you can list every possible value in advance.
-
When you want to create multiple instances with different properties.
Not quite.
That's what classes are for. Each enum value is a singleton, there's only one instance for each value.
-
When you need to perform arithmetic operations on the values.
Not quite.
Use
intordoublefor arithmetic. Enums represent distinct categories, not numeric values (though they do have an index).
Next lesson
#
The next chapter covers sharing behavior between classes
using inheritance and abstract classes.
Define an abstract CliElement base class, and
build out the Command and CommandRunner architecture.
Unless stated otherwise, the documentation on this site reflects Dart 3.13.3. Page last updated on 2026-09-15. View source or report an issue.