跳转至正文

向 Android 应用添加 Flutter View

了解如何通过 Flutter View 进行高级集成。

通过 FlutterView 集成比前文介绍的 FlutterActivity 和 FlutterFragment 方式需要更多工作。

从根本上说,Dart 侧的 Flutter 框架需要访问各种 activity 级别的事件与生命周期才能正常工作。由于 FlutterView(它是一个 android.view.View)可以添加到开发者应用拥有的任意 activity 中,而 FlutterView 无法访问 activity 级别的事件,因此开发者必须手动将这些连接桥接到 FlutterEngine

你如何将应用的 activity 事件传递给 FlutterView,取决于你的应用本身。

示例

#
Add Flutter View sample video

与 FlutterActivity 和 FlutterFragment 的指南不同,FlutterView 集成更适合用示例项目来演示。

示例项目位于 https://github.com/flutter/samples/tree/main/add_to_app/android_view,演示了简单的 FlutterView 集成:在 RecycleView 卡片列表的部分 cell 中使用 FlutterView,如上方 gif 所示。

通用做法

#

FlutterView 级别集成的要点是:你必须在自己的应用代码中重现 Activity、FlutterViewFlutterEngine 之间在 FlutterActivityAndFragmentDelegate 中的各种交互。FlutterActivityAndFragmentDelegate 中的连接在使用 FlutterActivityFlutterFragment 时会自动完成;但本场景下 FlutterView 被添加到你应用中的 ActivityFragment,因此你必须手动重建这些连接。否则 FlutterView 将无法渲染任何内容,或缺少其他功能。

示例 FlutterViewEngine 类展示了一种可能的实现:在 ActivityFlutterViewFlutterEngine 之间建立应用特定的连接。

需要实现的 API

#

要让 Flutter 至少能绘制任何内容,最低限度的实现包括:

FlutterViewActivity 不再可见时,还必须调用反向的 detachFromFlutterEngine 以及 LifecycleChannel 类上的其他生命周期方法,以避免资源泄漏。

此外,请参阅 FlutterViewEngine 演示类或 FlutterActivityAndFragmentDelegate 中的其余实现,以确保剪贴板、系统 UI 叠加层、插件等其他功能正常工作。

按内容自适应尺寸的 View

#

通常,FlutterView 需要通过自身尺寸或匹配父级尺寸来固定大小,如 示例项目 所示。不过,现在可以让 FlutterView 根据内容自行确定尺寸:对高度或宽度使用 content_wrapFlutterView 即可自适应,如 按内容定尺寸示例项目 所示。

  • To enable Content-sized view when deploying your app, add the following setting to your project's AndroidManifest.xml file under the <application> tag:

  • 在部署应用时 启用 按内容自适应尺寸的 View,请在项目 AndroidManifest.xml<application> 标签下添加以下设置:

xml
<meta-data
  android:name="io.flutter.embedding.android.EnableContentSizing"
  android:value="true" />

限制

#

由于按内容定尺寸的 Flutter View 要求 Flutter 应用能够自行确定尺寸,部分 widget 不受支持。

  • A widget with unbounded size, like a ListView.

  • A widget that defers to its child for the size, like LayoutBuilder.

  • 尺寸无界的 widget,例如 ListView

  • 将尺寸委托给子 widget 的 widget,例如 LayoutBuilder

实践中,这意味着不少常用 widget 不受支持,例如 ScaffoldBuilderCupertinoTimerPicker,或任何内部依赖 LayoutBuilder 的 widget。如有疑问,可用 UnconstrainedBox 测试某 widget 是否适用于按内容定尺寸的 View,如下例所示:

dart
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context)
  => MaterialApp(home: MyPage());
}

class MyPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: UnconstrainedBox(
          // TODO: Edit this line to check if a widget
          // can cause problems with content-sized views.
          child: Text('This works!'),
          // child: Column(children: [Column(children: [Expanded(child: Text('This blows up!'))])]),
          // child: ListView(children: [Text('This blows up!')]),
        )
    );
  }
}