Pub.dev #
No application is built entirely from scratch — almost every Dart project depends on external packages for battle-tested functionality: HTTP clients, JSON serializers, state management, date utilities, and hundreds of other needs. Pub.dev is the official repository of Dart and Flutter packages, and dart pub is the tool managing its entire lifecycle: searching, downloading, updating, and publishing. Understanding how the package system works — especially semantic version constraints and how to choose the right package — is the skill that determines whether your project’s dependencies become an asset or technical debt.
The Dart Package Ecosystem #
Pub.dev provides more than 40,000 packages you can use for free. Each package is assessed by several factors displayed as scores:
flowchart LR
PD["pub.dev"] --> Likes["❤ Likes\n(community popularity)"]
PD --> Points["🏆 Pub Points\n(automatic quality)"]
PD --> Pop["📊 Popularity\n(actual usage)"]
Points --> PA["📄 API documentation"]
Points --> PB["🛡 null safety"]
Points --> PC["🎯 platform support"]
Points --> PD2["📜 LICENSE"]
Points --> PE["✅ pub.dev guidelines"]Three main metrics to watch when choosing a package:
| Metric | Meaning | Interpretation |
|---|---|---|
| Likes | How many developers liked it | Subjective community popularity |
| Pub Points | Automatic score 0-160 | Technical quality: docs, null safety, platform support |
| Popularity | Percentage of other packages depending on it | Real usage in the ecosystem |
pubspec.yaml — Project Configuration
#
pubspec.yaml is the heart of every Dart project — this file defines the project’s identity, dependencies, and various configurations. Every project has exactly one pubspec.yaml in its root directory.
# Package identity
name: aplikasi_toko # required — package name, all lowercase + underscore
version: 1.2.3 # required for public packages, SemVer format
description: > # short description (> means a scalar block in YAML)
Aplikasi toko online dengan fitur katalog produk,
keranjang belanja, dan integrasi pembayaran.
homepage: https://github.com/username/aplikasi_toko
repository: https://github.com/username/aplikasi_toko
issue_tracker: https://github.com/username/aplikasi_toko/issues
# SDK constraint — supported Dart versions
environment:
sdk: '>=3.0.0 <4.0.0' # required — without it dart pub get fails
# Runtime dependencies — used by production code
dependencies:
http: ^1.2.0
intl: ^0.19.0
shared_preferences: ^2.2.0
path: ^1.9.0
# Development dependencies — only for testing and tooling
dev_dependencies:
test: ^1.25.0
mockito: ^5.4.0
build_runner: ^2.4.0
lints: ^3.0.0
# Overrides — force a specific version to resolve conflicts
dependency_overrides:
some_package: '2.0.0'
Project Directory Structure #
aplikasi_toko/
├── pubspec.yaml ← project configuration
├── pubspec.lock ← locked versions (commit to repo for apps)
├── analysis_options.yaml ← analyzer and linter configuration
├── lib/
│ ├── main.dart
│ └── src/ ← internal code (not exposed as a public API)
├── bin/ ← executable scripts
├── test/ ← test files
├── example/ ← usage examples (for packages)
└── .dart_tool/ ← generated, DON'T commit to the repo
Version Constraints — Semantics You Must Understand #
A version constraint determines which package versions are compatible with your project. Dart follows Semantic Versioning (SemVer): MAJOR.MINOR.PATCH.
MAJOR — breaking changes (API not backward-compatible)
MINOR — new features that are backward-compatible
PATCH — bug fixes that are backward-compatible
Constraint Syntax #
dependencies:
# Exact version — inflexible, avoid unless there's a strong reason
paket_a: '1.2.3'
# Caret syntax (most common) — allows MINOR and PATCH updates, not MAJOR
# ^1.2.3 is equivalent to: >=1.2.3 <2.0.0
paket_b: ^1.2.3
# For 0.x.y versions — caret is stricter because MAJOR is still 0
# ^0.3.0 is equivalent to: >=0.3.0 <0.4.0 (only PATCH is free)
paket_c: ^0.3.0
# Explicit range — full control
paket_d: '>=1.0.0 <2.5.0'
# Minimum version only — no upper bound (dangerous)
paket_e: '>=1.0.0' # ✗ avoid — a MAJOR update could be breaking
# Any — accept any version (very dangerous)
paket_f: any # ✗ don't use
# Path — local package (for development)
paket_lokal:
path: ../paket_lokal
# Git — package from a Git repository
paket_git:
git:
url: https://github.com/user/paket.git
ref: main # branch, tag, or commit hash
// Guidance for choosing a constraint:
//
// ^x.y.z — use this for almost every case
// MINOR and PATCH updates automatic, MAJOR requires manual approval
//
// >=x.y.z <a.b.c — when you need a more specific range
// e.g. compatible with two MAJOR versions at once
//
// AVOID 'any' and unbounded ranges — they can cause conflicts that are
// very hard to debug when packages depend on each other
pubspec.lock — Version Safety
#
pubspec.lock records the exact version of every package (including transitive dependencies) currently in use. This matters for reproducibility:
# pubspec.lock — DON'T edit manually, let dart pub manage it
packages:
http:
dependency: "direct main"
description:
name: http
sha256: "761a297c042deedc1ffbb156d6e3dde3800e0826b9a
url: "https://pub.dev"
source: hosted
version: "1.2.2" # the exact version in use
pubspec.lock RULES:
✓ Application packages (deployed apps) — COMMIT to version control
Ensures all developers and production use the same versions
✗ Library packages (published packages) — DON'T commit
Libraries must be compatible with various versions of their dependencies
dart pub Commands — Everything You Need to Know
#
Installation and Synchronization #
# Download all dependencies from pubspec.yaml
dart pub get
# Force using the versions in pubspec.lock (for CI/production)
dart pub get --enforce-lockfile
# Add a new package and update pubspec.yaml directly
dart pub add http
dart pub add http intl path # add several at once
# Add as a dev dependency
dart pub add dev:test
dart pub add dev:mockito dev:build_runner
# Add with a specific constraint
dart pub add 'http:^1.2.0'
dart pub add 'http:>=1.0.0 <2.0.0'
# Remove a package
dart pub remove http
Updates and Upgrades #
# Check which packages can be updated
dart pub outdated
# Example output:
# Package Current Upgradable Resolvable Latest
# http 1.1.0 1.2.2 1.2.2 1.2.2
# intl 0.18.0 0.18.1 0.19.0 0.19.0
# Upgrade all packages to the newest version still within constraints
dart pub upgrade
# Upgrade specific packages
dart pub upgrade http intl
# Upgrade BEYOND constraints (also updates pubspec.yaml)
dart pub upgrade --major-versions
dart pub upgrade --major-versions http # only for http
Inspection and Diagnostics #
# Show all dependencies (direct and transitive)
dart pub deps
# Show in a different format
dart pub deps --style=list # flat list
dart pub deps --style=compact # concise
# Check whether pubspec.yaml is valid and dependencies can be resolved
dart pub publish --dry-run # for packages to be published
# Show downloaded package versions
dart pub cache list
# Clean the cache (useful if there are problems)
dart pub cache clean
# Info about a specific package
dart pub info http
Global Packages #
Packages can be installed globally — available in all projects without adding them to pubspec.yaml:
# Install a package as a global tool
dart pub global activate dart_style
dart pub global activate webdev
dart pub global activate flutterfire_cli
# Run a globally installed tool
dart pub global run dart_style:format .
# If PATH is configured, you can call it directly
dartfmt .
# List installed global packages
dart pub global list
# Deactivate a global package
dart pub global deactivate dart_style
Dependency Types #
dependencies — Runtime
#
Packages needed by production code. Bundled with the application:
dependencies:
# HTTP client
http: ^1.2.0
# Internationalization and date/number formatting
intl: ^0.19.0
# Persistent key-value storage
shared_preferences: ^2.2.0
# File path manipulation
path: ^1.9.0
# Additional collection utilities (groupBy, firstOrNull, etc.)
collection: ^1.18.0
# Structured logging
logging: ^1.2.0
dev_dependencies — Development Only
#
Packages only needed during development — not bundled into production:
dev_dependencies:
# Testing framework
test: ^1.25.0
# Mock generation
mockito: ^5.4.0
build_runner: ^2.4.0
# Linting rules
lints: ^3.0.0
# Coverage reporting
coverage: ^1.7.0
# JSON serialization code generation
json_serializable: ^6.7.0
dependency_overrides — Resolving Conflicts
#
When two packages need different versions of the same dependency, use dependency_overrides to force a specific version:
# Situation: package_a needs http ^0.13.0, package_b needs http ^1.0.0
# Solution: override to a version compatible with both
dependency_overrides:
http: ^1.2.0 # force all packages to use this version
// ANTI-PATTERN: using dependency_overrides carelessly
// ✗ Can cause runtime errors if packages aren't truly compatible
// ✗ Only use as a temporary solution while waiting for a package update
// CORRECT: report the conflict to the package maintainer as an issue
// and use the override only if there's no other way
Choosing the Right Package #
Not all packages on pub.dev are equal in quality. Here’s a checklist for evaluating a package before adding it:
EVALUATE A PACKAGE BEFORE USING IT:
Popularity and trust:
□ Pub Points ≥ 120 (out of 160)
□ Popularity ≥ 70%
□ Likes sufficient for the topic (relative)
□ Maintained by dart.dev, flutter.dev, or a trusted organization
Maintenance activity:
□ Last update < 6 months ago
□ Still actively accepting and closing issues
□ CHANGELOG exists and is well written
□ Version ≥ 1.0.0 (stable) or you're ready for breaking changes
Technical quality:
□ Null safety supported (Dart 2.12+)
□ API documentation available (pub.dev shows the %)
□ Example code on the pub.dev page
□ Test coverage exists (check GitHub)
□ An appropriate LICENSE (MIT, Apache, BSD — not GPL for commercial products)
Compatibility:
□ Supports the platforms you target (mobile, web, desktop)
□ SDK constraint compatible with your Dart/Flutter version
□ No problematic transitive dependencies
Recommended Essential Packages #
dependencies:
# Networking
http: ^1.2.0 # Dart's official HTTP client
dio: ^5.4.0 # HTTP client with interceptors and cancellation
# JSON
json_annotation: ^4.8.0 # annotations for code generation
# State management (Flutter)
provider: ^6.1.0 # simple state management
riverpod: ^2.5.0 # more powerful state management
# Utilities
collection: ^1.18.0 # groupBy, firstOrNull, etc.
path: ^1.9.0 # cross-platform path manipulation
intl: ^0.19.0 # date, number, and text formatting
logging: ^1.2.0 # structured logging
equatable: ^2.0.5 # value equality without boilerplate
uuid: ^4.3.0 # generate UUIDs
dev_dependencies:
test: ^1.25.0 # testing framework
mockito: ^5.4.0 # mocking for unit tests
build_runner: ^2.4.0 # code generation runner
json_serializable: ^6.7.0 # JSON code generation
lints: ^3.0.0 # official Dart lint rules
dart_code_metrics: ^5.7.0 # code quality analysis
Creating Your Own Package #
When there’s functionality you want to share across projects or publish to the community, create your own package.
Package Structure #
# Create a new package
dart create --template=package nama_package
# Generated structure:
nama_package/
├── lib/
│ ├── nama_package.dart ← public entry point
│ └── src/ ← internal implementation
│ └── fitur_utama.dart
├── test/
│ └── nama_package_test.dart
├── example/
│ └── nama_package_example.dart
├── pubspec.yaml
├── README.md ← required for pub points
├── CHANGELOG.md ← required for pub points
└── LICENSE ← required for pub points
Exposing the Public API #
// lib/nama_package.dart — the package's public entry point
// Export only what package users need to know
library nama_package;
export 'src/fitur_utama.dart' show KelasUtama, FungsiUtama;
export 'src/model.dart' show ModelData;
// src/internal_helper.dart is NOT exported — stays private
// Package users import it with:
// import 'package:nama_package/nama_package.dart';
Publishing to Pub.dev #
# Make sure you're logged in with a Google account
dart pub login
# Check whether the package is ready to publish
dart pub publish --dry-run
# Output shows all files to be uploaded
# and validates pubspec.yaml and the package structure
# Publish (can't be undone for the same version!)
dart pub publish
# Update to a new version — increment the version in pubspec.yaml first
# Then publish again
# For packages not ready to publish, prevent accidental publication
publish_to: none # rejects dart pub publish
# pubspec.yaml for a package to be published
name: nama_package
version: 1.0.0 # required
description: >
A short, clear description (60-180 characters is ideal for pub.dev display)
homepage: https://github.com/user/nama_package
repository: https://github.com/user/nama_package
topics: # pub.dev categories (max 5)
- utility
- dart
environment:
sdk: '>=3.0.0 <4.0.0'
Package Management Anti-Patterns #
Adding a Package for One Small Function #
// ANTI-PATTERN: adding a big package for one small function
// dependencies:
// uuid: ^4.3.0 ← just to generate a simple random string
// CORRECT: evaluate whether it can be implemented yourself
import 'dart:math';
String generateId() {
final random = Random.secure();
final bytes = List.generate(16, (_) => random.nextInt(256));
// simple UUID v4 implementation
bytes[6] = (bytes[6] & 0x0f) | 0x40;
bytes[8] = (bytes[8] & 0x3f) | 0x80;
return bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
}
// For more serious UUID needs, the uuid package is still worth using
Not Locking Versions for Applications #
# ANTI-PATTERN: overly loose constraints for an application
dependencies:
http: any # ✗ could change at any time
intl: '>=0.17.0' # ✗ no upper bound — a MAJOR update possible
# CORRECT: use caret syntax — flexible but controlled
dependencies:
http: ^1.2.0 # ✓ minor/patch updates, but not major
intl: ^0.19.0 # ✓ consistent and predictable
Not Committing pubspec.lock for Applications
#
# ANTI-PATTERN: a .gitignore that ignores pubspec.lock
echo "pubspec.lock" >> .gitignore # ✗ for application packages
# CORRECT: commit pubspec.lock for application packages
# Make sure pubspec.lock is NOT in .gitignore
# For library packages — indeed not committed
# Add to .gitignore only for libraries:
# pubspec.lock # only if this is a library package
Using dependency_overrides Permanently
#
# ANTI-PATTERN: dependency_overrides as a permanent solution
dependency_overrides:
http: 0.13.6 # ✗ forced to an old version with no update plan
# CORRECT: use only temporarily while reporting the issue
# and remove it as soon as the upstream package is updated
Summary #
pubspec.yamlis the center of project configuration — defining the name, version, SDK constraint, and all dependencies.environment.sdkis required ordart pub getfails.- Caret syntax
^x.y.zis the standard for almost all dependencies — allowing automatic MINOR and PATCH updates, but requiring explicit approval for potentially breaking MAJOR updates.pubspec.lockmust be committed for application packages — ensuring the entire team and production use the exact same versions. Library packages don’t need to commit the lock file.dart pub addis the easiest way to add a package — automatically determining the right constraint and runningdart pub get. No manualpubspec.yamlediting needed.dart pub outdatedshows which packages can be updated before deciding ondart pub upgrade— always check first to avoid surprises.dev_dependenciesfor packages only needed during development (testing, code generation, linting) — not bundled into production and not affecting your package’s users.- Evaluate packages before adding: Pub Points ≥ 120, actively maintained, null safety, good README and CHANGELOG, and a license that fits your needs.
publish_to: noneto prevent accidental publication to pub.dev during development — remove or replace it when ready to publish.- Avoid
dependency_overridesunless truly forced — it’s a workaround that masks compatibility problems that should be solved at the package level.- Global packages (
dart pub global activate) are useful for developer tools used across many projects without adding them to everypubspec.yaml.