๐ฆ Lesson 4: Packages & Libraries
Why Packages? The Problem They Solve
๐ง Core Concept
A package is a reusable collection of Dart code with a well-defined public API. Instead of copying utility functions between projects, you create a package once and import it everywhere. Dart's package manager (pub) handles downloading, versioning, and dependency resolution for you.
โ Without Packages
- Copy-paste code between projects
- Manually track updates across files
- No version control for shared code
- Bugs get duplicated
- Reinventing the wheel constantly
โ With Packages
- Write once, import everywhere
- Update one source, all projects benefit
- Semantic versioning built-in
- Bug fixes propagate automatically
- 40,000+ packages on pub.dev
Anatomy of a Dart Package
๐ง Every Dart package follows a standard layout:
Dart enforces conventions that make packages predictable. When you run dart create -t package super_utils, you get this structure:
๐ Click a file to see details
Select a file from the tree to see its purpose and example contents.
๐ Critical Convention: lib/src/ is Private
Files inside lib/src/ are conventionally private. Package consumers should never import them directly. Instead, you re-export the public parts from your main library file.
This convention lets package authors refactor internal code without breaking consumers.
pubspec.yaml โ Your Package's Identity Card
๐ง Every package has one pubspec.yaml file at its root
This file tells Dart your package's name, version, dependencies, and environment requirements. It's the single source of truth for package configuration.
๐๏ธ Interactive Builder
๐ Generated pubspec.yaml
name: super_utils
version: 1.0.0
description: A collection of useful Dart utilities
environment:
sdk: ^3.12.0
dependencies:
http: ^1.3.0
intl: ^0.19.0
dev_dependencies:
test: ^1.24.0
lints: ^5.0.0
โข
^1.3.0 means "any version >=1.3.0 and <2.0.0"โข
>=1.3.0 <1.5.0 is an explicit rangeโข
any accepts any version (avoid this in production)โข After editing pubspec.yaml, always run
dart pub get
Designing a Clean Public API with Exports
๐ง Your main library file acts as the "front door" to your package
Users should only ever import package:your_package/your_package.dart. Inside that file, you use export statements to expose exactly what you want them to use.
Example: The Main Library File
// lib/super_utils.dart โ The ONLY file users should import
library super_utils;
// Export public modules
export 'src/string_utils.dart';
export 'src/math_utils.dart';
export 'src/date_utils.dart';
// Keep internal helpers hidden!
// Do NOT export 'src/internal_helpers.dart'
๐ฎ Toggle Which Files to Export:
๐ Generated lib/super_utils.dart
library super_utils;
export 'src/string_utils.dart';
export 'src/math_utils.dart';
๐ Example: What's Inside a Utility File?
Here's what lib/src/string_utils.dart might contain:
// lib/src/string_utils.dart
// These functions are "private" to the package structure
// but become public when exported from super_utils.dart
String reverse(String str) {
return str.split('').reversed.join('');
}
int countVowels(String str) {
return str
.toLowerCase()
.replaceAll(RegExp(r'[^aeiou]'), '')
.length;
}
bool isPalindrome(String str) {
final cleaned = str.toLowerCase().replaceAll(RegExp(r'[^a-z0-9]'), '');
return cleaned == reverse(cleaned);
}
Managing Dependencies
๐ง Three types of dependencies in pubspec.yaml
| Type | Section | Example Use |
|---|---|---|
| Regular | dependencies: |
Packages your code imports at runtime |
| Dev | dev_dependencies: |
Testing, linting โ only needed during development |
| Dependency Overrides | dependency_overrides: |
Force a specific version (temporary fix) |
Dependency Sources
dependencies:
# From pub.dev (most common)
http: ^1.3.0
# From a local path
super_utils:
path: ../super_utils
# From a Git repository
my_package:
git:
url: https://github.com/user/my_package.git
ref: main
# From a hosted private repo
private_pkg:
hosted: https://my-private-pub.dev
version: ^2.0.0
๐ Dependency Graph
Dart Workspaces (Multi-Package Projects)
๐ง When you have multiple related packages, a workspace ties them together
A workspace lets you manage multiple packages in one repository. One dart pub get resolves dependencies for all packages, ensuring consistency.
Root pubspec.yaml
name: _
publish_to: none
environment:
sdk: ^3.12.0
workspace:
- super_utils
- cli_app
- web_service
- Single
dart pub getfor all packages - Consistent dependency versions
- Easier cross-package refactoring
- Shared analysis options
Finding Packages on pub.dev
๐ง pub.dev is the official Dart/Flutter package repository
With over 40,000 packages, you'll find solutions for HTTP, JSON, databases, CLI tools, and more. Always check package scores (likes, pub points, popularity) before adopting a dependency.
A composable, multi-platform, Future-based API for HTTP requests.
โญ 1200+ likes | โ High pub points | ๐ Updated weeklyInternationalization and localization facilities for date/number formatting.
โญ 950+ likes | โ High pub points | ๐ Updated weeklyA string-based path manipulation library. Works across platforms.
โญ 850+ likes | โ High pub points | ๐ Updated weeklyPractice Projects
Apply what you've learned by building these hands-on projects.
๐ฏ Package Master Quiz
Score: 0/6 | Hints: 3