Flutter 混合开发系列 包含以下:android
- 嵌入原生View-Android
- 嵌入原生View-iOS
- 与原生通讯-MethodChannel
- 与原生通讯-BasicMessageChannel
- 与原生通讯-EventChannel
- 添加 Flutter 到 Android Activity
- 添加 Flutter 到 Android Fragment
- 添加 Flutter 到 iOS
每一个工做日分享一篇,欢迎关注、点赞及转发。ios
建议使用 Xcode 进行开发,在 Android Studio 左侧 project tab下选中 ios 目录下任意一个文件,右上角会出现 Open iOS module in Xcode ,git
点击便可打开,打开后以下:微信
在Runner 目录下建立 iOS View,此 View 继承 FlutterPlatformView ,返回一个简单的 UILabel :app
import Foundation import Flutter class MyFlutterView: NSObject,FlutterPlatformView { let label = UILabel() init(_ frame: CGRect,viewID: Int64,args :Any?,messenger :FlutterBinaryMessenger) { label.text = "我是 iOS View" } func view() -> UIView { return label } }
建立 MyFlutterViewFactory:less
import Foundation import Flutter class 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() } }
在 AppDelegate 中注册:async
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 = MyFlutterViewFactory(messenger: registrar.messenger()) registrar.register(factory, withId: "plugins.flutter.io/custom_platform_view") return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }
记住 plugins.flutter.io/custom_platform_view ,这个字符串在 Flutter 中须要与其保持一致。ide
在 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(), ), ); } }
上面嵌入的是 iOS View,所以经过 defaultTargetPlatform == TargetPlatform.iOS 判断当前平台加载,在 iOS 上运行效果:ui
Flutter 端修改以下:
UiKitView( viewType: 'plugins.flutter.io/custom_platform_view', creationParams: {'text': 'Flutter传给IOSTextView的参数'}, creationParamsCodec: StandardMessageCodec(), )
修改 MyFlutterView :
import Foundation import Flutter class 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 } }
最终效果:
修改 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()), ]), ); } }
在 原生View 中也建立一个 MethodChannel 用于通讯:
import Foundation import Flutter class 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 } }
与上面发送信息不一样的是,Flutter 向原生请求数据,原生返回数据到 Flutter 端,修改 MyFlutterView onMethodCall:
import Foundation import Flutter class 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 } }
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']}'; }); }, ),
固然页面有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())), ]), ); } }
此时点击 传递参数给原生View 按钮哪一个View会改变内容,实际上只有最后一个会改变。
如何改变指定View的内容?重点是 MethodChannel,只需修改上面3个通道的名称不相同便可:
原生 View 使用 viewId 构建不一样名称的 MethodChannel:
import Foundation import Flutter class 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 } }
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(), )
给第一个发送消息:
platforms[0] .invokeMethod('setText', {'name': 'laomeng', 'age': 18});
老孟Flutter博客(330个控件用法+实战入门系列文章):http://laomengit.com
欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:
![]() |
![]() |