[-Flutter插件篇 -] 从自定义插件开始说起

Flutter可以为你提供一个强大华丽简洁高效的跨平台UI界面,
但无论外表多么绚丽美女,没有内在也只是空壳,你会喜欢她吗?(还用问,当然会)
使用插件可以让Flutter轻松与当前平台进行联系,调用平台中的方法。
这篇先不虚头巴脑的介绍一堆MethodChannel的概念,先看怎么用。本文你将了解:

[1].如何创建一个Flutter插件的
[2].Flutter中如何和Android以及iOS交互(本文使用Kotlin和Swift)
[3].Flutter插件的使用
复制代码

1.Flutter插件创建与结构简析
1.1:创建一个Flutter插件

File-->new-->new Flutter Project...

  • 填写信息

  • 包名及语言选择


1.2:Flutter插件项目结构

写代码的地方有三块:

android下面写Android原生代码,使用Java或Kotlin,如果用JNI还可能涉及C++
ios文件夹下面写iOS原生代码,使用Object-c或Swift
lib文件夹下面写Flutter代码,使用Dart语言
复制代码

也就是说一个插件可能涉及到6种语言,哈哈,颤抖吧人类...


1.3:运行插件示例

虽然复杂,但是简单必有简单的成本,复杂必有复杂的价值。

注意有个坑点:mac上需要装cocoapods

---->[本机信息]----
toly:~ mac$ ruby -v
ruby 2.3.7p456 (2018-03-28 revision 63024) [universal.x86_64-darwin18]
toly:~ mac$ gem -v
2.5.2.3
toly:~ mac$ gem sources -l 
*** CURRENT SOURCES ***
https://rubygems.org/

---->[替换ruby源]----
gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/

---->[替换ruby源完成]----
toly:~ mac$ gem sources -l
*** CURRENT SOURCES ***
https://gems.ruby-china.com

---->[安装cocoapods]----
toly:~ mac$ sudo gem install -n /usr/local/bin cocoapods
toly:~ mac$ pod setup
复制代码

2.第一个插件代码分析

这里创建一个ia_version的项目专门看看示例的插件是如何完成的。

2.1:Flutter代码:

可以看到ia_version.dart中定义了一个类IaVersion,其中有一个MethodChannel类型静态常量_channel,接受一个字符串,在静态方法platformVersion中使用异步调用_channel的getPlatformVersion方法获取版本进行返回。

---->[lib/ia_version.dart]----
class IaVersion {
  static const MethodChannel _channel =
      const MethodChannel('ia_version');

  static Future<String> get platformVersion async {
    final String version = await _channel.invokeMethod('getPlatformVersion');
    return version;
  }
}
复制代码

2.2:Android方Kotlin代码:
[1].定义IaVersionPlugin类继承自MethodCallHandler。
[2].创建静态方法registerWith,传入一个Registrar类型变量registrar。  
[3].通过registrar的messenger和标识符创建MethodChannel对象,  
[4].将IaVersionPlugin对象设置给MethodChannel进行回调处理。  
[5].覆写了onMethodCall方法,回调MethodCall和Result对象,
在方法体中根据方法名`getPlatformVersion`来用result对象执行方法传入Android版本信息。

---->[com.toly1994.ia_version.IaVersionPlugin]----
class IaVersionPlugin: MethodCallHandler {
  companion object {
    @JvmStatic
    fun registerWith(registrar: Registrar) {
      val channel = MethodChannel(registrar.messenger(), "ia_version")
      channel.setMethodCallHandler(IaVersionPlugin())
    }
  }

  override fun onMethodCall(call: MethodCall, result: Result) {
    if (call.method == "getPlatformVersion") {
      result.success("Android ${android.os.Build.VERSION.RELEASE}")
    } else {
      result.notImplemented()
    }
  }
}
复制代码

2.3:iOS方Swift代码
[1].定义SwiftIaVersionPlugin类继承自NSObject, FlutterPlugin  
[2].创建静态方法register,传入一个FlutterPluginRegistrar类型变量registrar。
[3].通过registrar的messenger和标识符创建FlutterMethodChannel对象,  
[4].将SwiftIaVersionPlugin对象设置给MethodChannel进行回调处理。  
[5].handle方法,回调FlutterMethodCall和FlutterResult对象,
用result对象执行方法传入iOS版本信息。

---->[ios/Classes/SwiftIaVersionPlugin.swift]----
public class SwiftIaVersionPlugin: NSObject, FlutterPlugin {
  public static func register(with registrar: FlutterPluginRegistrar) {
    let channel = FlutterMethodChannel(name: "ia_version", binaryMessenger: registrar.messenger())
    let instance = SwiftIaVersionPlugin()
    registrar.addMethodCallDelegate(instance, channel: channel)
  }

  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
    result("iOS " + UIDevice.current.systemVersion)
  }
}
复制代码

还有两个文件使用OC写的,关于OC我不是太懂,下面是Flutter群里一位朋友的介绍

---->[ios/Classes/IaVersionPlugin.h]----
@interface IaVersionPlugin : NSObject<FlutterPlugin>
@end

---->[ios/Classes/IaVersionPlugin.m]----
@implementation IaVersionPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
  [SwiftIaVersionPlugin registerWithRegistrar:registrar];
}
@end
复制代码

2.4:使用插件

使用的时候就很方便了,调用一下就ok。

import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:ia_version/ia_version.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();
  }
  Future<void> initPlatformState() async {//异步初始化平台状态
    String platformVersion;
    try {//捕捉PlatformException.
      platformVersion = await IaVersion.platformVersion;//通过插件获取平台版本
    } on PlatformException {
      platformVersion = 'Failed to get platform version.';
    }
    // 如果在异步平台消息运行期间widget从树中删除,
    // 我们希望丢弃响应,而不是调用setState来更新不存在的外观。
    if (!mounted) return;
    setState(() {//更新状态
      _platformVersion = platformVersion;
    });
  }
  @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'),
        ),
      ),
    );
  }
}
复制代码

3.获取缓存文件夹

相信大家都用过path_provider,感觉很方便就可以在Flutter中获取文件路径
下面我们看一下如何让一个插件获取缓存文件夹,如果前面看明白了,应该so easy


3.1:dart插件文件
import 'dart:async';
import 'dart:io';

import 'package:flutter/services.dart';

class IaPath {
  static const MethodChannel _channel =//插件的渠道标识名
      const MethodChannel('com.toly1994.ia_path');

  Future<Directory> getTemporaryDirectory() async {
    final String path =
    await _channel.invokeMethod<String>('getTemporaryDirectory');
    if (path == null) {
      return null;
    }
    return Directory(path);
  }
}
复制代码

3.2:Android文件
class IaPathPlugin(registrar: Registrar) : MethodCallHandler {
  private var mRegistrar: Registrar? = registrar
  
  companion object {
    @JvmStatic
    fun registerWith(registrar: Registrar) {
      val channel = MethodChannel(registrar.messenger(), "com.toly1994.ia_path")
      val instance = IaPathPlugin(registrar)
      channel.setMethodCallHandler(instance)
    }
  }
  
  override fun onMethodCall(call: MethodCall, result: Result) {
    when(call.method){
      "getTemporaryDirectory"->{
        result.success(geTemporaryDirectory())
      }
    }
  }
  private fun geTemporaryDirectory(): String {
   return mRegistrar!!.context().cacheDir.path;
  }
}
复制代码

3.3:iOS的文件
public class SwiftIaPathPlugin: NSObject, FlutterPlugin {
  public static func register(with registrar: FlutterPluginRegistrar) {
    let channel = FlutterMethodChannel(name: "com.toly1994.ia_path", binaryMessenger: registrar.messenger())
    let instance = SwiftIaPathPlugin()
    registrar.addMethodCallDelegate(instance, channel: channel)
  }

  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
    switch call.method {
       case "getTemporaryDirectory"  :
          result(NSTemporaryDirectory())
       default :
          result("UnKnown")
    }
  }
}
复制代码

3.4:使用
import 'package:flutter/material.dart';
import 'package:ia_path/ia_path.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
  String _str = 'CacheDir:\n';
  @override
  void initState() {
    super.initState();
    IaPath().getTemporaryDirectory().then((dir){
      setState(() {
        _str+=dir.path;
      });
    });
  }
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Container(
          child: Text('Running on: $_str\n'),
        ),
      ),
    );
  }
}
复制代码

4.其他工程引用插件
4.1:发布到公网

有道墙隔着,发不发得了就看你自己了。

---->[发布]----
flutter packages pub publish

---->[使用]----
dependencies:
  ia_path: ^0.0.1
复制代码

4.2:本地使用

经测试,使用无误

dependencies:
  ia_path:
    path: /Volumes/coder/Project/Flutter/plugins/ia_path
复制代码

当然你也可以直接在本项目中调用Android和iOS方法,就像插件里做的那样。
本文讲了一下插件的自定义和在两个平台上的代码处理,
下一篇将详细讲述MethodChannel,让你在Flutter中无后顾之忧。


结语

本文到此接近尾声了,如果想快速尝鲜Flutter,《Flutter七日》会是你的必备佳品;如果想细细探究它,那就跟随我的脚步,完成一次Flutter之旅。
另外本人有一个Flutter微信交流群,欢迎小伙伴加入,共同探讨Flutter的问题,本人微信号:zdl1994328,期待与你的交流与切磋。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值