| |

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:

TemplatePurpose
consoleCommand-line application
packageReusable Dart package
server-shelfHTTP server with Shelf
webWeb application
flutterFlutter application (requires Flutter SDK)
package-ffiPackage 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.

CommandPurpose
dart runRun a Dart file or package executable
dart compileCompile to executable, AOT, JIT, or kernel
dart analyzeRun the static analyzer
dart fixApply automated fixes
dart formatFormat Dart source code
dart testRun tests
dart pubManage dependencies
dart docGenerate API documentation
dart createScaffold a new project
dart pub globalManage 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

TemplatePurpose
consoleCommand-line application
packageReusable Dart package
server-shelfHTTP server with Shelf
webWeb application
flutterFlutter application
package-ffiPackage with FFI bindings

The pubspec.yaml Fields

FieldPurpose
nameProject name
descriptionShort description
versionSemantic version
publish_toPublication control
environmentSDK constraint
dependenciesRuntime packages
dev_dependenciesDevelopment packages

The pub Commands

CommandPurpose
dart pub add pkgAdd a dependency
dart pub remove pkgRemove a dependency
dart pub getFetch dependencies
dart pub upgradeUpdate dependencies
dart pub outdatedShow outdated packages

The dart CLI Commands

CommandPurpose
dart runRun the application
dart compileCompile the application
dart analyzeStatic analysis
dart fixAutomated fixes
dart formatFormat source code
dart testRun tests
dart docGenerate documentation
dart pubManage dependencies

The analysis_options.yaml Sections

SectionPurpose
includePredefined rule set
analyzer.languageStrict type checking
analyzer.errorsSeverity overrides
linter.rulesSpecific 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

PitfallWhy It HappensFix
Dependency conflictIncompatible version constraintsdart pub upgrade or relax constraints
Analyzer errorsMissing dependencies or wrong SDKCheck pubspec.yaml
Format differencesDifferent formatter versionsUse the SDK’s formatter
Test failuresMissing test dependencyAdd test to dev_dependencies
pub get failureNetwork or cache issueClear 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

ItemValue
Scaffold commanddart create
Default templateconsole
Package managerdart pub
Manifestpubspec.yaml
Lockfilepubspec.lock
Analyzer configanalysis_options.yaml
Executablesbin/
Library codelib/
Teststest/
Lint packagepackage:lints/recommended.yaml
Test packagepackage:test
Strict flagsstrict-casts, strict-inference, strict-raw-types

Key takeaways:

  • The dart create command scaffolds a project from a template. The console template produces a command-line application with bin/, lib/, and test/ directories. The package template produces a reusable library without the bin/ directory. The server-shelf template produces an HTTP server .
  • The pubspec.yaml file is the project manifest. It declares the project’s name, version, description, dependencies, dev dependencies, and SDK constraint. The environment field specifies the Dart SDK version using the caret operator for semantic versioning .
  • The dart pub commands manage dependencies. dart pub add adds a dependency. dart pub remove removes one. dart pub get fetches dependencies. dart pub upgrade updates them. dart pub outdated shows which are outdated .
  • The dart CLI provides the entire toolchain. dart run executes code. dart compile produces native code. dart analyze runs static analysis. dart fix applies automated fixes. dart format formats code. dart test runs tests. dart doc generates documentation .
  • The analysis_options.yaml file configures the linter and analyzer. The include directive loads a predefined rule set. The analyzer.language section enables strict type checking. The linter.rules section enables or disables specific rules .
  • The pubspec.lock file 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 format command enforces a consistent style. It applies the official Dart style guide to every file. The --output=none --set-exit-if-changed combination 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!