继续创造,加速成长!这是我参加「日新方案 6 月更文应战」的第24天,点击检查活动详情

  • 本文首要介绍Flutter学习之原生客户端交互,首要是在flutter运用iOS中的view。

1. 创立iOS工程View在Flutter中展示

咱们想要在flutter运用iOS中的view,应该怎么交互呢。咱们运用Xcode翻开咱们的Runner.xcworkspace

Flutter学习之嵌入原生iOSView
咱们在 iOS工程中创立一个测试view

Flutter学习之嵌入原生iOSView

import UIKit
import Flutter
class TestView:NSObject, FlutterPlatformView{
 
  lazy var nameLabel: UILabel = {
    let label = UILabel()
    label.font = .systemFont(ofSize: 15)
    label.textColor = .white
//    label.backgroundColor = .red
    label.textAlignment = .center
    return label
  }()
 
  init(_ frame: CGRect,viewID: Int64,args :Any?,messenger :FlutterBinaryMessenger) {
    super.init()
    nameLabel.text = "我是 iOS Test View"
   }
 
  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
 
  func view() -> UIView {
   
    return nameLabel
  }
 
}

同时创立TestViewFactory

import UIKit
import Flutter
class TestViewFactory: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 TestView(frame,viewID: viewId,args: args,messenger: messenger)
   }
   
   func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
     return FlutterStandardMessageCodec.sharedInstance()
   }
  
}

完成FlutterPlatformViewFactory的协议办法,回来咱们的TestView目标,经过完成协议办法进行交互。

import UIKit
import Flutter
class TestViewFactory: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 TestView(frame,viewID: viewId,args: args,messenger: messenger)
   }
   
   func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
     return FlutterStandardMessageCodec.sharedInstance()
   }
  
}

咱们在delegate中进行注册,进行通讯。

import UIKit
import 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 = TestViewFactory(messenger: registrar.messenger())
  registrar.register(factory, withId: "plugins.flutter.io/custom_platform_view")
  return super.application(application, didFinishLaunchingWithOptions: launchOptions)
 }
}

咱们在flutter项目中创立一个展示的页面


import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
class IosViewPage extends StatelessWidget {
  const IosViewPage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    String? title = Get.arguments['title'];
    return Scaffold(
      appBar: AppBar(title: Text(title ?? ''),),
      body: Center(
        child:iosView(),
      ),
    );
  }
  Widget iosView() {
     if(defaultTargetPlatform == TargetPlatform.iOS){
      return  const UiKitView(
        viewType: 'plugins.flutter.io/custom_platform_view',
        creationParams: {'text': 'Flutter传给IOSTextView的参数'},
        creationParamsCodec: StandardMessageCodec(),
      );
    }else {
       return Container();
     }
  }
}

显现效果如下:

Flutter学习之嵌入原生iOSView

2. Flutter向ios传值

咱们在flutter页面中传一些参数

return  const UiKitView(
  viewType: 'plugins.flutter.io/custom_platform_view',
  creationParams: {'text': 'Flutter传给IOSLabel的参数'},
  creationParamsCodec: StandardMessageCodec(),
);

这里viewType就是咱们注册的时分运用的标识符creationParams为创立iOS view时带的参数creationParamsCodec:将 creationParams 编码后再发送给平台侧,它应该与传递给构造函数的解码器匹配
咱们在iOS项目中判断是否传递了参数

init(_ frame: CGRect,viewID: Int64,args :Any?,messenger :FlutterBinaryMessenger) {
    super.init()
    if args is NSDictionary {
      let dict = args as! NSDictionary
      nameLabel.text = dict.value(forKey: "text") as? String
     
    }else{
      nameLabel.text = "这是一个 iOS view"
    }
   
   }

运行结果

Flutter学习之嵌入原生iOSView

咱们在运行中经过按钮点击改动iOS View的内容

static const platform = MethodChannel('com.flutter.test.TestView');

咱们在flutter页面定义一个办法通道

 RaisedButton(
child: const Text('传递参数给原生View'),
onPressed: () {
  platform.invokeMethod('userInfo', {'name': 'Jack', 'city': "New York"});
},

点击的时分传参给iOS页面

let method = FlutterMethodChannel(name: "com.flutter.test.TestView", binaryMessenger: messenger)
   
    method.setMethodCallHandler { (call, reslut) in
      if (call.method == "userInfo") {
        let dict: Dictionary? = call.arguments as? Dictionary<String, Any>
        self.nameLabel.text = "my Name is:\(dict?["name"] ?? "")\n from:\(dict?["city"] ?? "")"
       
      }
    }

在页面中设置回调处理,判断办法获取参数,进行展示

Flutter学习之嵌入原生iOSView

设置回调的时分还有一个result没有运用,这个是用于咱们原生页面向flutter传参运用的。


    let method = FlutterMethodChannel(name: "com.flutter.test.TestView", binaryMessenger: messenger)
   
    method.setMethodCallHandler { (call, reslut) in
      if (call.method == "userInfo") {
        let dict: Dictionary? = call.arguments as? Dictionary<String, Any>
        self.nameLabel.text = "my Name is:\(dict?["name"] ?? "")\n from:\(dict?["city"] ?? "")"
       
      }else if (call.method == "callBack") {
       
        reslut(["title": self.nameLabel.text])
       
      }
    }

这里咱们运用reslut进行回传,在flutter中页面进行调用

RaisedButton(
  child:  Text(callbackData),
  onPressed: () async {
  var result =  await IosViewPage.platform.invokeMethod('callBack');
  setState((){
    callbackData = '${result['title']}';
  });
  },
),

咱们进行回调显现咱们iOS中view的label文字到按钮上

Flutter学习之嵌入原生iOSView

3. 多个原生View通讯抵触问题

当咱们的页面有多个原生view,他们的通讯怎么区别

Flutter学习之嵌入原生iOSView

当咱们点击的时分发现只要最好一个view产生改动,咱们如何改动。可以从2个方面,一个让这个办法变成唯一的。将一个唯一 id 经过初始化参数传递给原生 View,原生 View运用这个id 构建不同称号的MethodChannel。或者是经过viewID,原生 View 生成时,体系会为其生成唯一id:viewId,运用 viewId 构建不同称号的MethodChannel

let method = FlutterMethodChannel(name: "com.flutter.test.TestView\(viewID)", binaryMessenger: messenger)

咱们在原生iOS的view中FlutterMethodChannel的name拼接viewID

var iosViews = [];
return   UiKitView(
  viewType: 'plugins.flutter.io/custom_platform_view',
  creationParams: const {'text': 'Flutter传给IOSLabel的参数'},//初始值
  creationParamsCodec: const StandardMessageCodec(),
  onPlatformViewCreated: (viewID) => iosViews.add(MethodChannel('com.flutter.test.TestView$viewID')),
);

运用的时分取出对应的办法即可

Flutter学习之嵌入原生iOSView

终究结果

Flutter学习之嵌入原生iOSView

4. 小结

Flutter中嵌入原生iOS视图,首要经过flutter中的UiKitView进行获取原生工程APPDelegate中注册的FlutterPlatformView类型从而获取加载到flutter页面。flutter和原生页面交互经过FlutterMethodChannel,根据一些key, method办法等进行交互回调函数的彼此回调完成通讯