在前面的学习中,我们掌握了 Dart 的核心语法和异步编程等关键技术。随着项目规模扩大,代码的组织和复用变得越来越重要。今天我们要学习的库与包管理,就是解决代码模块化、复用和依赖管理的核心知识,也是高效开发的关键。
一、自定义库:模块化组织代码
在 Dart 中,库(Library) 是代码组织的基本单位,一个库可以包含多个类、函数、变量等,通过 library
、export
、part
等关键字实现代码的拆分与组合。
1. 基础库定义与导入
最简单的库就是一个 Dart 文件,我们可以直接通过 import
语句导入使用:
dart
// 定义一个工具库(utils.dart)
class StringUtils {
static bool isEmpty(String? str) => str == null || str.trim().isEmpty;
}
int add(int a, int b) => a + b;
dart
// 导入并使用库(main.dart)
import 'utils.dart'; // 导入当前目录下的 utils.dart
void main() {
print(StringUtils.isEmpty("")); // 输出:true
print(add(2, 3)); // 输出:5
}
import
路径规则:
- 相对路径:
'utils.dart'
(同一目录)、'lib/utils.dart'
(子目录) - 绝对路径:从项目根目录开始的完整路径(较少使用)
- 包路径:
'package:my_project/utils.dart'
(用于 pub 包中的库)
2. 控制可见性:_ 私有成员
在库中,用 _
开头的成员(类、函数、变量)是私有成员,仅在当前库中可见:
dart
// 工具库(utils.dart)
class _PrivateClass {
void method() => print("私有类方法");
}
void publicFunction() => print("公共函数");
void _privateFunction() => print("私有函数");
dart
// 导入使用(main.dart)
import 'utils.dart';
void main() {
publicFunction(); // 正常调用:公共函数
// _privateFunction(); // 编译错误:无法访问私有函数
// var obj = _PrivateClass(); // 编译错误:无法访问私有类
}
私有成员机制确保了库的封装性,只暴露必要的接口。
3. 库的拆分与组合:part/part of
当一个库的代码量很大时,可以拆分成多个文件,用 part
和 part of
关联:
dart
// 主库(math_operations.dart)
library math_operations; // 声明库名
part 'add_operation.dart'; // 引入拆分的文件
part 'subtract_operation.dart';
int multiply(int a, int b) => a * b;
dart
// 拆分文件(add_operation.dart)
part of math_operations; // 声明属于哪个库
int add(int a, int b) => a + b;
dart
// 拆分文件(subtract_operation.dart)
part of math_operations;
int subtract(int a, int b) => a - b;
dart
// 使用组合后的库(main.dart)
import 'math_operations.dart';
void main() {
print(add(5, 3)); // 输出:8(来自 add_operation.dart)
print(subtract(5, 3)); // 输出:2(来自 subtract_operation.dart)
print(multiply(5, 3)); // 输出:15(来自主库)
}
这种方式适合将一个逻辑紧密的库拆分成多个文件,保持代码结构清晰。
4. 导出库:export 与库的聚合
通过 export
可以将一个库的内容导出,供其他库导入,实现库的聚合:
dart
// 基础工具库(base_utils.dart)
class Logger {
static void log(String message) => print("[Log] $message");
}
dart
// 扩展工具库(extended_utils.dart)
export 'base_utils.dart'; // 导出基础工具库
class Validator {
static bool isNumber(String str) => double.tryParse(str) != null;
}
dart
// 使用(main.dart)
import 'extended_utils.dart'; // 只需导入扩展库
void main() {
Logger.log("测试日志"); // 可用:来自 base_utils.dart(被导出)
print(Validator.isNumber("123")); // 可用:来自 extended_utils.dart
}
export
常用于创建 "聚合库",让使用者通过一个入口导入多个相关库。
二、pubspec.yaml:包配置与依赖管理
Dart 生态通过包(Package) 共享代码,每个包都有一个 pubspec.yaml
文件,用于描述包信息和依赖关系。
1. pubspec.yaml 的基本结构
一个典型的 pubspec.yaml
如下:
yaml
name: my_app # 项目/包名称(必填)
description: A sample Dart application. # 描述(可选)
version: 1.0.0 # 版本号(遵循语义化版本:主版本.次版本.修订号)
environment:
sdk: '>=3.0.0 <4.0.0' # 支持的 Dart SDK 版本范围
dependencies:
# 生产环境依赖(必填时添加)
http: ^0.13.5 # HTTP 网络请求库
path: ^1.8.3 # 路径处理库
dev_dependencies:
# 开发环境依赖(仅开发时使用)
lints: ^2.1.0 # 代码规范检查工具
test: ^1.24.0 # 测试框架
2. 依赖管理:pub get 与版本规则
配置好依赖后,执行以下命令安装依赖:
bash
dart pub get
命令会:
- 从 pub.dev(Dart 官方包仓库)下载指定依赖
- 生成
pubspec.lock
文件,记录依赖的精确版本 - 将依赖缓存到本地(
~/.pub-cache/
)
版本号遵循语义化版本,常用规则:
^1.2.3
:兼容 1.2.3 及以上但低于 2.0.0 的版本(推荐)1.2.3
:精确匹配 1.2.3 版本>=1.2.3 <1.3.0
:指定版本范围
3. 导入包中的库
安装依赖后,通过 package:
路径导入包中的库:
dart
// 导入 http 包中的库
import 'package:http/http.dart' as http;
// 导入 path 包中的库
import 'package:path/path.dart' as path;
void main() async {
// 使用 http 库发送网络请求
final response = await http.get(Uri.parse('https://api.example.com'));
print('HTTP 状态码:${response.statusCode}');
// 使用 path 库处理路径
final joinedPath = path.join('dir', 'file.txt');
print('拼接路径:$joinedPath'); // 输出:dir/file.txt
}
as http
为库指定前缀,避免不同库中同名成员的冲突。
4. 依赖覆盖与本地包
开发中可能需要:
- 覆盖依赖版本(如使用某个包的特定分支)
- 依赖本地开发的包
可以在 pubspec.yaml
中配置:
yaml
dependencies:
# 依赖本地包(相对路径)
my_local_package:
path: ../my_local_package
# 依赖 Git 仓库中的包
my_git_package:
git:
url: https://github.com/username/my_git_package.git
ref: main # 分支/标签/commit
三、常用官方与热门包介绍
Dart 生态有大量高质量的包,以下是开发中常用的几个:
1. http:网络请求
http
是官方维护的 HTTP 客户端库,用于发送 GET、POST 等网络请求:
dart
import 'package:http/http.dart' as http;
void main() async {
// 发送 GET 请求
final response = await http.get(
Uri.parse('https://jsonplaceholder.typicode.com/todos/1'),
);
if (response.statusCode == 200) {
print('响应内容:${response.body}');
} else {
print('请求失败,状态码:${response.statusCode}');
}
}
2. path:路径处理
path
库提供跨平台的路径处理工具,解决不同操作系统(Windows/Linux/macOS)路径格式差异:
dart
import 'package:path/path.dart' as path;
void main() {
// 拼接路径
print(path.join('home', 'user', 'file.txt')); // 输出:home/user/file.txt
// 获取文件名
print(path.basename('/home/user/file.txt')); // 输出:file.txt
// 获取文件扩展名
print(path.extension('image.jpg')); // 输出:.jpg
}
3. json_serializable:JSON 序列化
json_serializable
是处理 JSON 序列化的热门包,通过代码生成实现类型安全的 JSON 转换:
- 配置依赖:
yaml
dependencies:
json_annotation: ^4.8.1
dev_dependencies:
build_runner: ^2.4.4
json_serializable: ^6.7.1
- 定义模型类:
dart
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart'; // 生成的代码文件
@JsonSerializable()
class User {
final String name;
final int age;
User({required this.name, required this.age});
// 生成 JSON 转对象的方法
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
// 生成对象转 JSON 的方法
Map<String, dynamic> toJson() => _$UserToJson(this);
}
- 生成代码:
bash
dart run build_runner build
- 使用:
dart
import 'user.dart';
void main() {
// JSON 字符串转对象
final json = '{"name":"Alice", "age":20}';
final user = User.fromJson(jsonDecode(json));
print('${user.name}, ${user.age}'); // 输出:Alice, 20
// 对象转 JSON
final userJson = user.toJson();
print(userJson); // 输出:{name: Alice, age: 20}
}
4. lints:代码规范检查
lints
提供代码规范检查,帮助团队保持一致的代码风格:
- 配置依赖后,创建
analysis_options.yaml
:
yaml
include: package:lints/recommended.yaml
- 运行检查:
bash
dart analyze
工具会提示代码中不符合规范的地方(如未使用的变量、不规范的命名等)。
四、库与包的最佳实践
- 合理拆分库:一个库专注于单一功能,避免过大或过小。
- 明确导出接口 :用
export
精心设计库的公共接口,隐藏内部实现。 - 控制依赖数量:只依赖必要的包,避免过度依赖导致项目臃肿。
- 指定精确版本 :在
pubspec.yaml
中使用^
约束版本,平衡兼容性和稳定性。 - 定期更新依赖 :通过
dart pub outdated
检查可更新的依赖,及时修复安全问题。