引言
作为一名 Flutter 开发者,当你封装了一个好用的组件或插件,想要将其开源给全球开发者使用时,发布到 pub.dev 是必经之路。然而,由于众所周知的网络原因以及官方严格的审核规范,很多开发者在“最后一公里”频频碰壁。
本文将结合我近期发布 summer_plugin 插件的真实经历,为你梳理一份详尽的 Pub.dev 发布避坑指南。我们将重点解决终端代理超时、镜像源误用、域名验证失败以及搜索索引延迟等核心痛点,带你一次性打通插件上架的通关秘籍!
环境准备与检查
开发同时兼容多个平台的 Flutter 插件,首要条件是配置好所有目标平台的开发环境。因为我们需要在不同平台上编译和调试原生代码,所以必须确保本地环境已正确安装相应的工具链。
你需要准备:Flutter SDK、Android Studio(包含 Android SDK)、Xcode(macOS 环境,用于 iOS)、Visual Studio(Windows 环境,需包含 C++ 桌面开发工作负载)。配置完成后,打开终端,运行以下命令以检查环境是否完备。
flutter doctor -v
创建插件
创建项目
flutter create --template=plugin --platforms=android,ios,windows -a kotlin -i swift summer_plugin
创建项目后会看到这样一个目录

编写Dart端通信接口
插件的核心在于 Dart 代码与各原生平台代码之间的通信。Flutter 提供了 MethodChannel 来实现跨平台的异步方法调用。
我们需要在 lib 目录下的 Dart 文件中定义通道名称,并暴露出供 Flutter 业务层调用的方法。以下代码展示了如何初始化 MethodChannel 并定义一个获取系统版本号的方法。
import 'dart:async';
import 'package:flutter/services.dart';
public class HelloPlugin {
// 定义通道名称,必须与各原生端保持完全一致
static const MethodChannel _channel =
const MethodChannel('hello_plugin');
// 暴露给 Flutter 层调用的异步方法
static Future<String?> getPlatformVersion() async {
// 通过 invokeMethod 调用原生端对应名称的方法
final String? version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
}
编写Android端
这里那Android端举个例子,为了让插件在 Android 设备上正常运行,我们需要在 Android 端监听 Dart 发来的方法请求并作出响应。
打开 android/src/main/kotlin/包名/SummerPlugin.kt 文件(此处包名根据你创建时的配置而定)。我们需要继承 FlutterPlugin 并实现 MethodCallHandler 接口,将 Kotlin 代码绑定到指定的 MethodChannel 上。
package com.example.summer_plugin
import android.os.Build
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
class HelloPlugin: FlutterPlugin, MethodCallHandler {
private lateinit var channel : MethodChannel
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
// 绑定与 Dart 端同名的通道
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "hello_plugin")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
// 匹配 Dart 端调用的方法名
if (call.method == "getPlatformVersion") {
// 返回 Android 系统版本号
result.success("Android ${Build.VERSION.RELEASE}")
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
flutter项目中使用
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:summer_plugin/summer_plugin.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
final _summerPlugin = SummerPlugin();
@override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
// We also handle the message potentially returning null.
try {
platformVersion =
await _summerPlugin.getPlatformVersion() ?? 'Unknown platform version';
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
Future<String>
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Text('Running on: $_platformVersion\n'),
),
),
);
}
}
插件本地测试
在将插件发布到 pub.dev 之前,必须进行全面的功能测试。Flutter 创建插件模板时,自动生成了一个 example 目录,里面包含了一个完整的 Flutter 应用工程,该工程已自动将刚刚编写的插件作为本地依赖引入。
进入 example 目录,运行代码以在不同平台上进行测试验证。
# 进入测试工程目录
cd example
# 或者在 Android 设备/模拟器上运行
flutter run -d <设备ID>
# 或者直接
flutter run
其他端亦是如此
发布插件到 pub.dev
# 第一步:检查包是否满足发布条件(确保没有静态错误)
flutter pub publish --dry-run
# 第二步:正式发布到 pub.dev
flutter pub publish --server=https://pub.dev
遇到的问题
下面的内容是作者的步骤
Q1:想要成功发布插件需要一个publisher,那这个publisher去哪里整?
在执行flutter pub publish --server=https://pub.dev这一步之前需要去pub.dev上用谷歌邮箱登陆一下,然后创建一个publisher

创建时需要你有一个域名,然后DNS解析把上面他提供的内容加进入


上面我选的是第一个

添加后就可以回到上一个页面开始验证了,成功后再把域名填入创建publisher时需要填的地方即可
这样就可以成功的创建一个publisher了
Q2:发布超时了怎么办?
作者发布时遇到了超时的问题
Waiting for your authorization... Authorization received, processing... ClientException with SocketException: Operation timed out (OS Error: Operation timed out, errno = 60), address = accounts.google.com, port = 63706, uri=https://accounts.google.com/o/oauth2/token Failed to update packages.
作者的解决方法如下:
首先要翻墙开代理,然后让执行命令所在的终端也可以请求到国外的网站
再命令行执行下面命令
export http_proxy=http://127.0.0.1:7890
export https_proxy=http://127.0.0.1:7890
export all_proxy=socks5://127.0.0.1:7890
主播用的代理的端口号是7890,这个要根据个人用的代理的端口号修改
主播用的代理的端口号是7890,这个要根据个人用的代理的端口号修改
主播用的代理的端口号是7890,这个要根据个人用的代理的端口号修改
这样是为了让你的命令行临时可以访问到国外的网站
可以curl谷歌的网站测试一下
curl www.google.com

这个时候再执行flutter pub publish --server=https://pub.dev应该就可以发布了
Q3:发布成功,但在 Pub.dev 搜索框搜不到?
这是正常现象。Pub.dev 的包页面是实时更新的,但搜索索引需要额外的时间进行重建(通常几分钟到几小时不等)。
验证方法: 直接访问 https://pub.dev/packages/你的包名,只要页面能打开,就说明发布成功了,耐心等待索引更新即可。
补充
Package validation found the following potential issue: * It's strongly recommended to include a "homepage" or "repository" field in your pubspec.yaml The server may enforce additional checks.
解决方法
打开你的 pubspec.yaml 文件,在其中添加 homepage 或 repository 字段。推荐做法是两者都加:
name: summer_plugin
version: 0.0.1
description: Your plugin description here.
homepage: https://github.com/your-username/summer_plugin
repository: https://github.com/your-username/summer_plugin
issue_tracker: https://github.com/your-username/summer_plugin/issues
各字段含义:
homepage:插件的主页链接,可以是项目官网或 GitHub 仓库地址repository:源码仓库地址(推荐填 GitHub/GitLab 链接)issue_tracker(可选):问题反馈地址
结语
发布一个高质量的 Flutter 插件,不仅是代码的开源,更是开发者工程化能力的体现。跨越网络代理的鸿沟、理清域名验证的逻辑、遵循官方的审核规范,是每一个进阶开发者必经的考验。
希望这篇避坑指南能帮你省去折腾的时间。如果你也有发布过程中的奇葩经历,欢迎在评论区交流!

3028

被折叠的 条评论
为什么被折叠?



