首頁>技術>

Flutter 混合開發系列 包含如下:

嵌入原生View-Android嵌入原生View-iOS與原生通訊-MethodChannel與原生通訊-BasicMessageChannel與原生通訊-EventChannel新增 Flutter 到 Android Activity新增 Flutter 到 Android Fragment新增 Flutter 到 iOS

iOS View

建議使用 Xcode 進行開發,在 Android Studio 左側 project tab下選中 ios 目錄下任意一個檔案,右上角會出現 Open iOS module in Xcode

Runner 目錄下建立 iOS View,此 View 繼承 FlutterPlatformView ,返回一個簡單的 UILabel

import Foundationimport Flutterclass MyFlutterView: NSObject,FlutterPlatformView {    let label = UILabel()    init(_ frame: CGRect,viewID: Int64,args :Any?,messenger :FlutterBinaryMessenger) {        label.text = "我是 iOS View"    }    func view() -> UIView {        return label    }   }1234567891011121314
getView :返回iOS View註冊PlatformView

建立 MyFlutterViewFactory:

import Foundationimport Flutterclass MyFlutterViewFactory: NSObject,FlutterPlatformViewFactory {    var messenger:FlutterBinaryMessenger    init(messenger:FlutterBinaryMessenger) {        self.messenger = messenger        super.init()    }    func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {        return MyFlutterView(frame,viewID: viewId,args: args,messenger: messenger)    }    func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {        return FlutterStandardMessageCodec.sharedInstance()    }}1234567891011121314151617181920

AppDelegate 中註冊:

import UIKitimport Flutter@UIApplicationMain@objc class AppDelegate: FlutterAppDelegate {  override func application(    _ application: UIApplication,    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?  ) -> Bool {    GeneratedPluginRegistrant.register(with: self)    let registrar:FlutterPluginRegistrar = self.registrar(forPlugin: "plugins.flutter.io/custom_platform_view_plugin")!    let factory = MyFlutterViewFactory(messenger: registrar.messenger())    registrar.register(factory, withId: "plugins.flutter.io/custom_platform_view")    return super.application(application, didFinishLaunchingWithOptions: launchOptions)  }}1234567891011121314151617

記住 plugins.flutter.io/custom_platform_view ,這個字串在 Flutter 中需要與其保持一致。

嵌入Flutter

在 Flutter 中呼叫

class PlatformViewDemo extends StatelessWidget {  @override  Widget build(BuildContext context) {    Widget platformView() {      if (defaultTargetPlatform == TargetPlatform.android) {        return AndroidView(          viewType: 'plugins.flutter.io/custom_platform_view',          onPlatformViewCreated: (viewId) {            print('viewId:$viewId');            platforms                .add(MethodChannel('com.flutter.guide.MyFlutterView_$viewId'));          },          creationParams: {'text': 'Flutter傳給AndroidTextView的引數'},          creationParamsCodec: StandardMessageCodec(),        );      }else if(defaultTargetPlatform == TargetPlatform.iOS){        return UiKitView(          viewType: 'plugins.flutter.io/custom_platform_view',        );      }    }    return Scaffold(      appBar: AppBar(),      body: Center(        child: platformView(),      ),    );  }}12345678910111213141516171819202122232425262728

上面嵌入的是 iOS View,因此透過 defaultTargetPlatform == TargetPlatform.iOS 判斷當前平臺載入,在 iOS 上執行效果:

設定初始化引數

Flutter 端修改如下:

UiKitView(          viewType: 'plugins.flutter.io/custom_platform_view',          creationParams: {'text': 'Flutter傳給IOSTextView的引數'},          creationParamsCodec: StandardMessageCodec(),        )1234
creationParams :傳遞的引數,外掛可以將此引數傳遞給 AndroidView 的建構函式。creationParamsCodec :將 creationParams 編碼後再發送給平臺側,它應該與傳遞給建構函式的編解碼器匹配。值的範圍:StandardMessageCodecJSONMessageCodecStringCodecBinaryCodec

修改 MyFlutterView :

import Foundationimport Flutterclass MyFlutterView: NSObject,FlutterPlatformView {    let label = UILabel()    init(_ frame: CGRect,viewID: Int64,args :Any?,messenger :FlutterBinaryMessenger) {        super.init()        if(args is NSDictionary){            let dict = args as! NSDictionary            label.text  = dict.value(forKey: "text") as! String        }    }    func view() -> UIView {        return label    }}1234567891011121314151617181920

最終效果:

Flutter 向 iOS View 傳送訊息

修改 Flutter 端,建立 MethodChannel 用於通訊:

class PlatformViewDemo extends StatefulWidget {  @override  _PlatformViewDemoState createState() => _PlatformViewDemoState();}class _PlatformViewDemoState extends State<PlatformViewDemo> {  static const platform =      const MethodChannel('com.flutter.guide.MyFlutterView');  @override  Widget build(BuildContext context) {        Widget platformView() {      if (defaultTargetPlatform == TargetPlatform.android) {        return AndroidView(          viewType: 'plugins.flutter.io/custom_platform_view',          creationParams: {'text': 'Flutter傳給AndroidTextView的引數'},          creationParamsCodec: StandardMessageCodec(),        );      } else if (defaultTargetPlatform == TargetPlatform.iOS) {        return UiKitView(          viewType: 'plugins.flutter.io/custom_platform_view',          creationParams: {'text': 'Flutter傳給IOSTextView的引數'},          creationParamsCodec: StandardMessageCodec(),        );      }    }    return Scaffold(      appBar: AppBar(),      body: Column(children: [        RaisedButton(          child: Text('傳遞引數給原生View'),          onPressed: () {            platform.invokeMethod('setText', {'name': 'laomeng', 'age': 18});          },        ),        Expanded(child: platformView()),      ]),    );  }}12345678910111213141516171819202122232425262728293031323334353637383940

在 原生View 中也建立一個 MethodChannel 用於通訊:

import Foundationimport Flutterclass MyFlutterView: NSObject,FlutterPlatformView {    let label = UILabel()    init(_ frame: CGRect,viewID: Int64,args :Any?,messenger :FlutterBinaryMessenger) {        super.init()        if(args is NSDictionary){            let dict = args as! NSDictionary            label.text  = dict.value(forKey: "text") as! String        }        let methodChannel = FlutterMethodChannel(name: "com.flutter.guide.MyFlutterView", binaryMessenger: messenger)        methodChannel.setMethodCallHandler { (call, result) in            if (call.method == "setText") {                if let dict = call.arguments as? Dictionary<String, Any> {                    let name:String = dict["name"] as? String ?? ""                    let age:Int = dict["age"] as? Int ?? -1                    self.label.text = "hello,\(name),年齡:\(age)"                }            }        }    }    func view() -> UIView {        return label    }}12345678910111213141516171819202122232425262728293031
Flutter 向 Android View 獲取訊息

與上面傳送資訊不同的是,Flutter 向原生請求資料,原生返回資料到 Flutter 端,修改 MyFlutterView onMethodCall

import Foundationimport Flutterclass MyFlutterView: NSObject,FlutterPlatformView {    let label = UILabel()    init(_ frame: CGRect,viewID: Int64,args :Any?,messenger :FlutterBinaryMessenger) {        super.init()        if(args is NSDictionary){            let dict = args as! NSDictionary            label.text  = dict.value(forKey: "text") as! String        }        let methodChannel = FlutterMethodChannel(name: "com.flutter.guide.MyFlutterView", binaryMessenger: messenger)        methodChannel.setMethodCallHandler { (call, result:FlutterResult) in            if (call.method == "setText") {                if let dict = call.arguments as? Dictionary<String, Any> {                    let name:String = dict["name"] as? String ?? ""                    let age:Int = dict["age"] as? Int ?? -1                    self.label.text = "hello,\(name),年齡:\(age)"                }            }else if (call.method == "getData") {                if let dict = call.arguments as? Dictionary<String, Any> {                    let name:String = dict["name"] as? String ?? ""                    let age:Int = dict["age"] as? Int ?? -1                    result(["name":name,"age":age])                }            }        }    }    func view() -> UIView {        return label    }}12345678910111213141516171819202122232425262728293031323334353637

result() 是返回的資料。

Flutter 端接收資料:

var _data = '獲取資料';RaisedButton(  child: Text('$_data'),  onPressed: () async {    var result = await platform        .invokeMethod('getData', {'name': 'laomeng', 'age': 18});    setState(() {      _data = '${result['name']},${result['age']}';    });  },),1234567891011
解決多個原生View通訊衝突問題

當然頁面有3個原生View,

class PlatformViewDemo extends StatefulWidget {  @override  _PlatformViewDemoState createState() => _PlatformViewDemoState();}class _PlatformViewDemoState extends State<PlatformViewDemo> {  static const platform =      const MethodChannel('com.flutter.guide.MyFlutterView');  var _data = '獲取資料';  @override  Widget build(BuildContext context) {        Widget platformView() {      if (defaultTargetPlatform == TargetPlatform.android) {        return AndroidView(          viewType: 'plugins.flutter.io/custom_platform_view',          creationParams: {'text': 'Flutter傳給AndroidTextView的引數'},          creationParamsCodec: StandardMessageCodec(),        );      } else if (defaultTargetPlatform == TargetPlatform.iOS) {        return UiKitView(          viewType: 'plugins.flutter.io/custom_platform_view',          creationParams: {'text': 'Flutter傳給IOSTextView的引數'},          creationParamsCodec: StandardMessageCodec(),        );      }    }    return Scaffold(      appBar: AppBar(),      body: Column(children: [        Row(          children: [            RaisedButton(              child: Text('傳遞引數給原生View'),              onPressed: () {                platform                    .invokeMethod('setText', {'name': 'laomeng', 'age': 18});              },            ),            RaisedButton(              child: Text('$_data'),              onPressed: () async {                var result = await platform                    .invokeMethod('getData', {'name': 'laomeng', 'age': 18});                setState(() {                  _data = '${result['name']},${result['age']}';                });              },            ),          ],        ),        Expanded(child: Container(color: Colors.red, child: platformView())),        Expanded(child: Container(color: Colors.blue, child: platformView())),        Expanded(child: Container(color: Colors.yellow, child: platformView())),      ]),    );  }}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859

此時點選 傳遞引數給原生View 按鈕哪個View會改變內容,實際上只有最後一個會改變。

如何改變指定View的內容?重點是 MethodChannel,只需修改上面3個通道的名稱不相同即可:

第一種方法:將一個唯一 id 透過初始化引數傳遞給原生 View,原生 View使用這個id 構建不同名稱的 MethodChannel第二種方法(推薦):原生 View 生成時,系統會為其生成唯一id:viewId,使用 viewId 構建不同名稱的 MethodChannel

原生 View 使用 viewId 構建不同名稱的 MethodChannel

import Foundationimport Flutterclass MyFlutterView: NSObject,FlutterPlatformView {    let label = UILabel()    init(_ frame: CGRect,viewID: Int64,args :Any?,messenger :FlutterBinaryMessenger) {        super.init()        if(args is NSDictionary){            let dict = args as! NSDictionary            label.text  = dict.value(forKey: "text") as! String        }        let methodChannel = FlutterMethodChannel(name: "com.flutter.guide.MyFlutterView_\(viewID)", binaryMessenger: messenger)        methodChannel.setMethodCallHandler { (call, result:FlutterResult) in            ...        }    }    func view() -> UIView {        return label    }}12345678910111213141516171819202122232425

Flutter 端為每一個原生 View 建立不同的MethodChannel

var platforms = [];UiKitView(  viewType: 'plugins.flutter.io/custom_platform_view',  onPlatformViewCreated: (viewId) {    print('viewId:$viewId');    platforms        .add(MethodChannel('com.flutter.guide.MyFlutterView_$viewId'));  },  creationParams: {'text': 'Flutter傳給AndroidTextView的引數'},  creationParamsCodec: StandardMessageCodec(),)1234567891011

給第一個傳送訊息:

platforms[0]    .invokeMethod('setText', {'name': 'laomeng', 'age': 18});1

原文連結:https://mengqd.blog.csdn.net/article/details/109150821

8
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Json Web Token 2020 攻擊指南