这个活生生的例子会教你开发flutter插件,功能是封装Android和iOS端的分享到facebook和twitter的flutter接口。使用的分别是两端的系统分享功能,不需要集成facebook和twitter 的 sdk。

例子插件网址:https://pub.dartlang.org/packages/flutter_share_go#-readme-tab-

展示一下样式:

ios 中分享到facebook:

android中分享到facebook

开始开发插件

步骤一:创建插件项目

这里用的Android studio创建的项目,可以直接创建flutter plugin项目,你也可以用命令创建:

flutter create --org com.example --template=plugin "plugin_name"

将上面的“plugin_name”换成你的插件名字就行了,我这个插件名字叫flutter_share_go,所以命令就是:

flutter create --org com.example --template=plugin flutter_share_go

这里没有支持swift和kotlin,原因是使用插件的很多项目可能并不支持,以免造成不必要的麻烦,但是你仍然可以选择支持,命令如下:

flutter create --template=plugin -i swift -a kotlin flutter_share_go

查看创建好的项目结构:

__Android文件夹中:__Android端原生的代码
__iOS文件夹中:__iOS端原生的代码
__example文件:__是方便测试你的插件的例子代码
__lib文件:__插件对原生接口的封装,也就是直接在dart代码中调用的接口

分别在Android studio和xcode中编辑Android和iOS代码

先在Android studio中编写Android部分代码

用Android studio打开项目中的android文件夹,因为此时缺少flutter库依赖,需要配置一下:

在项目根目录建立一个libs文件夹,用来存放flutter库,flutter库可以到你电脑的flutter sdk路径中寻找:

/bin/cache/artifacts/engine

在engine路径下有很多的android支持库,随便拷贝一个Android平台的库到libs文件夹下,右键flutter.jar,弹出菜单选择Add As Library…。这里不用担心,我们等下设置一下让它只是编译时使用,不会打包进来。

然后点击菜单File/Project Structure…,找到flutter_text_plugin的Dependencies中,将flutter库的Scope从Implementation改成Compile Only。

开始添加Android代码
添加如下两个文件:

PackageUtils.java 工具类

package com.doglobal.flutter_share_go;import android.content.Context;import android.content.Intent;import android.content.pm.PackageManager;import android.net.Uri;public class PackageUtils {    public static boolean isPkgInstalled(Context context, String packageName) {        if (packageName == null || "".equals(packageName))            return false;        android.content.pm.ApplicationInfo info = null;        try {            info = context.getPackageManager().getApplicationInfo(packageName, 0);            return info != null;        } catch (PackageManager.NameNotFoundException e) {            return false;        }    }    public static void goDownloadPage(Context context, String pkgName) {        Context app = context.getApplicationContext();        try {            Intent intent = new Intent(Intent.ACTION_VIEW,                    Uri.parse("market://details?id=" + pkgName));            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);            app.startActivity(intent);        } catch (android.content.ActivityNotFoundException anfe) {            Intent intent = new Intent(Intent.ACTION_VIEW,                    Uri.parse("https://play.google.com/store/apps/details?id=" + pkgName));            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);            app.startActivity(intent);        }    }}

ShareHelper.java 工具类

package com.doglobal.flutter_share_go;import android.app.Activity;import android.content.Intent;import android.text.TextUtils;public class ShareHelper {    public static final String CHANNEL_FACEBOOK_PKG_NAME = "com.facebook.katana";    public static final String CHANNEL_TWITTER_PKG_NAME = "com.twitter.android";    public static void shareMain(Activity activity, String shareText) {        shareText(activity, null, null, shareText, null, "");    }    public static void shareTextToChannel(Activity activity,String pkgName, String shareText,                                          String shareUrl) {        if (PackageUtils.isPkgInstalled(activity, pkgName)) {            shareText(activity, null, null, shareText, pkgName, shareUrl);        } else {            PackageUtils.goDownloadPage(activity, pkgName);        }    }    /**     * @param dlgTitle 选择弹框标题     * @param subject  分享主题     * @param shareText  分享内容     */    public static void shareText(Activity activity, String dlgTitle, String subject,                                 String shareText, String pkgName, String shareUrl) {        if (shareText == null || "".equals(shareText)) {            return;        }        String content = shareText + ": " + shareUrl + " .";        Intent intent = new Intent(Intent.ACTION_SEND);        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);        intent.setType("text/plain");        if (!TextUtils.isEmpty(subject)) {            intent.putExtra(Intent.EXTRA_SUBJECT, subject);        }        intent.putExtra(Intent.EXTRA_TEXT, content);        if (!TextUtils.isEmpty(pkgName)) {            intent.setPackage(pkgName);        }        // 设置弹出框标题        if (TextUtils.isEmpty(dlgTitle)) { // 自定义标题            Intent chooser = Intent.createChooser(intent, dlgTitle);            chooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);            activity.startActivity(chooser);        } else { // 系统默认标题            activity.startActivity(intent);        }    }}

然后在FlutterShareGoPlugin.java文件中添加调用逻辑。
这个文件就是java端对dart端的调用接口。我们先来看下代码,再进行解析:

package com.doglobal.flutter_share_go;import android.app.Activity;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;import io.flutter.plugin.common.PluginRegistry.Registrar;/** FlutterShareGoPlugin */public class FlutterShareGoPlugin implements MethodCallHandler {  private Activity mActivity; //分析一  private FlutterShareGoPlugin(Activity context) {    mActivity = context;  }  /** Plugin registration. */  public static void registerWith(Registrar registrar) {    final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_share_go");    channel.setMethodCallHandler(new FlutterShareGoPlugin(registrar.activity()));  }  @Override  public void onMethodCall(MethodCall call, Result result) {    if (call.method.equals("getPlatformVersion")) {      result.success("Android " + android.os.Build.VERSION.RELEASE);    } else if (call.method.equals("shareToFBPlatform")) {  //分析二      String content = call.argument("shareContent");      String shareUrl = call.argument("shareUrl");      ShareHelper.shareTextToChannel(mActivity, ShareHelper.CHANNEL_FACEBOOK_PKG_NAME, content, shareUrl);    } else if (call.method.equals("shareToTwitterPlatform")) {      String content = call.argument("shareContent");      String shareUrl = call.argument("shareUrl");      ShareHelper.shareTextToChannel(mActivity, ShareHelper.CHANNEL_TWITTER_PKG_NAME, content, shareUrl);    }    else {      result.notImplemented();    }  }}
  • 分析一

我们添加一个构造方法,用来存储activity

  • 分析二

在方法onMethodCall中根据dart传递过来的方法名调用java端不同的代码逻辑

至此,Android端代码就告一段落了。

iOS端开发,在xcode中开发

如图,我们在Android studio中打开一个iOS文件,在右上角会有用xcode打开的按钮,我们点击打开就可以用xcode编辑了。

打开项目添加上图中的文件:

FlutterShareGoPlugin.m文件

#import "FlutterShareGoPlugin.h"#import "ShareHelper.h"#import <Social/Social.h>@interface FlutterShareGoPlugin()@property (nonatomic, strong)UIViewController * rootViewController;@end@implementation FlutterShareGoPlugin+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {  FlutterMethodChannel* channel = [FlutterMethodChannel      methodChannelWithName:@"flutter_share_go"            binaryMessenger:[registrar messenger]];  FlutterShareGoPlugin* instance = [[FlutterShareGoPlugin alloc] init];  [registrar addMethodCallDelegate:instance channel:channel];}- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {  if ([@"getPlatformVersion" isEqualToString:call.method]) {    result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);  }  else if ([@"shareToFBPlatform" isEqualToString:call.method]) {      NSDictionary *arguments = [call arguments];      NSString * shareContent = arguments[@"shareContent"];      NSString * shareUrl = arguments[@"shareUrl"];      [ShareHelper shareToPlatformType:SLServiceTypeFacebook withContent:shareContent withShareUrl:shareUrl];      result(nil);  }  else if ([@"shareToTwitterPlatform" isEqualToString:call.method]) {      NSDictionary *arguments = [call arguments];      NSString * shareContent = arguments[@"shareContent"];      NSString * shareUrl = arguments[@"shareUrl"];      [ShareHelper shareToPlatformType:SLServiceTypeTwitter withContent:shareContent withShareUrl:shareUrl];      result(nil);  }  else {    result(FlutterMethodNotImplemented);  }}@end

PackageUtil.h

#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface PackageUtil : NSObject// Is intalled specific app or not+ (BOOL)isInstalledWithUrlScheme:(NSString *)urlScheme;@endNS_ASSUME_NONNULL_END

PackageUtil.m

#import "PackageUtil.h"@implementation PackageUtil+ (BOOL)isInstalledWithUrlScheme:(NSString *)urlScheme {    if (urlScheme == nil || urlScheme.length == 0) {        return false;    }    return [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:urlScheme]];}@end

ShareHelper.h

#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface ShareHelper : NSObject+(void)shareToPlatformType:(NSString *)platformType withContent:(NSString *)content withShareUrl:(NSString*)url;@endNS_ASSUME_NONNULL_END

ShareHelper.m

#import "ShareHelper.h"#import <Social/Social.h>#import "PackageUtil.h"@implementation ShareHelper/* SOCIAL_EXTERN NSString *const SLServiceTypeTwitter NS_AVAILABLE(10_8, 6_0);//Twitter SOCIAL_EXTERN NSString *const SLServiceTypeFacebook NS_AVAILABLE(10_8, 6_0);//Facebook SOCIAL_EXTERN NSString *const SLServiceTypeSinaWeibo NS_AVAILABLE(10_8, 6_0); SOCIAL_EXTERN NSString *const SLServiceTypeTencentWeibo NS_AVAILABLE(10_9, 7_0); SOCIAL_EXTERN NSString *const SLServiceTypeLinkedIn NS_AVAILABLE(10_9, NA); */+(void)shareToPlatformType:(NSString *)platformType withContent:(NSString *)content withShareUrl:(NSString*)url{    if (platformType == nil || platformType.length == 0) {        NSLog(@"unspecified platform");        return;    }    if([platformType isEqualToString:SLServiceTypeFacebook] && ![[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"fb://"]]) {        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"itms-apps://itunes.apple.com/app/id284882215"]];        NSLog(@"UnInstall facebook");        return;    }    if([platformType isEqualToString:SLServiceTypeTwitter] && ![[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"twitter://"]]) {        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"itms-apps://itunes.apple.com/app/id333903271"]];        NSLog(@"UnInstall twitter");        return;    }    // create controller for ServiceType    SLComposeViewController *composeVC = [SLComposeViewController composeViewControllerForServiceType:platformType];    // image    // [composeVC addImage:[UIImage imageNamed:@"Nameless"]];    // content    [composeVC setInitialText:content];    // share url    [composeVC addURL:[NSURL URLWithString:url]];    // share controller    if([[[UIApplication sharedApplication] keyWindow].rootViewController isKindOfClass:[UINavigationController class]]) {        UINavigationController *vc = (UINavigationController *)[[UIApplication sharedApplication] keyWindow].rootViewController;        [vc pushViewController:composeVC animated:YES];    } else {        UINavigationController *vc = (UINavigationController *)[[UIApplication sharedApplication] keyWindow].rootViewController;        [vc presentViewController:composeVC animated:YES completion:nil];    }    // callback user hanlder    composeVC.completionHandler = ^(SLComposeViewControllerResult result){        if (result == SLComposeViewControllerResultDone) {        }        else if (result == SLComposeViewControllerResultCancelled) {        }    };}@end

最后需要我们在plist文件中添加白名单,用于iOS9适配,查看是否安装了facebook和twitter:

iOS 部分代码到此也告一段落了。

添加flutter接口,供dart代码调用

打开项目中lib文件夹下的flutter_share_go文件

FlutterShareGo.dart

import 'dart:async';import 'package:flutter/services.dart';class FlutterShareGo {  static const MethodChannel _channel =      const MethodChannel('flutter_share_go');  static Future<String> get platformVersion async {    final String version = await _channel.invokeMethod('getPlatformVersion');    return version;  }  static Future<void> shareToFBPlatform(String shareContent, String shareUrl) async {    assert(shareContent != null);    assert(shareContent.isNotEmpty);    final Map<String, dynamic> params = <String, dynamic> {      'shareContent': shareContent,      'shareUrl':shareUrl,    };    await _channel.invokeMethod("shareToFBPlatform", params);  }  static Future<void> shareToTwitterPlatform(String shareContent, String shareUrl) async {    assert(shareContent != null);    assert(shareContent.isNotEmpty);    final Map<String, dynamic> params = <String, dynamic> {      'shareContent': shareContent,      'shareUrl':shareUrl,    };    await _channel.invokeMethod("shareToTwitterPlatform", params);  }}

flutter端代码就告一段落了。

测试插件:

打开项目中example文件下的main.dart:

main.dart

import 'package:flutter/material.dart';import 'dart:async';import 'package:flutter/services.dart';import 'package:flutter_share_go/flutter_share_go.dart';void main() => runApp(MyApp());class MyApp extends StatefulWidget {  @override  _MyAppState createState() => _MyAppState();}class _MyAppState extends State<MyApp> {  String _platformVersion = 'Unknown';  @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.    try {      platformVersion = await FlutterShareGo.platformVersion;    } 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;    });  }  @override  Widget build(BuildContext context) {    return MaterialApp(      home: Scaffold(        appBar: AppBar(          title: const Text('Plugin example app'),        ),        body: Column(          children: <Widget>[            Text('Running on: $_platformVersion\n'),            RaisedButton(              child: Text("分享到facebook按钮"),              color: Colors.red,              onPressed: () {                FlutterShareGo.shareToFBPlatform("test share to fb content", "http://tryenough.com");              },            )          ],        ),      ),    );  }}

运行就会出现文章最上面的效果了。祝你顺利。

最后发布可以参照如下步骤:

运行命令,并解决所有出现的问题:

 flutter packages pub publish --dry-run

问题解决完就可以发布了,命令:

 flutter packages pub publish