Dart 2 🎯 Project Setup with dart create, CLI Tools, and pubspec.yaml
The first chapter covered how Dart executes code at the machine level. This chapter covers what comes before execution: creating a project, understanding the file structure, and managing dependencies. Every Dart project, from a command-line utility to a Flutter application, is built on the same foundation. The dart create command scaffolds it. The pub tool manages it. The pubspec.yaml file describes it. The Dart SDK provides the compiler, the analyzer, the formatter, and the test runner that turn the project into a working application.
Dart is not just a language. It is an ecosystem with its own package manager, its own tooling, and its own conventions. The pub.dev registry hosts over 50,000 packages. The dart command-line tool provides a unified interface for every task. The analysis_options.yaml file configures the linter and the analyzer. Understanding this ecosystem is the difference between writing Dart code and building Dart software.
Key point: The dart create command scaffolds a new project from a template. The template determines the directory structure and the starting files. The dart pub commands manage dependencies. The pubspec.yaml file declares the project’s name, version, dependencies, dev dependencies, and SDK constraints. The analysis_options.yaml file configures the linter. The dart CLI provides the compiler, the formatter, the analyzer, the test runner, and the documentation generator.
Why Project Setup Matters
A Dart project is not just a folder of files. It is a structured workspace with defined roles. Getting the structure right at the start prevents problems later: dependency conflicts, analyzer errors, missing imports, and build failures.
The dependency problem. Dart projects depend on packages from pub.dev. Each package has its own dependencies. The pub tool resolves the version constraints, downloads the packages, and writes a lockfile that records the exact versions. The resolution algorithm is deterministic. Two developers with the same pubspec.yaml and pubspec.lock get the same dependency tree.
The tooling problem. Dart provides a comprehensive toolchain: a compiler, an analyzer, a formatter, a test runner, a documentation generator, and a package manager. The dart command is the entry point for all of them. The developer does not need to install separate tools for each task.
The convention problem. Dart projects follow a standard structure: lib/ for library code, bin/ for executables, test/ for tests, web/ for web assets. Following the convention makes the project navigable to any Dart developer. The dart create command scaffolds the structure from a template.
The trade-off. The dart create command provides templates for common project types: console, package, server-shelf, web, flutter. Each template scaffolds a different structure. Choosing the right template saves configuration. The templates are conventions, not requirements. A project can deviate from the template if the structure requires it.
a. Creating a Project with dart create
The dart create command scaffolds a new project from a template. The command takes the project name and an optional --template flag.
dart create my_app
The default template is console. It scaffolds a command-line application with a main() function, a test, an analysis_options.yaml, and a pubspec.yaml. The project is immediately runnable with dart run.
dart create --template console my_app
The --template flag selects a different template. The available templates are:
| Template | Purpose |
|---|---|
console | Command-line application |
package | Reusable Dart package |
server-shelf | HTTP server with Shelf |
web | Web application |
flutter | Flutter application (requires Flutter SDK) |
package-ffi | Package with FFI bindings |
The --template flag can be abbreviated. The -t flag is the short form.
dart create -t package my_package
The project name must be a valid Dart identifier: lowercase with underscores, no spaces, no hyphens. The dart create command validates the name and rejects invalid ones.
The scaffolded project has this structure for the console template:
my_app/
├── bin/
│ └── my_app.dart
├── lib/
│ └── my_app.dart
├── test/
│ └── my_app_test.dart
├── .gitignore
├── analysis_options.yaml
├── CHANGELOG.md
├── pubspec.yaml
└── README.md
The bin/ directory holds the executable entry point. The lib/ directory holds the library code. The test/ directory holds the tests. The analysis_options.yaml configures the linter. The pubspec.yaml declares the dependencies.
For the package template, the structure is similar but without the bin/ directory. A package is a library, not an executable.
For the server-shelf template, the bin/ directory contains a server.dart file that starts an HTTP server using the Shelf framework.
For the web template, the web/ directory contains the HTML, CSS, and Dart files.
b. The pubspec.yaml File
The pubspec.yaml file is the project manifest. It declares the project’s name, version, description, dependencies, dev dependencies, and SDK constraints.
name: my_app
description: A sample command-line application.
version: 1.0.0
publish_to: none
environment:
sdk: ^3.5.0
dependencies:
path: ^1.9.0
dev_dependencies:
lints: ^4.0.0
test: ^1.25.0
The name field is the project name. The description field is a short description. The version field follows semantic versioning. The publish_to field set to none prevents accidental publication to pub.dev.
The environment field declares the Dart SDK constraint. The ^3.5.0 syntax means “3.5.0 or later, but less than 4.0.0.” The caret operator follows semantic versioning.
The dependencies field lists the packages the project needs at runtime. The dev_dependencies field lists the packages needed only during development: the linter, the test runner, the build tools.
The pubspec.lock file is generated by the pub tool. It records the exact version of every dependency, including transitive ones. It should be committed to version control for applications but not for packages. The distinction matters because a package’s consumers need flexibility in their dependency versions.
To add a dependency, run dart pub add package_name. The command updates pubspec.yaml and runs dart pub get automatically.
dart pub add http
dart pub add --dev test
The --dev flag adds the package to dev_dependencies instead of dependencies.
To remove a dependency, run dart pub remove package_name.
To update dependencies to their latest allowed versions, run dart pub upgrade.
To fetch dependencies without upgrading, run dart pub get.
The dart pub outdated command shows which dependencies are outdated and what the latest versions are.
c. The Dart CLI Tools
The dart command is the entry point for the entire toolchain. It has subcommands for every task.
| Command | Purpose |
|---|---|
dart run | Run a Dart file or package executable |
dart compile | Compile to executable, AOT, JIT, or kernel |
dart analyze | Run the static analyzer |
dart fix | Apply automated fixes |
dart format | Format Dart source code |
dart test | Run tests |
dart pub | Manage dependencies |
dart doc | Generate API documentation |
dart create | Scaffold a new project |
dart pub global | Manage globally installed packages |
The dart run command runs a Dart file. If the project has a bin/ directory with a file named my_app.dart, the command dart run my_app runs it.
dart run
dart run bin/my_app.dart
dart run my_app
The dart analyze command runs the static analyzer. It reads the analysis_options.yaml file and reports errors, warnings, and lints.
dart analyze
dart analyze lib/
dart analyze --fatal-infos
The --fatal-infos flag treats info-level diagnostics as errors. The --fatal-warnings flag treats warnings as errors. These flags are useful in CI pipelines.
The dart fix command applies automated fixes for common linter violations.
dart fix --dry-run
dart fix --apply
The dart format command formats Dart source code. It applies a consistent style to every file.
dart format .
dart format lib/
dart format --output=none --set-exit-if-changed .
The --output=none --set-exit-if-changed combination is used in CI to verify that the code is formatted without modifying it.
The dart test command runs the tests in the test/ directory.
dart test
dart test test/my_app_test.dart
dart test --coverage=coverage
The --coverage flag produces a coverage report. The dart pub global activate coverage command installs the coverage tool.
The dart doc command generates API documentation from dartdoc comments.
dart doc
dart doc --output=docs
d. The analysis_options.yaml File
The analysis_options.yaml file configures the linter and the analyzer. It lives at the root of the project.
include: package:lints/recommended.yaml
analyzer:
language:
strict-casts: true
strict-inference: true
strict-raw-types: true
errors:
invalid_annotation_target: ignore
linter:
rules:
- always_declare_return_types
- avoid_empty_else
- prefer_const_constructors
- prefer_final_locals
- sort_constructors_first
- unnecessary_await_in_return
The include directive includes a predefined rule set. The package:lints/recommended.yaml set is the standard for Dart projects. The package:flutter_lints/flutter.yaml set is the standard for Flutter projects.
The analyzer section configures the analyzer itself. The language section enables strict type checking. The strict-casts option disallows implicit casts from dynamic. The strict-inference option requires explicit types when inference fails. The strict-raw-types option disallows raw generic types.
The errors section overrides the severity of specific diagnostics. The invalid_annotation_target: ignore directive suppresses a warning that appears in some code generation scenarios.
The linter section enables or disables specific lint rules. The rules list contains the rule names. A rule can be enabled with its name or disabled with a # comment.
The dart analyze command reads the analysis_options.yaml file and applies the rules. The dart fix command applies the automated fixes for the rules that have them.
Complete Example Session
This session creates a Dart project, explores the structure, adds a dependency, and runs the toolchain.
# ============================================
# PART 1: CREATE THE PROJECT
# ============================================
dart create -t console my_app
# Output:
# Creating my_app using template console...
# .gitignore
# analysis_options.yaml
# CHANGELOG.md
# README.md
# pubspec.yaml
# bin/my_app.dart
# lib/my_app.dart
# test/my_app_test.dart
#
# Running pub get...
# Resolving dependencies...
# Got dependencies!
#
# To run the project: dart run
cd my_app
# ============================================
# PART 2: EXPLORE THE STRUCTURE
# ============================================
ls -la
# Output:
# .gitignore
# analysis_options.yaml
# bin/
# CHANGELOG.md
# lib/
# pubspec.yaml
# README.md
# test/
ls bin/
# Output:
# my_app.dart
ls lib/
# Output:
# my_app.dart
ls test/
# Output:
# my_app_test.dart
# ============================================
# PART 3: THE pubspec.yaml
# ============================================
cat pubspec.yaml
# Output:
# name: my_app
# description: A sample command-line application.
# version: 1.0.0
# publish_to: none
#
# environment:
# sdk: ^3.5.0
#
# dependencies:
# path: ^1.9.0
#
# dev_dependencies:
# lints: ^4.0.0
# test: ^1.25.0
# ============================================
# PART 4: THE bin/my_app.dart
# ============================================
cat bin/my_app.dart
# Output:
# import 'package:my_app/my_app.dart' as my_app;
#
# void main(List<String> arguments) {
# print('Hello world: ${my_app.calculate()}!');
# }
# ============================================
# PART 5: THE lib/my_app.dart
# ============================================
cat lib/my_app.dart
# Output:
# int calculate() {
# return 6 * 7;
# }
# ============================================
# PART 6: RUN THE APPLICATION
# ============================================
dart run
# Output:
# Hello world: 42!
# ============================================
# PART 7: RUN THE TESTS
# ============================================
dart test
# Output:
# 00:00 +0: loading test/my_app_test.dart
# 00:00 +0: test/my_app_test.dart
# 00:00 +1: All tests passed!
# ============================================
# PART 8: RUN THE ANALYZER
# ============================================
dart analyze
# Output:
# Analyzing my_app...
# No issues found!
# ============================================
# PART 9: ADD A DEPENDENCY
# ============================================
dart pub add http
# Output:
# Resolving dependencies...
# + http 1.2.0
# Changed 1 dependency!
# The pubspec.yaml now includes http in dependencies.
# ============================================
# PART 10: FORMAT THE CODE
# ============================================
dart format .
# Output:
# Formatted bin/my_app.dart
# Formatted lib/my_app.dart
# Formatted test/my_app_test.dart
# ============================================
# PART 11: THE DART CLI SUMMARY
# ============================================
# dart run → run the application
# dart compile → compile to exe, aot, jit, kernel
# dart analyze → static analysis
# dart fix → automated fixes
# dart format → format source code
# dart test → run tests
# dart pub → manage dependencies
# dart doc → generate documentation
# dart create → scaffold a new project
# ============================================
# PART 12: THE PROJECT STRUCTURE
# ============================================
# bin/ → executables
# lib/ → library code
# test/ → tests
# pubspec.yaml → dependencies and metadata
# analysis_options.yaml → linter configuration
# pubspec.lock → exact dependency versions
The twelve parts cover creating the project, exploring the structure, the pubspec.yaml, the bin/my_app.dart, the lib/my_app.dart, running the application, running the tests, running the analyzer, adding a dependency, formatting the code, the Dart CLI summary, and the project structure.
Quick Reference
The dart create Templates
| Template | Purpose |
|---|---|
console | Command-line application |
package | Reusable Dart package |
server-shelf | HTTP server with Shelf |
web | Web application |
flutter | Flutter application |
package-ffi | Package with FFI bindings |
The pubspec.yaml Fields
| Field | Purpose |
|---|---|
name | Project name |
description | Short description |
version | Semantic version |
publish_to | Publication control |
environment | SDK constraint |
dependencies | Runtime packages |
dev_dependencies | Development packages |
The pub Commands
| Command | Purpose |
|---|---|
dart pub add pkg | Add a dependency |
dart pub remove pkg | Remove a dependency |
dart pub get | Fetch dependencies |
dart pub upgrade | Update dependencies |
dart pub outdated | Show outdated packages |
The dart CLI Commands
| Command | Purpose |
|---|---|
dart run | Run the application |
dart compile | Compile the application |
dart analyze | Static analysis |
dart fix | Automated fixes |
dart format | Format source code |
dart test | Run tests |
dart doc | Generate documentation |
dart pub | Manage dependencies |
The analysis_options.yaml Sections
| Section | Purpose |
|---|---|
include | Predefined rule set |
analyzer.language | Strict type checking |
analyzer.errors | Severity overrides |
linter.rules | Specific rules |
Best Practices
✅ Do This:
# Use a template for new projects
dart create -t console my_app # ✅
# Add dependencies with pub add
dart pub add http # ✅
# Run the analyzer before committing
dart analyze # ✅
# Format the code
dart format . # ✅
# Use the lints package
include: package:lints/recommended.yaml # ✅
# Enable strict type checking
strict-casts: true
strict-inference: true
strict-raw-types: true # ✅
❌ Don’t Do This:
# Don't edit pubspec.lock by hand
vim pubspec.lock # ❌
# Don't commit pubspec.lock for packages
git add pubspec.lock # packages should not commit it # ⚠️
# Don't skip the analyzer
# The analyzer catches real bugs # ❌
# Don't ignore the formatter
# Consistent formatting matters for readability # ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Dependency conflict | Incompatible version constraints | dart pub upgrade or relax constraints |
| Analyzer errors | Missing dependencies or wrong SDK | Check pubspec.yaml |
| Format differences | Different formatter versions | Use the SDK’s formatter |
| Test failures | Missing test dependency | Add test to dev_dependencies |
pub get failure | Network or cache issue | Clear cache with dart pub cache repair |
Real-World Examples
1. Create a Console App
dart create -t console my_app
2. Create a Package
dart create -t package my_package
3. Create a Server
dart create -t server-shelf my_server
4. Add a Dependency
dart pub add http
5. Add a Dev Dependency
dart pub add --dev test
6. Run the App
dart run
7. Run the Tests
dart test
8. Analyze the Code
dart analyze
9. Format the Code
dart format .
10. Generate Documentation
dart doc
Visual
The Project Structure
┌──────────────────────────────────────────────┐
│ my_app/ │
│ ├── bin/ │
│ │ └── my_app.dart ← executable │
│ ├── lib/ │
│ │ └── my_app.dart ← library code │
│ ├── test/ │
│ │ └── my_app_test.dart ← tests │
│ ├── .gitignore │
│ ├── analysis_options.yaml ← linter config │
│ ├── CHANGELOG.md │
│ ├── pubspec.yaml ← manifest │
│ ├── pubspec.lock ← exact versions │
│ └── README.md │
│ │
└──────────────────────────────────────────────┘
The pubspec.yaml Flow
┌──────────────────────────────────────────────┐
│ pubspec.yaml │
│ │ │
│ ▼ │
│ dart pub get │
│ ├─ Resolve version constraints │
│ ├─ Download packages │
│ ├─ Write pubspec.lock │
│ └─ Populate .dart_tool/ │
│ │
│ The resolution is deterministic. │
│ Same manifest, same lockfile, same tree. │
│ │
└──────────────────────────────────────────────┘
The Dart CLI Toolchain
┌──────────────────────────────────────────────┐
│ dart │
│ ├─ run → execute Dart code │
│ ├─ compile → produce native code │
│ ├─ analyze → static analysis │
│ ├─ fix → automated corrections │
│ ├─ format → consistent style │
│ ├─ test → run test suite │
│ ├─ pub → manage dependencies │
│ ├─ doc → generate documentation │
│ └─ create → scaffold projects │
│ │
└──────────────────────────────────────────────┘
The analysis_options.yaml
┌──────────────────────────────────────────────┐
│ include: package:lints/recommended.yaml │
│ │
│ analyzer: │
│ language: │
│ strict-casts: true │
│ strict-inference: true │
│ strict-raw-types: true │
│ │
│ linter: │
│ rules: │
│ - always_declare_return_types │
│ - prefer_const_constructors │
│ - prefer_final_locals │
│ │
│ The file configures both analyzer and linter.│
│ │
└──────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Scaffold command | dart create |
| Default template | console |
| Package manager | dart pub |
| Manifest | pubspec.yaml |
| Lockfile | pubspec.lock |
| Analyzer config | analysis_options.yaml |
| Executables | bin/ |
| Library code | lib/ |
| Tests | test/ |
| Lint package | package:lints/recommended.yaml |
| Test package | package:test |
| Strict flags | strict-casts, strict-inference, strict-raw-types |
Key takeaways:
- The
dart createcommand scaffolds a project from a template. Theconsoletemplate produces a command-line application withbin/,lib/, andtest/directories. Thepackagetemplate produces a reusable library without thebin/directory. Theserver-shelftemplate produces an HTTP server . - The
pubspec.yamlfile is the project manifest. It declares the project’s name, version, description, dependencies, dev dependencies, and SDK constraint. Theenvironmentfield specifies the Dart SDK version using the caret operator for semantic versioning . - The
dart pubcommands manage dependencies.dart pub addadds a dependency.dart pub removeremoves one.dart pub getfetches dependencies.dart pub upgradeupdates them.dart pub outdatedshows which are outdated . - The
dartCLI provides the entire toolchain.dart runexecutes code.dart compileproduces native code.dart analyzeruns static analysis.dart fixapplies automated fixes.dart formatformats code.dart testruns tests.dart docgenerates documentation . - The
analysis_options.yamlfile configures the linter and analyzer. Theincludedirective loads a predefined rule set. Theanalyzer.languagesection enables strict type checking. Thelinter.rulessection enables or disables specific rules . - The
pubspec.lockfile records exact versions. Applications should commit it. Packages should not. The distinction ensures that applications are reproducible and packages remain flexible for their consumers . - The
dart formatcommand enforces a consistent style. It applies the official Dart style guide to every file. The--output=none --set-exit-if-changedcombination verifies formatting in CI without modifying the code .
Remember: The dart create command scaffolds the project. The pubspec.yaml file declares the dependencies. The dart pub commands manage them. The dart CLI provides the toolchain. The analysis_options.yaml file configures the linter. The bin/ directory holds executables. The lib/ directory holds library code. The test/ directory holds tests. The pubspec.lock file records exact versions. The toolchain handles the rest.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!