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。

我曾经踩过的坑: 有一次我忘了装Android SDK的command-line tools,结果flutter doctor一直报错。折腾了半天才发现是SDK Manager里没勾选。所以建议你打开SDK Manager,把Android SDK Command-line Tools (latest) 也装上。

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 3.x以上版本,默认创建的是FFI(Foreign Function Interface)插件模板。但咱们做原生通信,还是用Method Channel更直观。可以在创建时加个参数:
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.ktMyFirstPlugin.swift 的文件,里面就是处理Method Channel调用的地方。

核心理解: Flutter插件 = Dart端(API定义) + 原生端(平台实现)。Dart端通过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,就能拿到原生平台的版本信息。至于底层怎么通信的,用户完全不用关心。

我的建议: 刚开始学插件开发,别急着写复杂逻辑。先把这个默认模板跑起来,在Android模拟器和iOS模拟器上都试试。能正常显示平台版本,说明环境搭建成功了。我曾经见过有人配了三天环境,结果第一步就没跑通,心态直接崩了。

好了,环境搭建和项目结构就讲到这里。下一章咱们会深入Method Channel,看看Dart和原生代码到底是怎么“聊天”的。到时候我会分享一些我在实际项目中遇到的坑,保证让你少走弯路。