移动端小白,30天掌握Flutter双端插件开发-上

公司有APP开发需求,广义上来讲,安卓、IOS也是前端,那这个任务自然就落在我头上了。需求都还是比较简单,无非就是展示,跳转,表单等基本操作,都可以hold住。但产品经理不讲武德,来偷袭我一个web前端,上来就是一个aar,一套framework,几个代码示例和pdf,让我把这些一个相机的sdk集成到项目中,安卓和苹果端都需要,这我哪招架的住。但没办法,硬着头皮也要上。

从来没接触移动端原生开发,三月末接到需求,看了下相关文档后,并咨询原生大佬一些问题后,4月1日开始尝试,百度,谷歌,咨询大佬,SegmentFault,StackOverflow,看github源码。终于在5月10日完成了这个插件的所有功能并用在了项目上,抛开中间的休息时间,满打满算差不多30天

学习最快的方法还是要有个好老师,简单的问题,自己琢磨3天不如老师1句话。

对于精通原生开发的大佬来讲,这并不是什么难事,但对于小白来讲,还是不容易的。这过程中有不少知识需要掌握,好在都有前人做了非常多的技术总结,下面罗列一下用的知识:

Flutter篇:

  1. Flutter开发之插件入门
  2. Flutter 异步(Async)操作

Andorid篇:

  1. Kotlin基础语法使用
  2. flutter 引入第三方aar实践
  3. api.flutter.dev(Flutter Api文档)

iOS篇:

  1. Swift语法全面解析
  2. iOS Flutter插件 导入第三方Framework

作为一个只有flutter经验的小白,插件开发中最简单的就是写一个基础的插件demo,可以使用最熟悉的dart语言。按着Flutter开发之插件入门一步一步来写,可以了解到插件与原生一共有3种方式,一种是触发即返回数据的MethodChannel,另一种是监听状态流EventChannel,还有一种BasicMessageChannel,用于用于传递字符串和半结构化的消息,而我们主要使用前2个。

1、MethodChannel

Flutter 与 Native 端相互调用,调用后可以返回结果,可以 Native 端主动调用,也可以Flutter主动调用,属于双向通信。此方式为最常用的方式, Native 端调用需要在主线程中执行。

flutter端

demo/lib/demo.dart中:

import 'dart:async';
import 'package:flutter/services.dart';

class Demo {
  // 声明通道名
  static const MethodChannel _channel = const MethodChannel('demo');
  // invokeMethod可以和原生进行通信
  static Future<bool> get setup async {
    return await _channel.invokeMethod('setup');
  }
}
复制代码

flutter端中就是这么简单 ,这里仅是flutter触发native端,navive端主动触发暂不讨论。

Android端

android/src/main/kotlin/com/example/demo/DemoPlugin.kt

import android.Manifest
import android.app.Activity
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.Result
import io.flutter.plugin.common.MethodChannel.MethodCallHandler

class DemoPlugin: FlutterPlugin, MethodCallHandler{
    private lateinit var channel : MethodChannel
    
    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        // 名称与flutter端的一致
        channel = MethodChannel(binding.binaryMessenger, "demo")
        channel.setMethodCallHandler(this)
    }
    
    override fun onMethodCall(call: MethodCall, result: Result) {
        when (call.method) {
          "setup" -> {
            println(call.arguments)
            result.success("setup")
          }
          else -> {
            result.notImplemented()
          }
        }
    }
}
复制代码

与安卓通信中,将MethodChannel注册到主class中,主要是通道的名称要与flutter端的一致。

iOS端

ios/Classes/SwiftDemoPlugin.swift

import Flutter
import UIKit

public class SwiftHzCameraPlugin: NSObject, FlutterPlugin{
  public static let instance: SwiftDemoPlugin = SwiftDemoPlugin()
  
  public static func register(with registrar: FlutterPluginRegistrar) {
    let channel = FlutterMethodChannel(name: "demo", binaryMessenger: registrar.messenger())
    registrar.addMethodCallDelegate(instance, channel: channel)
  }
  
  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
    switch (call.method) {
      case "setup":
        print(call.arguments)
        result("setup")
      default:
        result("nothing")
    }
  }
}

复制代码

和安卓端基本上是一致的,只是语法的不一样。

这里我们基本上完成一个插件最基本的功能,flutter主动与native端通信并获得返回信息。但这里只能触发一次,回调一次,并不是很灵活。有时候我们需要监听原生端的一些状态,那么EventChannel就是必不可少的了。

2、EventChannel

EventChannel主要用于数据流(event streams)的通信, Native 端主动发送数据给 Flutter,通常用于状态的监听,比如网络变化、传感器数据等。

flutter端

demo/lib/demo.dart中:

import 'dart:async';
import 'package:flutter/services.dart';

class Demo {
  // 依然是声明通道名,只不过这里是EventChannel
  static const EventChannel _eventChannel = const EventChannel('Demo_event');
  // 状态监听
  static StreamController<String> statusController = StreamController.broadcast();
  
  initEvent() {
    _eventChannel
      .receiveBroadcastStream()
      .listen((event) async {
          statusController.add(event);
      }
    }, onError: (dynamic error) {
        print(error);
    },cancelOnError: true);
  }
  
  Demo() {
    initEvent();
  }
}
复制代码

这里有一点变化,就是需要通过一个方法,在class初始化的时候进行调用,用来监听原生中eventSink传递过来的数据流,当chanel获取到数据的时候,将数据addStreamController里,方便在一般页面中进行调用。

Android端

还是在android/src/main/kotlin/com/example/demo/DemoPlugin.kt

import android.Manifest
import android.app.Activity
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.EventChannel

class DemoPlugin: FlutterPlugin, MethodCallHandler, ActivityAware{
    private lateinit var activity: Activity
    private var eventSink: EventChannel.EventSink? = null
    
    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
      // 名称与flutter端的一致
      val eventChannel = EventChannel(binding.binaryMessenger, "Demo_event")
      // 初始化StreamHandler
      eventChannel.setStreamHandler(
        object : EventChannel.StreamHandler {
          override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
            eventSink = events
          }
          override fun onCancel(arguments: Any?) {
            eventSink = null
          }
        }
      )
    } 
}
复制代码

在kotlin中需要主动向flutter端发送消息的时候,直接使用下面方法进行通讯

eventSink?.success("通讯成功")
复制代码

iOS端

ios/Classes/SwiftDemoPlugin.swift

import Flutter
import UIKit

public class SwiftHzCameraPlugin: NSObject, FlutterPlugin, HZCameraSocketDelegate{
  public static let instance: SwiftDemoPlugin = SwiftDemoPlugin()
  var eventSink:FlutterEventSink?
  
  // 数据流监听
  class SwiftStreamHandler: NSObject, FlutterStreamHandler {
    func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
      instance.eventSink = events
      return nil
    }

    func onCancel(withArguments arguments: Any?) -> FlutterError? {
       instance.eventSink = nil
       return nil
    }
  }
  
  public static func register(with registrar: FlutterPluginRegistrar) {
    let eventChannel = FlutterEventChannel(name: "Demo_event", binaryMessenger: registrar.messenger())
    eventChannel.setStreamHandler(SwiftStreamHandler())
  }
}
复制代码

这里和安卓端也是大同小异,同样使用下列方法进行通讯

self.eventSink!("通讯成功")
复制代码

总结

flutter与原生通讯的也就这寥寥几行代码就可以实现,可以抽象的理解注册触发,第一步都是先注册到环境中,再通过对应的事件触发。需要原生数据的时候,通过invokeMethod()对应的方法,调用原生事件并得到需要的数据,在用result()来返回数据。需要对原生的状态进行监听的时候,比如wifi状态,电量等,就使用onListeneventSink注册到环境中,当状态改变的时候即可主动触发事件。

当然,这些功能理解后,插件开发才刚入门,学会原生端的操作才是重中之重。

← To Be Continued 。。。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享