What is Platform View
To enable some existing Native controls to be directly referenced into the Flutter App, the Flutter team provides a Platform View that allows the Native View to be embedded into the Flutter Widget system, enabling Dart code to control the Native View. Platform Views include AndroidView and UIKitView
How to use Platform View
AndroidView
class AndroidView extends StatefulWidget { const AndroidView({ Key key, @required this.viewType, this.onPlatformViewCreated, this.hitTestBehavior = PlatformViewHitTestBehavior.opaque, this.layoutDirection, this.gestureRecognizers, this.creationParams, this.creationParamsCodec, }) : assert(viewType ! = null), assert(hitTestBehavior ! = null), assert(creationParams == null || creationParamsCodec ! = null), super(key: key); /// Embed the unique identifier of the Android view final String viewType; / / / Platform View created the callback final PlatformViewCreatedCallback onPlatformViewCreated; / / / final hit during testing behavior PlatformViewHitTestBehavior hitTestBehavior; // view TextDirection final TextDirection layoutDirection; / / / used for processing time conflicts, to distribute time management related operations final Set < Factory < OneSequenceGestureRecognizer > > gestureRecognizers; /// Final Dynamic creationParams is used when constructing the Android view; If creationParams is not null, the value must not be NULL final MessageCodec<dynamic> creationParamsCodec; @override State<AndroidView> createState() => _AndroidViewState(); }Copy the code
Note that:
- AndroidView only supports Android API 20 and above
- Using AndroidView in Flutter has a high performance overhead and should be avoided
Open the created Flutter project with Android Studio and go to the top menu of Tools->Flutter->Open for Editing in Android Studio.Click to open an Android project containing the introduced tripartite libraries:In the main project, create an Android View to embed into Flutter. This View is derived from PlatformView:
class CustomFlutterView(private val context: Context, messenger: BinaryMessenger, private val viewId: Int, params: Map<String, Any>?) : PlatformView, MethodChannel.MethodCallHandler{ private var binding: CustomFlutterViewBinding = CustomFlutterViewBinding.inflate(LayoutInflater.from(context)) private var methodChannel: MethodChannel init { params?.also { binding.tvReceiverFlutterMsg.text = it["init"] as String } methodChannel = MethodChannel(messenger, "com.mufeng.flutter_native_view") methodChannel.setMethodCallHandler(this) } override fun getView(): View { binding.sendMsgToFlutter.setOnClickListener { methodChannel.invokeMethod("sendMsgToFlutter", mapOf("text" to "Hello, CustomFlutterView_$viewId")) } return binding.root } override fun dispose() { methodChannel.setMethodCallHandler(null) Log. E ("TAG", "free resource ")} Override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { if (call.method == "updateText") { val author = call.argument("author") as String? binding.tvReceiverFlutterMsg.text = "Hello, $author" } else { result.notImplemented() } } }Copy the code
- GetView (): Returns the Android View to embed the Flutter hierarchy
- Dispose: call when you release this VIew, the VIew is not available after this method is called, this method needs to clean up all object references, otherwise it will cause memory leak
- Messenger: Used for messaging. This parameter is used when Flutter communicates with native
- ViewId: A unique ID is assigned when the View is generated
- Args: Initialization parameters passed by Flutter
Registered PlatformView
Create a PlatformViewFactory:
class CustomFlutterViewFactory(private val messenger: BinaryMessenger): PlatformViewFactory(StandardMessageCodec.INSTANCE){ override fun create(context: Context, viewId: Int, args: Any?) : PlatformView { return CustomFlutterView(context, messenger, viewId, args as Map<String, Any>) } }Copy the code
Create CustomPlugin:
class CustomPlugin : FlutterPlugin {
companion object {
const val VIEW_TYPE_ID: String = "com.mufeng.flutter_native_view/custom_platform_view"
fun registerWith(registrar: PluginRegistry.Registrar) {
registrar.platformViewRegistry()
.registerViewFactory(VIEW_TYPE_ID, CustomFlutterViewFactory(registrar.messenger()))
}
}
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
val messenger: BinaryMessenger = binding.binaryMessenger
binding.platformViewRegistry.registerViewFactory(VIEW_TYPE_ID, CustomFlutterViewFactory(messenger))
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
}
}
Copy the code
Note: the string VIEW_TYPE_ID must be consistent with the Flutter end
Register with MainActivity in App:
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
flutterEngine.plugins.add(CustomPlugin())
}
}
Copy the code
Note that if a Flutter Plugin is developed without MainActivity, it needs to be modified using the onAttachedToEngine() and **registerWith()** methods of the corresponding Plugin class
Called in Flutter
class _MyHomePageState extends State<MyHomePage> { static const platform = const MethodChannel('com.mufeng.flutter_native_view'); String result; @override void initState() { super.initState(); platform.setMethodCallHandler((call) { setState(() { result = call.arguments['text']; }); ${call.arguments['text']}"); return; }); } @override Widget build(BuildContext context) { Widget platformView() { if (defaultTargetPlatform == TargetPlatform.android) { return AndroidView( viewType: "com.mufeng.flutter_native_view/custom_platform_view", creationParams: {'init': 'Initialization parameters passed from Flutter '}, creationParamsCodec: StandardMessageCodec(), onPlatformViewCreated: (viewId) {print('View created; ViewId: $viewId'); }); } } return Scaffold( appBar: AppBar( title: Text( 'Platform View', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold), ), centerTitle: true, ), body: Column( children: [ TextButton(onPressed: () { platform.invokeMethod('updateText', {'author': 'MuFeng'}); }, child: Text(' pass parameters to Native View'), Text(result??' wait for Native to send data '), Expanded(Child: platformView()),],),); }}Copy the code
To see how it works on Android:
UIKitView
class UiKitView extends StatefulWidget { const UiKitView({ Key key, @required this.viewType, this.onPlatformViewCreated, this.hitTestBehavior = PlatformViewHitTestBehavior.opaque, this.layoutDirection, this.creationParams, this.creationParamsCodec, this.gestureRecognizers, }) : assert(viewType ! = null), assert(hitTestBehavior ! = null), assert(creationParams == null || creationParamsCodec ! = null), super(key: key); // the unique identifier embedded in the iOS view final String viewType; / / / Platform View created the callback final PlatformViewCreatedCallback onPlatformViewCreated; / / / final hit during testing behavior PlatformViewHitTestBehavior hitTestBehavior; // view TextDirection final TextDirection layoutDirection; /// Final Dynamic creationParams is used in Android view construction. If creationParams is not null, the value must not be NULL final MessageCodec<dynamic> creationParamsCodec; / / / used for processing time conflicts, to distribute time management related operations final Set < Factory < OneSequenceGestureRecognizer > > gestureRecognizers; @override State<UiKitView> createState() => _UiKitViewState(); }Copy the code
To develop a Flutter project using Xcode, Open the created Flutter project in Android Studio. At the top of Android Studio, click Tools->Flutter->Open iOS Module in Xcode to Open an iOS project
The first step is to create an iOS View in the Runner directory that inherits the FlutterPlatformView
import Foundation import Flutter class CustomFlutterView:NSObject, FlutterPlatformView { let label = UILabel() init(_ frame: CGRect, viewID: Int64, params: Any? , messenger: FlutterBinaryMessenger) { super.init() if(params is NSDictionary){ let dict = params as! NSDictionary label.text = "\(dict.value(forKey: "init") as? String ?? } let methodChannel = FlutterMethodChannel(name: "com.mufeng. Flutter_native_view ", binaryMessenger: messenger) methodChannel.setMethodCallHandler {(call, result) in if(call.method == "updateText"){ if let dict = call.arguments as? Dictionary<String, Any>{ let author: String = dict["author"] as? String ?? "" self.label.text = "Hello, \(author), AddOnClick {(view) in var arguments = Dictionary<String, Any>() arguments["text"] = "CustomFlutterView_\(viewID)" methodChannel.invokeMethod("sendMsgToFlutter", arguments: arguments) } } func view() -> UIView { return label } }Copy the code
Step 2 create CustomFlutterViewFactory:
import Foundation
import Flutter
class CustomFlutterViewFactory: NSObject, FlutterPlatformViewFactory {
var messenger: FlutterBinaryMessenger
init(messenger: FlutterBinaryMessenger) {
self.messenger = messenger
super.init()
}
func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments params: Any?) -> FlutterPlatformView{
return CustomFlutterView(frame, viewID: viewId, params: params, messenger: messenger)
}
func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
return FlutterStandardMessageCodec.sharedInstance()
}
}
Copy the code
Step 3: Register in the AppDelegate:
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
let VIEW_TYPE_ID: String = "com.mufeng.flutter_native_view/custom_platform_view"
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
let registrar: FlutterPluginRegistrar = self.registrar(forPlugin: VIEW_TYPE_ID)!
let factory = CustomFlutterViewFactory(messenger: registrar.messenger())
registrar.register(factory, withId: VIEW_TYPE_ID)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Copy the code
Note: the string VIEW_TYPE_ID must be consistent with the Flutter end
Step 4, call in Flutter
class _MyHomePageState extends State<MyHomePage> { static const platform = const MethodChannel('com.mufeng.flutter_native_view'); String result; @override void initState() { super.initState(); platform.setMethodCallHandler((call) { setState(() { result = call.arguments['text']; }); ${call.arguments['text']}"); return; }); } @override Widget build(BuildContext context) { Widget platformView() { if (defaultTargetPlatform == TargetPlatform.android) { return AndroidView( viewType: "com.mufeng.flutter_native_view/custom_platform_view", creationParams: {'init': 'Initialization parameters passed from Flutter '}, creationParamsCodec: StandardMessageCodec(), onPlatformViewCreated: (viewId) {print('View created; ViewId: $viewId'); }); }else if(defaultTargetPlatform == TargetPlatform.android){ return UiKitView( viewType: "com.mufeng.flutter_native_view/custom_platform_view", creationParams: {'init': 'Initialization parameters passed from Flutter '}, creationParamsCodec: StandardMessageCodec(), onPlatformViewCreated: (viewId) {print('View created; ViewId: $viewId'); }); }else{return Text(' currently unsupported platform type '); } } return Scaffold( appBar: AppBar( title: Text( 'Platform View', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold), ), centerTitle: true, ), body: Column( children: [ TextButton(onPressed: () { platform.invokeMethod('updateText', {'author': 'MuFeng'}); }, child: Text(' pass parameters to Native View'), Text(result??' wait for Native to send data '), Expanded(Child: platformView()),],),); }}Copy the code
To see how this works in iOS:
This article code address github.com/hanlin19900…
To embed Native components in a Flutter, you need to attach textures to the Flutter