Dart package and library management
When the project scale grows, splitting code into multiple files and modules is essential.
This chapter introduces Dart's package management system pub, the dependency configuration file pubspec.yaml, import/export syntax, and how to create custom libraries.
pubspec.yaml Configuration
pubspec.yaml is the core configuration file for every Dart project, declaring the project's metadata and dependencies.
A typical pubspec.yaml file structure is as follows:
Example
name: my_dart_app # Project name (required, lowercase + underscore)
description: A Dart example project# Project description
version: 1.0.0 # Version number
# publish_to: none # If you don't want to publish to pub.dev, uncomment this line
environment:
sdk: '>=3.0.0 <4.0.0' # Dart SDK version range
dependencies:
# Third-party packages that the project depends on at runtime
http: ^1.1.0 # HTTP client
path: ^1.8.0 # Path utilities
dev_dependencies:
# Dependencies needed only during development (testing, linting, etc.)
test: ^1.24.0 # Testing framework
lints: ^2.0.0 # Official lint rules
# Optional: executable entry point
# executables:
# my_app: main
Explanation of version number syntax:
| Syntax | Meaning | Example |
|---|---|---|
| ^1.1.0 | Compatible with 1.1.0 to 2.0.0 (exclusive) | Most commonly used, recommended |
| 1.1.0 | Exact version | Not very flexible |
| >=1.1.0 <1.5.0 | Version range | Use when precise control is needed |
| any | Any version | Not recommended |
The caret symbol (^) is Dart's default version constraint method. ^1.1.0 is equivalent to >=1.1.0 <2.0.0. This means minor and patch versions can be upgraded automatically, but major versions cannot be upgraded (major version upgrades may include breaking changes).
Install dependencies
$ dart pub get # 安装依赖 $ dart pub upgrade # 升级依赖到最新兼容版本 $ dart pub outdated # 查看哪些依赖有更新
Find dependencies on pub.dev
pub.dev is the official package repository for Dart and Flutter, similar to npm (JavaScript) or PyPI (Python).
You canhttps://pub.devSearch and browse tens of thousands of Dart packages.
Examples of common third-party packages:
| Package name | Purpose | Description |
|---|---|---|
| http | HTTP request | Officially maintained HTTP client |
| path | Path operations | Cross-platform path processing tool |
| test | Unit Testing | Official testing framework |
| json_serializable | JSON serialization | Automatically generate JSON conversion code |
| dio | HTTP client | A more powerful third-party HTTP library than http. |
| riverpod | State management | Popular state management solution for Flutter projects |
Example
Add the http package and use it:
// http: ^1.1.0
// Then run: dart pub get
import 'package:http/http.dart' as http;
void main() async {
// Send a GET request
var url = Uri.parse('https://www.example.com');
var response = await http.get(url);
print('Status code: ${response.statusCode}');
print('Response body length: ${response.body.length} characters');
}
import syntax
import is used to bring in code from other libraries into a Dart file.
Dart supports multiple import styles, each suited to different scenarios.
Import Dart built-in libraries
Example
import 'dart:math'; // Math library (random numbers, trigonometric functions, etc.)
import 'dart:convert'; // Encoding conversion library (JSON, Base64, etc.)
import 'dart:io'; // I/O library (files, network, etc.)
void main() {
// Using functions from dart:math
print('Pi: $pi');
print('2 to the 10th power: ${pow(2, 10)}');
// Using functions from dart:convert
var jsonStr = '{"name": "example", "age": 10}';
var decoded = jsonDecode(jsonStr);
print('Parsed JSON: $decoded');
print('Username: ${decoded['name']}');
}
圆周率: 3.141592653589793
2 的 10 次方: 1024
解析后的 JSON: {name: example, age: 10}
用户名: example
Import third-party packages
Example
import 'package:http/http.dart';
import 'package:path/path.dart' as p;
Import local files
Example
import 'src/utils.dart'; // Subdirectory under the same directory
import '../models/user.dart'; // File in the parent directory
import 'constants.dart'; // File in the same directory
Modifiers of import
| Modifiers | Syntax | Purpose |
|---|---|---|
| Prefix (as) | import 'lib.dart' as myLib; | Give the library an alias to avoid naming conflicts |
| Import only part (show) | import 'lib.dart' show foo, bar; | Import only the specified names |
| Exclude part (hide) | import 'lib.dart' hide foo; | Import everything except the specified names |
| Lazy loading (deferred as) | import 'lib.dart' deferred as lib; | Load on demand to reduce startup time |
Example
Practical usage of various import modifiers:
import 'dart:math' as math; // Prefix import: use math.pow() instead of pow()
import 'dart:math' show pi, sqrt; // Import only pi and sqrt
import 'dart:math' hide Random; // Import everything except Random
void main() {
// Standard import
print('sin(0) = ${sin(0)}');
// Prefix import: needs to be accessed via prefix
print('cos(0) = ${math.cos(0)}');
// show import: only pi and sqrt can be used
print('π = $pi');
print('sqrt(16) = ${sqrt(16)}');
// print(sin(0)); // Error: sin is not imported
// hide import: Random is unavailable, everything else is available
print('max(3, 7) = ${max(3, 7)}');
// Random(); // Error: Random is excluded
}
sin(0) = 0.0 cos(0) = 1.0 π = 3.141592653589793 sqrt(16) = 4.0 max(3, 7) = 7
show and hide are not just for convenience—they are also tools for code quality. Using show makes dependencies clearer: you can see at a glance which symbols from the library this file uses.
export syntax
export is used to re-expose the public APIs of other libraries, mainly for creating aggregate libraries.
Suppose you have the following file structure:
lib/ ├── my_package.dart # 入口文件(聚合导出) ├── src/ │ ├── models/ │ │ └── user.dart # User 类 │ ├── services/ │ │ └── api_service.dart # API 服务 │ └── utils/ │ └── helpers.dart # 工具函数
Example
Using export to create an aggregate library entry point:
// Aggregate export: uniformly exposes all public APIs
export 'src/models/user.dart';
export 'src/services/api_service.dart';
// Only export part of the contents in helpers
export 'src/utils/helpers.dart' show formatDate, validateEmail;
This way, users only need to import one file:
Example
import 'package:my_package/my_package.dart';
void main() {
// Can directly use all exported classes
var user = User('example');
var api = ApiService();
}
Creation of custom libraries
Creating a custom library requires no special syntax—every Dart file is a library.
However, you can use the library keyword to explicitly declare a library name, and use part/part of to split large libraries.
Using library to declare a library name
Example
// library declares the library name (optional, but helpful for documentation)
library calculator;
/// Addition operation
int add(int a, int b) => a + b;
/// Subtraction operation
int subtract(int a, int b) => a - b;
/// Multiplication operation
int multiply(int a, int b) => a * b;
/// Division operation, throws an exception when the divisor is 0
double divide(int a, int b) {
if (b == 0) {
throw ArgumentError(Divisor cannot be 0);
}
return a / b;
}
Use part to split large libraries
When a library's code is too long, you can use part to split it into multiple physical files, but they still logically belong to the same library.
Example
Main file declares part:
// Main library file
library user_system;
// Declare the other files that make up this library
part 'src/user_model.dart';
part 'src/user_service.dart';
part 'src/user_validator.dart';
// Library-level public API
String libraryVersion = '1.0.0';
Example
part file declares part of:
// part of declares which library this file belongs to
part of '../user_system.dart';
// Can access libraryVersion in the main library
class User {
String name;
User(this.name);
void printVersion() {
print('EXAMPLE User module version: $libraryVersion');
}
}
part/part of is not commonly used in modern Dart development; most teams prefer import/export to organize code. The downside of part is that part files share all private members, breaking encapsulation. Unless there is a clear need, import/export is recommended.
Dart core library overview
| Library name | Import method | Main Features |
|---|---|---|
| dart:core | Automatic import | Basic types, collections, exceptions, etc. |
| dart:math | import 'dart:math'; | Mathematical constants and functions |
| dart:convert | import 'dart:convert'; | JSON, UTF-8, Base64 encoding/decoding |
| dart:io | import 'dart:io'; | File, network, and process operations |
| dart:async | import 'dart:async'; | Asynchronous tools such as Future, Stream, etc. |
| dart:collection | import 'dart:collection'; | More collection types (Queue, etc.) |
| dart:developer | import 'dart:developer'; | Debugging and performance analysis tools |
dart:core is automatically imported, so you don't need to write import 'dart:core' to use basic types like int, String, List, Map, etc.
other extensions