Integrating Native Views in Flutter: A Comprehensive Guide
Written on
Chapter 1: Introduction to Flutter's Widget Tree
Flutter's widget architecture is designed to be self-contained, which means that native Android and iOS views cannot directly be integrated into its structure. This limitation poses a challenge for developers aiming to incorporate existing platform components, such as web browsers, into their Flutter applications. Fortunately, there's a solution: Platform Views.
Platform views enable developers to seamlessly embed native components within their Flutter applications. The two primary widgets for this purpose are AndroidView and UIKitView. In this section, we will explore how these widgets can be effectively integrated into the Flutter widget tree.
The video titled "Native Platform Views for mobile and beyond | FlutterVikings 2022" provides an insightful overview of how platform views can enhance your Flutter applications.
Section 1.1: Understanding Platform Views
Platform view widgets serve as a bridge to the native operating system. For instance, the AndroidView widget plays several crucial roles, including:
- Transferring the graphic texture from the native view to Flutter's rendering surface for each frame.
- Managing hit testing and input gestures, converting these into the corresponding native inputs.
- Constructing an accessibility tree that facilitates command and response transfers between native components and Flutter.
However, this method does introduce some overhead due to the "simulation" of Android and iOS components. Therefore, the use of Platform Views is most beneficial when integrating complex elements, like a native video player or Google Maps, that would be labor-intensive to replicate in Flutter.
In summary:
- Avoid Platform Views if the implementation is straightforward.
- Opt for Platform Views when dealing with complex components that would consume considerable development time.
Section 1.2: Implementing Platform Views
To implement a platform view for both Android and iOS, you'll start with an initial setup that includes defining a view type and passing parameters to the platform side. Here’s a basic outline:
Widget build(BuildContext context) {
const String viewType = '';
final Map creationParams = {};
switch (defaultTargetPlatform) {
case TargetPlatform.android:
// Implement Android logic.case TargetPlatform.iOS:
// Implement iOS logic.default:
throw UnsupportedError('Unsupported platform');}
}
Now, let's delve into implementing the AndroidView.
Subsection 1.2.1: Implementing AndroidView
There are two primary methods to implement an Android view:
- Hybrid Composition: The Platform Views are rendered normally while Flutter widgets are rendered into textures.
- Texture Layer: Both Platform Views and Flutter content are rendered directly into a Surface.
Each approach has its advantages and drawbacks. While Hybrid Composition offers impressive performance for Android views, it can reduce Flutter's performance, leading to lower frame rates. Conversely, the Texture Layer method enhances performance on both ends but may encounter issues with fast scrolling and text magnifiers.
For this tutorial, we will adopt the Texture Layer method. Please ensure to refer to the Flutter documentation if you wish to explore the Hybrid Composition approach.
First, include the necessary imports:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
Next, within your switch statement, integrate the following code for the Android view:
case TargetPlatform.android:
AndroidView(
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
),
You might wonder about the StandardMessageCodec class. This class defines the formatting of creation parameters, with various data types represented in specific formats for Android.
Now, moving on to the Android side, implement the following Kotlin code:
package dev.flutter.example
import android.content.Context
import android.graphics.Color
import android.view.View
import android.widget.TextView
import io.flutter.plugin.platform.PlatformView
internal class AndroidView(context: Context, id: Int, creationParams: Map?) : PlatformView {
private val textView: TextView
override fun getView(): View {
return textView}
override fun dispose() {}
init {
textView = TextView(context)
textView.setBackgroundColor(Color.rgb(255, 0, 0))
textView.text = "This text was rendered natively on Android"
}
}
Create a factory class that generates an instance of the AndroidView:
package dev.flutter.example
import android.content.Context
import io.flutter.plugin.common.StandardMessageCodec
import io.flutter.plugin.platform.PlatformView
import io.flutter.plugin.platform.PlatformViewFactory
class AndroidViewFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
val creationParams = args as Map?
return AndroidView(context, viewId, creationParams)
}
}
Finally, register the platform view in your MainActivity.kt:
package dev.flutter.example
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
flutterEngine
.platformViewsController
.registry
.registerViewFactory("", AndroidViewFactory())
}
}
Congratulations! You have now successfully integrated your Android view into Flutter.
Subsection 1.2.2: Implementing UiKitView
The process for integrating native iOS code is similar to that for Android. Use the same imports and include the following in your switch statement:
case TargetPlatform.iOS:
UiKitView(
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
),
The StandardMessageCodec is utilized here as well. For iOS, data types are represented in various formats, similar to Android.
To implement your native widget using Swift, define the factory and view:
import Flutter
import UIKit
class FLUiKitViewFactory: NSObject, FlutterPlatformViewFactory {
private var messenger: FlutterBinaryMessenger
init(messenger: FlutterBinaryMessenger) {
self.messenger = messenger
super.init()
}
func create(
withFrame frame: CGRect,
viewIdentifier viewId: Int64,
arguments args: Any?
) -> FlutterPlatformView {
return FLUiKitView(
frame: frame,
viewIdentifier: viewId,
arguments: args,
binaryMessenger: messenger)
}
public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
return FlutterStandardMessageCodec.sharedInstance()}
}
class FLUiKitView: NSObject, FlutterPlatformView {
private var _view: UIView
init(
frame: CGRect,
viewIdentifier viewId: Int64,
arguments args: Any?,
binaryMessenger messenger: FlutterBinaryMessenger?
) {
_view = UIView()
super.init()
createUiKitView(view: _view)
}
func view() -> UIView {
return _view}
func createUiKitView(view _view: UIView){
_view.backgroundColor = UIColor.red
let label = UILabel()
label.text = "This text was rendered natively on iOS"
label.textColor = UIColor.yellow
label.textAlignment = .center
label.frame = CGRect(x: 0, y: 0, width: 100, height: 50.0)
_view.addSubview(label)
}
}
Next, register your platform view in AppDelegate.swift:
import Flutter
import UIKit
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
weak var registrar = self.registrar(forPlugin: "plugin-name")
let factory = FLUiKitViewFactory(messenger: registrar!.messenger())
self.registrar(forPlugin: "")!.register(
factory,
withId: "")
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
And that's a wrap! You've successfully implemented the UiKitView for iOS.
Chapter 2: Conclusion
In this guide, we explored how to render native views within Flutter applications using Platform Views. If you found this information useful, please consider giving it a clap!
To deepen your understanding, check out these additional resources:
The video "Introduction into Flutter - Flutter Architecture - Why I love Flutter!" provides foundational insights into Flutter's architecture, enhancing your grasp of native view integration.