Project Structure
Understand the standard layout and organization of Dart projects.
What is it?
Project structure refers to the standard directory layout and file organization conventions used in Dart applications and packages. Following these conventions ensures consistency across projects and enables tools like pub to work correctly.
Why does it exist?
A standardized project structure:
- Makes projects easier to navigate and understand
- Enables the package manager (
pub) to find and manage files - Separates concerns (public API vs implementation)
- Makes it easier to share and reuse code
- Provides a clear location for tests and documentation
- Follows community best practices
Basic Project Structure
Default layout
my_project/
├── lib/
│ ├── src/
│ │ └── (private implementation)
│ └── my_project.dart
├── bin/
│ └── main.dart
├── test/
│ └── my_project_test.dart
├── pubspec.yaml
├── pubspec.lock
├── README.md
├── CHANGELOG.md
└── analysis_options.yaml
Core Directories
lib/
The lib/ directory contains the main source code of your package or application.
lib/
├── src/ # Private implementation
│ ├── models/
│ ├── services/
│ └── utils/
├── models.dart # Public API exports
├── services.dart # Public API exports
├── my_project.dart # Main entry point
└── main.dart # Application entry
lib/src/
Private implementation that should not be imported directly by users.
lib/src/
├── core/
├── models/
├── services/
└── utils/
Rules:
- Files in
src/are considered internal - Users should import only from
lib/root - Use
exportto expose public APIs
lib/main.dart
The application entry point (for applications, not packages).
// lib/main.dart
import 'package:my_app/src/app.dart';
void main() {
// Application bootstrap
runApp(MyApp());
}
bin/
The bin/ directory contains executable scripts.
bin/
├── main.dart # Main executable
├── my_cli.dart # CLI tool entry
└── scripts/
└── helper.dart
bin/main.dart
// bin/main.dart
import 'package:my_app/my_app.dart';
void main(List<String> args) {
print('Hello from my_app!');
// Parse args and run the CLI
}
test/
The test/ directory contains tests for your package.
test/
├── unit/
│ ├── models_test.dart
│ └── services_test.dart
├── integration/
│ └── api_test.dart
├── helpers/
│ └── test_utils.dart
└── my_project_test.dart
Example test file
// test/unit/models_test.dart
import 'package:test/test.dart';
import 'package:my_app/models.dart';
void main() {
group('User', () {
test('should create user with name', () {
final user = User(name: 'Alice');
expect(user.name, equals('Alice'));
});
});
}
web/
For web applications with build_runner:
web/
├── index.html
├── main.dart
├── styles.css
└── assets/
├── images/
└── fonts/
example/
For packages that provide examples:
example/
├── lib/
│ └── example.dart
├── test/
│ └── example_test.dart
├── pubspec.yaml
└── README.md
Core Files
pubspec.yaml
The package manifest file.
name: my_app
description: A Dart application
version: 1.0.0
repository: https://github.com/username/my_app
environment:
sdk: '>=3.0.0 <4.0.0'
dependencies:
http: ^1.0.0
path: ^1.8.0
dev_dependencies:
test: ^1.24.0
build_runner: ^2.4.0
flutter:
uses-material-design: true
pubspec.lock
Automatically generated lock file.
# This file is generated automatically
# Do not edit manually
packages:
http:
dependency: "direct main"
description:
name: http
sha256: "123456..."
source: hosted
version: "1.0.0"
analysis_options.yaml
Dart analyzer configuration.
include: package:lints/recommended.yaml
analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
exclude:
- build/**
- lib/**/*.g.dart
linter:
rules:
- avoid_print
- prefer_const_constructors
- prefer_final_fields
- require_trailing_commas
README.md
Project documentation.
# My App
A Dart application that does something great.
## Getting Started
1. Install dependencies
2. Run the app
## Usage
```bash
dart run bin/main.dart
License
MIT
---
## CHANGELOG.md
Version history.
```markdown
# Changelog
## 1.0.0
- Initial release
- Basic functionality
## 0.1.0
- First beta version
- Core features
.gitignore
Version control exclusions.
# Dart
.dart_tool/
build/
pubspec.lock
pubspec.lock.json
# IDE
.idea/
.vscode/
*.iml
# OS
.DS_Store
Thumbs.db
# Logs
*.log
Advanced Structure
Large application
my_app/
├── lib/
│ ├── core/
│ │ ├── di/ # Dependency injection
│ │ ├── config/ # Configuration
│ │ └── constants/ # Constants
│ ├── features/
│ │ ├── auth/
│ │ │ ├── models/
│ │ │ ├── services/
│ │ │ ├── widgets/
│ │ │ └── auth.dart
│ │ ├── home/
│ │ │ ├── models/
│ │ │ ├── services/
│ │ │ ├── widgets/
│ │ │ └── home.dart
│ │ └── settings/
│ │ └── ...
│ ├── shared/
│ │ ├── widgets/
│ │ ├── utils/
│ │ └── extensions/
│ ├── app.dart
│ └── main.dart
├── assets/
│ ├── images/
│ ├── fonts/
│ └── translations/
├── scripts/
│ ├── build.dart
│ └── deploy.sh
├── tools/
│ └── gen_code.dart
├── docs/
│ └── api/
├── pubspec.yaml
└── analysis_options.yaml
Package Types
Application package
For runnable applications:
my_app/
├── lib/
│ └── src/
├── bin/
│ └── main.dart
├── pubspec.yaml
└── ...
Library package
For reusable libraries:
my_package/
├── lib/
│ ├── my_package.dart
│ └── src/
├── pubspec.yaml
└── ...
Flutter package
For Flutter plugins or packages:
my_flutter_package/
├── lib/
├── test/
├── example/ # Flutter example app
├── pubspec.yaml
└── README.md
Best Practices
- Keep
lib/organized by feature or layer - Expose only public APIs from
lib/root - Keep
src/for private implementation - Always include a
pubspec.yaml - Document your package with README.md
- Write tests in
test/ - Use
analysis_options.yamlfor code quality - Follow the Effective Dart style guide
Common Mistakes
Importing from lib/src
Wrong:
import 'package:my_app/src/models/user.dart';
Correct:
import 'package:my_app/models.dart';
Missing pubspec dependencies
Wrong:
dependencies:
# Forgetting to include a dependency
Correct:
dependencies:
http: ^1.0.0
Not ignoring build output
Wrong:
# Not in .gitignore
build/
Correct:
# In .gitignore
build/
.dart_tool/
Summary
Following the standard Dart project structure makes your code easier to maintain, share, and understand. Whether you're building a library, application, or Flutter app, these conventions help you and your team work more effectively.
Next Steps
Now that you understand project structure, continue to:
Did You Know?
- The
pubtool was originally called "pub" because it's short for "package" - The
lib/andbin/separation is inspired by Ruby'slib/andbin/conventions - Dart projects can include multiple entry points in
bin/ - The
test/directory can have any structure, but the convention is to mirrorlib/ - You can use
dart pub getto download dependencies listed inpubspec.yaml