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】:
![]() |
![]() |