移動端小白,30天掌握Flutter雙端外掛開發-上

李威爾發表於2021-05-20

公司有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 。。。

相關文章