现在位置: 首页 > Dart 教程 > 正文

Dart 包与库管理

当项目规模增长时,将代码拆分为多个文件和模块是必不可少的。

本章介绍 Dart 的包管理系统 pub、依赖配置文件 pubspec.yaml、import/export 语法以及如何创建自定义库。


pubspec.yaml 配置

pubspec.yaml 是每个 Dart 项目的核心配置文件,声明了项目的元数据和依赖。

一个典型的 pubspec.yaml 文件结构如下:

实例

# 文件路径:pubspec.yaml
name
: my_dart_app              # 项目名称(必填,小写+下划线)
description
: 一个 Dart 示例项目  # 项目描述
version
: 1.0.0                 # 版本号
# publish_to: none             # 如果不想发布到 pub.dev,取消这行注释

environment
:
  sdk
: '>=3.0.0 <4.0.0'      # Dart SDK 版本范围

dependencies
:
 # 项目运行时依赖的第三方包
  http
: ^1.1.0                # HTTP 客户端
  path
: ^1.8.0                # 路径工具

dev_dependencies
:
 # 仅开发时需要的依赖(测试、代码检查等)
  test
: ^1.24.0               # 测试框架
  lints
: ^2.0.0               # 官方 lint 规则

# 可选:可执行文件入口
# executables:
#   my_app: main

版本号的写法说明:

写法含义示例
^1.1.0兼容 1.1.0 到 2.0.0(不含)最常用,推荐使用
1.1.0精确版本不太灵活
>=1.1.0 <1.5.0版本范围需要精确控制时使用
any任意版本不推荐

^ 符号(caret)是 Dart 的默认版本约束方式。^1.1.0 等价于 >=1.1.0 <2.0.0。这意味着可以自动升级次版本和补丁版本,但不能升级主版本(主版本升级可能包含破坏性变更)。

安装依赖

$ dart pub get           # 安装依赖
$ dart pub upgrade       # 升级依赖到最新兼容版本
$ dart pub outdated      # 查看哪些依赖有更新

pub.dev 查找依赖

pub.dev 是 Dart 和 Flutter 的官方包仓库,类似于 npm(JavaScript)或 PyPI(Python)。

你可以在 https://pub.dev 上搜索和浏览数以万计的 Dart 包。

常用的第三方包举例:

包名用途说明
httpHTTP 请求官方维护的 HTTP 客户端
path路径操作跨平台的路径处理工具
test单元测试官方测试框架
json_serializableJSON 序列化自动生成 JSON 转换代码
dioHTTP 客户端比 http 更强大的第三方 HTTP 库
riverpod状态管理Flutter 项目的热门状态管理方案

实例

添加 http 包并使用:

// 首先在 pubspec.yaml 的 dependencies 中添加:
//   http: ^1.1.0
// 然后运行:dart pub get

import 'package:http/http.dart' as http;

void main() async {
  // 发送一个 GET 请求
  var url = Uri.parse('https://www.runoob.com');
  var response = await http.get(url);

  print('状态码: ${response.statusCode}');
  print('响应体长度: ${response.body.length} 字符');
}

import 导入语法

import 用于在一个 Dart 文件中引入其他库的代码。

Dart 支持多种导入方式,每种适用于不同的场景。

导入 Dart 内置库

实例

// dart: 前缀表示 Dart 内置的核心库
import 'dart:math';    // 数学库(随机数、三角函数等)
import 'dart:convert'; // 编码转换库(JSON、Base64 等)
import 'dart:io';      // I/O 库(文件、网络等)

void main() {
  // 使用 dart:math 中的函数
  print('圆周率: $pi');
  print('2 的 10 次方: ${pow(2, 10)}');

  // 使用 dart:convert 中的函数
  var jsonStr = '{"name": "runoob", "age": 10}';
  var decoded = jsonDecode(jsonStr);
  print('解析后的 JSON: $decoded');
  print('用户名: ${decoded['name']}');
}
圆周率: 3.141592653589793
2 的 10 次方: 1024
解析后的 JSON: {name: runoob, age: 10}
用户名: runoob

导入第三方包

实例

// package: 前缀表示 pub.dev 上的第三方包
import 'package:http/http.dart';
import 'package:path/path.dart' as p;

导入本地文件

实例

// 相对路径导入:导入同项目中的其他 Dart 文件
import 'src/utils.dart';          // 同目录下的子目录
import '../models/user.dart';    // 上级目录中的文件
import 'constants.dart';         // 同目录下的文件

import 的修饰符

修饰符语法用途
前缀(as)import 'lib.dart' as myLib;给库起别名,避免命名冲突
只导入部分(show)import 'lib.dart' show foo, bar;只导入指定的名称
排除部分(hide)import 'lib.dart' hide foo;导入除指定名称外的所有内容
延迟加载(deferred as)import 'lib.dart' deferred as lib;按需加载,减少启动时间

实例

各种导入修饰符的实际用法:

import 'dart:math';              // 标准导入
import 'dart:math' as math;      // 前缀导入:使用 math.pow() 而非 pow()
import 'dart:math' show pi, sqrt;  // 只导入 pi 和 sqrt
import 'dart:math' hide Random;    // 导入除 Random 外的所有内容

void main() {
  // 标准导入
  print('sin(0) = ${sin(0)}');

  // 前缀导入:需要通过前缀访问
  print('cos(0) = ${math.cos(0)}');

  // show 导入:只能使用 pi 和 sqrt
  print('π = $pi');
  print('sqrt(16) = ${sqrt(16)}');
  // print(sin(0));  // 错误:sin 没有被导入

  // hide 导入:Random 不可用,其他都可以
  print('max(3, 7) = ${max(3, 7)}');
  // Random();  // 错误:Random 被排除了
}
sin(0) = 0.0
cos(0) = 1.0
π = 3.141592653589793
sqrt(16) = 4.0
max(3, 7) = 7

show 和 hide 不只是为了便利——它们也是代码质量的工具。使用 show 可以让依赖关系更清晰:你能一眼看出这个文件使用了库中的哪些符号。


export 导出语法

export 用于将其他库的公开 API 重新暴露出去,主要用于创建聚合库。

假设你有以下文件结构:

lib/
├── my_package.dart          # 入口文件(聚合导出)
├── src/
│   ├── models/
│   │   └── user.dart        # User 类
│   ├── services/
│   │   └── api_service.dart # API 服务
│   └── utils/
│       └── helpers.dart     # 工具函数

实例

使用 export 创建聚合库入口:

// 文件:lib/my_package.dart
// 聚合导出:将所有公开 API 统一暴露

export 'src/models/user.dart';
export 'src/services/api_service.dart';
// 只导出 helpers 中的部分内容
export 'src/utils/helpers.dart' show formatDate, validateEmail;

这样使用者只需要导入一个文件:

实例

// 使用者只需导入入口文件
import 'package:my_package/my_package.dart';

void main() {
  // 可以直接使用所有 export 的类
  var user = User('runoob');
  var api = ApiService();
}

自定义库的创建

创建一个自定义库不需要特殊语法——每个 Dart 文件就是一个库。

但你可以使用 library 关键字明确声明库名,以及使用 part/part of 拆分大型库。

使用 library 声明库名

实例

// 文件:lib/calculator.dart
// library 声明库名(可选,但有助于文档化)
library calculator;

/// 加法运算
int add(int a, int b) => a + b;

/// 减法运算
int subtract(int a, int b) => a - b;

/// 乘法运算
int multiply(int a, int b) => a * b;

/// 除法运算,除数为 0 时抛出异常
double divide(int a, int b) {
  if (b == 0) {
    throw ArgumentError('除数不能为 0');
  }
  return a / b;
}

使用 part 拆分大型库

当一个库的代码太长时,可以用 part 将其拆分为多个物理文件,但它们在逻辑上仍然属于同一个库。

实例

主文件声明 part:

// 文件:lib/user_system.dart
// 主库文件
library user_system;

// 声明组成这个库的其他文件
part 'src/user_model.dart';
part 'src/user_service.dart';
part 'src/user_validator.dart';

// 库级别的公开 API
String libraryVersion = '1.0.0';

实例

part 文件声明 part of:

// 文件:lib/src/user_model.dart
// part of 声明自己属于哪个库
part of '../user_system.dart';

// 可以访问主库中的 libraryVersion
class User {
  String name;
  User(this.name);

  void printVersion() {
    print('RUNOOB User 模块版本: $libraryVersion');
  }
}

part/part of 在现代 Dart 开发中不常用,多数团队更倾向于使用 import/export 来组织代码。part 的缺点是 part 文件之间共享所有私有成员,破坏了封装性。除非有明确需求,否则推荐使用 import/export。


Dart 核心库速览

库名导入方式主要功能
dart:core自动导入基础类型、集合、异常等
dart:mathimport 'dart:math';数学常量和函数
dart:convertimport 'dart:convert';JSON、UTF-8、Base64 编解码
dart:ioimport 'dart:io';文件、网络、进程操作
dart:asyncimport 'dart:async';Future、Stream 等异步工具
dart:collectionimport 'dart:collection';更多集合类型(Queue 等)
dart:developerimport 'dart:developer';调试和性能分析工具

dart:core 是自动导入的,你不需要写 import 'dart:core' 就能使用 int、String、List、Map 等基础类型。