flutter-platform-views
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chineseflutter-platform-views-and-web-embedding
flutter-platform-views-and-web-embedding
Goal
目标
Guides developers through implementing Flutter Platform Views for Android, iOS, and macOS, as well as embedding Flutter into existing web applications. Assumes the user has a configured Flutter environment and is comfortable with Dart, JavaScript, and the relevant native platform languages (Kotlin, Swift).
指导开发者实现面向Android、iOS、macOS的Flutter Platform Views,以及将Flutter嵌入现有Web应用。本文假设用户已配置好Flutter环境,熟悉Dart、JavaScript以及相关原生平台语言(Kotlin、Swift)。
Instructions
操作指南
1. Determine the Target Platform and Embedding Strategy (Decision Logic)
1. 确定目标平台和嵌入策略(决策逻辑)
Before writing code, you must determine the target platform and the specific embedding strategy required by the user.
STOP AND ASK THE USER:
"Which platform are you targeting for native view embedding?
- Android (Requires choosing between Hybrid Composition or Texture Layer)
- iOS (Hybrid Composition only)
- macOS (Hybrid Composition only, gesture support limited)
- Web (Requires choosing between Full Page mode or Embedded/Multi-view mode)"
Decision Tree:
- If Android: Ask the user: "Do you prioritize native Android view fidelity (Hybrid Composition) or Flutter rendering performance (Texture Layer)?"
- If Web: Ask the user: "Are you taking over the full page, or embedding Flutter into specific HTML elements (Embedded/Multi-view mode)?"
编写代码前,你必须先确定目标平台以及用户所需的具体嵌入策略。
请先询问用户:
"你要在哪个平台上实现原生视图嵌入?
- Android(需要在混合合成(Hybrid Composition)和纹理层(Texture Layer)之间选择)
- iOS(仅支持混合合成(Hybrid Composition))
- macOS(仅支持混合合成(Hybrid Composition),手势支持有限)
- Web(需要在全页面模式和嵌入/多视图模式之间选择)"
决策树:
- 如果是Android: 询问用户:"你优先考虑原生Android视图保真度(混合合成 Hybrid Composition)还是Flutter渲染性能(纹理层 Texture Layer)?"
- 如果是Web: 询问用户:"你需要占用全页面,还是将Flutter嵌入到特定HTML元素中(嵌入/多视图模式)?"
2. Implement Android Platform Views
2. 实现Android Platform Views
Based on the user's choice in Step 1, implement the Dart and Kotlin sides.
Dart Implementation:
If the user chose Hybrid Composition (Best fidelity, lower Flutter FPS):
dart
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
Widget buildHybridAndroidView(BuildContext context, String viewType, Map<String, dynamic> creationParams) {
return PlatformViewLink(
viewType: viewType,
surfaceFactory: (context, controller) {
return AndroidViewSurface(
controller: controller as AndroidViewController,
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
);
},
onCreatePlatformView: (params) {
return PlatformViewsService.initSurfaceAndroidView(
id: params.id,
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
onFocus: () {
params.onFocusChanged(true);
},
)
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
..create();
},
);
}If the user chose Texture Layer (Best Flutter FPS, janky quick scrolling):
dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
Widget buildTextureAndroidView(BuildContext context, String viewType, Map<String, dynamic> creationParams) {
return AndroidView(
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}Kotlin Implementation (Platform Side):
Create the View, the Factory, and register it in the .
MainActivitykotlin
package dev.flutter.example
import android.content.Context
import android.graphics.Color
import android.view.View
import android.widget.TextView
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.StandardMessageCodec
import io.flutter.plugin.platform.PlatformView
import io.flutter.plugin.platform.PlatformViewFactory
// 1. Define the View
internal class NativeView(context: Context, id: Int, creationParams: Map<String?, Any?>?) : PlatformView {
private val textView: TextView = TextView(context).apply {
textSize = 72f
setBackgroundColor(Color.rgb(255, 255, 255))
text = "Rendered on a native Android view (id: $id)"
}
override fun getView(): View = textView
override fun dispose() {}
}
// 2. Define the Factory
class NativeViewFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
val creationParams = args as Map<String?, Any?>?
return NativeView(context, viewId, creationParams)
}
}
// 3. Register in MainActivity
class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
flutterEngine
.platformViewsController
.registry
.registerViewFactory("<platform-view-type>", NativeViewFactory())
}
}Validate-and-Fix: If the user is embedding a or , instruct them to manually call on the view when content changes, as they do not invalidate themselves automatically.
SurfaceViewSurfaceTextureinvalidate()根据用户在第一步的选择,实现Dart和Kotlin端的代码。
Dart实现:
如果用户选择了混合合成(Hybrid Composition)(保真度最优,Flutter FPS更低):
dart
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
Widget buildHybridAndroidView(BuildContext context, String viewType, Map<String, dynamic> creationParams) {
return PlatformViewLink(
viewType: viewType,
surfaceFactory: (context, controller) {
return AndroidViewSurface(
controller: controller as AndroidViewController,
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
);
},
onCreatePlatformView: (params) {
return PlatformViewsService.initSurfaceAndroidView(
id: params.id,
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
onFocus: () {
params.onFocusChanged(true);
},
)
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
..create();
},
);
}如果用户选择了纹理层(Texture Layer)(Flutter FPS最优,快速滚动时可能卡顿):
dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
Widget buildTextureAndroidView(BuildContext context, String viewType, Map<String, dynamic> creationParams) {
return AndroidView(
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}Kotlin实现(平台端):
创建View、Factory,并在中注册。
MainActivitykotlin
package dev.flutter.example
import android.content.Context
import android.graphics.Color
import android.view.View
import android.widget.TextView
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.StandardMessageCodec
import io.flutter.plugin.platform.PlatformView
import io.flutter.plugin.platform.PlatformViewFactory
// 1. 定义View
internal class NativeView(context: Context, id: Int, creationParams: Map<String?, Any?>?) : PlatformView {
private val textView: TextView = TextView(context).apply {
textSize = 72f
setBackgroundColor(Color.rgb(255, 255, 255))
text = "Rendered on a native Android view (id: $id)"
}
override fun getView(): View = textView
override fun dispose() {}
}
// 2. 定义Factory
class NativeViewFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
val creationParams = args as Map<String?, Any?>?
return NativeView(context, viewId, creationParams)
}
}
// 3. 在MainActivity中注册
class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
flutterEngine
.platformViewsController
.registry
.registerViewFactory("<platform-view-type>", NativeViewFactory())
}
}校验修复: 如果用户要嵌入或,请告知他们在内容变化时需要手动调用视图的方法,因为这类组件不会自动触发失效刷新。
SurfaceViewSurfaceTextureinvalidate()3. Implement iOS Platform Views
3. 实现iOS Platform Views
iOS uses Hybrid Composition exclusively.
Dart Implementation:
dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
Widget buildIosView(BuildContext context, String viewType, Map<String, dynamic> creationParams) {
return UiKitView(
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}Swift Implementation (Platform Side):
swift
import Flutter
import UIKit
// 1. Define the View
class FLNativeView: NSObject, FlutterPlatformView {
private var _view: UIView
init(frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?, binaryMessenger messenger: FlutterBinaryMessenger?) {
_view = UIView()
super.init()
createNativeView(view: _view)
}
func view() -> UIView { return _view }
func createNativeView(view _view: UIView){
_view.backgroundColor = UIColor.blue
let nativeLabel = UILabel()
nativeLabel.text = "Native text from iOS"
nativeLabel.textColor = UIColor.white
nativeLabel.textAlignment = .center
nativeLabel.frame = CGRect(x: 0, y: 0, width: 180, height: 48.0)
_view.addSubview(nativeLabel)
}
}
// 2. Define the Factory
class FLNativeViewFactory: 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 FLNativeView(frame: frame, viewIdentifier: viewId, arguments: args, binaryMessenger: messenger)
}
public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
return FlutterStandardMessageCodec.sharedInstance()
}
}
// 3. Register in AppDelegate
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
guard let pluginRegistrar = self.registrar(forPlugin: "plugin-name") else { return false }
let factory = FLNativeViewFactory(messenger: pluginRegistrar.messenger())
pluginRegistrar.register(factory, withId: "<platform-view-type>")
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}iOS仅支持混合合成(Hybrid Composition)模式。
Dart实现:
dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
Widget buildIosView(BuildContext context, String viewType, Map<String, dynamic> creationParams) {
return UiKitView(
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}Swift实现(平台端):
swift
import Flutter
import UIKit
// 1. 定义View
class FLNativeView: NSObject, FlutterPlatformView {
private var _view: UIView
init(frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?, binaryMessenger messenger: FlutterBinaryMessenger?) {
_view = UIView()
super.init()
createNativeView(view: _view)
}
func view() -> UIView { return _view }
func createNativeView(view _view: UIView){
_view.backgroundColor = UIColor.blue
let nativeLabel = UILabel()
nativeLabel.text = "Native text from iOS"
nativeLabel.textColor = UIColor.white
nativeLabel.textAlignment = .center
nativeLabel.frame = CGRect(x: 0, y: 0, width: 180, height: 48.0)
_view.addSubview(nativeLabel)
}
}
// 2. 定义Factory
class FLNativeViewFactory: 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 FLNativeView(frame: frame, viewIdentifier: viewId, arguments: args, binaryMessenger: messenger)
}
public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
return FlutterStandardMessageCodec.sharedInstance()
}
}
// 3. 在AppDelegate中注册
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
guard let pluginRegistrar = self.registrar(forPlugin: "plugin-name") else { return false }
let factory = FLNativeViewFactory(messenger: pluginRegistrar.messenger())
pluginRegistrar.register(factory, withId: "<platform-view-type>")
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}4. Implement macOS Platform Views
4. 实现macOS Platform Views
macOS uses Hybrid Composition. Note that gesture support is currently limited.
Dart Implementation:
dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
Widget buildMacOsView(BuildContext context, String viewType, Map<String, dynamic> creationParams) {
return AppKitView(
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}Swift Implementation (Platform Side):
swift
import Cocoa
import FlutterMacOS
// 1. Define the View
class NativeView: NSView {
init(viewIdentifier viewId: Int64, arguments args: Any?, binaryMessenger messenger: FlutterBinaryMessenger?) {
super.init(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
wantsLayer = true
layer?.backgroundColor = NSColor.systemBlue.cgColor
createNativeView(view: self)
}
required init?(coder nsCoder: NSCoder) { super.init(coder: nsCoder) }
func createNativeView(view _view: NSView) {
let nativeLabel = NSTextField()
nativeLabel.frame = CGRect(x: 0, y: 0, width: 180, height: 48.0)
nativeLabel.stringValue = "Native text from macOS"
nativeLabel.isEditable = false
nativeLabel.sizeToFit()
_view.addSubview(nativeLabel)
}
}
// 2. Define the Factory
class NativeViewFactory: NSObject, FlutterPlatformViewFactory {
private var messenger: FlutterBinaryMessenger
init(messenger: FlutterBinaryMessenger) {
self.messenger = messenger
super.init()
}
func create(withViewIdentifier viewId: Int64, arguments args: Any?) -> NSView {
return NativeView(viewIdentifier: viewId, arguments: args, binaryMessenger: messenger)
}
public func createArgsCodec() -> (FlutterMessageCodec & NSObjectProtocol)? {
return FlutterStandardMessageCodec.sharedInstance()
}
}
// 3. Register in MainFlutterWindow.swift
class MainFlutterWindow: NSWindow {
override func awakeFromNib() {
let registrar = flutterViewController.registrar(forPlugin: "plugin-name")
let factory = NativeViewFactory(messenger: registrar.messenger)
registrar.register(factory, withId: "<platform-view-type>")
super.awakeFromNib()
}
}macOS使用混合合成(Hybrid Composition)模式,请注意目前手势支持有限。
Dart实现:
dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
Widget buildMacOsView(BuildContext context, String viewType, Map<String, dynamic> creationParams) {
return AppKitView(
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}Swift实现(平台端):
swift
import Cocoa
import FlutterMacOS
// 1. 定义View
class NativeView: NSView {
init(viewIdentifier viewId: Int64, arguments args: Any?, binaryMessenger messenger: FlutterBinaryMessenger?) {
super.init(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
wantsLayer = true
layer?.backgroundColor = NSColor.systemBlue.cgColor
createNativeView(view: self)
}
required init?(coder nsCoder: NSCoder) { super.init(coder: nsCoder) }
func createNativeView(view _view: NSView) {
let nativeLabel = NSTextField()
nativeLabel.frame = CGRect(x: 0, y: 0, width: 180, height: 48.0)
nativeLabel.stringValue = "Native text from macOS"
nativeLabel.isEditable = false
nativeLabel.sizeToFit()
_view.addSubview(nativeLabel)
}
}
// 2. 定义Factory
class NativeViewFactory: NSObject, FlutterPlatformViewFactory {
private var messenger: FlutterBinaryMessenger
init(messenger: FlutterBinaryMessenger) {
self.messenger = messenger
super.init()
}
func create(withViewIdentifier viewId: Int64, arguments args: Any?) -> NSView {
return NativeView(viewIdentifier: viewId, arguments: args, binaryMessenger: messenger)
}
public func createArgsCodec() -> (FlutterMessageCodec & NSObjectProtocol)? {
return FlutterStandardMessageCodec.sharedInstance()
}
}
// 3. 在MainFlutterWindow.swift中注册
class MainFlutterWindow: NSWindow {
override func awakeFromNib() {
let registrar = flutterViewController.registrar(forPlugin: "plugin-name")
let factory = NativeViewFactory(messenger: registrar.messenger)
registrar.register(factory, withId: "<platform-view-type>")
super.awakeFromNib()
}
}5. Implement Web Embedding
5. 实现Web嵌入
If the user chose Embedded/Multi-view mode, implement the JS and Dart configurations.
JavaScript Implementation ( or HTML script):
flutter_bootstrap.jsjavascript
_flutter.loader.load({
onEntrypointLoaded: async function onEntrypointLoaded(engineInitializer) {
let engine = await engineInitializer.initializeEngine({
multiViewEnabled: true, // Enables embedded mode.
});
let app = await engine.runApp();
// Add a view to a specific host element
let viewId = app.addView({
hostElement: document.querySelector('#flutter-host-element'),
initialData: { greeting: 'Hello from JS!' }
});
}
});Dart Implementation ():
Validate-and-Fix: Ensure is used instead of . will fail with a null error in multi-view mode.
main.dartrunWidgetrunApprunAppimplicitViewdart
import 'dart:ui' show FlutterView;
import 'package:flutter/widgets.dart';
void main() {
// MUST use runWidget, not runApp, for multi-view web embedding.
runWidget(
MultiViewApp(
viewBuilder: (BuildContext context) => const MyEmbeddedWidget(),
),
);
}
class MultiViewApp extends StatefulWidget {
const MultiViewApp({super.key, required this.viewBuilder});
final WidgetBuilder viewBuilder;
State<MultiViewApp> createState() => _MultiViewAppState();
}
class _MultiViewAppState extends State<MultiViewApp> with WidgetsBindingObserver {
Map<Object, Widget> _views = <Object, Widget>{};
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_updateViews();
}
void didChangeMetrics() {
_updateViews();
}
void _updateViews() {
final Map<Object, Widget> newViews = <Object, Widget>{};
for (final FlutterView view in WidgetsBinding.instance.platformDispatcher.views) {
final Widget viewWidget = _views[view.viewId] ?? _createViewWidget(view);
newViews[view.viewId] = viewWidget;
}
setState(() {
_views = newViews;
});
}
Widget _createViewWidget(FlutterView view) {
return View(
view: view,
child: Builder(builder: widget.viewBuilder),
);
}
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
Widget build(BuildContext context) {
return ViewCollection(views: _views.values.toList(growable: false));
}
}
class MyEmbeddedWidget extends StatelessWidget {
const MyEmbeddedWidget({super.key});
Widget build(BuildContext context) {
// Retrieve the viewId to handle specific logic if needed
final int viewId = View.of(context).viewId;
return Directionality(
textDirection: TextDirection.ltr,
child: Center(child: Text('Rendered in View ID: $viewId')),
);
}
}如果用户选择了嵌入/多视图模式,请实现JS和Dart端的配置。
JavaScript实现(或HTML脚本):
flutter_bootstrap.jsjavascript
_flutter.loader.load({
onEntrypointLoaded: async function onEntrypointLoaded(engineInitializer) {
let engine = await engineInitializer.initializeEngine({
multiViewEnabled: true, // 启用嵌入模式
});
let app = await engine.runApp();
// 将视图添加到指定的宿主元素中
let viewId = app.addView({
hostElement: document.querySelector('#flutter-host-element'),
initialData: { greeting: 'Hello from JS!' }
});
}
});Dart实现():
校验修复: 确保使用而非,在多视图模式下会抛出为空的错误。
main.dartrunWidgetrunApprunAppimplicitViewdart
import 'dart:ui' show FlutterView;
import 'package:flutter/widgets.dart';
void main() {
// 多视图Web嵌入必须使用runWidget,不能用runApp
runWidget(
MultiViewApp(
viewBuilder: (BuildContext context) => const MyEmbeddedWidget(),
),
);
}
class MultiViewApp extends StatefulWidget {
const MultiViewApp({super.key, required this.viewBuilder});
final WidgetBuilder viewBuilder;
State<MultiViewApp> createState() => _MultiViewAppState();
}
class _MultiViewAppState extends State<MultiViewApp> with WidgetsBindingObserver {
Map<Object, Widget> _views = <Object, Widget>{};
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_updateViews();
}
void didChangeMetrics() {
_updateViews();
}
void _updateViews() {
final Map<Object, Widget> newViews = <Object, Widget>{};
for (final FlutterView view in WidgetsBinding.instance.platformDispatcher.views) {
final Widget viewWidget = _views[view.viewId] ?? _createViewWidget(view);
newViews[view.viewId] = viewWidget;
}
setState(() {
_views = newViews;
});
}
Widget _createViewWidget(FlutterView view) {
return View(
view: view,
child: Builder(builder: widget.viewBuilder),
);
}
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
Widget build(BuildContext context) {
return ViewCollection(views: _views.values.toList(growable: false));
}
}
class MyEmbeddedWidget extends StatelessWidget {
const MyEmbeddedWidget({super.key});
Widget build(BuildContext context) {
// 可获取viewId以处理特定业务逻辑
final int viewId = View.of(context).viewId;
return Directionality(
textDirection: TextDirection.ltr,
child: Center(child: Text('Rendered in View ID: $viewId')),
);
}
}Constraints
限制条件
- Do not use when configuring Flutter Web for multi-view embedding. You must use
runAppand manage therunWidgetlifecycle viaFlutterView.WidgetsBindingObserver - Do not use or
ShaderMaskwidgets over iOS Platform Views, as they are unsupported.ColorFilteredhas strict limitations.BackdropFilter - Do not assume Android or
SurfaceViewwill automatically invalidate when their content changes. You must manually callSurfaceTextureon the view or its parent.invalidate() - Do not wrap the entire output in a markdown code block. Return raw markdown text.
- Always verify the Android API level is 23+ before implementing Platform Views.
- 为Flutter Web配置多视图嵌入时禁止使用,必须使用
runApp并通过runWidget管理WidgetsBindingObserver的生命周期。FlutterView - 禁止在iOS Platform Views上使用或
ShaderMask组件,它们不受支持,ColorFiltered也有严格的使用限制。BackdropFilter - 不要假设Android的或
SurfaceView在内容变化时会自动失效刷新,你必须手动调用视图或其父视图的SurfaceTexture方法。invalidate() - 不要将全部输出包裹在Markdown代码块中,返回原始Markdown文本即可。
- 实现Platform Views前请务必确认Android API等级为23及以上。