测试实践
1. 单元测试
// test/example_plugin_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:example_plugin/example_plugin.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('ExamplePlugin', () {
late ExamplePlugin plugin;
setUp(() {
plugin = ExamplePlugin();
});
test('getPlatformVersion returns correct version', () async {
expect(
await plugin.getPlatformVersion(),
contains(RegExp(r'^iOS|Android'))
);
});
});
}
2. 集成测试
// example/integration_test/plugin_integration_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:example_plugin/example_plugin.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('verify platform integration', (tester) async {
final plugin = ExamplePlugin();
final version = await plugin.getPlatformVersion();
expect(version.isNotEmpty, true);
});
}
性能优化
1. 内存管理
// lib/src/resource_manager.dart
class ResourceManager {
static final _instance = ResourceManager._internal();
final _cache = <String, WeakReference<dynamic>>{};
factory ResourceManager() {
return _instance;
}
ResourceManager._internal();
void cacheResource(String key, dynamic resource) {
_cache[key] = WeakReference(resource);
}
dynamic getResource(String key) {
final ref = _cache[key];
return ref?.target;
}
void clearCache() {
_cache.clear();
}
}
2. 性能监控
// lib/src/performance_monitor.dart
class PerformanceMonitor {
static final _timers = <String, Stopwatch>{};
static void startOperation(String name) {
final timer = Stopwatch()..start();
_timers[name] = timer;
}
static Duration endOperation(String name) {
final timer = _timers.remove(name);
timer?.stop();
return timer?.elapsed ?? Duration.zero;
}
static void logMetrics() {
// 实现性能指标记录
}
}
发布管理
1. 版本控制
## pubspec.yaml
name: example_plugin
version: 1.0.0
description: A Flutter plugin example.
environment:
sdk: ">=2.12.0 <3.0.0"
flutter: ">=2.0.0"
2. 发布流程
- 更新版本号
- 更新 CHANGELOG.md
- 运行测试套件
- 发布到 pub.dev
## CHANGELOG.md
## 1.0.0
* 初始版本发布
* 实现基本功能
* 添加单元测试
错误处理
1. 异常定义
// lib/src/exceptions.dart
class PluginException implements Exception {
final String message;
final String? code;
final dynamic details;
PluginException(this.message, {this.code, this.details});
@override
String toString() => 'PluginException($code): $message';
}
2. 错误日志
// lib/src/logger.dart
class Logger {
static void debug(String message) {
_log('DEBUG', message);
}
static void error(String message, {dynamic error, StackTrace? stackTrace}) {
_log('ERROR', message, error: error, stackTrace: stackTrace);
}
static void _log(String level, String message, {
dynamic error,
StackTrace? stackTrace,
}) {
// 实现日志记录逻辑
}
}
国际化支持
1. 多语言配置
// lib/src/i18n/messages.dart
class Messages {
static final Map<String, Map<String, String>> _localizedValues = {
'en': {
'error_not_supported': 'Feature not supported',
'error_permission_denied': 'Permission denied',
},
'zh': {
'error_not_supported': '功能不支持',
'error_permission_denied': '权限被拒绝',
},
};
static String getString(String key, String languageCode) {
return _localizedValues[languageCode]?[key] ?? key;
}
}
2. 本地化实现
// lib/src/i18n/localization.dart
class PluginLocalization {
final String languageCode;
PluginLocalization(this.languageCode);
String getString(String key) {
return Messages.getString(key, languageCode);
}
static PluginLocalization of(BuildContext context) {
final locale = Localizations.localeOf(context);
return PluginLocalization(locale.languageCode);
}
}
最佳实践
1. 接口按”平台能力交集”设计,特有能力用能力查询暴露
5 个平台能力永远不对齐(见实战案例的全屏/音量坑)。接口设计取交集;平台特有能力用 supportsX() 这类能力查询暴露,让 App 侧自己判断。反例:Windows 不支持就抛 notImplemented 让 App 崩——不支持不是异常,是预期内的分支。
2. Dart 层用 plugin_platform_interface 做抽象
别让 App 直接裸调 MethodChannel。包一层 PlatformInterface,社区 fork 才能写 mock/fake 做单元测试,你自己写 example 测试也不用起真机。这是”高级”和”能用”插件的分水岭。
3. CHANGELOG 的 breaking change 必须写迁移步骤
发 2.0.0 只写”重构 API”,等于逼用户去 diff 源码。每个 breaking change 写清”旧写法 → 新写法”三行代码,用户升级意愿差一个数量级。README 同理:放一个能直接跑的最小示例,而不是 API 列表。
4. 小插件别玩 federated 拆分
接口包 + 各平台实现包分离(federated 结构)是给 video_player 这种体量的准备的。小插件拆成 6 个包,改个方法名要发 6 个版本,维护成本翻倍。先一个包做到 1.0,真有人要单独维护某平台实现时再拆。
实战案例
插件”高级”在哪?我的体会是平台差异的收敛。flutter_aj_player 要支持 5 个平台,但各平台能力不一样:Android/iOS 有系统级全屏,Windows/macOS 的窗口全屏逻辑完全不同。我用 auto_orientation 处理移动端转屏,桌面端则靠 window_manager(在 flutter_brightness 项目里验证过)。另一个高级点是手势:brightness_volume 实现的”左侧上下滑调亮度、右侧调音量”,在 iOS 上要处理系统音量 HUD 的冲突——调系统音量时会弹出苹果那个硕大的音量框,体验很割裂,最后只能降级为应用内音量。跨平台插件的”高级”,本质是给每个平台的”不行”找替代方案。
总结
Flutter 插件开发高级特性的关键点:
- 完善的测试覆盖
- 优秀的性能表现
- 规范的发布流程
- 健壮的错误处理
- 完整的国际化支持
通过本系列文章的学习,你应该已经掌握了 Flutter 插件开发的全部要点,能够开发出高质量的跨平台插件。
