2、开发环境搭建:Flutter SDK配置、Android Studio与Xcode环境准备、创建第一个插件项目、插件项目结构解析
说实话,做Flutter插件开发,环境搭建这一步最容易被忽视。很多人觉得不就是装个SDK嘛,结果一上手就各种报错。我当年第一次配Flutter环境时,光Android Studio的坑就踩了整整一下午。今天咱们就把这事彻底捋清楚。
2.1 Flutter SDK配置:别小看这一步
Flutter SDK的安装,说白了就是下载+解压+配环境变量。但这里有几个细节,我建议你注意一下。
第一步:下载SDK
去Flutter官网下载对应你操作系统的SDK包。我个人习惯用稳定版(stable channel),别追新,beta版有时候会出幺蛾子。
第二步:解压与路径
解压后放到一个你记得住的位置。比如我放在 /Users/你的用户名/development/flutter。注意路径里不要有中文和空格,否则后面编译时会哭的。
第三步:配置环境变量
以macOS为例,编辑 ~/.zshrc 或 ~/.bash_profile:
export FLUTTER_HOME=/Users/你的用户名/development/flutter
export PATH=$PATH:$FLUTTER_HOME/bin
然后执行 source ~/.zshrc 让它生效。验证一下:
flutter doctor
嗯,这里要注意。第一次跑 flutter doctor 时,它会下载一些依赖,耐心等它跑完。如果卡住了,多半是网络问题,换个镜像源试试。
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
这样下载依赖会快很多。
2.2 Android Studio与Xcode环境准备
Flutter插件开发,Android端和iOS端都得照顾到。所以这两个IDE都得配好。
Android Studio配置
安装Android Studio后,记得装Flutter和Dart插件。怎么装?打开Android Studio,Preferences -> Plugins,搜索Flutter,安装。它会顺带把Dart插件也装上。
然后配置Android SDK。我个人习惯用命令行:
flutter config --android-sdk /Users/你的用户名/Library/Android/sdk
再跑一次 flutter doctor,看看Android toolchain那项是不是绿色的勾。如果是黄色感叹号,点开看看缺什么,一般就是缺某个版本的SDK或NDK。
Xcode配置
macOS用户必须装Xcode。从App Store下载就行,十几G,提前准备好空间。
装完后,打开Xcode,同意协议,然后安装命令行工具:
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
sudo xcodebuild -runFirstLaunch
跑 flutter doctor 时,iOS toolchain那项应该就是绿色的了。如果提示CocoaPods没装,执行:
sudo gem install cocoapods
嗯,这里要注意。CocoaPods版本太旧也会报错,建议装最新版。
2.3 创建第一个插件项目
环境配好了,咱们来创建第一个Flutter插件项目。Flutter提供了专门的命令:
flutter create --template=plugin --org com.example my_first_plugin
解释一下参数:
--template=plugin:指定创建的是插件项目,不是普通应用--org com.example:组织标识,相当于Android的包名my_first_plugin:项目名称,建议全小写加下划线
命令执行后,你会看到类似这样的输出:
Creating project my_first_plugin...
my_first_plugin/CHANGELOG.md
my_first_plugin/LICENSE
my_first_plugin/README.md
my_first_plugin/analysis_options.yaml
my_first_plugin/my_first_plugin.iml
my_first_plugin/pubspec.yaml
my_first_plugin/lib/my_first_plugin.dart
my_first_plugin/lib/my_first_plugin_platform_interface.dart
my_first_plugin/lib/my_first_plugin_method_channel.dart
my_first_plugin/android/
my_first_plugin/ios/
my_first_plugin/example/
看到这个结构,说明项目创建成功了。我个人习惯先跑一下 flutter pub get,把依赖拉下来。
flutter create --template=plugin_ffi --org com.example my_ffi_plugin
不过咱们这门课主要讲Method Channel,所以用默认模板就行。
2.4 插件项目结构解析
项目创建好了,咱们来看看每个文件是干嘛的。我当年第一次看到这个结构时,也是一脸懵。别急,咱们一个一个说。
核心文件一览
| 文件/目录 | 作用 |
|---|---|
lib/my_first_plugin.dart |
插件的主入口,Dart端API定义 |
lib/my_first_plugin_platform_interface.dart |
平台接口抽象类,定义统一API |
lib/my_first_plugin_method_channel.dart |
Method Channel的具体实现 |
android/ |
Android原生代码目录(Kotlin/Java) |
ios/ |
iOS原生代码目录(Swift/Objective-C) |
example/ |
示例应用,用来测试插件 |
pubspec.yaml |
插件的配置文件,声明依赖、版本等 |
重点文件详解
lib/my_first_plugin.dart:这是插件的门面。用户调用插件时,直接import这个文件。里面一般会暴露一个类,比如 MyFirstPlugin,提供静态方法。
lib/my_first_plugin_platform_interface.dart:这个文件定义了一个抽象类。为什么要这么设计?说白了,就是为了支持多平台。你想想看,Android和iOS的实现肯定不一样,但对外暴露的API得统一。这个抽象类就是那个“统一接口”。
lib/my_first_plugin_method_channel.dart:这里才是真正干活的地方。它实现了上面那个抽象类,通过Method Channel跟原生端通信。代码里你会看到类似这样的结构:
class MyFirstPluginMethodChannel extends MyFirstPluginPlatform {
final MethodChannel _channel = const MethodChannel('com.example/my_first_plugin');
@override
Future<String?> getPlatformVersion() async {
final version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
}
嗯,这里要注意。Method Channel的名字要跟原生端保持一致,否则通信会失败。我见过有人把名字写错,排查了半天。
android/ 和 ios/ 目录:这两个目录里放着原生代码。Android端默认用Kotlin,iOS端默认用Swift。打开看看,你会发现一个类似 MyFirstPlugin.kt 或 MyFirstPlugin.swift 的文件,里面就是处理Method Channel调用的地方。
example目录的作用
每个插件项目都会自动生成一个 example/ 目录,里面是一个完整的Flutter应用。这个应用引用了你的插件,用来测试。我个人习惯在开发插件时,直接修改example里的代码来调试,这样不用额外建项目。
打开 example/lib/main.dart,你会看到它调用了插件的方法:
import 'package:my_first_plugin/my_first_plugin.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Plugin Example')),
body: Center(
child: FutureBuilder<String?>(
future: MyFirstPlugin.platformVersion,
builder: (context, snapshot) {
return Text('Running on: ${snapshot.data}');
},
),
),
),
);
}
}
看到了吗?这就是插件的基本用法。用户只需要调用 MyFirstPlugin.platformVersion,就能拿到原生平台的版本信息。至于底层怎么通信的,用户完全不用关心。
好了,环境搭建和项目结构就讲到这里。下一章咱们会深入Method Channel,看看Dart和原生代码到底是怎么“聊天”的。到时候我会分享一些我在实际项目中遇到的坑,保证让你少走弯路。