// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'basic.dart'; import 'debug.dart'; import 'focus_manager.dart'; import 'focus_scope.dart'; import 'framework.dart'; /// Embeds an Android view in the Widget hierarchy. /// /// Requires Android API level 20 or greater. /// /// Embedding Android views is an expensive operation and should be avoided when a Flutter /// equivalent is possible. /// /// The embedded Android view is painted just like any other Flutter widget and transformations /// apply to it as well. /// /// {@template flutter.widgets.AndroidView.layout} /// The widget fills all available space, the parent of this object must provide bounded layout /// constraints. /// {@endtemplate} /// /// {@template flutter.widgets.AndroidView.gestures} /// The widget participates in Flutter's gesture arenas, and dispatches touch events to the /// platform view iff it won the arena. Specific gestures that should be dispatched to the platform /// view can be specified in the `gestureRecognizers` constructor parameter. If /// the set of gesture recognizers is empty, a gesture will be dispatched to the platform /// view iff it was not claimed by any other gesture recognizer. /// {@endtemplate} /// /// The Android view object is created using a [PlatformViewFactory](/javadoc/io/flutter/plugin/platform/PlatformViewFactory.html). /// Plugins can register platform view factories with [PlatformViewRegistry#registerViewFactory](/javadoc/io/flutter/plugin/platform/PlatformViewRegistry.html#registerViewFactory-java.lang.String-io.flutter.plugin.platform.PlatformViewFactory-). /// /// Registration is typically done in the plugin's registerWith method, e.g: /// /// ```java /// public static void registerWith(Registrar registrar) { /// registrar.platformViewRegistry().registerViewFactory("webview", WebViewFactory(registrar.messenger())); /// } /// ``` /// /// {@template flutter.widgets.AndroidView.lifetime} /// The platform view's lifetime is the same as the lifetime of the [State] object for this widget. /// When the [State] is disposed the platform view (and auxiliary resources) are lazily /// released (some resources are immediately released and some by platform garbage collector). /// A stateful widget's state is disposed when the widget is removed from the tree or when it is /// moved within the tree. If the stateful widget has a key and it's only moved relative to its siblings, /// or it has a [GlobalKey] and it's moved within the tree, it will not be disposed. /// {@endtemplate} class AndroidView extends StatefulWidget { /// Creates a widget that embeds an Android view. /// /// {@template flutter.widgets.AndroidView.constructorArgs} /// The `viewType` and `hitTestBehavior` parameters must not be null. /// If `creationParams` is not null then `creationParamsCodec` must not be null. /// {@endtemplate} const AndroidView({ Key? key, required this.viewType, this.onPlatformViewCreated, this.hitTestBehavior = PlatformViewHitTestBehavior.opaque, this.layoutDirection, this.gestureRecognizers, this.creationParams, this.creationParamsCodec, this.clipBehavior = Clip.hardEdge, }) : assert(viewType != null), assert(hitTestBehavior != null), assert(creationParams == null || creationParamsCodec != null), assert(clipBehavior != null), super(key: key); /// The unique identifier for Android view type to be embedded by this widget. /// /// A [PlatformViewFactory](/javadoc/io/flutter/plugin/platform/PlatformViewFactory.html) /// for this type must have been registered. /// /// See also: /// /// * [AndroidView] for an example of registering a platform view factory. final String viewType; /// {@template flutter.widgets.AndroidView.onPlatformViewCreated} /// Callback to invoke after the platform view has been created. /// /// May be null. /// {@endtemplate} final PlatformViewCreatedCallback? onPlatformViewCreated; /// {@template flutter.widgets.AndroidView.hitTestBehavior} /// How this widget should behave during hit testing. /// /// This defaults to [PlatformViewHitTestBehavior.opaque]. /// {@endtemplate} final PlatformViewHitTestBehavior hitTestBehavior; /// {@template flutter.widgets.AndroidView.layoutDirection} /// The text direction to use for the embedded view. /// /// If this is null, the ambient [Directionality] is used instead. /// {@endtemplate} final TextDirection? layoutDirection; /// Which gestures should be forwarded to the Android view. /// /// {@template flutter.widgets.AndroidView.gestureRecognizers.descHead} /// The gesture recognizers built by factories in this set participate in the gesture arena for /// each pointer that was put down on the widget. If any of these recognizers win the /// gesture arena, the entire pointer event sequence starting from the pointer down event /// will be dispatched to the platform view. /// /// When null, an empty set of gesture recognizer factories is used, in which case a pointer event sequence /// will only be dispatched to the platform view if no other member of the arena claimed it. /// {@endtemplate} /// /// For example, with the following setup vertical drags will not be dispatched to the Android /// view as the vertical drag gesture is claimed by the parent [GestureDetector]. /// /// ```dart /// GestureDetector( /// onVerticalDragStart: (DragStartDetails d) {}, /// child: AndroidView( /// viewType: 'webview', /// ), /// ) /// ``` /// /// To get the [AndroidView] to claim the vertical drag gestures we can pass a vertical drag /// gesture recognizer factory in [gestureRecognizers] e.g: /// /// ```dart /// GestureDetector( /// onVerticalDragStart: (DragStartDetails details) {}, /// child: SizedBox( /// width: 200.0, /// height: 100.0, /// child: AndroidView( /// viewType: 'webview', /// gestureRecognizers: >[ /// new Factory( /// () => new EagerGestureRecognizer(), /// ), /// ].toSet(), /// ), /// ), /// ) /// ``` /// /// {@template flutter.widgets.AndroidView.gestureRecognizers.descFoot} /// A platform view can be configured to consume all pointers that were put down in its bounds /// by passing a factory for an [EagerGestureRecognizer] in [gestureRecognizers]. /// [EagerGestureRecognizer] is a special gesture recognizer that immediately claims the gesture /// after a pointer down event. /// /// The `gestureRecognizers` property must not contain more than one factory with the same [Factory.type]. /// /// Changing `gestureRecognizers` results in rejection of any active gesture arenas (if the /// platform view is actively participating in an arena). /// {@endtemplate} // We use OneSequenceGestureRecognizers as they support gesture arena teams. // TODO(amirh): get a list of GestureRecognizers here. // https://github.com/flutter/flutter/issues/20953 final Set>? gestureRecognizers; /// Passed as the args argument of [PlatformViewFactory#create](/javadoc/io/flutter/plugin/platform/PlatformViewFactory.html#create-android.content.Context-int-java.lang.Object-) /// /// This can be used by plugins to pass constructor parameters to the embedded Android view. final dynamic creationParams; /// The codec used to encode `creationParams` before sending it to the /// platform side. It should match the codec passed to the constructor of [PlatformViewFactory](/javadoc/io/flutter/plugin/platform/PlatformViewFactory.html#PlatformViewFactory-io.flutter.plugin.common.MessageCodec-). /// /// This is typically one of: [StandardMessageCodec], [JSONMessageCodec], [StringCodec], or [BinaryCodec]. /// /// This must not be null if [creationParams] is not null. final MessageCodec? creationParamsCodec; /// {@macro flutter.material.Material.clipBehavior} /// /// Defaults to [Clip.hardEdge], and must not be null. final Clip clipBehavior; @override State createState() => _AndroidViewState(); } // TODO(amirh): describe the embedding mechanism. // TODO(ychris): remove the documentation for conic path not supported once https://github.com/flutter/flutter/issues/35062 is resolved. /// Embeds an iOS view in the Widget hierarchy. /// /// {@macro flutter.rendering.RenderUiKitView} /// /// Embedding iOS views is an expensive operation and should be avoided when a Flutter /// equivalent is possible. /// /// {@macro flutter.widgets.AndroidView.layout} /// /// {@macro flutter.widgets.AndroidView.gestures} /// /// {@macro flutter.widgets.AndroidView.lifetime} /// /// Construction of UIViews is done asynchronously, before the UIView is ready this widget paints /// nothing while maintaining the same layout constraints. /// /// If a conic path clipping is applied to a UIKitView, /// a quad path is used to approximate the clip due to limitation of Quartz. class UiKitView extends StatefulWidget { /// Creates a widget that embeds an iOS view. /// /// {@macro flutter.widgets.AndroidView.constructorArgs} 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); // TODO(amirh): reference the iOS API doc once available. /// The unique identifier for iOS view type to be embedded by this widget. /// /// A PlatformViewFactory for this type must have been registered. final String viewType; /// {@macro flutter.widgets.AndroidView.onPlatformViewCreated} final PlatformViewCreatedCallback? onPlatformViewCreated; /// {@macro flutter.widgets.AndroidView.hitTestBehavior} final PlatformViewHitTestBehavior hitTestBehavior; /// {@macro flutter.widgets.AndroidView.layoutDirection} final TextDirection? layoutDirection; /// Passed as the `arguments` argument of [-\[FlutterPlatformViewFactory createWithFrame:viewIdentifier:arguments:\]](/objcdoc/Protocols/FlutterPlatformViewFactory.html#/c:objc(pl)FlutterPlatformViewFactory(im)createWithFrame:viewIdentifier:arguments:) /// /// This can be used by plugins to pass constructor parameters to the embedded iOS view. final dynamic creationParams; /// The codec used to encode `creationParams` before sending it to the /// platform side. It should match the codec returned by [-\[FlutterPlatformViewFactory createArgsCodec:\]](/objcdoc/Protocols/FlutterPlatformViewFactory.html#/c:objc(pl)FlutterPlatformViewFactory(im)createArgsCodec) /// /// This is typically one of: [StandardMessageCodec], [JSONMessageCodec], [StringCodec], or [BinaryCodec]. /// /// This must not be null if [creationParams] is not null. final MessageCodec? creationParamsCodec; /// Which gestures should be forwarded to the UIKit view. /// /// {@macro flutter.widgets.AndroidView.gestureRecognizers.descHead} /// /// For example, with the following setup vertical drags will not be dispatched to the UIKit /// view as the vertical drag gesture is claimed by the parent [GestureDetector]. /// /// ```dart /// GestureDetector( /// onVerticalDragStart: (DragStartDetails details) {}, /// child: UiKitView( /// viewType: 'webview', /// ), /// ) /// ``` /// /// To get the [UiKitView] to claim the vertical drag gestures we can pass a vertical drag /// gesture recognizer factory in [gestureRecognizers] e.g: /// /// ```dart /// GestureDetector( /// onVerticalDragStart: (DragStartDetails details) {}, /// child: SizedBox( /// width: 200.0, /// height: 100.0, /// child: UiKitView( /// viewType: 'webview', /// gestureRecognizers: >[ /// new Factory( /// () => new EagerGestureRecognizer(), /// ), /// ].toSet(), /// ), /// ), /// ) /// ``` /// /// {@macro flutter.widgets.AndroidView.gestureRecognizers.descFoot} // We use OneSequenceGestureRecognizers as they support gesture arena teams. // TODO(amirh): get a list of GestureRecognizers here. // https://github.com/flutter/flutter/issues/20953 final Set>? gestureRecognizers; @override State createState() => _UiKitViewState(); } /// Embeds an HTML element in the Widget hierarchy in Flutter Web. /// /// *NOTE*: This only works in Flutter Web. To embed web content on other /// platforms, consider using the `flutter_webview` plugin. /// /// Embedding HTML is an expensive operation and should be avoided when a /// Flutter equivalent is possible. /// /// The embedded HTML is painted just like any other Flutter widget and /// transformations apply to it as well. This widget should only be used in /// Flutter Web. /// /// {@macro flutter.widgets.AndroidView.layout} /// /// Due to security restrictions with cross-origin `