Flutter 插件开发与包发布

Flutter 自定义插件开发:从 Package 到 Plugin,pub.dev 发布流程,Dart FFI 扩展,代码生成与本地插件接入。

开篇:Flutter 生态的力量

Flutter 的繁荣很大程度上归功于其包生态系统。pub.dev 上有超过 3 万个包,覆盖了从 UI 组件到原生能力扩展的方方面面。作为 Flutter 开发者,你不仅要学会使用这些包,更要掌握如何创建和发布自己的包——这既能解决重复代码的问题,也是回馈社区的方式。

本章将介绍两种包类型:纯 Dart 的 Package(可跨所有平台)和包含原生代码的 Plugin(Android/iOS/macOS/Linux/Windows/Web),并涵盖发布流程、版本管理和最佳实践。


一、Package 与 Plugin 的区别

类型组成平台支持典型例子
Dart Package纯 Dart 代码所有平台dio, intlx, animations
Flutter PackageDart + 资源文件所有平台flutter_svg, lottie
Plugin PackageDart + 原生代码 + 多平台实现支持的平台path_provider, shared_preferences

一句话总结:纯逻辑用 Package,需要原生能力用 Plugin,优先 Dart 实现以降低维护成本。


二、创建 Dart Package

# 创建纯 Dart 包
flutter create --template=package my_utils

cd my_utils
// lib/my_utils.dart
library my_utils;

export 'src/validators.dart';
export 'src/formatters.dart';
export 'src/extensions.dart';

// lib/src/validators.dart
class Validators {
  static bool isValidEmail(String email) {
    return RegExp(r'^[\w.-]+@[\w.-]+\.\w+$').hasMatch(email);
  }
  
  static bool isValidPhone(String phone) {
    return RegExp(r'^1[3-9]\d{9}$').hasMatch(phone);
  }
  
  static bool isEmpty(String? value) => value?.trim().isEmpty ?? true;
}

// lib/src/extensions.dart
extension StringUtils on String {
  String truncate(int length, {String suffix = '...'}) {
    if (this.length <= length) return this;
    return '${substring(0, length - suffix.length)}$suffix';
  }
  
  String capitalize() {
    if (isEmpty) return this;
    return '${this[0].toUpperCase()}${substring(1)}';
  }
}

// test/my_utils_test.dart
import 'package:test/test.dart';
import 'package:my_utils/my_utils.dart';

void main() {
  group('Validators', () {
    test('validates email correctly', () {
      expect(Validators.isValidEmail('test@example.com'), isTrue);
      expect(Validators.isValidEmail('invalid'), isFalse);
    });
  });
  
  group('String extensions', () {
    test('truncates long strings', () {
      expect('Hello World'.truncate(8), equals('Hello...'));
    });
  });
}

一句话总结:Dart Package 的 src/ 目录下文件默认私有,通过 lib/ 根级的 export 暴露公共 API,是最清晰的模块边界设计。


三、创建 Plugin

# 创建多平台插件
flutter create --template=plugin \
  --platforms=android,ios,macos,windows,linux \
  flutter_my_sensor
// lib/flutter_my_sensor.dart
import 'flutter_my_sensor_platform_interface.dart';

class FlutterMySensor {
  Future<SensorData> readAccelerometer() async {
    return FlutterMySensorPlatform.instance.readAccelerometer();
  }
}

// lib/flutter_my_sensor_platform_interface.dart
abstract class FlutterMySensorPlatform extends PlatformInterface {
  // ...
  Future<SensorData> readAccelerometer();
}

一句话总结:Flutter Plugin 遵循"Dart API + Platform Interface + 各平台实现"的分层架构,确保多平台的一致性和可测试性。


四、pub.dev 发布流程

# pubspec.yaml
name: my_awesome_package
version: 1.0.0
description: A brief description of my package
homepage: https://github.com/username/package
repository: https://github.com/username/package
issue_tracker: https://github.com/username/package/issues

dependencies:
  flutter:
    sdk: flutter

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  # 如果有资源文件
# 1. 登录 pub.dev(仅首次)
flutter pub login

# 2. 检查包质量
flutter pub publish --dry-run

# 3. 正式发布
flutter pub publish

# 4. 版本遵循 SemVer
# 1.0.0 -> 1.0.1 (patch) -> 1.1.0 (minor) -> 2.0.0 (major)

五、代码生成(build_runner)

# pubspec.yaml
dependencies:
  json_annotation: ^4.8.0
  freezed_annotation: ^2.4.0

dev_dependencies:
  build_runner: ^2.4.0
  json_serializable: ^6.7.0
  freezed: ^2.4.0
// lib/models/user.dart
import 'package:freezed_annotation/freezed_annotation.dart';

part 'user.freezed.dart';
part 'user.g.dart';

@freezed
class User with _$User {
  const factory User({
    required String id,
    required String name,
    String? email,
    @Default([]) List<String> roles,
  }) = _User;
  
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

// 生成代码
// dart run build_runner build --delete-conflicting-outputs

一句话总结:build_runner 配合注解消除了 Dart 中的样板代码,代码生成已成为 Flutter 生态的标准实践。


FAQ

Q1: Package 命名有什么规范?

  • 小写 + 下划线分隔(snake_case)
  • 前缀避免与他人冲突(如 companyname_feature
  • 描述性:一看就知道用途

Q2: 如何管理本地依赖?

dependencies:
  my_local_package:
    path: ../my_local_package
  
  git_package:
    git:
      url: https://github.com/user/repo.git
      ref: main

Q3: 发布后发现 bug 怎么办?

  • 紧急修复:发布 patch 版本(1.0.1)
  • 回退版本:flutter pub downgrade my_package(用户侧)
  • 撤回包:pub.dev 不支持撤回,只能发布新版本修复

相关阅读

  • https://plumephp.com/flutter-platform-channels/ — 平台通道与原生交互

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「Flutter」更多文章

  1. Widget 体系与布局系统
  2. Flutter 状态管理全解析
  3. Flutter 测试策略与自动化