If you are learning Dart or Flutter, you will quickly come across terms like libraries, packages, imports, dependencies, pubspec.yaml, and pub.dev.
At first, these terms can be confusing.
You may wonder:
- What is a Dart library?
- What is a Dart package?
- Is a package the same as a library?
- What does
importdo? - What is
pubspec.yaml? - How do I install a package?
- Where can I find Dart packages?
- How do packages work in Flutter?
- How can I create my own Dart package?
Understanding these concepts is extremely important because real-world Dart and Flutter applications rarely contain all their code in a single file.
Instead, developers divide applications into reusable pieces and use packages created by the Dart and Flutter community.
In this complete beginner-friendly guide, you will learn Dart packages and libraries from the basics to practical usage, including examples, project structure, dependencies, versioning, package installation, popular package categories, creating your own package, and best practices.
What Are Dart Libraries?
A library is a collection of related Dart code that can be used together.
A library can contain:
- Functions
- Classes
- Variables
- Constants
- Extensions
- Mixins
- Other Dart code
Libraries help developers organize code into separate, reusable modules.
For example, imagine you create a file called:
math_utils.dart
It could contain:
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
You can then use these functions from another Dart file.
import 'math_utils.dart';
void main() {
print(add(10, 5));
print(subtract(10, 5));
}
The output would be:
15
5
This is one of the simplest examples of how libraries help organize Dart code.
Every Dart File Is a Library
One important concept for beginners is that a Dart file is treated as a library.
For example:
lib/
├── main.dart
├── user.dart
├── product.dart
└── helpers.dart
Each Dart file can contain code that can be imported and used by another part of the application.
This makes it possible to divide a large application into smaller and more manageable files.
What Is a Dart Package?
A Dart package is a reusable collection of Dart code and other resources organized into a defined project structure.
A package can contain one or more libraries.
For example, a package might provide functionality for:
- HTTP requests
- Date formatting
- State management
- Local storage
- Authentication
- Database access
- File handling
- Image processing
- Networking
- Testing
- Command-line applications
Instead of implementing everything yourself, you can add an existing package to your project and use its functionality.
Package vs Library: What’s the Difference?
This is one of the most common beginner questions.
The easiest way to understand the difference is:
A library is a collection of reusable Dart code, while a package is a distributable project that can contain one or more libraries and other resources.
Think about it like this:
PACKAGE
│
├── Library 1
│ ├── Classes
│ ├── Functions
│ └── Constants
│
├── Library 2
│ ├── Classes
│ └── Functions
│
├── Tests
├── Documentation
├── pubspec.yaml
└── Other files
A package provides the overall structure.
Libraries provide the actual reusable code within that package.
A Simple Real-World Analogy
Imagine a toolbox.
The toolbox represents a package.
Inside the toolbox you might have:
- Hammer
- Screwdriver
- Wrench
- Pliers
Each group of tools can be compared to a library.
So:
Package = Toolbox
Library = Collection of related tools
Class = Individual tool
Function = Operation performed by a tool
This isn’t a technical definition, but it is a useful way for beginners to understand the relationship.
Why Do Developers Use Packages?
Imagine you are building a Flutter application that needs to make API requests.
You could write your own HTTP networking system from scratch.
That would require handling:
- Connections
- Requests
- Responses
- Headers
- Errors
- Timeouts
- JSON
- Authentication
- Interceptors
- Network failures
Instead, you can use an existing package designed for HTTP communication.
This can save significant development time.
Packages allow developers to focus on building their application instead of reinventing functionality that already exists.
Main Benefits of Dart Packages
1. Save Development Time
You don’t need to implement everything yourself.
2. Reuse Code
The same functionality can be used across multiple projects.
3. Reduce Code Duplication
Instead of copying similar code into every project, you can use a package.
4. Use Community Solutions
Other developers may have already solved a problem you are facing.
5. Improve Maintainability
A well-designed package can keep complex functionality separated from your application code.
6. Add Features Quickly
Packages can provide functionality such as:
- Authentication
- Notifications
- Networking
- Storage
- State management
- Database access
with relatively little application code.
What Is pub.dev?
pub.dev is the package repository used by the Dart and Flutter ecosystem.
Developers can publish Dart and Flutter packages there, and other developers can discover and use them.
When you need a package, you can search for it on pub.dev.
For example, you may search for packages related to:
- HTTP
- JSON
- Firebase
- State management
- Local storage
- Permissions
- Bluetooth
- Notifications
- Image caching
- Authentication
Before adding a package to a production application, you should check its documentation, maintenance activity, compatibility, license, and package quality.
What Is pubspec.yaml?
If you work with Dart or Flutter, you will frequently use a file called:
pubspec.yaml
This file contains important information about your project.
For example:
name: my_flutter_app
description: A Flutter application.
environment:
sdk: ^3.0.0
dependencies:
flutter:
sdk: flutter
http: ^1.0.0
The pubspec.yaml file can define:
- Project name
- Description
- Version
- SDK constraints
- Dependencies
- Development dependencies
- Assets
- Fonts
- Package configuration
It is one of the most important configuration files in a Dart or Flutter project.
What Is a Dependency?
A dependency is external code that your project relies on.
For example, if your application uses an HTTP package, your project has a dependency on that package.
You might have:
dependencies:
http: ^1.0.0
This tells Dart’s package manager that your application depends on the http package.
Your application can then import functionality from that package.
How Do You Add a Package to a Flutter Project?
Suppose you want to add an HTTP package.
You can add it to your project using Flutter’s package command.
flutter pub add http
Flutter updates your pubspec.yaml file.
It may look similar to:
dependencies:
http: ^1.0.0
The exact version depends on the current package release and your project’s constraints.
What Does flutter pub get Do?
After modifying dependencies, you may see or use:
flutter pub get
This command resolves and retrieves the project’s dependencies.
For Dart projects, the equivalent command is:
dart pub get
The basic workflow is:
Add dependency
↓
pubspec.yaml
↓
pub get
↓
Dependency resolution
↓
Package available to your project
How Do You Import a Package?
After adding a package, you can import it into your Dart code.
For example:
import 'package:http/http.dart' as http;
You can then use functionality provided by the package.
For example:
final response = await http.get(
Uri.parse('https://example.com'),
);
The package: import tells Dart that the library comes from a package.
What Does package: Mean?
You will frequently see imports such as:
import 'package:flutter/material.dart';
or:
import 'package:http/http.dart';
The general format is:
package:package_name/library_name.dart
For example:
import 'package:http/http.dart';
means:
package
↓
http package
↓
http.dart library
Relative Imports vs Package Imports
Dart supports different import styles.
Relative Import
Suppose you have:
lib/
├── main.dart
└── utils/
└── helpers.dart
From main.dart, you could write:
import 'utils/helpers.dart';
This is a relative import.
Package Import
Inside the same package, you can also use a package import:
import 'package:my_app/utils/helpers.dart';
For application code, package imports are often preferred for consistency and easier refactoring, especially in larger projects.
What Is an SDK Package?
Dart and Flutter come with many libraries and packages as part of their SDKs.
For example:
import 'dart:math';
The dart: prefix refers to libraries provided by the Dart SDK.
Examples include:
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
These provide built-in functionality.
Dart SDK Libraries
Some commonly used Dart libraries include:
dart:async
Used for asynchronous programming.
It provides concepts such as:
FutureStreamStreamController
Example:
Future<void> loadData() async {
await Future.delayed(
const Duration(seconds: 1),
);
print("Data loaded");
}
dart:convert
Useful for converting data formats such as JSON.
For example:
import 'dart:convert';
final jsonString = '{"name":"Ankit"}';
final data = jsonDecode(jsonString);
print(data['name']);
dart:math
Provides mathematical functionality.
import 'dart:math';
void main() {
print(sqrt(25));
}
Output:
5.0
dart:io
Provides APIs for input/output operations on platforms where the library is supported.
It can be used for things such as:
- Files
- Directories
- Sockets
- HTTP server functionality
Note that dart:io is not available in the same way on every Flutter target, particularly web applications.
Flutter Libraries
Flutter also provides many libraries.
For example:
import 'package:flutter/material.dart';
The Material library provides widgets and functionality based on Material Design.
It includes widgets such as:
Scaffold
AppBar
Text
Container
Column
Row
Card
Button
TextField
ListView
Another commonly used library is:
import 'package:flutter/cupertino.dart';
which provides Cupertino-style widgets and APIs.
What Is a Third-Party Package?
A third-party package is created outside the core Dart or Flutter SDK.
These packages can be created by:
- Individual developers
- Open-source contributors
- Companies
- Organizations
- The Dart/Flutter community
Examples include packages for:
- Networking
- State management
- Local databases
- Notifications
- Authentication
- Permissions
- Image caching
- Bluetooth
- Maps
Popular Dart and Flutter Package Categories
There are thousands of packages in the Dart ecosystem.
Rather than memorizing package names, it is more useful to understand the major categories.
HTTP and API Packages
Applications often need to communicate with backend servers.
Common functionality includes:
GET
POST
PUT
PATCH
DELETE
A networking package can simplify these operations.
Example:
final response = await http.get(
Uri.parse('https://example.com/users'),
);
JSON Packages and Libraries
APIs frequently return JSON.
Example JSON:
{
"name": "Ankit",
"age": 24
}
Dart provides JSON encoding and decoding through dart:convert.
import 'dart:convert';
final data = jsonDecode(jsonString);
For larger applications, code generation packages can also help create model serialization code.
State Management Packages
Flutter applications often need to manage state.
State might include:
- Logged-in user
- Cart items
- Selected language
- Theme
- API data
- Loading status
- Form values
Popular state-management approaches in the Flutter ecosystem include:
- Provider
- Riverpod
- Bloc
- Cubit
- GetX
- Signals
Each has different design philosophies.
You should choose a state-management approach based on your application’s requirements rather than simply choosing the most popular package.
Local Storage Packages
Applications frequently need to store information locally.
Examples:
- Login preferences
- Settings
- Tokens
- Cached data
- User preferences
Different storage solutions are available depending on the type and complexity of the data.
For example:
Simple key-value data
↓
Key-value storage
Structured local data
↓
Local database
The correct package depends on your requirements.
Image Packages
Flutter applications frequently work with images.
Packages can provide features such as:
- Image caching
- Image loading
- Image manipulation
- Network image handling
- Placeholder images
For example, an image caching package can prevent your application from repeatedly downloading the same image.
Notification Packages
Mobile applications frequently use notifications.
Packages can help implement:
- Local notifications
- Push notifications
- Notification scheduling
- Notification actions
A notification architecture may involve both Flutter/Dart code and platform-specific services.
Permission Packages
Mobile applications sometimes need runtime permissions.
Examples include:
- Camera
- Microphone
- Location
- Bluetooth
- Notifications
- Photos
Packages can simplify interaction with platform permission systems.
Authentication Packages
Applications may need authentication systems such as:
- Email/password
- Google Sign-In
- Apple Sign-In
- OAuth
- Token-based authentication
Packages can provide integrations with authentication providers.
Database Packages
Dart and Flutter applications can work with different types of databases.
For example:
Local Database
↓
SQLite / Isar / Hive / other solutions
Remote Database
↓
Backend API / Firebase / Supabase / PostgreSQL-backed services
The package or library you choose depends on whether your data is local, remote, relational, document-based, or another type.
How Packages Work Internally
When you add a package to your project, several things happen.
Suppose your application uses:
dependencies:
some_package: ^1.2.0
The process is roughly:
Your Project
↓
pubspec.yaml
↓
Dependency Resolver
↓
Package Versions
↓
Package Downloaded/Resolved
↓
Package Available to Dart
↓
import
↓
Use Package APIs
Dart’s package manager handles dependency resolution and retrieves the required package sources and dependencies.
What Is Dependency Resolution?
Imagine your project depends on:
Package A
But Package A depends on:
Package B
And Package B depends on:
Package C
Your dependency tree might look like:
Your App
|
└── Package A
|
└── Package B
|
└── Package C
The package manager must determine compatible versions of these dependencies.
This is called dependency resolution.
What Is pubspec.lock?
When dependency versions are resolved, Dart projects may use a lock file to record the specific resolved versions.
For Flutter applications, this is typically:
pubspec.lock
The lock file helps make dependency resolution more reproducible across development environments.
You generally should not manually edit it unless you have a specific reason and understand the consequences.
What Is a Version Constraint?
You may see something like:
dependencies:
http: ^1.0.0
The version constraint tells the package manager which versions are acceptable under the specified constraint.
You may also encounter constraints such as:
http: '>=1.0.0 <2.0.0'
Version constraints help packages work together without unexpectedly accepting incompatible releases.
What Does the ^ Symbol Mean?
You may frequently see:
http: ^1.2.0
The ^ is a version constraint operator.
It generally means that compatible updates within the allowed semantic-versioning range can be selected.
For example, depending on the version and Dart’s version-constraint rules, a constraint beginning with ^1.2.0 can allow compatible 1.x releases while excluding 2.0.0 and later.
Always check the package’s current version and Dart’s package versioning rules when you need precise behavior.
What Is Semantic Versioning?
Many Dart packages use semantic versioning, commonly called SemVer.
A version looks like:
MAJOR.MINOR.PATCH
For example:
2.4.1
means:
2 → Major
4 → Minor
1 → Patch
Generally:
Major
May contain breaking changes.
Minor
Usually adds backward-compatible features.
Patch
Usually contains bug fixes and small compatible changes.
Understanding versions is important when managing dependencies.
What Is a Transitive Dependency?
A direct dependency is something you explicitly add to your project.
For example:
dependencies:
http: ^1.0.0
A transitive dependency is a dependency required by one of your dependencies.
For example:
Your App
|
└── Package A
|
└── Package B
You directly depend on Package A.
Package B is a transitive dependency.
You may not import Package B directly, but your application depends on it indirectly through Package A.
What Is a Development Dependency?
Not every package is required when your application runs.
Some packages are used only during development.
Examples include:
- Testing tools
- Code generators
- Linting tools
- Build tools
- Development utilities
These can be placed under:
dev_dependencies:
For example:
dev_dependencies:
flutter_test:
sdk: flutter
The exact packages depend on your project.
dependencies vs dev_dependencies
The difference is important.
dependencies
Packages required by your application.
dependencies:
http: ^1.0.0
dev_dependencies
Packages used primarily during development, testing, generation, or tooling.
dev_dependencies:
flutter_test:
sdk: flutter
A package belongs in the appropriate section based on how your project uses it.
How to Search for a Good Dart Package
Finding a package is easy.
Finding a good package requires more care.
Before installing a package, check:
1. Documentation
Is the documentation clear?
2. Maintenance
Is the package actively maintained?
3. Compatibility
Does it support your Dart and Flutter versions?
4. Community Adoption
Is it widely used?
5. Issues
Are there many unresolved issues?
6. Release History
Has the package received updates recently?
7. License
Is its license compatible with your project?
8. Dependencies
Does it introduce a large dependency tree for a small feature?
9. Platform Support
Does it support Android, iOS, web, Windows, macOS, and Linux if your project requires those platforms?
Don’t Install a Package for Everything
Packages are extremely useful, but you should not automatically install one for every small problem.
For example, if you need a simple helper function:
double calculateTotal(
double price,
int quantity,
) {
return price * quantity;
}
you probably don’t need a package.
Packages make the most sense when they provide substantial, reusable functionality that would otherwise take significant time or introduce unnecessary complexity.
What Is a Package API?
A package exposes an API, or Application Programming Interface.
The API is the set of classes, methods, functions, properties, and other public functionality that your application can use.
For example:
final response = await http.get(
Uri.parse('https://example.com'),
);
Your application doesn’t need to understand every internal detail of the HTTP package.
You use its public API.
Think of it like a restaurant:
You
↓
Menu
↓
Order
↓
Restaurant handles the internal work
↓
You receive the result
The menu is similar to the package’s public API.
What Are Public and Private Members?
Dart uses an underscore to indicate a library-private identifier.
For example:
class UserService {
void login() {
_validateUser();
}
void _validateUser() {
print("Validating...");
}
}
Here:
login()
is publicly accessible.
_validateUser()
is private to the library.
This allows package authors to expose only the functionality that users need.
Creating Your Own Dart Library
You can create a simple reusable library.
Create:
lib/
└── calculator.dart
Add:
library calculator;
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
Then import it:
import 'calculator.dart';
void main() {
print(add(5, 10));
print(multiply(5, 10));
}
This is a basic example of reusable Dart code.
Creating Your Own Dart Package
If you want to create a complete reusable package, Dart provides package tooling.
A typical package project might look like:
my_package/
├── lib/
│ └── my_package.dart
├── test/
│ └── my_package_test.dart
├── example/
├── README.md
├── CHANGELOG.md
├── LICENSE
└── pubspec.yaml
The exact structure can vary depending on the package.
The lib Folder
The lib folder contains the public Dart code of a package.
For example:
lib/
├── my_package.dart
├── src/
│ ├── calculator.dart
│ └── helpers.dart
A common architecture is to keep implementation details inside src/ and expose the intended public API through a main library file.
The test Folder
The test directory contains automated tests.
For example:
test/
└── calculator_test.dart
Tests help package developers verify that their code works correctly.
A package with good test coverage is generally easier to maintain.
What Is README.md?
A package’s README file explains:
- What the package does
- How to install it
- How to use it
- Examples
- Supported platforms
- Configuration
- Other important information
Good documentation is extremely important for open-source packages.
What Is CHANGELOG.md?
A changelog records important changes between package versions.
For example:
## 2.0.0
- Added new API
- Improved performance
- Changed configuration
## 1.1.0
- Added feature X
This helps users understand what changed after updating the package.
How to Create a Dart Package
A basic workflow looks like:
Create Package
↓
Design API
↓
Write Code
↓
Add Tests
↓
Write Documentation
↓
Test Package
↓
Publish Package
You can create a package using Dart’s package tooling.
For example:
dart create -t package my_package
This creates a package project structure.
Building a Flutter Package
You can also create packages specifically for Flutter.
A Flutter package may contain:
- Dart code
- Flutter widgets
- Platform integrations
- Android code
- iOS code
- Web code
- Desktop code
This is particularly useful when you want to create reusable Flutter functionality.
Dart Package vs Flutter Package
A Dart package can work with the Dart ecosystem without requiring Flutter.
A Flutter package can depend on Flutter and provide Flutter-specific functionality.
For example:
Dart Package
↓
Dart language functionality
while:
Flutter Package
↓
Dart
+
Flutter
+
Widgets / platform integration
If a package imports Flutter libraries such as:
import 'package:flutter/material.dart';
it is Flutter-dependent.
Package vs Plugin
You may also hear the term plugin.
A Flutter plugin is generally a package that provides a Dart API and may also include platform-specific implementation.
For example, a plugin might expose:
await SomePlugin.start();
while internally communicating with:
Flutter/Dart
↓
Plugin API
↓
Android Kotlin/Java
↓
Android API
or:
Flutter/Dart
↓
Plugin API
↓
iOS Swift/Objective-C
↓
iOS API
Plugins are particularly useful for accessing platform-specific functionality.
Example: Why Plugins Are Useful
Suppose your Flutter application needs Bluetooth functionality.
Flutter code might call a package API such as:
await bluetoothService.startScan();
The package may then communicate with the underlying operating system.
Your application doesn’t need to implement every Bluetooth API directly.
This is one of the biggest advantages of the package/plugin ecosystem.
Local Packages
Packages don’t always have to come from pub.dev.
You can use a local package during development.
For example:
dependencies:
my_package:
path: ../my_package
This is useful when:
- Developing multiple packages together
- Testing a package locally
- Sharing internal code between applications
- Developing a package and application at the same time
Git Dependencies
A package can also be referenced from a Git repository.
For example:
dependencies:
my_package:
git:
url: https://github.com/example/my_package.git
This can be useful for development or for using a package version that has not been published to pub.dev.
For production applications, however, you should understand the stability and versioning implications before depending directly on an arbitrary Git branch or commit.
Why Package Management Matters in Large Applications
Imagine a Flutter application with:
100+ Dart files
50+ screens
20+ services
10+ models
Multiple APIs
Authentication
Database
Notifications
Storage
Payments
Without good organization, the codebase can quickly become difficult to maintain.
Packages and libraries help divide responsibilities.
For example:
Application
│
├── Authentication
├── Networking
├── Database
├── Storage
├── Notifications
├── UI
└── Utilities
Each area can have clear boundaries.
Libraries and Clean Architecture
Packages and libraries can also support architectural patterns.
For example:
lib/
├── core/
├── data/
├── domain/
├── presentation/
└── features/
Each directory can contain multiple Dart libraries.
A larger project may further separate:
features/
├── auth/
│ ├── data/
│ ├── domain/
│ └── presentation/
│
├── home/
│ ├── data/
│ ├── domain/
│ └── presentation/
The exact architecture depends on the application.
The important idea is that libraries allow you to keep code organized into meaningful units.
Common Mistakes Beginners Make
Mistake 1: Installing Too Many Packages
More packages don’t automatically mean better architecture.
Every dependency adds:
- Maintenance
- Version management
- Potential bugs
- Security considerations
- Upgrade work
Use packages when they provide meaningful value.
Mistake 2: Never Checking Documentation
Don’t blindly copy code from random tutorials.
Read the package’s documentation.
Understand:
- Installation
- Configuration
- API usage
- Platform requirements
- Permissions
- Version compatibility
Mistake 3: Ignoring Package Updates
Old packages may become incompatible with newer Dart or Flutter versions.
Keep your dependencies under control and update them intentionally.
Mistake 4: Using Unmaintained Packages
A package might have worked perfectly two years ago but may no longer be actively maintained.
Check the package’s current health before adopting it.
Mistake 5: Using a Package for a Tiny Function
If something can be safely implemented in a few lines, adding a dependency may create unnecessary complexity.
Best Practices for Using Dart Packages
1. Use Meaningful Dependencies
Choose packages that solve real problems.
2. Read the Documentation
Understand how the package works before integrating it.
3. Check Platform Support
Especially important for Flutter applications.
4. Check Version Compatibility
Make sure the package supports your Dart and Flutter versions.
5. Check Maintenance
Look at release history and issue activity.
6. Avoid Unnecessary Dependencies
Keep your dependency tree manageable.
7. Test Package Updates
Don’t blindly update every dependency in a production application.
8. Understand Licenses
Make sure package licenses are appropriate for your project.
9. Keep Packages Updated
Regularly review dependencies for important updates and security fixes.
10. Prefer Well-Documented Packages
Good documentation saves development time.
How to Learn Dart Packages Effectively
If you’re a beginner, don’t try to memorize package names.
Instead, learn the workflow.
Problem
↓
Search pub.dev
↓
Compare packages
↓
Read documentation
↓
Check compatibility
↓
Add dependency
↓
Run pub get
↓
Import package
↓
Use API
↓
Test functionality
This workflow is far more valuable than memorizing hundreds of package names.
Practical Example: Adding an HTTP Package
Suppose you need to call an API.
Step 1: Add the package
flutter pub add http
Step 2: Import it
import 'package:http/http.dart' as http;
Step 3: Make a request
Future<void> fetchUsers() async {
final response = await http.get(
Uri.parse('https://example.com/users'),
);
print(response.statusCode);
}
Step 4: Handle the response
You can then decode JSON and convert it into your application’s model objects.
This demonstrates the complete package workflow:
Package
↓
Dependency
↓
Import
↓
API
↓
Application Feature
Packages Make Flutter Development Faster
Imagine building an application without packages.
You would potentially need to build:
HTTP Client
Authentication
Storage
JSON Serialization
State Management
Image Caching
Notifications
Permissions
Database
Analytics
from scratch.
Packages allow you to use existing solutions and spend more time on the features that make your application unique.
Are Packages Always Safe?
No.
You should treat third-party dependencies as part of your application’s supply chain.
Before using a package, consider:
- Who maintains it?
- Is it actively updated?
- Does it have a good reputation?
- Does it have unnecessary dependencies?
- What permissions does it require?
- What platforms does it access?
- Is the license suitable?
- Does the package handle sensitive data?
- Are there known security concerns?
This is particularly important for production applications.
Dart Packages and Security
Dependencies can introduce security risks if they contain vulnerable or malicious code.
Therefore, production developers should:
- Review dependencies
- Keep important packages updated
- Avoid unnecessary packages
- Review package permissions and behavior
- Monitor important dependency changes
- Use trusted sources
A package is effectively part of your application once you depend on it.
How Packages Improve Code Reusability
Suppose you have built a custom authentication system for one application.
Instead of copying the authentication code into another project, you could potentially extract the reusable functionality into a package.
Then another application can depend on it.
Application A
↓
Authentication Package
↑
Application B
↑
Application C
This is the power of reusable software components.
When Should You Create Your Own Package?
You may want to create a package when:
Multiple projects use the same code
For example:
App A → Common API Client
App B → Common API Client
App C → Common API Client
You have a reusable component
For example:
- UI component library
- Authentication helper
- Networking layer
- Utility collection
You want to share your code publicly
You can publish a package so other developers can use it.
You want to separate a large system
A package can help establish clear boundaries between components.
Example Package Architecture
A well-organized package might look like:
my_package/
│
├── lib/
│ ├── my_package.dart
│ │
│ └── src/
│ ├── models/
│ ├── services/
│ ├── helpers/
│ └── utilities/
│
├── test/
│ ├── models_test.dart
│ └── services_test.dart
│
├── example/
│
├── README.md
├── CHANGELOG.md
├── LICENSE
└── pubspec.yaml
The public API can be intentionally kept small while implementation details remain private.
What Is a Barrel File?
In Dart projects, you may see a file that exports several libraries.
For example:
export 'src/user.dart';
export 'src/product.dart';
export 'src/order.dart';
Then users can import the package’s main library rather than importing every internal file individually.
For example:
import 'package:my_package/my_package.dart';
This can make the package API cleaner.
import vs export
These two keywords are easy to confuse.
import
Use import when your library wants to use another library.
import 'user.dart';
export
Use export when you want to make another library’s public members available through your library’s API.
export 'user.dart';
Think:
import → I want to use this.
export → I want users of my library to access this through me.
What Is show in Dart Imports?
You can limit which members you import.
For example:
import 'math_utils.dart' show add;
Now only add is imported from that library.
This can make the intended dependency clearer.
What Is hide?
You can also hide specific members.
For example:
import 'math_utils.dart' hide subtract;
This prevents the hidden member from being available through that import.
Most beginners won’t need show and hide immediately, but they become useful in larger projects.
What Is an Import Alias?
Sometimes two libraries contain members with the same name.
You can give an import an alias:
import 'package:http/http.dart' as http;
Then use:
http.get(...);
Aliases are useful for improving readability and avoiding naming conflicts.
The Complete Dart Package Workflow
Let’s put everything together.
FIND A PACKAGE
↓
pub.dev
↓
CHECK DOCUMENTATION
↓
CHECK VERSION & SUPPORT
↓
ADD TO pubspec.yaml
↓
pub get
↓
import
↓
USE PACKAGE API
↓
TEST
↓
UPDATE WHEN NECESSARY
This is the workflow you will repeatedly use as a Dart or Flutter developer.
Dart Packages vs Libraries: Quick Comparison
| Feature | Library | Package |
|---|---|---|
| Collection of Dart code | Yes | Yes |
| Can contain classes/functions | Yes | Yes |
| Can be imported | Yes | Yes |
| Can contain multiple libraries | Not applicable | Yes |
Has pubspec.yaml | Not necessarily as a standalone library | Yes |
| Can be published to pub.dev | Usually as part of a package | Yes |
| Can contain tests/docs/examples | Not necessarily | Yes |
| Main purpose | Organize/reuse code | Distribute/reuse a project |
Dart Package Ecosystem
The Dart ecosystem can be visualized like this:
DART ECOSYSTEM
|
┌────────────────┼────────────────┐
↓ ↓ ↓
Dart SDK Flutter pub.dev
| | |
Built-in libs Flutter libs Third-party
| | |
└────────────────┼────────────────┘
↓
Your Application
This ecosystem allows developers to combine built-in functionality, Flutter APIs, and community packages.
Final Thoughts
Dart packages and libraries are fundamental parts of modern Dart and Flutter development.
A library helps organize reusable Dart code.
A package provides a structured, distributable collection that can contain one or more libraries, tests, documentation, examples, and other resources.
When building Flutter applications, you will constantly interact with:
Dart
↓
Libraries
↓
Packages
↓
Dependencies
↓
pubspec.yaml
↓
pub.dev
↓
Flutter Application
Once you understand this workflow, adding new functionality to a Flutter application becomes much easier.
Instead of writing every feature from scratch, you can search for an appropriate package, evaluate it carefully, add it as a dependency, import its API, and integrate it into your application.
At the same time, remember that more packages do not automatically mean better applications. Good developers choose dependencies carefully, understand what they are adding, keep their projects maintainable, and create their own reusable packages when there is a genuine need.
If you are learning Flutter, mastering Dart packages and libraries is an important step toward moving from simple tutorials to real-world application development.
Frequently Asked Questions
What is a Dart library?
A Dart library is a collection of related Dart code such as classes, functions, variables, constants, and other declarations that can be reused by importing the library.
What is a Dart package?
A Dart package is a structured project containing reusable code and resources. A package can contain one or more Dart libraries.
What is pub.dev?
pub.dev is the package repository for Dart and Flutter packages, where developers can discover and publish packages.
What is pubspec.yaml?
pubspec.yaml is a configuration file that describes a Dart or Flutter project and its dependencies, SDK constraints, assets, and other project metadata.
What is a dependency?
A dependency is an external package or component that your application relies on.
What is flutter pub get?
It resolves and retrieves the dependencies declared by your Flutter project’s pubspec.yaml.
What is the difference between dependencies and dev_dependencies?
dependencies are packages required by the application, while dev_dependencies are primarily used for development, testing, code generation, or tooling.
Can I create my own Dart package?
Yes. Dart provides tooling for creating packages, and you can use them privately, locally, through Git, or publish suitable packages to pub.dev.
Are Dart packages free?
Many Dart and Flutter packages are open-source and free to use, but you should always check the package’s license and terms before using it commercially.
Should beginners use packages?
Yes, but thoughtfully. Packages are an important part of professional Flutter development. Beginners should learn how to evaluate packages instead of installing one for every small problem.
Can a Dart package be used in Flutter?
Yes. A Dart package that doesn’t depend on Flutter can generally be used by Flutter applications. Flutter-specific packages can additionally depend on Flutter APIs.
What to Learn After Dart Packages and Libraries
If you’re following a Dart-to-Flutter learning path, the next useful topics are:
- Dart Collections: List, Set and Map
- Dart Null Safety
- Dart Future, Async and Await
- Dart Streams
- Dart Object-Oriented Programming
- What Is Flutter?
- Flutter Widgets Explained
- Flutter StatelessWidget vs StatefulWidget
- Flutter State Management
- How to Connect Flutter With REST APIs
- Flutter Firebase Integration
- Flutter Supabase Integration
Understanding these concepts will give you a strong foundation for building professional Dart and Flutter applications.




