Flutter AndroidView
在 Flutter 《Flutter 内嵌原生视图能力》Anroid 的 Virtual displays 模式下,AndroidView 是开发者在 Flutter 侧的开端。关于 Virtual displays 如何使用,请点击阅读《Flutter 内嵌原生视图 Android 端接入实现》。在本文中,将深入 AndroidView 的内部,讲解其底层实现原理。
由于 AndroidView 比较复杂,在深入研究之前,请先完成几篇前置文章的学习《Flutter Android Virtual Display 实现原理》《Flutter RenderAndroidView》《Flutter AndroidViewController》,有了这些文章“打底”,我们将底气十足,再学起 AndroidView 来,则小菜一碟。
AndroidView 是一个 Flutter 组件(StatefulWidget),表示一个嵌入 Flutter 中的 Android 原生视图。它的特性如下:
- 布局:该组件会尽可能充满可用空间。
- 动画:AndroidView 模式下,组件可视为 Flutter 组件,可参与动画、变换。
- 手势:AndroidView 参与 Flutter 的手势竞技场(gesture arenas),如果竞争胜出,手势将由该组件传给 Native 侧的 Platform View。特殊手势可通过
gestureRecognizers
构造参数指定。 - 生命周期:AndroidView 是一个 StatefulWidget,原生视图的生命周期与 AndroidView 的
State
一致
注意,本文基于 Flutter Engine 2.10.5 版本。未来我也会基于 Flutter 3 新版本进行回顾,到时再更新本文。Flutter Engine 2.10.5 对应的 commit 为:57d3bac3dd5cb5b0e464ab70e7bc8a0d8cf083ab
在注释中提到,嵌入 Android 视图是一项昂贵的操作,应当尽可能避免使用这一功能。
AndroidView
组成结构
AndroidView 是一个 Flutter StatefulWidget,由如下部分组成:
- AndroidView:Flutter StatefulWidget
_AndroidViewState
:AndroidView 对应的状态(State)_AndroidPlatformView
:_AndroidViewState
build 方法中创建的 Widget- RenderAndroidView:由
_AndroidPlatformView
创建,是一个 RenderObject,已经到 Flutter 三颗树的最底层了
这三层结构还是比较好理解的。再加上一个东东:
- AndroidViewController,可以理解为
_AndroidViewState
内部实现比较复杂,创建一个专门的控制类,并且,该类是在_AndroidViewState
内创建,但是会传入_AndroidPlatformView
,共内部、底层使用。
我们之前做的积累派上用场了:《Flutter Android Virtual Display 实现原理》《Flutter RenderAndroidView》《Flutter AndroidViewController》。是不是有底气了?
输入参数
StatefulWidget 的主要作用是管理组件入参,具体包括以下:
// 平台视图创建成功后回调
final PlatformViewCreatedCallback? onPlatformViewCreated;
// 视图类型
final String viewType;
// 点击测试相关
final PlatformViewHitTestBehavior hitTestBehavior;
// 布局方向
final TextDirection? layoutDirection;
// 手势识别器
final Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers;
// 原生侧 PlatformView 视图的构建参数
final dynamic creationParams;
// 构建参数的 Channel 所采用的编码器
final MessageCodec<dynamic>? creationParamsCodec;
// 是否需要裁剪
final Clip clipBehavior;
其中:
- PlatformViewCreatedCallback:请参见《Flutter AndroidViewController#PlatformViewCreatedCallback 的订阅者》
- gestureRecognizers:控制什么样的手势向 Android 转发
AndroidView 的内部逻辑和生命周期,都在 _AndroidViewState
中。接下来,我们进入 State 部分。
_AndroidViewState
_AndroidViewState
是 AndroidView 的 State,想到 State,会想到生命周期、内部状态、build,总之,关键的逻辑都在 _AndroidViewState
中。
状态属性
首先看 _AndroidViewState
中包含以下属性:
// 每个原生视图都有一个 id,由 Flutter 生成
int? _id;
// 大部分逻辑都封装在 _controller 内部
late AndroidViewController _controller;
// 文本方向
TextDirection? _layoutDirection;
// 表示是否初始化过
bool _initialized = false;
// 焦点
FocusNode? _focusNode;
其中,AndroidViewController 是一个比较关键的属性:_controller
已在《Flutter AndroidViewController》和《Flutter TextureAndroidViewController》两篇文章介绍过。
_controller
在 _AndroidViewState
初始化时进行创建,接下来我们看初始化流程。
_initializeOnce
初始化
_initializeOnce
是 _AndroidViewState
中的初始化逻辑 ,由 _initialized
属性避免重复初始化。由 didChangeDependencies
方法所触发:
@override
void didChangeDependencies() {
super.didChangeDependencies();
//...
_initializeOnce();
//...
}
void _initializeOnce() {
if (_initialized) {
return;
}
_initialized = true;
_createNewAndroidView();
_focusNode = FocusNode(debugLabel: 'AndroidView(id: $_id)');
}
_createNewAndroidView
方法实现如下,可以看到,同时对 _id
和 _controller
进行初始化:
void _createNewAndroidView() {
_id = platformViewsRegistry.getNextPlatformViewId();
_controller = PlatformViewsService.initAndroidView(
id: _id!,
viewType: widget.viewType,
layoutDirection: _layoutDirection!,
creationParams: widget.creationParams,
creationParamsCodec: widget.creationParamsCodec,
onFocus: () {
_focusNode!.requestFocus();
},
);
if (widget.onPlatformViewCreated != null) {
_controller.addOnPlatformViewCreatedListener(widget.onPlatformViewCreated!);
}
}
其中,PlatformViewsService.initAndroidView
创建出 Flutter TextureAndroidViewController。id 则由 platformViewsRegistry.getNextPlatformViewId() 在 Flutter 侧生成,其实现:
int getNextPlatformViewId() => _nextPlatformViewId++;
build
梳理到这里,开始讲到 AndroidView 的渲染过程,文章开头梳理了 AndroidView 的结构,这里的 build 创建一个 _AndroidPlatformView
,后面小节会介绍 _AndroidPlatformView
,实际上它里面没什么代码,它的作用,只是对更加底层的 RenderAndroidView 进行封装,将 RenderAndroidView 的 RenderObject 通过 _AndroidPlatformView
封装为 Widget,然后这里的 state(AndroidView 组件在 build 时创建了 _AndroidPlatformView
。具体如下:
@override
Widget build(BuildContext context) {
return Focus(
focusNode: _focusNode,
onFocusChange: _onFocusChange,
child: _AndroidPlatformView(
controller: _controller,
hitTestBehavior: widget.hitTestBehavior,
gestureRecognizers: widget.gestureRecognizers ?? _emptyRecognizersSet,
clipBehavior: widget.clipBehavior,
),
);
}
其中,对于 _AndroidPlatformView
,我们放到后面小节中梳理。
PlatformViewsService.initAndroidView
initAndroidView
是一个静态方法,用于创建一个新的 Flutter TextureAndroidViewController(实现了 Flutter AndroidViewController),这个控制器用于管理 Android 侧原生视图。
方法的参数包括:
id
:一个唯一的标识符,通常由platformViewsRegistry
生成。viewType
:要创建的Android视图类型的标识符。这个视图类型的工厂必须已经在平台端注册过。layoutDirection
:布局方向,不能为空。creationParams
:创建参数,这些参数将作为PlatformViewFactory#create
的args参数传递。creationParamsCodec
:用于编码creationParams
的编解码器。与原生侧匹配onFocus
:当 Android 视图请求获取输入焦点时,将调用此回调函数。
在方法体中,创建了一个TextureAndroidViewController
实例,并将onFocus
回调函数存储在_instance._focusCallbacks
中,最后返回创建的控制器实例。
注意,==Android 侧的原生视图只有在首次调用AndroidViewController.setSize
后才会被创建。==具体参见《Flutter TextureAndroidViewController#setSize 设置大小》。
实现源码如下:
static TextureAndroidViewController initAndroidView({
required int id,
required String viewType,
required TextDirection layoutDirection,
dynamic creationParams,
MessageCodec<dynamic>? creationParamsCodec,
VoidCallback? onFocus,
}) {
//...
final TextureAndroidViewController controller = TextureAndroidViewController._(
viewId: id,
viewType: viewType,
layoutDirection: layoutDirection,
creationParams: creationParams,
creationParamsCodec: creationParamsCodec,
);
// 全局字典,id 到 _focusCallbacks 的映射
_instance._focusCallbacks[id] = onFocus ?? () {};
return controller;
}
_AndroidPlatformView
该类继承自 LeafRenderObjectWidget
,它的作用,只是对更加底层的 RenderAndroidView 进行封装,将 RenderAndroidView 的 RenderObject 封装为 Widget,
createRenderObject
对应的是 RenderAndroidView:
@override
RenderObject createRenderObject(BuildContext context) =>
RenderAndroidView(
viewController: controller,
hitTestBehavior: hitTestBehavior,
gestureRecognizers: gestureRecognizers,
clipBehavior: clipBehavior,
);
updateRenderObject
@override
void updateRenderObject(BuildContext context, RenderAndroidView renderObject) {
renderObject.viewController = controller;
renderObject.hitTestBehavior = hitTestBehavior;
renderObject.updateGestureRecognizers(gestureRecognizers);
renderObject.clipBehavior = clipBehavior;
}
本文作者:Maeiee
本文链接:Flutter AndroidView
版权声明:如无特别声明,本文即为原创文章,版权归 Maeiee 所有,未经允许不得转载!
喜欢我文章的朋友请随缘打赏,鼓励我创作更多更好的作品!