前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[-Flutter插件篇 -] 从自定义插件开始说起

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

作者头像
张风捷特烈
发布2020-04-30 15:18:35
1.7K0
发布2020-04-30 15:18:35
举报
文章被收录于专栏:Android知识点总结

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

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

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

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

  • 填写信息
  • 包名及语言选择

1.2:Flutter插件项目结构

写代码的地方有三块:

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

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


1.3:运行插件示例

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

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

代码语言:javascript
复制
---->[本机信息]----
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方法获取版本进行返回。

代码语言:javascript
复制
---->[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代码:
代码语言:javascript
复制
[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代码
代码语言:javascript
复制
[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群里一位朋友的介绍

代码语言:javascript
复制
---->[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。

代码语言:javascript
复制
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插件文件
代码语言:javascript
复制
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文件
代码语言:javascript
复制
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的文件
代码语言:javascript
复制
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:使用
代码语言:javascript
复制
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:发布到公网

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

代码语言:javascript
复制
---->[发布]----
flutter packages pub publish

---->[使用]----
dependencies:
  ia_path: ^0.0.1

4.2:本地使用

经测试,使用无误

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

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019年08月05日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.Flutter插件创建与结构简析
    • 1.1:创建一个Flutter插件
      • 1.2:Flutter插件项目结构
        • 1.3:运行插件示例
        • 2.第一个插件代码分析
          • 2.1:Flutter代码:
            • 2.2:Android方Kotlin代码:
              • 2.3:iOS方Swift代码
                • 2.4:使用插件
                • 3.获取缓存文件夹
                  • 3.1:dart插件文件
                    • 3.2:Android文件
                      • 3.3:iOS的文件
                        • 3.4:使用
                        • 4.其他工程引用插件
                          • 4.1:发布到公网
                            • 4.2:本地使用
                            相关产品与服务
                            腾讯云代码分析
                            腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档